Add minetest.set_noiseparam_defaults() Lua API
[oweals/minetest.git] / src / util / serialize.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "serialize.h"
21 #include "pointer.h"
22 #include "porting.h"
23 #include "util/string.h"
24 #include "../exceptions.h"
25
26 #include <sstream>
27 #include <iomanip>
28 #include <vector>
29
30 // Creates a string with the length as the first two bytes
31 std::string serializeString(const std::string &plain)
32 {
33         //assert(plain.size() <= 65535);
34         if(plain.size() > 65535)
35                 throw SerializationError("String too long for serializeString");
36         char buf[2];
37         writeU16((u8*)&buf[0], plain.size());
38         std::string s;
39         s.append(buf, 2);
40         s.append(plain);
41         return s;
42 }
43
44 // Creates a string with the length as the first two bytes from wide string
45 std::string serializeWideString(const std::wstring &plain)
46 {
47         //assert(plain.size() <= 65535);
48         if(plain.size() > 65535)
49                 throw SerializationError("String too long for serializeString");
50         char buf[2];
51         writeU16((u8*)buf, plain.size());
52         std::string s;
53         s.append(buf, 2);
54         for(u32 i=0; i<plain.size(); i++)
55         {
56                 writeU16((u8*)buf, plain[i]);
57                 s.append(buf, 2);
58         }
59         return s;
60 }
61
62 // Reads a string with the length as the first two bytes
63 std::string deSerializeString(std::istream &is)
64 {
65         char buf[2];
66         is.read(buf, 2);
67         if(is.gcount() != 2)
68                 throw SerializationError("deSerializeString: size not read");
69         u16 s_size = readU16((u8*)buf);
70         if(s_size == 0)
71                 return "";
72         Buffer<char> buf2(s_size);
73         is.read(&buf2[0], s_size);
74         std::string s;
75         s.reserve(s_size);
76         s.append(&buf2[0], s_size);
77         return s;
78 }
79
80 // Reads a wide string with the length as the first two bytes
81 std::wstring deSerializeWideString(std::istream &is)
82 {
83         char buf[2];
84         is.read(buf, 2);
85         if(is.gcount() != 2)
86                 throw SerializationError("deSerializeString: size not read");
87         u16 s_size = readU16((u8*)buf);
88         if(s_size == 0)
89                 return L"";
90         std::wstring s;
91         s.reserve(s_size);
92         for(u32 i=0; i<s_size; i++)
93         {
94                 is.read(&buf[0], 2);
95                 wchar_t c16 = readU16((u8*)buf);
96                 s.append(&c16, 1);
97         }
98         return s;
99 }
100
101 // Creates a string with the length as the first four bytes
102 std::string serializeLongString(const std::string &plain)
103 {
104         char buf[4];
105         writeU32((u8*)&buf[0], plain.size());
106         std::string s;
107         s.append(buf, 4);
108         s.append(plain);
109         return s;
110 }
111
112 // Reads a string with the length as the first four bytes
113 std::string deSerializeLongString(std::istream &is)
114 {
115         char buf[4];
116         is.read(buf, 4);
117         if(is.gcount() != 4)
118                 throw SerializationError("deSerializeLongString: size not read");
119         u32 s_size = readU32((u8*)buf);
120         if(s_size == 0)
121                 return "";
122         Buffer<char> buf2(s_size);
123         is.read(&buf2[0], s_size);
124         std::string s;
125         s.reserve(s_size);
126         s.append(&buf2[0], s_size);
127         return s;
128 }
129
130 // Creates a string encoded in JSON format (almost equivalent to a C string literal)
131 std::string serializeJsonString(const std::string &plain)
132 {
133         std::ostringstream os(std::ios::binary);
134         os<<"\"";
135         for(size_t i = 0; i < plain.size(); i++)
136         {
137                 char c = plain[i];
138                 switch(c)
139                 {
140                         case '"': os<<"\\\""; break;
141                         case '\\': os<<"\\\\"; break;
142                         case '/': os<<"\\/"; break;
143                         case '\b': os<<"\\b"; break;
144                         case '\f': os<<"\\f"; break;
145                         case '\n': os<<"\\n"; break;
146                         case '\r': os<<"\\r"; break;
147                         case '\t': os<<"\\t"; break;
148                         default:
149                         {
150                                 if(c >= 32 && c <= 126)
151                                 {
152                                         os<<c;
153                                 }
154                                 else
155                                 {
156                                         u32 cnum = (u32) (u8) c;
157                                         os<<"\\u"<<std::hex<<std::setw(4)<<std::setfill('0')<<cnum;
158                                 }
159                                 break;
160                         }
161                 }
162         }
163         os<<"\"";
164         return os.str();
165 }
166
167 // Reads a string encoded in JSON format
168 std::string deSerializeJsonString(std::istream &is)
169 {
170         std::ostringstream os(std::ios::binary);
171         char c, c2;
172
173         // Parse initial doublequote
174         is >> c;
175         if(c != '"')
176                 throw SerializationError("JSON string must start with doublequote");
177
178         // Parse characters
179         for(;;)
180         {
181                 c = is.get();
182                 if(is.eof())
183                         throw SerializationError("JSON string ended prematurely");
184                 if(c == '"')
185                 {
186                         return os.str();
187                 }
188                 else if(c == '\\')
189                 {
190                         c2 = is.get();
191                         if(is.eof())
192                                 throw SerializationError("JSON string ended prematurely");
193                         switch(c2)
194                         {
195                                 default:  os<<c2; break;
196                                 case 'b': os<<'\b'; break;
197                                 case 'f': os<<'\f'; break;
198                                 case 'n': os<<'\n'; break;
199                                 case 'r': os<<'\r'; break;
200                                 case 't': os<<'\t'; break;
201                                 case 'u':
202                                 {
203                                         char hexdigits[4+1];
204                                         is.read(hexdigits, 4);
205                                         if(is.eof())
206                                                 throw SerializationError("JSON string ended prematurely");
207                                         hexdigits[4] = 0;
208                                         std::istringstream tmp_is(hexdigits, std::ios::binary);
209                                         int hexnumber;
210                                         tmp_is >> std::hex >> hexnumber;
211                                         os<<((char)hexnumber);
212                                         break;
213                                 }
214                         }
215                 }
216                 else
217                 {
218                         os<<c;
219                 }
220         }
221         return os.str();
222 }
223
224
225 bool deSerializeStringToStruct(std::string valstr,
226         std::string format, void *out, size_t olen)
227 {
228         size_t len = olen;
229         std::vector<std::string *> strs_alloced;
230         std::string *str;
231         char *f, *snext;
232         size_t pos;
233
234         char *s = &valstr[0];
235         char *buf = new char[len];
236         char *bufpos = buf;
237
238         char *fmtpos, *fmt = &format[0];
239         while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
240                 fmt = NULL;
241
242                 bool is_unsigned = false;
243                 int width = 0;
244                 char valtype = *f;
245
246                 width = (int)strtol(f + 1, &f, 10);
247                 if (width && valtype == 's')
248                         valtype = 'i';
249
250                 switch (valtype) {
251                         case 'u':
252                                 is_unsigned = true;
253                                 /* FALLTHROUGH */
254                         case 'i':
255                                 if (width == 16) {
256                                         bufpos += PADDING(bufpos, u16);
257                                         if ((bufpos - buf) + sizeof(u16) <= len) {
258                                                 if (is_unsigned)
259                                                         *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
260                                                 else
261                                                         *(s16 *)bufpos = (s16)strtol(s, &s, 10);
262                                         }
263                                         bufpos += sizeof(u16);
264                                 } else if (width == 32) {
265                                         bufpos += PADDING(bufpos, u32);
266                                         if ((bufpos - buf) + sizeof(u32) <= len) {
267                                                 if (is_unsigned)
268                                                         *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
269                                                 else
270                                                         *(s32 *)bufpos = (s32)strtol(s, &s, 10);
271                                         }
272                                         bufpos += sizeof(u32);
273                                 } else if (width == 64) {
274                                         bufpos += PADDING(bufpos, u64);
275                                         if ((bufpos - buf) + sizeof(u64) <= len) {
276                                                 if (is_unsigned)
277                                                         *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
278                                                 else
279                                                         *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
280                                         }
281                                         bufpos += sizeof(u64);
282                                 }
283                                 s = strchr(s, ',');
284                                 break;
285                         case 'b':
286                                 snext = strchr(s, ',');
287                                 if (snext)
288                                         *snext++ = 0;
289
290                                 bufpos += PADDING(bufpos, bool);
291                                 if ((bufpos - buf) + sizeof(bool) <= len)
292                                         *(bool *)bufpos = is_yes(std::string(s));
293                                 bufpos += sizeof(bool);
294
295                                 s = snext;
296                                 break;
297                         case 'f':
298                                 bufpos += PADDING(bufpos, float);
299                                 if ((bufpos - buf) + sizeof(float) <= len)
300                                         *(float *)bufpos = strtof(s, &s);
301                                 bufpos += sizeof(float);
302
303                                 s = strchr(s, ',');
304                                 break;
305                         case 's':
306                                 while (*s == ' ' || *s == '\t')
307                                         s++;
308                                 if (*s++ != '"') //error, expected string
309                                         goto fail;
310                                 snext = s;
311
312                                 while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
313                                         snext++;
314                                 *snext++ = 0;
315
316                                 bufpos += PADDING(bufpos, std::string *);
317
318                                 str = new std::string(s);
319                                 pos = 0;
320                                 while ((pos = str->find("\\\"", pos)) != std::string::npos)
321                                         str->erase(pos, 1);
322
323                                 if ((bufpos - buf) + sizeof(std::string *) <= len)
324                                         *(std::string **)bufpos = str;
325                                 bufpos += sizeof(std::string *);
326                                 strs_alloced.push_back(str);
327
328                                 s = *snext ? snext + 1 : NULL;
329                                 break;
330                         case 'v':
331                                 while (*s == ' ' || *s == '\t')
332                                         s++;
333                                 if (*s++ != '(') //error, expected vector
334                                         goto fail;
335
336                                 if (width == 2) {
337                                         bufpos += PADDING(bufpos, v2f);
338
339                                         if ((bufpos - buf) + sizeof(v2f) <= len) {
340                                         v2f *v = (v2f *)bufpos;
341                                                 v->X = strtof(s, &s);
342                                                 s++;
343                                                 v->Y = strtof(s, &s);
344                                         }
345
346                                         bufpos += sizeof(v2f);
347                                 } else if (width == 3) {
348                                         bufpos += PADDING(bufpos, v3f);
349                                         if ((bufpos - buf) + sizeof(v3f) <= len) {
350                                                 v3f *v = (v3f *)bufpos;
351                                                 v->X = strtof(s, &s);
352                                                 s++;
353                                                 v->Y = strtof(s, &s);
354                                                 s++;
355                                                 v->Z = strtof(s, &s);
356                                         }
357
358                                         bufpos += sizeof(v3f);
359                                 }
360                                 s = strchr(s, ',');
361                                 break;
362                         default: //error, invalid format specifier
363                                 goto fail;
364                 }
365
366                 if (s && *s == ',')
367                         s++;
368
369                 if ((size_t)(bufpos - buf) > len) //error, buffer too small
370                         goto fail;
371         }
372
373         if (f && *f) { //error, mismatched number of fields and values
374 fail:
375                 for (size_t i = 0; i != strs_alloced.size(); i++)
376                         delete strs_alloced[i];
377                 delete[] buf;
378                 return false;
379         }
380
381         memcpy(out, buf, olen);
382         delete[] buf;
383         return true;
384 }
385
386
387 bool serializeStructToString(std::string *outstr,
388         std::string format, void *value)
389 {
390         char sbuf[2048];
391         int sbuflen = sizeof(sbuf) - 1;
392         sbuf[sbuflen] = 0;
393         std::string str;
394         int pos = 0;
395         size_t fpos;
396         char *f;
397
398         char *bufpos = (char *)value;
399         char *fmtpos, *fmt = &format[0];
400         while ((f = strtok_r(fmt, ",", &fmtpos))) {
401                 fmt = NULL;
402                 bool is_unsigned = false;
403                 int width = 0, nprinted = 0;
404                 char valtype = *f;
405
406                 width = (int)strtol(f + 1, &f, 10);
407                 if (width && valtype == 's')
408                         valtype = 'i';
409
410                 switch (valtype) {
411                         case 'u':
412                                 is_unsigned = true;
413                                 /* FALLTHROUGH */
414                         case 'i':
415                                 if (width == 16) {
416                                         bufpos += PADDING(bufpos, u16);
417                                         nprinted = snprintf(sbuf + pos, sbuflen,
418                                                                 is_unsigned ? "%u, " : "%d, ",
419                                                                 *((u16 *)bufpos));
420                                         bufpos += sizeof(u16);
421                                 } else if (width == 32) {
422                                         bufpos += PADDING(bufpos, u32);
423                                         nprinted = snprintf(sbuf + pos, sbuflen,
424                                                                 is_unsigned ? "%u, " : "%d, ",
425                                                                 *((u32 *)bufpos));
426                                         bufpos += sizeof(u32);
427                                 } else if (width == 64) {
428                                         bufpos += PADDING(bufpos, u64);
429                                         nprinted = snprintf(sbuf + pos, sbuflen,
430                                                                 is_unsigned ? "%llu, " : "%lli, ",
431                                                                 (unsigned long long)*((u64 *)bufpos));
432                                         bufpos += sizeof(u64);
433                                 }
434                                 break;
435                         case 'b':
436                                 bufpos += PADDING(bufpos, bool);
437                                 nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
438                                                                         *((bool *)bufpos) ? "true" : "false");
439                                 bufpos += sizeof(bool);
440                                 break;
441                         case 'f':
442                                 bufpos += PADDING(bufpos, float);
443                                 nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
444                                                                         *((float *)bufpos));
445                                 bufpos += sizeof(float);
446                                 break;
447                         case 's':
448                                 bufpos += PADDING(bufpos, std::string *);
449                                 str = **((std::string **)bufpos);
450
451                                 fpos = 0;
452                                 while ((fpos = str.find('"', fpos)) != std::string::npos) {
453                                         str.insert(fpos, 1, '\\');
454                                         fpos += 2;
455                                 }
456
457                                 nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
458                                                                         (*((std::string **)bufpos))->c_str());
459                                 bufpos += sizeof(std::string *);
460                                 break;
461                         case 'v':
462                                 if (width == 2) {
463                                         bufpos += PADDING(bufpos, v2f);
464                                         v2f *v = (v2f *)bufpos;
465                                         nprinted = snprintf(sbuf + pos, sbuflen,
466                                                                                 "(%f, %f), ", v->X, v->Y);
467                                         bufpos += sizeof(v2f);
468                                 } else {
469                                         bufpos += PADDING(bufpos, v3f);
470                                         v3f *v = (v3f *)bufpos;
471                                         nprinted = snprintf(sbuf + pos, sbuflen,
472                                                                                 "(%f, %f, %f), ", v->X, v->Y, v->Z);
473                                         bufpos += sizeof(v3f);
474                                 }
475                                 break;
476                         default:
477                                 return false;
478                 }
479                 if (nprinted < 0) //error, buffer too small
480                         return false;
481                 pos     += nprinted;
482                 sbuflen -= nprinted;
483         }
484
485         // this is to trim off the trailing comma
486         if (pos >= 2)
487                 sbuf[pos - 2] = 0;
488
489         *outstr = sbuf;
490
491         return true;
492 }