]> git.lizzy.rs Git - minetest.git/blob - src/util/serialize.cpp
Add ItemStack key-value meta storage
[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 SerializationError eof_ser_err("Attempted read past end of data");
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
114         writeU16((u8 *)&buf[0], plain.size());
115         s.append(buf, 2);
116
117         s.append(plain);
118         return s;
119 }
120
121 std::string deSerializeString(std::istream &is)
122 {
123         std::string s;
124         char buf[2];
125
126         is.read(buf, 2);
127         if (is.gcount() != 2)
128                 throw SerializationError("deSerializeString: size not read");
129
130         u16 s_size = readU16((u8 *)buf);
131         if (s_size == 0)
132                 return s;
133
134         Buffer<char> buf2(s_size);
135         is.read(&buf2[0], s_size);
136         if (is.gcount() != s_size)
137                 throw SerializationError("deSerializeString: couldn't read all chars");
138
139         s.reserve(s_size);
140         s.append(&buf2[0], s_size);
141         return s;
142 }
143
144 ////
145 //// Wide String
146 ////
147
148 std::string serializeWideString(const std::wstring &plain)
149 {
150         std::string s;
151         char buf[2];
152
153         if (plain.size() > WIDE_STRING_MAX_LEN)
154                 throw SerializationError("String too long for serializeWideString");
155
156         writeU16((u8 *)buf, plain.size());
157         s.append(buf, 2);
158
159         for (u32 i = 0; i < plain.size(); i++) {
160                 writeU16((u8 *)buf, plain[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         char buf[4];
200
201         if (plain.size() > LONG_STRING_MAX_LEN)
202                 throw SerializationError("String too long for serializeLongString");
203
204         writeU32((u8*)&buf[0], plain.size());
205         std::string s;
206         s.append(buf, 4);
207         s.append(plain);
208         return s;
209 }
210
211 std::string deSerializeLongString(std::istream &is)
212 {
213         std::string s;
214         char buf[4];
215
216         is.read(buf, 4);
217         if (is.gcount() != 4)
218                 throw SerializationError("deSerializeLongString: size not read");
219
220         u32 s_size = readU32((u8 *)buf);
221         if (s_size == 0)
222                 return s;
223
224         // We don't really want a remote attacker to force us to allocate 4GB...
225         if (s_size > LONG_STRING_MAX_LEN) {
226                 throw SerializationError("deSerializeLongString: "
227                         "string too long: " + itos(s_size) + " bytes");
228         }
229
230         Buffer<char> buf2(s_size);
231         is.read(&buf2[0], s_size);
232         if ((u32)is.gcount() != s_size)
233                 throw SerializationError("deSerializeLongString: couldn't read all chars");
234
235         s.reserve(s_size);
236         s.append(&buf2[0], s_size);
237         return s;
238 }
239
240 ////
241 //// JSON
242 ////
243
244 std::string serializeJsonString(const std::string &plain)
245 {
246         std::ostringstream os(std::ios::binary);
247         os << "\"";
248
249         for (size_t i = 0; i < plain.size(); i++) {
250                 char c = plain[i];
251                 switch (c) {
252                         case '"':
253                                 os << "\\\"";
254                                 break;
255                         case '\\':
256                                 os << "\\\\";
257                                 break;
258                         case '/':
259                                 os << "\\/";
260                                 break;
261                         case '\b':
262                                 os << "\\b";
263                                 break;
264                         case '\f':
265                                 os << "\\f";
266                                 break;
267                         case '\n':
268                                 os << "\\n";
269                                 break;
270                         case '\r':
271                                 os << "\\r";
272                                 break;
273                         case '\t':
274                                 os << "\\t";
275                                 break;
276                         default: {
277                                 if (c >= 32 && c <= 126) {
278                                         os << c;
279                                 } else {
280                                         u32 cnum = (u8)c;
281                                         os << "\\u" << std::hex << std::setw(4)
282                                                 << std::setfill('0') << cnum;
283                                 }
284                                 break;
285                         }
286                 }
287         }
288
289         os << "\"";
290         return os.str();
291 }
292
293 std::string deSerializeJsonString(std::istream &is)
294 {
295         std::ostringstream os(std::ios::binary);
296         char c, c2;
297
298         // Parse initial doublequote
299         is >> c;
300         if (c != '"')
301                 throw SerializationError("JSON string must start with doublequote");
302
303         // Parse characters
304         for (;;) {
305                 c = is.get();
306                 if (is.eof())
307                         throw SerializationError("JSON string ended prematurely");
308
309                 if (c == '"') {
310                         return os.str();
311                 } else 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                         } else {
394                                 tmp_os << c;
395                         }
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         } else
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 = NULL;
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 : NULL;
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 = NULL;
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 }