]> git.lizzy.rs Git - minetest.git/blob - src/util/serialize.cpp
Make minor style change(unescape_string())
[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 // 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         std::string s;
72         if(s_size == 0)
73                 return s;
74         Buffer<char> buf2(s_size);
75         is.read(&buf2[0], s_size);
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         std::wstring s;
90         if(s_size == 0)
91                 return 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         std::string s;
122         if(s_size == 0)
123                 return s;
124         Buffer<char> buf2(s_size);
125         is.read(&buf2[0], s_size);
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 // Casts *buf to a signed or unsigned fixed-width integer of 'w' width
389 #define SIGN_CAST(w, buf) (is_unsigned ? *((u##w *) buf) : *((s##w *) buf))
390
391 bool serializeStructToString(std::string *out,
392         std::string format, void *value)
393 {
394         std::ostringstream os;
395         std::string str;
396         char *f;
397         size_t strpos;
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;
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                                         os << SIGN_CAST(16, bufpos);
419                                         bufpos += sizeof(u16);
420                                 } else if (width == 32) {
421                                         bufpos += PADDING(bufpos, u32);
422                                         os << SIGN_CAST(32, bufpos);
423                                         bufpos += sizeof(u32);
424                                 } else if (width == 64) {
425                                         bufpos += PADDING(bufpos, u64);
426                                         os << SIGN_CAST(64, bufpos);
427                                         bufpos += sizeof(u64);
428                                 }
429                                 break;
430                         case 'b':
431                                 bufpos += PADDING(bufpos, bool);
432                                 os << std::boolalpha << *((bool *) bufpos);
433                                 bufpos += sizeof(bool);
434                                 break;
435                         case 'f':
436                                 bufpos += PADDING(bufpos, float);
437                                 os << *((float *) bufpos);
438                                 bufpos += sizeof(float);
439                                 break;
440                         case 's':
441                                 bufpos += PADDING(bufpos, std::string *);
442                                 str = **((std::string **) bufpos);
443
444                                 strpos = 0;
445                                 while ((strpos = str.find('"', strpos)) != std::string::npos) {
446                                         str.insert(strpos, 1, '\\');
447                                         strpos += 2;
448                                 }
449
450                                 os << str;
451                                 bufpos += sizeof(std::string *);
452                                 break;
453                         case 'v':
454                                 if (width == 2) {
455                                         bufpos += PADDING(bufpos, v2f);
456                                         v2f *v = (v2f *) bufpos;
457                                         os << '(' << v->X << ", " << v->Y << ')';
458                                         bufpos += sizeof(v2f);
459                                 } else {
460                                         bufpos += PADDING(bufpos, v3f);
461                                         v3f *v = (v3f *) bufpos;
462                                         os << '(' << v->X << ", " << v->Y << ", " << v->Z << ')';
463                                         bufpos += sizeof(v3f);
464                                 }
465                                 break;
466                         default:
467                                 return false;
468                 }
469                 os << ", ";
470         }
471         *out = os.str();
472
473         // Trim off the trailing comma and space
474         if (out->size() >= 2) {
475                 out->resize(out->size() - 2);
476         }
477
478         return true;
479 }
480
481 #undef SIGN_CAST