1 /*
2 LUA_SERIALIZE.CPP
3 
4 	Copyright (C) 2009 by Gregory Smith
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 	Serializes Lua objects
21 	Based on Pluto, but far less clever
22 */
23 
24 #include "lua_serialize.h"
25 #include "Logging.h"
26 
27 #include "BStream.h"
28 
29 const static int SAVED_REFERENCE_PSEUDOTYPE = -2;
30 const uint16 kVersion = 1;
31 
valid_key(int type)32 static bool valid_key(int type)
33 {
34 	return (type == LUA_TNUMBER ||
35 		type == LUA_TBOOLEAN ||
36 		type == LUA_TSTRING ||
37 		type == LUA_TTABLE ||
38 		type == LUA_TUSERDATA);
39 }
40 
save(lua_State * L,BOStreamBE & s,uint32 & counter)41 static void save(lua_State *L, BOStreamBE& s, uint32& counter)
42 {
43 	// if the object has already been written, write a reference to it
44 
45 	lua_pushvalue(L, -1);
46 	lua_rawget(L, 1);
47 	if (!lua_isnil(L, -1))
48 	{
49 		s << static_cast<int8>(SAVED_REFERENCE_PSEUDOTYPE)
50 		  << static_cast<uint32>(lua_tonumber(L, -1));
51 		lua_pop(L, 1);
52 		return;
53 	}
54 	lua_pop(L, 1);
55 
56 	s << static_cast<int8>(lua_type(L, -1));
57 	switch (lua_type(L, -1))
58 	{
59 		case LUA_TNIL:
60 			break;
61 		case LUA_TNUMBER:
62 			{
63 				s << static_cast<double>(lua_tonumber(L, -1));
64 			}
65 			break;
66 		case LUA_TBOOLEAN:
67 			s << static_cast<uint8>(lua_toboolean(L, -1) ? 1 : 0);
68 			break;
69 		case LUA_TSTRING:
70 			{
71 				s << static_cast<uint32>(lua_rawlen(L, -1));
72 				s.write(lua_tostring(L, -1), lua_rawlen(L, -1));
73 			}
74 			break;
75 		case LUA_TTABLE:
76 			{
77 				// add to the reference table
78 				lua_pushvalue(L, -1);
79 				lua_pushnumber(L, static_cast<lua_Number>(++counter));
80 				lua_rawset(L, 1);
81 
82 				// write the reference
83 				s << counter;
84 
85 				// write all k/v pairs
86 				lua_pushnil(L);
87 				while (lua_next(L, -2))
88 				{
89 					if (valid_key(lua_type(L, -2))) {
90 						// another key
91 						lua_pushvalue(L, -2);
92 
93 						save(L, s, counter);
94 						lua_pop(L, 1);
95 
96 						save(L, s, counter);
97 						lua_pop(L, 1);
98 					} else {
99 						lua_pop(L, 1);
100 					}
101 				}
102 
103 				lua_pushnil(L);
104 				save(L, s, counter);
105 				lua_pop(L, 1);
106 			}
107 			break;
108 		case LUA_TUSERDATA:
109 			{
110 				// add to the reference table
111 				lua_pushvalue(L, -1);
112 				lua_pushnumber(L, static_cast<lua_Number>(++counter));
113 				lua_rawset(L, 1);
114 
115 				// write the reference
116 				s << counter;
117 
118 				// assume that this is one of our userdata
119 				lua_getmetatable(L, -1);
120 				lua_gettable(L, LUA_REGISTRYINDEX);
121 
122 				s << static_cast<uint8>(lua_rawlen(L, -1));
123 				s.write(lua_tostring(L, -1), lua_rawlen(L, -1));
124 				lua_pop(L, 1);
125 
126 				lua_getfield(L, -1, "index");
127 
128 				s << static_cast<uint32>(lua_tonumber(L, -1));
129 				lua_pop(L, 1);
130 			}
131 			break;
132 
133 		default:
134 			// we silently ignore other types
135 			break;
136 	}
137 }
138 
lua_save(lua_State * L,std::streambuf * sb)139 bool lua_save(lua_State *L, std::streambuf* sb)
140 {
141 	lua_assert(lua_gettop(L) == 1);
142 
143 	// create a references table
144 	lua_newtable(L);
145 
146 	// put it at the bottom of the stack
147 	lua_insert(L, 1);
148 
149 	uint32 counter = 0;
150 	BOStreamBE s(sb);
151 	try
152 	{
153 		s << kVersion;
154 		save(L, s, counter);
155 	}
156 	catch (const basic_bstream::failure& e)
157 	{
158 		logWarning("failed to save Lua data; %s", e.what());
159 		lua_settop(L, 0);
160 		return false;
161 	}
162 
163 	// remove the reference table
164 	lua_remove(L, 1);
165 	return true;
166 }
167 
restore(lua_State * L,BIStreamBE & s)168 static int restore(lua_State *L, BIStreamBE& s)
169 {
170 	int8 type;
171 	s >> type;
172 
173 	switch (type)
174 	{
175 		case LUA_TNIL:
176 			lua_pushnil(L);
177 			break;
178 		case LUA_TBOOLEAN:
179 			uint8 b;
180 			s >> b;
181 			lua_pushboolean(L, b == 1);
182 			break;
183 		case LUA_TNUMBER:
184 			{
185 				double d;
186 				s >> d;
187 				lua_pushnumber(L, static_cast<lua_Number>(d));
188 			}
189 			break;
190 		case LUA_TSTRING:
191 			{
192 				uint32 length;
193 				s >> length;
194 				std::vector<char> v(length);
195 				s.read(&v[0], v.size());
196 				lua_pushlstring(L, &v[0], v.size());
197 			}
198 			break;
199 		case LUA_TTABLE:
200 			{
201 				uint32 reference;
202 				s >> reference;
203 
204 				// add to the reference table
205 				lua_newtable(L);
206 				lua_pushnumber(L, static_cast<lua_Number>(reference));
207 				lua_pushvalue(L, -2);
208 				lua_rawset(L, 1);
209 
210 				int key_type = restore(L, s);
211 				while (key_type != LUA_TNIL)
212 				{
213 					restore(L, s); // value
214 					if (lua_isnil(L, -2))
215 					{
216 						// maybe an invalid userdata?
217 						lua_pop(L, 2);
218 					}
219 					else
220 					{
221 						lua_rawset(L, -3);
222 					}
223 					key_type = restore(L, s); // next key
224 				}
225 				lua_pop(L, 1);
226 			}
227 			break;
228 		case LUA_TUSERDATA:
229 			{
230 				uint32 reference;
231 				s >> reference;
232 
233 				uint8 length;
234 				s >> length;
235 				std::vector<char> v(length);
236 				s.read(&v[0], v.size());
237 				lua_pushlstring(L, &v[0], v.size());
238 
239 				uint32 index;
240 				s >> index;
241 
242 				// get the metatable
243 				lua_gettable(L, LUA_REGISTRYINDEX);
244 				// get the accessor we added
245 				lua_getfield(L, -1, "__new");
246 				if (lua_isfunction(L, -1))
247 				{
248 					lua_pushnumber(L, static_cast<lua_Number>(index));
249 					lua_call(L, 1, 1);
250 				}
251 
252 				lua_remove(L, -2);
253 
254 				// add to the reference table
255 				lua_pushnumber(L, static_cast<lua_Number>(reference));
256 				lua_pushvalue(L, -2);
257 				lua_rawset(L, 1);
258 
259 			}
260 			break;
261 
262 		case SAVED_REFERENCE_PSEUDOTYPE:
263 			{
264 				uint32 index;
265 				s >> index;
266 				lua_pushnumber(L, static_cast<lua_Number>(index));
267 				lua_rawget(L, 1);
268 			}
269 			break;
270 		default:
271 			lua_pushnil(L);
272 			break;
273 	}
274 
275 	return type;
276 }
277 
lua_restore(lua_State * L,std::streambuf * sb)278 bool lua_restore(lua_State *L, std::streambuf* sb)
279 {
280 	// create a reference table
281 	lua_newtable(L);
282 
283         // put it at the bottom of the stack
284         lua_insert(L, 1);
285 
286 	BIStreamBE s(sb);
287 	try {
288 		int16 version;
289 		s >> version;
290 		if (version > kVersion)
291 		{
292 			logWarning("failed to restore Lua data; saved data is newer version");
293 			return false;
294 		}
295 
296 		restore(L, s);
297 	}
298 	catch (const basic_bstream::failure& e)
299 	{
300 		logWarning("failed to restore Lua data; %s", e.what());
301 		lua_settop(L, 0);
302 		return false;
303 	}
304 
305 	// remove the reference table
306 	lua_remove(L, 1);
307 	return true;
308 }
309