I made a table serializer/saver

Showcase your libraries, tools and other projects that help your fellow love users.
Post Reply
User avatar
markgo
Party member
Posts: 190
Joined: Sat Jan 05, 2013 12:21 am
Location: USA

I made a table serializer/saver

Post by markgo »

Was bored and wanted to play around with saving tables. Here is my serializer! It supports cycles/self references (can be disabled to be faster). It doesn't support functions/userdata/metatables. It pretty prints with optional aphanumerical ordering (faster if disabled).

WARNING: Not friendly with large tables (10,000+ entries)

Code: Select all

--[[
serialize a table

allowed key type   : number/string/boolean
allowed value type : number/string/boolean/table
does not handle    : metatables

cycles/references are allowed

syntax 
======

	string = table.serialize(table,norefs,dontsort)
	loadstring(string)()

parameters      value       desc
==========      =====       ====
norefs          boolean     don't save references (faster)
dontsort        boolean     don't prettify/arrange alphabetically (faster)
--]]

local format  = string.format
local concat  = table.concat
local type    = type
local tostring= tostring
local sort    = table.sort

local allowed_key_type = {
	number  = true,
	string  = true,
	boolean = true,
}

local allowed_value_type = {
	number = true,
	string = true,
	boolean= true,
	table  = true,
}

local varname = 't'

local serialformat = [[
local %s = 
{%s
}
%s
return %s
]]

function table.serialize(t,norefs,dontsort)
	local stack         = {len = 0}
	local references    = {[t]= varname}
	local assign_refs   = {len = 0}
	local dorefs        = not norefs
	local dosort        = not dontsort
	
	local lastkey,chunks
	while true do
		t,chunks,lastkey = t,chunks or {len = 0},lastkey or nil
		local indent     = string.rep('\t',stack.len+1)
		
		local dosubtable
		for k,v in next,t,lastkey do
			local ktype = type(k)
			local vtype = type(v)
			if allowed_key_type[ktype] and allowed_value_type[vtype] then
				
				
				local formattedv = v
				if vtype == 'boolean' then
					formattedv = tostring(v)
				elseif vtype == 'string' then
					formattedv = format('%q',v)
				end
				
				
				
				local formattedk
				if ktype ~= 'string' then
					formattedk = '['..tostring(k)..']'
				else
					formattedk = format('[%q]',k)
				end
				
				
				if vtype ~= 'table' then
					chunks.len = chunks.len+1
					chunks[chunks.len] = format('\n%s%s = %s,',indent,formattedk,formattedv)
				else
				
					local fullindices
					if dorefs then
						local formattedi = {len = 0}
						local refkey
						for i = 1,stack.len do
							local s                   = stack[i]
							formattedi.len            = formattedi.len+1
							formattedi[formattedi.len]= s.formattedk
						end
						formattedi.len            = formattedi.len+1
						formattedi[formattedi.len]= formattedk
						fullindices               = concat(formattedi)
					end
				
				
					if not references[v] then
						chunks.len = chunks.len+1
						stack.len = stack.len + 1
						stack[stack.len] = {
							t         = t,
							chunks    = chunks,
							lastkey   = k,
							keytype   = ktype,
							formattedk= formattedk,
							}
						
						chunks[chunks.len]= format('\n%s%s = ',indent,formattedk)
						
						t,chunks,lastkey  = v,{len= 0},nil
						
						dosubtable        = true
					
						references[v]     = dorefs and format('%s%s',varname,fullindices or '') or true
					
						break
					elseif dorefs then
						assign_refs.len             = assign_refs.len+1
						assign_refs[assign_refs.len]= format('%s%s = %s',varname,fullindices,references[v])
					end
				end
				
				
			end
		end
		
		if not dosubtable then
			local prevchunks = stack[stack.len] and stack[stack.len].chunks
			if dosort then sort(chunks) end
			local stringpairs = concat(chunks)
			if prevchunks then
			
			
				local indent              = string.rep('\t',stack.len)
				local prevchunk           = prevchunks[prevchunks.len]
				prevchunks[prevchunks.len]= prevchunk .. format('\n%s{%s\n%s},',indent,stringpairs,indent)
				
				local s                   = stack[stack.len]
				
				t,chunks,lastkey          = s.t,s.chunks,s.lastkey
				stack.len                 = stack.len - 1
				
				
				
			else
				if dosort then sort(assign_refs) end
				local fullrefs  = concat(assign_refs,'\n')
				return format(serialformat,varname,stringpairs,fullrefs,varname)
			end
		end
	end
end
output test:

Code: Select all

t              = {1,true,'string';  one= 1,[true]= true,string= 'string'}
t.t            = t
t.t2           = {1,true,'string';  one= 1,[true]= true,string= 'string'}
t.t2.t2        = t.t2
t.t2.t         = t
t._t2          = t.t2
t['line\ntest']= 'test'

t = loadstring( table.serialize(t) )()
--=======================================================
--[[
local t = 
{
	["line\
test"] = "test",
	["one"] = 1,
	["string"] = "string",
	["t2"] = 
	{
		["one"] = 1,
		["string"] = "string",
		[1] = 1,
		[2] = true,
		[3] = "string",
		[true] = true,
	},
	[1] = 1,
	[2] = true,
	[3] = "string",
	[true] = true,
}
t["_t2"] = t["t2"]
t["t"] = t
t["t2"]["t"] = t
t["t2"]["t2"] = t["t2"]
return t
--]]
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest