]> git.lizzy.rs Git - dragonfireclient.git/blob - src/util/serialize.cpp
Replace usage of long long with u64/s64
[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 // Creates a string with the length as the first two bytes
32 std::string serializeString(const std::string &plain)
33 {
34         //assert(plain.size() <= 65535);
35         if(plain.size() > 65535)
36                 throw SerializationError("String too long for serializeString");
37         char buf[2];
38         writeU16((u8*)&buf[0], plain.size());
39         std::string s;
40         s.append(buf, 2);
41         s.append(plain);
42         return s;
43 }
44
45 // Creates a string with the length as the first two bytes from wide string
46 std::string serializeWideString(const std::wstring &plain)
47 {
48         //assert(plain.size() <= 65535);
49         if(plain.size() > 65535)
50                 throw SerializationError("String too long for serializeString");
51         char buf[2];
52         writeU16((u8*)buf, plain.size());
53         std::string s;
54         s.append(buf, 2);
55         for(u32 i=0; i<plain.size(); i++)
56         {
57                 writeU16((u8*)buf, plain[i]);
58                 s.append(buf, 2);
59         }
60         return s;
61 }
62
63 // Reads a string with the length as the first two bytes
64 std::string deSerializeString(std::istream &is)
65 {
66         char buf[2];
67         is.read(buf, 2);
68         if(is.gcount() != 2)
69                 throw SerializationError("deSerializeString: size not read");
70         u16 s_size = readU16((u8*)buf);
71         if(s_size == 0)
72                 return "";
73         Buffer<char> buf2(s_size);
74         is.read(&buf2[0], s_size);
75         std::string s;
76         s.reserve(s_size);
77         s.append(&buf2[0], s_size);
78         return s;
79 }
80
81 // Reads a wide string with the length as the first two bytes
82 std::wstring deSerializeWideString(std::istream &is)
83 {
84         char buf[2];
85         is.read(buf, 2);
86         if(is.gcount() != 2)
87                 throw SerializationError("deSerializeString: size not read");
88         u16 s_size = readU16((u8*)buf);
89         if(s_size == 0)
90                 return L"";
91         std::wstring s;
92         s.reserve(s_size);
93         for(u32 i=0; i<s_size; i++)
94         {
95                 is.read(&buf[0], 2);
96                 wchar_t c16 = readU16((u8*)buf);
97                 s.append(&c16, 1);
98         }
99         return s;
100 }
101
102 // Creates a string with the length as the first four bytes
103 std::string serializeLongString(const std::string &plain)
104 {
105         char buf[4];
106         writeU32((u8*)&buf[0], plain.size());
107         std::string s;
108         s.append(buf, 4);
109         s.append(plain);
110         return s;
111 }
112
113 // Reads a string with the length as the first four bytes
114 std::string deSerializeLongString(std::istream &is)
115 {
116         char buf[4];
117         is.read(buf, 4);
118         if(is.gcount() != 4)
119                 throw SerializationError("deSerializeLongString: size not read");
120         u32 s_size = readU32((u8*)buf);
121         if(s_size == 0)
122                 return "";
123         Buffer<char> buf2(s_size);
124         is.read(&buf2[0], s_size);
125         std::string s;
126         s.reserve(s_size);
127         s.append(&buf2[0], s_size);
128         return s;
129 }
130
131 // Creates a string encoded in JSON format (almost equivalent to a C string literal)
132 std::string serializeJsonString(const std::string &plain)
133 {
134         std::ostringstream os(std::ios::binary);
135         os<<"\"";
136         for(size_t i = 0; i < plain.size(); i++)
137         {
138                 char c = plain[i];
139                 switch(c)
140                 {
141                         case '"': os<<"\\\""; break;
142                         case '\\': os<<"\\\\"; break;
143                         case '/': os<<"\\/"; break;
144                         case '\b': os<<"\\b"; break;
145                         case '\f': os<<"\\f"; break;
146                         case '\n': os<<"\\n"; break;
147                         case '\r': os<<"\\r"; break;
148                         case '\t': os<<"\\t"; break;
149                         default:
150                         {
151                                 if(c >= 32 && c <= 126)
152                                 {
153                                         os<<c;
154                                 }
155                                 else
156                                 {
157                                         u32 cnum = (u32) (u8) c;
158                                         os<<"\\u"<<std::hex<<std::setw(4)<<std::setfill('0')<<cnum;
159                                 }
160                                 break;
161                         }
162                 }
163         }
164         os<<"\"";
165         return os.str();
166 }
167
168 // Reads a string encoded in JSON format
169 std::string deSerializeJsonString(std::istream &is)
170 {
171         std::ostringstream os(std::ios::binary);
172         char c, c2;
173
174         // Parse initial doublequote
175         is >> c;
176         if(c != '"')
177                 throw SerializationError("JSON string must start with doublequote");
178
179         // Parse characters
180         for(;;)
181         {
182                 c = is.get();
183                 if(is.eof())
184                         throw SerializationError("JSON string ended prematurely");
185                 if(c == '"')
186                 {
187                         return os.str();
188                 }
189                 else if(c == '\\')
190                 {
191                         c2 = is.get();
192                         if(is.eof())
193                                 throw SerializationError("JSON string ended prematurely");
194                         switch(c2)
195                         {
196                                 default:  os<<c2; break;
197                                 case 'b': os<<'\b'; break;
198                                 case 'f': os<<'\f'; break;
199                                 case 'n': os<<'\n'; break;
200                                 case 'r': os<<'\r'; break;
201                                 case 't': os<<'\t'; break;
202                                 case 'u':
203                                 {
204                                         char hexdigits[4+1];
205                                         is.read(hexdigits, 4);
206                                         if(is.eof())
207                                                 throw SerializationError("JSON string ended prematurely");
208                                         hexdigits[4] = 0;
209                                         std::istringstream tmp_is(hexdigits, std::ios::binary);
210                                         int hexnumber;
211                                         tmp_is >> std::hex >> hexnumber;
212                                         os<<((char)hexnumber);
213                                         break;
214                                 }
215                         }
216                 }
217                 else
218                 {
219                         os<<c;
220                 }
221         }
222         return os.str();
223 }
224
225
226 bool deSerializeStringToStruct(std::string valstr,
227         std::string format, void *out, size_t olen)
228 {
229         size_t len = olen;
230         std::vector<std::string *> strs_alloced;
231         std::string *str;
232         char *f, *snext;
233         size_t pos;
234
235         char *s = &valstr[0];
236         char *buf = new char[len];
237         char *bufpos = buf;
238
239         char *fmtpos, *fmt = &format[0];
240         while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
241                 fmt = NULL;
242
243                 bool is_unsigned = false;
244                 int width = 0;
245                 char valtype = *f;
246
247                 width = (int)strtol(f + 1, &f, 10);
248                 if (width && valtype == 's')
249                         valtype = 'i';
250
251                 switch (valtype) {
252                         case 'u':
253                                 is_unsigned = true;
254                                 /* FALLTHROUGH */
255                         case 'i':
256                                 if (width == 16) {
257                                         bufpos += PADDING(bufpos, u16);
258                                         if ((bufpos - buf) + sizeof(u16) <= len) {
259                                                 if (is_unsigned)
260                                                         *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
261                                                 else
262                                                         *(s16 *)bufpos = (s16)strtol(s, &s, 10);
263                                         }
264                                         bufpos += sizeof(u16);
265                                 } else if (width == 32) {
266                                         bufpos += PADDING(bufpos, u32);
267                                         if ((bufpos - buf) + sizeof(u32) <= len) {
268                                                 if (is_unsigned)
269                                                         *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
270                                                 else
271                                                         *(s32 *)bufpos = (s32)strtol(s, &s, 10);
272                                         }
273                                         bufpos += sizeof(u32);
274                                 } else if (width == 64) {
275                                         bufpos += PADDING(bufpos, u64);
276                                         if ((bufpos - buf) + sizeof(u64) <= len) {
277                                                 if (is_unsigned)
278                                                         *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
279                                                 else
280                                                         *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
281                                         }
282                                         bufpos += sizeof(u64);
283                                 }
284                                 s = strchr(s, ',');
285                                 break;
286                         case 'b':
287                                 snext = strchr(s, ',');
288                                 if (snext)
289                                         *snext++ = 0;
290
291                                 bufpos += PADDING(bufpos, bool);
292                                 if ((bufpos - buf) + sizeof(bool) <= len)
293                                         *(bool *)bufpos = is_yes(std::string(s));
294                                 bufpos += sizeof(bool);
295
296                                 s = snext;
297                                 break;
298                         case 'f':
299                                 bufpos += PADDING(bufpos, float);
300                                 if ((bufpos - buf) + sizeof(float) <= len)
301                                         *(float *)bufpos = strtof(s, &s);
302                                 bufpos += sizeof(float);
303
304                                 s = strchr(s, ',');
305                                 break;
306                         case 's':
307                                 while (*s == ' ' || *s == '\t')
308                                         s++;
309                                 if (*s++ != '"') //error, expected string
310                                         goto fail;
311                                 snext = s;
312
313                                 while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
314                                         snext++;
315                                 *snext++ = 0;
316
317                                 bufpos += PADDING(bufpos, std::string *);
318
319                                 str = new std::string(s);
320                                 pos = 0;
321                                 while ((pos = str->find("\\\"", pos)) != std::string::npos)
322                                         str->erase(pos, 1);
323
324                                 if ((bufpos - buf) + sizeof(std::string *) <= len)
325                                         *(std::string **)bufpos = str;
326                                 bufpos += sizeof(std::string *);
327                                 strs_alloced.push_back(str);
328
329                                 s = *snext ? snext + 1 : NULL;
330                                 break;
331                         case 'v':
332                                 while (*s == ' ' || *s == '\t')
333                                         s++;
334                                 if (*s++ != '(') //error, expected vector
335                                         goto fail;
336
337                                 if (width == 2) {
338                                         bufpos += PADDING(bufpos, v2f);
339
340                                         if ((bufpos - buf) + sizeof(v2f) <= len) {
341                                         v2f *v = (v2f *)bufpos;
342                                                 v->X = strtof(s, &s);
343                                                 s++;
344                                                 v->Y = strtof(s, &s);
345                                         }
346
347                                         bufpos += sizeof(v2f);
348                                 } else if (width == 3) {
349                                         bufpos += PADDING(bufpos, v3f);
350                                         if ((bufpos - buf) + sizeof(v3f) <= len) {
351                                                 v3f *v = (v3f *)bufpos;
352                                                 v->X = strtof(s, &s);
353                                                 s++;
354                                                 v->Y = strtof(s, &s);
355                                                 s++;
356                                                 v->Z = strtof(s, &s);
357                                         }
358
359                                         bufpos += sizeof(v3f);
360                                 }
361                                 s = strchr(s, ',');
362                                 break;
363                         default: //error, invalid format specifier
364                                 goto fail;
365                 }
366
367                 if (s && *s == ',')
368                         s++;
369
370                 if ((size_t)(bufpos - buf) > len) //error, buffer too small
371                         goto fail;
372         }
373
374         if (f && *f) { //error, mismatched number of fields and values
375 fail:
376                 for (size_t i = 0; i != strs_alloced.size(); i++)
377                         delete strs_alloced[i];
378                 delete[] buf;
379                 return false;
380         }
381
382         memcpy(out, buf, olen);
383         delete[] buf;
384         return true;
385 }
386
387
388 bool serializeStructToString(std::string *outstr,
389         std::string format, void *value)
390 {
391         char sbuf[2048];
392         int sbuflen = sizeof(sbuf) - 1;
393         sbuf[sbuflen] = 0;
394         std::string str;
395         int pos = 0;
396         size_t fpos;
397         char *f;
398
399         char *bufpos = (char *)value;
400         char *fmtpos, *fmt = &format[0];
401         while ((f = strtok_r(fmt, ",", &fmtpos))) {
402                 fmt = NULL;
403                 bool is_unsigned = false;
404                 int width = 0, nprinted = 0;
405                 char valtype = *f;
406
407                 width = (int)strtol(f + 1, &f, 10);
408                 if (width && valtype == 's')
409                         valtype = 'i';
410
411                 switch (valtype) {
412                         case 'u':
413                                 is_unsigned = true;
414                                 /* FALLTHROUGH */
415                         case 'i':
416                                 if (width == 16) {
417                                         bufpos += PADDING(bufpos, u16);
418                                         nprinted = snprintf(sbuf + pos, sbuflen,
419                                                                 is_unsigned ? "%u, " : "%d, ",
420                                                                 *((u16 *)bufpos));
421                                         bufpos += sizeof(u16);
422                                 } else if (width == 32) {
423                                         bufpos += PADDING(bufpos, u32);
424                                         nprinted = snprintf(sbuf + pos, sbuflen,
425                                                                 is_unsigned ? "%u, " : "%d, ",
426                                                                 *((u32 *)bufpos));
427                                         bufpos += sizeof(u32);
428                                 } else if (width == 64) {
429                                         bufpos += PADDING(bufpos, u64);
430                                         nprinted = snprintf(sbuf + pos, sbuflen,
431                                                                 is_unsigned ? "%llu, " : "%lli, ",
432                                                                 *((u64 *)bufpos));
433                                         bufpos += sizeof(u64);
434                                 }
435                                 break;
436                         case 'b':
437                                 bufpos += PADDING(bufpos, bool);
438                                 nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
439                                                                         *((bool *)bufpos) ? "true" : "false");
440                                 bufpos += sizeof(bool);
441                                 break;
442                         case 'f':
443                                 bufpos += PADDING(bufpos, float);
444                                 nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
445                                                                         *((float *)bufpos));
446                                 bufpos += sizeof(float);
447                                 break;
448                         case 's':
449                                 bufpos += PADDING(bufpos, std::string *);
450                                 str = **((std::string **)bufpos);
451
452                                 fpos = 0;
453                                 while ((fpos = str.find('"', fpos)) != std::string::npos) {
454                                         str.insert(fpos, 1, '\\');
455                                         fpos += 2;
456                                 }
457
458                                 nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
459                                                                         (*((std::string **)bufpos))->c_str());
460                                 bufpos += sizeof(std::string *);
461                                 break;
462                         case 'v':
463                                 if (width == 2) {
464                                         bufpos += PADDING(bufpos, v2f);
465                                         v2f *v = (v2f *)bufpos;
466                                         nprinted = snprintf(sbuf + pos, sbuflen,
467                                                                                 "(%f, %f), ", v->X, v->Y);
468                                         bufpos += sizeof(v2f);
469                                 } else {
470                                         bufpos += PADDING(bufpos, v3f);
471                                         v3f *v = (v3f *)bufpos;
472                                         nprinted = snprintf(sbuf + pos, sbuflen,
473                                                                                 "(%f, %f, %f), ", v->X, v->Y, v->Z);
474                                         bufpos += sizeof(v3f);
475                                 }
476                                 break;
477                         default:
478                                 return false;
479                 }
480                 if (nprinted < 0) //error, buffer too small
481                         return false;
482                 pos     += nprinted;
483                 sbuflen -= nprinted;
484         }
485
486         // this is to trim off the trailing comma
487         if (pos >= 2)
488                 sbuf[pos - 2] = 0;
489
490         *outstr = sbuf;
491
492         return true;
493 }