Skip to content

Instantly share code, notes, and snippets.

@badcc
Created August 27, 2016 23:50
Show Gist options
  • Save badcc/4c264f64e706944262d66406477f98d1 to your computer and use it in GitHub Desktop.
Save badcc/4c264f64e706944262d66406477f98d1 to your computer and use it in GitHub Desktop.
-- `*` prefix means list.
-- e.g. `*ui6` would be a list of 6 bit wide unsigned integers.
local Schema = [[
Version ui
Thing *i
Items *s
Test i4
pi f64
e f32
]]
-- This is the table encoded in the Base64 string below.
local Data = {
Version = 2,
Thing = { 1, 2, 3, 5, 100 },
Items = { 'hey', 'hello', 'test' },
Test = -2,
pi = 3.14159,
e = 2.71828
}
local B = b.MakeBuffer()
B.Schema = Schema
B:FromSchema(Data)
print(B:ToBase64())
B:FromBase64('CHdYhFf0HyiLTR8AQLrY0BuTL/YwH8n6mhbAf+hkFgNhfrCA')
local Data = B:ToSchema()
print(Data.Items[1], Data.Items[2], Data.Items[3])
print(Data.Version, Data.Test, Data.pi, Data.e)
-- FastBuffer
-- @BadccVoid
-- Tell me if you have optimizations!
local pow,log,ceil,floor=math.pow,math.log,math.ceil,math.floor
local insert,concat=table.insert,table.concat
local char,byte=string.char,string.byte
local abs=math.abs
local frexp,ldexp=math.frexp,math.ldexp
local log2=log(2)
local ntb,btn do
ntb,btn={},{}
local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for i = 1,#b do
local j=b:sub(i,i)
ntb[i-1],btn[j]=j,i-1
end
end
local p2m do
p2m = {}
for i = 0,64 do
p2m[i]=2^i
end
end
local b = {}
b.__index = b
function b.MakeBuffer()
return setmetatable({
d = {},
i = 0
}, b)
end
local function NextPower(n)
if (n==0) then return 1 end
for i=0,64 do
if (n<p2m[i]) then return i end
end
end
local function NextPowerOf2(n)
return p2m[NextPower(n)]
end
local function Write(b,v)
local i=b.i+1
b.i=i
b.d[i]=v
end
local function Read(b)
local i=b.i+1
b.i=i
return b.d[i]
end
function b:WriteBool(v)
Write(self,v and 1 or 0)
return true
end
function b:ReadBool(v)
return Read(self) == 1
end
function b:WriteUnsignedW(w,v)
assert(v>=0,v..' signed') -- Unsigned
assert(floor(v)==v,v..' not integer') -- Integer
assert(v<=p2m[w],v..' too big')
for i=1,w do
Write(self,v%2)
v=floor(v*0.5)
end
return true
end
function b:ReadUnsignedW(w)
local r=0
for i=1,w do
r=r+p2m[i-1]*Read(self)
end
return r
end
function b:WriteUnsigned(v)
local w=NextPower(v)
self:WriteUnsignedW(5,w)
self:WriteUnsignedW(w,v)
return true
end
function b:ReadUnsigned()
local w=self:ReadUnsignedW(5)
return self:ReadUnsignedW(w)
end
function b:WriteSignedW(w,v)
self:WriteBool(v>=0)
return self:WriteUnsignedW(w-1,abs(v))
end
function b:ReadSignedW(w)
local s=self:ReadBool() and 1 or -1
return self:ReadUnsignedW(w-1)*s
end
function b:WriteSigned(v)
self:WriteBool(v>=0)
return self:WriteUnsigned(abs(v))
end
function b:ReadSigned()
local s=self:ReadBool() and 1 or -1
return self:ReadUnsigned()*s
end
function b:WriteFloatW(wm,we,f)
local s=f>=0
self:WriteBool(s)
if (not s) then f=-f end
local m,e=frexp(f)
m=(m-0.5)/0.5*p2m[wm]
m=floor(m+0.5)
self:WriteUnsignedW(wm,m)
return self:WriteSignedW(we,e)
end
function b:ReadFloatW(wm,we)
local s=self:ReadBool() and 1 or -1
local m,e=self:ReadUnsignedW(wm),self:ReadSignedW(we)
m=m/p2m[wm]*0.5+0.5
return s*ldexp(m,e)
end
function b:WriteFloat32(f)
return self:WriteFloatW(23,8,f)
end
function b:ReadFloat32()
return self:ReadFloatW(23,8)
end
function b:WriteFloat64(f)
return self:WriteFloatW(52,11,f)
end
function b:ReadFloat64()
return self:ReadFloatW(52,11)
end
function b:WriteString(s)
local mi,ma
for i=1,#s do
local c=byte(s:sub(i,i))
if (not mi or c<mi) then mi=c end
if (not ma or c>ma) then ma=c end
end
-- min,range,n,bytes[]
self:WriteUnsignedW(7,mi)
self:WriteUnsignedW(7,ma-mi)
self:WriteUnsigned(#s)
-- Optimial case: Minimize range and minimum
local w=NextPower(ma-mi)
for i=1,#s do
local c=s:sub(i,i):byte()-mi
self:WriteUnsignedW(w,c)
end
return true
end
function b:ReadString()
local mi=self:ReadUnsignedW(7)
local r=self:ReadUnsignedW(7)
local l=self:ReadUnsigned()
local w=NextPower(r)
local s={}
for i=1,l do
local u=self:ReadUnsignedW(w)
local c=u+mi
insert(s,char(c))
end
return concat(s)
end
function b:ToBase64()
local t={}
local a,i,d=0,self.i,self.d
for j=1,i do
local jm6=(j-1)%6 -- 2^6=64
a=a+p2m[jm6]*d[j]
if (jm6==5 or j==i) then
insert(t,ntb[a])
a=0
end
end
return concat(t)
end
function b:FromBase64(s)
self.i=0
self.d={}
for i=1,#s do
local c=btn[s:sub(i,i)]
for j=1,6 do
Write(self,c%2)
c=floor(c*0.5)
end
end
self.i=0
end
function SchemaAction(self,t,w,v)
local a=v and 'Write' or 'Read'
if (t=='ui') then
return w and self[a..'UnsignedW'](self,w,v) or self[a..'Unsigned'](self,v)
elseif (t=='i') then
return w and self[a..'SignedW'](self,w,v) or self[a..'Signed'](self,v)
elseif (t=='f') then
assert(w==32 or w==64, 'invalid float width')
return self[a..'Float'..w](self,v)
elseif (t=='s') then
return self[a..'String'](self,v)
elseif (t:sub(1,1)=='*') then
local l = not v and {}
if (v) then self['WriteUnsigned'](self,#v) end
for i=1,v and #v or self[a..'Unsigned'](self) do
local r=SchemaAction(self,t:sub(2),w,v and v[i])
if (not v) then insert(l,r) end
end
return l
end
end
function b:FromSchema(Data)
for n,t,w in self.Schema:gmatch('(%w+)%s*([%*%a]+)(%d*)') do
w=tonumber(w)
local v=Data[n]
SchemaAction(self,t,w,v)
end
end
function b:ToSchema()
local d={}
for n,t,w in self.Schema:gmatch('(%w+)%s*([%*%a]+)(%d*)') do
w=tonumber(w)
d[n]=SchemaAction(self,t,w)
end
return d
end
function b.TrimSchema(Schema)
return Schema:gsub('%s*(%w+)%s*([^\n]+)','%1 %2 '):sub(0,-2)
end
function b:Dump()
local s = ''
for i=1,self.i do
-- Print MSB
s=tostring(self.d[i])..s
if (i%8==0 and i~=self.i) then s=' '..s end
end
print(s)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment