+
+Encoder = util.class()
+
+--- Creates a new Encoder.
+-- @param data Data to be encoded.
+-- @param buffersize Buffersize of returned data.
+-- @param fastescape Use non-standard escaping (don't escape control chars)
+function Encoder.__init__(self, data, buffersize, fastescape)
+ self.data = data
+ self.buffersize = buffersize or 512
+ self.buffer = ""
+ self.fastescape = fastescape
+
+ getmetatable(self).__call = Encoder.source
+end
+
+--- Create an LTN12 source from the encoder object
+-- @return LTN12 source
+function Encoder.source(self)
+ local source = coroutine.create(self.dispatch)
+ return function()
+ local res, data = coroutine.resume(source, self, self.data, true)
+ if res then
+ return data
+ else
+ return nil, data
+ end
+ end
+end
+
+function Encoder.dispatch(self, data, start)
+ local parser = self.parsers[type(data)]
+
+ parser(self, data)
+
+ if start then
+ if #self.buffer > 0 then
+ coroutine.yield(self.buffer)
+ end
+
+ coroutine.yield()
+ end
+end
+
+function Encoder.put(self, chunk)
+ if self.buffersize < 2 then
+ corountine.yield(chunk)
+ else
+ if #self.buffer + #chunk > self.buffersize then
+ local written = 0
+ local fbuffer = self.buffersize - #self.buffer
+
+ coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
+ written = fbuffer
+
+ while #chunk - written > self.buffersize do
+ fbuffer = written + self.buffersize
+ coroutine.yield(chunk:sub(written + 1, fbuffer))
+ written = fbuffer
+ end
+
+ self.buffer = chunk:sub(written + 1)
+ else
+ self.buffer = self.buffer .. chunk
+ end
+ end
+end
+
+function Encoder.parse_nil(self)
+ self:put("null")
+end
+
+function Encoder.parse_bool(self, obj)
+ self:put(obj and "true" or "false")
+end
+
+function Encoder.parse_number(self, obj)
+ self:put(tostring(obj))
+end
+
+function Encoder.parse_string(self, obj)
+ if self.fastescape then
+ self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
+ else
+ self:put('"' ..
+ obj:gsub('[%c\\"]',
+ function(char)
+ return '\\u00%02x' % char:byte()
+ end
+ )
+ .. '"')
+ end
+end
+
+function Encoder.parse_iter(self, obj)
+ if obj == null then
+ return self:put("null")
+ end
+
+ if type(obj) == "table" and (#obj == 0 and next(obj)) then
+ self:put("{")
+ local first = true
+
+ for key, entry in pairs(obj) do
+ first = first or self:put(",")
+ first = first and false
+ self:parse_string(tostring(key))
+ self:put(":")
+ self:dispatch(entry)
+ end
+
+ self:put("}")
+ else
+ self:put("[")
+ local first = true
+
+ if type(obj) == "table" then
+ for i, entry in pairs(obj) do
+ first = first or self:put(",")
+ first = first and nil
+ self:dispatch(entry)
+ end
+ else
+ for entry in obj do
+ first = first or self:put(",")
+ first = first and nil
+ self:dispatch(entry)
+ end
+ end
+
+ self:put("]")
+ end
+end
+
+Encoder.parsers = {
+ ['nil'] = Encoder.parse_nil,
+ ['table'] = Encoder.parse_iter,
+ ['number'] = Encoder.parse_number,
+ ['string'] = Encoder.parse_string,
+ ['boolean'] = Encoder.parse_bool,
+ ['function'] = Encoder.parse_iter
+}
+
+
+