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