]> git.lizzy.rs Git - minetest.git/blob - src/util/serialize.cpp
Fix misc. MinGW and Valgrind warnings
[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 ////
358 //// String/Struct conversions
359 ////
360
361 bool deSerializeStringToStruct(std::string valstr,
362         std::string format, void *out, size_t olen)
363 {
364         size_t len = olen;
365         std::vector<std::string *> strs_alloced;
366         std::string *str;
367         char *f, *snext;
368         size_t pos;
369
370         char *s = &valstr[0];
371         char *buf = new char[len];
372         char *bufpos = buf;
373
374         char *fmtpos, *fmt = &format[0];
375         while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
376                 fmt = NULL;
377
378                 bool is_unsigned = false;
379                 int width = 0;
380                 char valtype = *f;
381
382                 width = (int)strtol(f + 1, &f, 10);
383                 if (width && valtype == 's')
384                         valtype = 'i';
385
386                 switch (valtype) {
387                         case 'u':
388                                 is_unsigned = true;
389                                 /* FALLTHROUGH */
390                         case 'i':
391                                 if (width == 16) {
392                                         bufpos += PADDING(bufpos, u16);
393                                         if ((bufpos - buf) + sizeof(u16) <= len) {
394                                                 if (is_unsigned)
395                                                         *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
396                                                 else
397                                                         *(s16 *)bufpos = (s16)strtol(s, &s, 10);
398                                         }
399                                         bufpos += sizeof(u16);
400                                 } else if (width == 32) {
401                                         bufpos += PADDING(bufpos, u32);
402                                         if ((bufpos - buf) + sizeof(u32) <= len) {
403                                                 if (is_unsigned)
404                                                         *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
405                                                 else
406                                                         *(s32 *)bufpos = (s32)strtol(s, &s, 10);
407                                         }
408                                         bufpos += sizeof(u32);
409                                 } else if (width == 64) {
410                                         bufpos += PADDING(bufpos, u64);
411                                         if ((bufpos - buf) + sizeof(u64) <= len) {
412                                                 if (is_unsigned)
413                                                         *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
414                                                 else
415                                                         *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
416                                         }
417                                         bufpos += sizeof(u64);
418                                 }
419                                 s = strchr(s, ',');
420                                 break;
421                         case 'b':
422                                 snext = strchr(s, ',');
423                                 if (snext)
424                                         *snext++ = 0;
425
426                                 bufpos += PADDING(bufpos, bool);
427                                 if ((bufpos - buf) + sizeof(bool) <= len)
428                                         *(bool *)bufpos = is_yes(std::string(s));
429                                 bufpos += sizeof(bool);
430
431                                 s = snext;
432                                 break;
433                         case 'f':
434                                 bufpos += PADDING(bufpos, float);
435                                 if ((bufpos - buf) + sizeof(float) <= len)
436                                         *(float *)bufpos = strtof(s, &s);
437                                 bufpos += sizeof(float);
438
439                                 s = strchr(s, ',');
440                                 break;
441                         case 's':
442                                 while (*s == ' ' || *s == '\t')
443                                         s++;
444                                 if (*s++ != '"') //error, expected string
445                                         goto fail;
446                                 snext = s;
447
448                                 while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
449                                         snext++;
450                                 *snext++ = 0;
451
452                                 bufpos += PADDING(bufpos, std::string *);
453
454                                 str = new std::string(s);
455                                 pos = 0;
456                                 while ((pos = str->find("\\\"", pos)) != std::string::npos)
457                                         str->erase(pos, 1);
458
459                                 if ((bufpos - buf) + sizeof(std::string *) <= len)
460                                         *(std::string **)bufpos = str;
461                                 bufpos += sizeof(std::string *);
462                                 strs_alloced.push_back(str);
463
464                                 s = *snext ? snext + 1 : NULL;
465                                 break;
466                         case 'v':
467                                 while (*s == ' ' || *s == '\t')
468                                         s++;
469                                 if (*s++ != '(') //error, expected vector
470                                         goto fail;
471
472                                 if (width == 2) {
473                                         bufpos += PADDING(bufpos, v2f);
474
475                                         if ((bufpos - buf) + sizeof(v2f) <= len) {
476                                         v2f *v = (v2f *)bufpos;
477                                                 v->X = strtof(s, &s);
478                                                 s++;
479                                                 v->Y = strtof(s, &s);
480                                         }
481
482                                         bufpos += sizeof(v2f);
483                                 } else if (width == 3) {
484                                         bufpos += PADDING(bufpos, v3f);
485                                         if ((bufpos - buf) + sizeof(v3f) <= len) {
486                                                 v3f *v = (v3f *)bufpos;
487                                                 v->X = strtof(s, &s);
488                                                 s++;
489                                                 v->Y = strtof(s, &s);
490                                                 s++;
491                                                 v->Z = strtof(s, &s);
492                                         }
493
494                                         bufpos += sizeof(v3f);
495                                 }
496                                 s = strchr(s, ',');
497                                 break;
498                         default: //error, invalid format specifier
499                                 goto fail;
500                 }
501
502                 if (s && *s == ',')
503                         s++;
504
505                 if ((size_t)(bufpos - buf) > len) //error, buffer too small
506                         goto fail;
507         }
508
509         if (f && *f) { //error, mismatched number of fields and values
510 fail:
511                 for (size_t i = 0; i != strs_alloced.size(); i++)
512                         delete strs_alloced[i];
513                 delete[] buf;
514                 return false;
515         }
516
517         memcpy(out, buf, olen);
518         delete[] buf;
519         return true;
520 }
521
522 // Casts *buf to a signed or unsigned fixed-width integer of 'w' width
523 #define SIGN_CAST(w, buf) (is_unsigned ? *((u##w *) buf) : *((s##w *) buf))
524
525 bool serializeStructToString(std::string *out,
526         std::string format, void *value)
527 {
528         std::ostringstream os;
529         std::string str;
530         char *f;
531         size_t strpos;
532
533         char *bufpos = (char *) value;
534         char *fmtpos, *fmt = &format[0];
535         while ((f = strtok_r(fmt, ",", &fmtpos))) {
536                 fmt = NULL;
537                 bool is_unsigned = false;
538                 int width = 0;
539                 char valtype = *f;
540
541                 width = (int)strtol(f + 1, &f, 10);
542                 if (width && valtype == 's')
543                         valtype = 'i';
544
545                 switch (valtype) {
546                         case 'u':
547                                 is_unsigned = true;
548                                 /* FALLTHROUGH */
549                         case 'i':
550                                 if (width == 16) {
551                                         bufpos += PADDING(bufpos, u16);
552                                         os << SIGN_CAST(16, bufpos);
553                                         bufpos += sizeof(u16);
554                                 } else if (width == 32) {
555                                         bufpos += PADDING(bufpos, u32);
556                                         os << SIGN_CAST(32, bufpos);
557                                         bufpos += sizeof(u32);
558                                 } else if (width == 64) {
559                                         bufpos += PADDING(bufpos, u64);
560                                         os << SIGN_CAST(64, bufpos);
561                                         bufpos += sizeof(u64);
562                                 }
563                                 break;
564                         case 'b':
565                                 bufpos += PADDING(bufpos, bool);
566                                 os << std::boolalpha << *((bool *) bufpos);
567                                 bufpos += sizeof(bool);
568                                 break;
569                         case 'f':
570                                 bufpos += PADDING(bufpos, float);
571                                 os << *((float *) bufpos);
572                                 bufpos += sizeof(float);
573                                 break;
574                         case 's':
575                                 bufpos += PADDING(bufpos, std::string *);
576                                 str = **((std::string **) bufpos);
577
578                                 strpos = 0;
579                                 while ((strpos = str.find('"', strpos)) != std::string::npos) {
580                                         str.insert(strpos, 1, '\\');
581                                         strpos += 2;
582                                 }
583
584                                 os << str;
585                                 bufpos += sizeof(std::string *);
586                                 break;
587                         case 'v':
588                                 if (width == 2) {
589                                         bufpos += PADDING(bufpos, v2f);
590                                         v2f *v = (v2f *) bufpos;
591                                         os << '(' << v->X << ", " << v->Y << ')';
592                                         bufpos += sizeof(v2f);
593                                 } else {
594                                         bufpos += PADDING(bufpos, v3f);
595                                         v3f *v = (v3f *) bufpos;
596                                         os << '(' << v->X << ", " << v->Y << ", " << v->Z << ')';
597                                         bufpos += sizeof(v3f);
598                                 }
599                                 break;
600                         default:
601                                 return false;
602                 }
603                 os << ", ";
604         }
605         *out = os.str();
606
607         // Trim off the trailing comma and space
608         if (out->size() >= 2)
609                 out->resize(out->size() - 2);
610
611         return true;
612 }
613
614 #undef SIGN_CAST
615
616 ////
617 //// Other
618 ////
619
620 std::string serializeHexString(const std::string &data, bool insert_spaces)
621 {
622         std::string result;
623         result.reserve(data.size() * (2 + insert_spaces));
624
625         static const char hex_chars[] = "0123456789abcdef";
626
627         const size_t len = data.size();
628         for (size_t i = 0; i != len; i++) {
629                 u8 byte = data[i];
630                 result.push_back(hex_chars[(byte >> 4) & 0x0F]);
631                 result.push_back(hex_chars[(byte >> 0) & 0x0F]);
632                 if (insert_spaces && i != len - 1)
633                         result.push_back(' ');
634         }
635
636         return result;
637 }