Merge 0.10->trunk
[prosody.git] / util / serialization.lua
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 --
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
8
9 local string_rep = string.rep;
10 local type = type;
11 local tostring = tostring;
12 local t_insert = table.insert;
13 local t_concat = table.concat;
14 local pairs = pairs;
15 local next = next;
16
17 local pcall = pcall;
18
19 local debug_traceback = debug.traceback;
20 local log = require "util.logger".init("serialization");
21 local envload = require"util.envload".envload;
22
23 local _ENV = nil;
24
25 local indent = function(i)
26         return string_rep("\t", i);
27 end
28 local function basicSerialize (o)
29         if type(o) == "number" or type(o) == "boolean" then
30                 -- no need to check for NaN, as that's not a valid table index
31                 if o == 1/0 then return "(1/0)";
32                 elseif o == -1/0 then return "(-1/0)";
33                 else return tostring(o); end
34         else -- assume it is a string -- FIXME make sure it's a string. throw an error otherwise.
35                 return (("%q"):format(tostring(o)):gsub("\\\n", "\\n"));
36         end
37 end
38 local function _simplesave(o, ind, t, func)
39         if type(o) == "number" then
40                 if o ~= o then func(t, "(0/0)");
41                 elseif o == 1/0 then func(t, "(1/0)");
42                 elseif o == -1/0 then func(t, "(-1/0)");
43                 else func(t, tostring(o)); end
44         elseif type(o) == "string" then
45                 func(t, (("%q"):format(o):gsub("\\\n", "\\n")));
46         elseif type(o) == "table" then
47                 if next(o) ~= nil then
48                         func(t, "{\n");
49                         for k,v in pairs(o) do
50                                 func(t, indent(ind));
51                                 func(t, "[");
52                                 func(t, basicSerialize(k));
53                                 func(t, "] = ");
54                                 if ind == 0 then
55                                         _simplesave(v, 0, t, func);
56                                 else
57                                         _simplesave(v, ind+1, t, func);
58                                 end
59                                 func(t, ";\n");
60                         end
61                         func(t, indent(ind-1));
62                         func(t, "}");
63                 else
64                         func(t, "{}");
65                 end
66         elseif type(o) == "boolean" then
67                 func(t, (o and "true" or "false"));
68         else
69                 log("error", "cannot serialize a %s: %s", type(o), debug_traceback())
70                 func(t, "nil");
71         end
72 end
73
74 local function append(t, o)
75         _simplesave(o, 1, t, t.write or t_insert);
76         return t;
77 end
78
79 local function serialize(o)
80         return t_concat(append({}, o));
81 end
82
83 local function deserialize(str)
84         if type(str) ~= "string" then return nil; end
85         str = "return "..str;
86         local f, err = envload(str, "@data", {});
87         if not f then return nil, err; end
88         local success, ret = pcall(f);
89         if not success then return nil, ret; end
90         return ret;
91 end
92
93 return {
94         append = append;
95         serialize = serialize;
96         deserialize = deserialize;
97 };