]> git.lizzy.rs Git - minetest.git/blob - src/util/serialize.cpp
Rollback: Fail on bad precondition instead of causing assertion error
[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 #include "../irrlichttypes.h"
26
27 #include <sstream>
28 #include <iomanip>
29 #include <vector>
30
31 ////
32 //// String
33 ////
34
35 std::string serializeString(const std::string &plain)
36 {
37         std::string s;
38         char buf[2];
39
40         if (plain.size() > STRING_MAX_LEN)
41                 throw SerializationError("String too long for serializeString");
42
43         writeU16((u8 *)&buf[0], plain.size());
44         s.append(buf, 2);
45
46         s.append(plain);
47         return s;
48 }
49
50 std::string deSerializeString(std::istream &is)
51 {
52         std::string s;
53         char buf[2];
54
55         is.read(buf, 2);
56         if (is.gcount() != 2)
57                 throw SerializationError("deSerializeString: size not read");
58
59         u16 s_size = readU16((u8 *)buf);
60         if (s_size == 0)
61                 return s;
62
63         Buffer<char> buf2(s_size);
64         is.read(&buf2[0], s_size);
65         if (is.gcount() != s_size)
66                 throw SerializationError("deSerializeString: couldn't read all chars");
67
68         s.reserve(s_size);
69         s.append(&buf2[0], s_size);
70         return s;
71 }
72
73 ////
74 //// Wide String
75 ////
76
77 std::string serializeWideString(const std::wstring &plain)
78 {
79         std::string s;
80         char buf[2];
81
82         if (plain.size() > WIDE_STRING_MAX_LEN)
83                 throw SerializationError("String too long for serializeWideString");
84
85         writeU16((u8 *)buf, plain.size());
86         s.append(buf, 2);
87
88         for (u32 i = 0; i < plain.size(); i++) {
89                 writeU16((u8 *)buf, plain[i]);
90                 s.append(buf, 2);
91         }
92         return s;
93 }
94
95 std::wstring deSerializeWideString(std::istream &is)
96 {
97         std::wstring s;
98         char buf[2];
99
100         is.read(buf, 2);
101         if (is.gcount() != 2)
102                 throw SerializationError("deSerializeWideString: size not read");
103
104         u16 s_size = readU16((u8 *)buf);
105         if (s_size == 0)
106                 return s;
107
108         s.reserve(s_size);
109         for (u32 i = 0; i < s_size; i++) {
110                 is.read(&buf[0], 2);
111                 if (is.gcount() != 2) {
112                         throw SerializationError(
113                                 "deSerializeWideString: couldn't read all chars");
114                 }
115
116                 wchar_t c16 = readU16((u8 *)buf);
117                 s.append(&c16, 1);
118         }
119         return s;
120 }
121
122 ////
123 //// Long String
124 ////
125
126 std::string serializeLongString(const std::string &plain)
127 {
128         char buf[4];
129
130         if (plain.size() > LONG_STRING_MAX_LEN)
131                 throw SerializationError("String too long for serializeLongString");
132
133         writeU32((u8*)&buf[0], plain.size());
134         std::string s;
135         s.append(buf, 4);
136         s.append(plain);
137         return s;
138 }
139
140 std::string deSerializeLongString(std::istream &is)
141 {
142         std::string s;
143         char buf[4];
144
145         is.read(buf, 4);
146         if (is.gcount() != 4)
147                 throw SerializationError("deSerializeLongString: size not read");
148
149         u32 s_size = readU32((u8 *)buf);
150         if (s_size == 0)
151                 return s;
152
153         // We don't really want a remote attacker to force us to allocate 4GB...
154         if (s_size > LONG_STRING_MAX_LEN) {
155                 throw SerializationError("deSerializeLongString: "
156                         "string too long: " + itos(s_size) + " bytes");
157         }
158
159         Buffer<char> buf2(s_size);
160         is.read(&buf2[0], s_size);
161         if (is.gcount() != s_size)
162                 throw SerializationError("deSerializeLongString: couldn't read all chars");
163
164         s.reserve(s_size);
165         s.append(&buf2[0], s_size);
166         return s;
167 }
168
169 ////
170 //// JSON
171 ////
172
173 std::string serializeJsonString(const std::string &plain)
174 {
175         std::ostringstream os(std::ios::binary);
176         os << "\"";
177
178         for (size_t i = 0; i < plain.size(); i++) {
179                 char c = plain[i];
180                 switch (c) {
181                         case '"':
182                                 os << "\\\"";
183                                 break;
184                         case '\\':
185                                 os << "\\\\";
186                                 break;
187                         case '/':
188                                 os << "\\/";
189                                 break;
190                         case '\b':
191                                 os << "\\b";
192                                 break;
193                         case '\f':
194                                 os << "\\f";
195                                 break;
196                         case '\n':
197                                 os << "\\n";
198                                 break;
199                         case '\r':
200                                 os << "\\r";
201                                 break;
202                         case '\t':
203                                 os << "\\t";
204                                 break;
205                         default: {
206                                 if (c >= 32 && c <= 126) {
207                                         os << c;
208                                 } else {
209                                         u32 cnum = (u8)c;
210                                         os << "\\u" << std::hex << std::setw(4)
211                                                 << std::setfill('0') << cnum;
212                                 }
213                                 break;
214                         }
215                 }
216         }
217
218         os << "\"";
219         return os.str();
220 }
221
222 std::string deSerializeJsonString(std::istream &is)
223 {
224         std::ostringstream os(std::ios::binary);
225         char c, c2;
226
227         // Parse initial doublequote
228         is >> c;
229         if (c != '"')
230                 throw SerializationError("JSON string must start with doublequote");
231
232         // Parse characters
233         for (;;) {
234                 c = is.get();
235                 if (is.eof())
236                         throw SerializationError("JSON string ended prematurely");
237
238                 if (c == '"') {
239                         return os.str();
240                 } else if (c == '\\') {
241                         c2 = is.get();
242                         if (is.eof())
243                                 throw SerializationError("JSON string ended prematurely");
244                         switch (c2) {
245                                 case 'b':
246                                         os << '\b';
247                                         break;
248                                 case 'f':
249                                         os << '\f';
250                                         break;
251                                 case 'n':
252                                         os << '\n';
253                                         break;
254                                 case 'r':
255                                         os << '\r';
256                                         break;
257                                 case 't':
258                                         os << '\t';
259                                         break;
260                                 case 'u': {
261                                         int hexnumber;
262                                         char hexdigits[4 + 1];
263
264                                         is.read(hexdigits, 4);
265                                         if (is.eof())
266                                                 throw SerializationError("JSON string ended prematurely");
267                                         hexdigits[4] = 0;
268
269                                         std::istringstream tmp_is(hexdigits, std::ios::binary);
270                                         tmp_is >> std::hex >> hexnumber;
271                                         os << (char)hexnumber;
272                                         break;
273                                 }
274                                 default:
275                                         os << c2;
276                                         break;
277                         }
278                 } else {
279                         os << c;
280                 }
281         }
282
283         return os.str();
284 }
285
286 ////
287 //// String/Struct conversions
288 ////
289
290 bool deSerializeStringToStruct(std::string valstr,
291         std::string format, void *out, size_t olen)
292 {
293         size_t len = olen;
294         std::vector<std::string *> strs_alloced;
295         std::string *str;
296         char *f, *snext;
297         size_t pos;
298
299         char *s = &valstr[0];
300         char *buf = new char[len];
301         char *bufpos = buf;
302
303         char *fmtpos, *fmt = &format[0];
304         while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
305                 fmt = NULL;
306
307                 bool is_unsigned = false;
308                 int width = 0;
309                 char valtype = *f;
310
311                 width = (int)strtol(f + 1, &f, 10);
312                 if (width && valtype == 's')
313                         valtype = 'i';
314
315                 switch (valtype) {
316                         case 'u':
317                                 is_unsigned = true;
318                                 /* FALLTHROUGH */
319                         case 'i':
320                                 if (width == 16) {
321                                         bufpos += PADDING(bufpos, u16);
322                                         if ((bufpos - buf) + sizeof(u16) <= len) {
323                                                 if (is_unsigned)
324                                                         *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
325                                                 else
326                                                         *(s16 *)bufpos = (s16)strtol(s, &s, 10);
327                                         }
328                                         bufpos += sizeof(u16);
329                                 } else if (width == 32) {
330                                         bufpos += PADDING(bufpos, u32);
331                                         if ((bufpos - buf) + sizeof(u32) <= len) {
332                                                 if (is_unsigned)
333                                                         *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
334                                                 else
335                                                         *(s32 *)bufpos = (s32)strtol(s, &s, 10);
336                                         }
337                                         bufpos += sizeof(u32);
338                                 } else if (width == 64) {
339                                         bufpos += PADDING(bufpos, u64);
340                                         if ((bufpos - buf) + sizeof(u64) <= len) {
341                                                 if (is_unsigned)
342                                                         *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
343                                                 else
344                                                         *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
345                                         }
346                                         bufpos += sizeof(u64);
347                                 }
348                                 s = strchr(s, ',');
349                                 break;
350                         case 'b':
351                                 snext = strchr(s, ',');
352                                 if (snext)
353                                         *snext++ = 0;
354
355                                 bufpos += PADDING(bufpos, bool);
356                                 if ((bufpos - buf) + sizeof(bool) <= len)
357                                         *(bool *)bufpos = is_yes(std::string(s));
358                                 bufpos += sizeof(bool);
359
360                                 s = snext;
361                                 break;
362                         case 'f':
363                                 bufpos += PADDING(bufpos, float);
364                                 if ((bufpos - buf) + sizeof(float) <= len)
365                                         *(float *)bufpos = strtof(s, &s);
366                                 bufpos += sizeof(float);
367
368                                 s = strchr(s, ',');
369                                 break;
370                         case 's':
371                                 while (*s == ' ' || *s == '\t')
372                                         s++;
373                                 if (*s++ != '"') //error, expected string
374                                         goto fail;
375                                 snext = s;
376
377                                 while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
378                                         snext++;
379                                 *snext++ = 0;
380
381                                 bufpos += PADDING(bufpos, std::string *);
382
383                                 str = new std::string(s);
384                                 pos = 0;
385                                 while ((pos = str->find("\\\"", pos)) != std::string::npos)
386                                         str->erase(pos, 1);
387
388                                 if ((bufpos - buf) + sizeof(std::string *) <= len)
389                                         *(std::string **)bufpos = str;
390                                 bufpos += sizeof(std::string *);
391                                 strs_alloced.push_back(str);
392
393                                 s = *snext ? snext + 1 : NULL;
394                                 break;
395                         case 'v':
396                                 while (*s == ' ' || *s == '\t')
397                                         s++;
398                                 if (*s++ != '(') //error, expected vector
399                                         goto fail;
400
401                                 if (width == 2) {
402                                         bufpos += PADDING(bufpos, v2f);
403
404                                         if ((bufpos - buf) + sizeof(v2f) <= len) {
405                                         v2f *v = (v2f *)bufpos;
406                                                 v->X = strtof(s, &s);
407                                                 s++;
408                                                 v->Y = strtof(s, &s);
409                                         }
410
411                                         bufpos += sizeof(v2f);
412                                 } else if (width == 3) {
413                                         bufpos += PADDING(bufpos, v3f);
414                                         if ((bufpos - buf) + sizeof(v3f) <= len) {
415                                                 v3f *v = (v3f *)bufpos;
416                                                 v->X = strtof(s, &s);
417                                                 s++;
418                                                 v->Y = strtof(s, &s);
419                                                 s++;
420                                                 v->Z = strtof(s, &s);
421                                         }
422
423                                         bufpos += sizeof(v3f);
424                                 }
425                                 s = strchr(s, ',');
426                                 break;
427                         default: //error, invalid format specifier
428                                 goto fail;
429                 }
430
431                 if (s && *s == ',')
432                         s++;
433
434                 if ((size_t)(bufpos - buf) > len) //error, buffer too small
435                         goto fail;
436         }
437
438         if (f && *f) { //error, mismatched number of fields and values
439 fail:
440                 for (size_t i = 0; i != strs_alloced.size(); i++)
441                         delete strs_alloced[i];
442                 delete[] buf;
443                 return false;
444         }
445
446         memcpy(out, buf, olen);
447         delete[] buf;
448         return true;
449 }
450
451 // Casts *buf to a signed or unsigned fixed-width integer of 'w' width
452 #define SIGN_CAST(w, buf) (is_unsigned ? *((u##w *) buf) : *((s##w *) buf))
453
454 bool serializeStructToString(std::string *out,
455         std::string format, void *value)
456 {
457         std::ostringstream os;
458         std::string str;
459         char *f;
460         size_t strpos;
461
462         char *bufpos = (char *) value;
463         char *fmtpos, *fmt = &format[0];
464         while ((f = strtok_r(fmt, ",", &fmtpos))) {
465                 fmt = NULL;
466                 bool is_unsigned = false;
467                 int width = 0;
468                 char valtype = *f;
469
470                 width = (int)strtol(f + 1, &f, 10);
471                 if (width && valtype == 's')
472                         valtype = 'i';
473
474                 switch (valtype) {
475                         case 'u':
476                                 is_unsigned = true;
477                                 /* FALLTHROUGH */
478                         case 'i':
479                                 if (width == 16) {
480                                         bufpos += PADDING(bufpos, u16);
481                                         os << SIGN_CAST(16, bufpos);
482                                         bufpos += sizeof(u16);
483                                 } else if (width == 32) {
484                                         bufpos += PADDING(bufpos, u32);
485                                         os << SIGN_CAST(32, bufpos);
486                                         bufpos += sizeof(u32);
487                                 } else if (width == 64) {
488                                         bufpos += PADDING(bufpos, u64);
489                                         os << SIGN_CAST(64, bufpos);
490                                         bufpos += sizeof(u64);
491                                 }
492                                 break;
493                         case 'b':
494                                 bufpos += PADDING(bufpos, bool);
495                                 os << std::boolalpha << *((bool *) bufpos);
496                                 bufpos += sizeof(bool);
497                                 break;
498                         case 'f':
499                                 bufpos += PADDING(bufpos, float);
500                                 os << *((float *) bufpos);
501                                 bufpos += sizeof(float);
502                                 break;
503                         case 's':
504                                 bufpos += PADDING(bufpos, std::string *);
505                                 str = **((std::string **) bufpos);
506
507                                 strpos = 0;
508                                 while ((strpos = str.find('"', strpos)) != std::string::npos) {
509                                         str.insert(strpos, 1, '\\');
510                                         strpos += 2;
511                                 }
512
513                                 os << str;
514                                 bufpos += sizeof(std::string *);
515                                 break;
516                         case 'v':
517                                 if (width == 2) {
518                                         bufpos += PADDING(bufpos, v2f);
519                                         v2f *v = (v2f *) bufpos;
520                                         os << '(' << v->X << ", " << v->Y << ')';
521                                         bufpos += sizeof(v2f);
522                                 } else {
523                                         bufpos += PADDING(bufpos, v3f);
524                                         v3f *v = (v3f *) bufpos;
525                                         os << '(' << v->X << ", " << v->Y << ", " << v->Z << ')';
526                                         bufpos += sizeof(v3f);
527                                 }
528                                 break;
529                         default:
530                                 return false;
531                 }
532                 os << ", ";
533         }
534         *out = os.str();
535
536         // Trim off the trailing comma and space
537         if (out->size() >= 2)
538                 out->resize(out->size() - 2);
539
540         return true;
541 }
542
543 #undef SIGN_CAST
544
545 ////
546 //// Other
547 ////
548
549 std::string serializeHexString(const std::string &data, bool insert_spaces)
550 {
551         std::string result;
552         result.reserve(data.size() * (2 + insert_spaces));
553
554         static const char hex_chars[] = "0123456789abcdef";
555
556         const size_t len = data.size();
557         for (size_t i = 0; i != len; i++) {
558                 u8 byte = data[i];
559                 result.push_back(hex_chars[(byte >> 4) & 0x0F]);
560                 result.push_back(hex_chars[(byte >> 0) & 0x0F]);
561                 if (insert_spaces && i != len - 1)
562                         result.push_back(' ');
563         }
564
565         return result;
566 }