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