123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- -- trickle v0.1.0 - Lua bitstream
- -- https://github.com/bjornbytes/trickle
- -- MIT License
- local trickle = {}
- local bit = bit32 or require 'bit'
- local lshift, rshift, band, bxor, bnot = bit.lshift, bit.rshift, bit.band, bit.bxor, bit.bnot
- local extract = bit.extract or function(x, i, n)
- return band(rshift(x, i), lshift(1, n or 1) - 1)
- end
- local replace = bit.replace or function(x, y, i, n)
- local mask = lshift(rshift(bit.bnot(0), 31 - (n - 1)), i)
- return bit.bxor(x, bit.band(bit.bxor(x, lshift(y, i)), mask))
- end
- function trickle.create(str)
- local stream = {
- str = str or '',
- byte = nil,
- byteLen = nil
- }
- return setmetatable(stream, trickle)
- end
- function trickle:truncate()
- if self.byte then
- self.str = self.str .. string.char(self.byte)
- self.byte = nil
- self.byteLen = nil
- end
- return self.str
- end
- function trickle:clear()
- self.str = ''
- self.byte = nil
- self.byteLen = nil
- return self
- end
- function trickle:write(x, sig)
- if sig == 'string' then self:writeString(x)
- elseif sig == 'bool' then self:writeBool(x)
- elseif sig == 'float' then self:writeFloat(x)
- else
- local n = sig:match('(%d+)bit')
- self:writeBits(x, n)
- end
- return self
- end
- function trickle:writeString(string)
- self:truncate()
- string = tostring(string)
- self.str = self.str .. string.char(#string) .. string
- end
- function trickle:writeBool(bool)
- local x = bool and 1 or 0
- self:writeBits(x, 1)
- end
- function trickle:writeFloat(float)
- self:writeString(float)
- end
- function trickle:writeBits(x, n)
- local idx = 0
- repeat
- if not self.byte then self.byte = 0 self.byteLen = 0 end
- local numWrite = math.min(n, (7 - self.byteLen) + 1)
- local toWrite = extract(x, idx, numWrite)
- self.byte = replace(self.byte, toWrite, self.byteLen, numWrite)
- self.byteLen = self.byteLen + numWrite
- if self.byteLen == 8 then
- self.str = self.str .. string.char(self.byte)
- self.byte = nil
- self.byteLen = nil
- end
- n = n - numWrite
- idx = idx + numWrite
- until n == 0
- end
- function trickle:read(kind)
- if kind == 'string' then return self:readString()
- elseif kind == 'bool' then return self:readBool()
- elseif kind == 'float' then return self:readFloat()
- else
- local n = tonumber(kind:match('(%d+)bit'))
- return self:readBits(n)
- end
- end
- function trickle:readString()
- if self.byte then
- self.str = self.str:sub(2)
- self.byte = nil
- self.byteLen = nil
- end
- local len = self.str:byte(1)
- local res = ''
- if len then
- self.str = self.str:sub(2)
- res = self.str:sub(1, len)
- self.str = self.str:sub(len + 1)
- end
- return res
- end
- function trickle:readBool()
- return self:readBits(1) > 0
- end
- function trickle:readFloat()
- return tonumber(self:readString())
- end
- function trickle:readBits(n)
- local x = 0
- local idx = 0
- while n > 0 do
- if not self.byte then self.byte = self.str:byte(1) or 0 self.byteLen = 0 end
- local numRead = math.min(n, (7 - self.byteLen) + 1)
- x = x + (extract(self.byte, self.byteLen, numRead) * (2 ^ idx))
- self.byteLen = self.byteLen + numRead
- if self.byteLen == 8 then
- self.str = self.str:sub(2)
- self.byte = nil
- self.byteLen = nil
- end
- n = n - numRead
- idx = idx + numRead
- end
- return x
- end
- function trickle:pack(data, signature)
- local keys
- if signature.delta then
- keys = {}
- for _, key in ipairs(signature.delta) do
- if type(key) == 'table' then
- local has = 0
- for i = 1, #key do
- if data[key[i]] ~= nil then
- keys[key[i]] = true
- has = has + 1
- else
- keys[key[i]] = false
- end
- end
- if has == 0 then self:write(0, '1bit')
- elseif has == #key then self:write(1, '1bit')
- else error('Only part of message delta group "' .. table.concat(key, ', ') .. '" was provided.') end
- else
- self:write(data[key] ~= nil and 1 or 0, '1bit')
- keys[key] = data[key] ~= nil and true or false
- end
- end
- end
- for _, sig in ipairs(signature) do
- if not keys or keys[sig[1]] ~= false then
- if type(sig[2]) == 'table' then
- self:write(#data[sig[1]], '4bits')
- for i = 1, #data[sig[1]] do self:pack(data[sig[1]][i], sig[2]) end
- else
- self:write(data[sig[1]], sig[2])
- end
- end
- end
- end
- function trickle:unpack(signature)
- local keys
- if signature.delta then
- keys = {}
- for i = 1, #signature.delta do
- local val = self:read('1bit') > 0
- if type(signature.delta[i]) == 'table' then
- for j = 1, #signature.delta[i] do keys[signature.delta[i][j]] = val end
- else
- keys[signature.delta[i]] = val
- end
- end
- end
- local data = {}
- for _, sig in ipairs(signature) do
- if not keys or keys[sig[1]] ~= false then
- if type(sig[2]) == 'table' then
- local ct = self:read('4bits')
- data[sig[1]] = {}
- for i = 1, ct do table.insert(data[sig[1]], self:unpack(sig[2])) end
- else
- data[sig[1]] = self:read(sig[2])
- end
- end
- end
- return data
- end
- trickle.__tostring = trickle.truncate
- trickle.__index = trickle
- return { create = trickle.create }
|