]> git.lizzy.rs Git - minetest.git/blob - src/util/serialize.cpp
281061229a29670148531e1f4f8780a9f56b9efe
[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 FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN;
32
33
34 ////
35 //// String
36 ////
37
38 std::string serializeString16(const std::string &plain)
39 {
40         std::string s;
41         char buf[2];
42
43         if (plain.size() > STRING_MAX_LEN)
44                 throw SerializationError("String too long for serializeString16");
45         s.reserve(2 + plain.size());
46
47         writeU16((u8 *)&buf[0], plain.size());
48         s.append(buf, 2);
49
50         s.append(plain);
51         return s;
52 }
53
54 std::string deSerializeString16(std::istream &is)
55 {
56         std::string s;
57         char buf[2];
58
59         is.read(buf, 2);
60         if (is.gcount() != 2)
61                 throw SerializationError("deSerializeString16: size not read");
62
63         u16 s_size = readU16((u8 *)buf);
64         if (s_size == 0)
65                 return s;
66
67         s.resize(s_size);
68         is.read(&s[0], s_size);
69         if (is.gcount() != s_size)
70                 throw SerializationError("deSerializeString16: couldn't read all chars");
71
72         return s;
73 }
74
75
76 ////
77 //// Long String
78 ////
79
80 std::string serializeString32(const std::string &plain)
81 {
82         std::string s;
83         char buf[4];
84
85         if (plain.size() > LONG_STRING_MAX_LEN)
86                 throw SerializationError("String too long for serializeLongString");
87         s.reserve(4 + plain.size());
88
89         writeU32((u8*)&buf[0], plain.size());
90         s.append(buf, 4);
91         s.append(plain);
92         return s;
93 }
94
95 std::string deSerializeString32(std::istream &is)
96 {
97         std::string s;
98         char buf[4];
99
100         is.read(buf, 4);
101         if (is.gcount() != 4)
102                 throw SerializationError("deSerializeLongString: size not read");
103
104         u32 s_size = readU32((u8 *)buf);
105         if (s_size == 0)
106                 return s;
107
108         // We don't really want a remote attacker to force us to allocate 4GB...
109         if (s_size > LONG_STRING_MAX_LEN) {
110                 throw SerializationError("deSerializeLongString: "
111                         "string too long: " + itos(s_size) + " bytes");
112         }
113
114         s.resize(s_size);
115         is.read(&s[0], s_size);
116         if ((u32)is.gcount() != s_size)
117                 throw SerializationError("deSerializeLongString: couldn't read all chars");
118
119         return s;
120 }
121
122 ////
123 //// JSON
124 ////
125
126 std::string serializeJsonString(const std::string &plain)
127 {
128         std::ostringstream os(std::ios::binary);
129         os << "\"";
130
131         for (char c : plain) {
132                 switch (c) {
133                         case '"':
134                                 os << "\\\"";
135                                 break;
136                         case '\\':
137                                 os << "\\\\";
138                                 break;
139                         case '/':
140                                 os << "\\/";
141                                 break;
142                         case '\b':
143                                 os << "\\b";
144                                 break;
145                         case '\f':
146                                 os << "\\f";
147                                 break;
148                         case '\n':
149                                 os << "\\n";
150                                 break;
151                         case '\r':
152                                 os << "\\r";
153                                 break;
154                         case '\t':
155                                 os << "\\t";
156                                 break;
157                         default: {
158                                 if (c >= 32 && c <= 126) {
159                                         os << c;
160                                 } else {
161                                         u32 cnum = (u8)c;
162                                         os << "\\u" << std::hex << std::setw(4)
163                                                 << std::setfill('0') << cnum;
164                                 }
165                                 break;
166                         }
167                 }
168         }
169
170         os << "\"";
171         return os.str();
172 }
173
174 std::string deSerializeJsonString(std::istream &is)
175 {
176         std::ostringstream os(std::ios::binary);
177         char c, c2;
178
179         // Parse initial doublequote
180         is >> c;
181         if (c != '"')
182                 throw SerializationError("JSON string must start with doublequote");
183
184         // Parse characters
185         for (;;) {
186                 c = is.get();
187                 if (is.eof())
188                         throw SerializationError("JSON string ended prematurely");
189
190                 if (c == '"') {
191                         return os.str();
192                 }
193
194                 if (c == '\\') {
195                         c2 = is.get();
196                         if (is.eof())
197                                 throw SerializationError("JSON string ended prematurely");
198                         switch (c2) {
199                                 case 'b':
200                                         os << '\b';
201                                         break;
202                                 case 'f':
203                                         os << '\f';
204                                         break;
205                                 case 'n':
206                                         os << '\n';
207                                         break;
208                                 case 'r':
209                                         os << '\r';
210                                         break;
211                                 case 't':
212                                         os << '\t';
213                                         break;
214                                 case 'u': {
215                                         int hexnumber;
216                                         char hexdigits[4 + 1];
217
218                                         is.read(hexdigits, 4);
219                                         if (is.eof())
220                                                 throw SerializationError("JSON string ended prematurely");
221                                         hexdigits[4] = 0;
222
223                                         std::istringstream tmp_is(hexdigits, std::ios::binary);
224                                         tmp_is >> std::hex >> hexnumber;
225                                         os << (char)hexnumber;
226                                         break;
227                                 }
228                                 default:
229                                         os << c2;
230                                         break;
231                         }
232                 } else {
233                         os << c;
234                 }
235         }
236
237         return os.str();
238 }
239
240 std::string serializeJsonStringIfNeeded(const std::string &s)
241 {
242         for (size_t i = 0; i < s.size(); ++i) {
243                 if (s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
244                         return serializeJsonString(s);
245         }
246         return s;
247 }
248
249 std::string deSerializeJsonStringIfNeeded(std::istream &is)
250 {
251         std::stringstream tmp_os(std::ios_base::binary | std::ios_base::in | std::ios_base::out);
252         bool expect_initial_quote = true;
253         bool is_json = false;
254         bool was_backslash = false;
255         for (;;) {
256                 char c = is.get();
257                 if (is.eof())
258                         break;
259
260                 if (expect_initial_quote && c == '"') {
261                         tmp_os << c;
262                         is_json = true;
263                 } else if(is_json) {
264                         tmp_os << c;
265                         if (was_backslash)
266                                 was_backslash = false;
267                         else if (c == '\\')
268                                 was_backslash = true;
269                         else if (c == '"')
270                                 break; // Found end of string
271                 } else {
272                         if (c == ' ') {
273                                 // Found end of word
274                                 is.unget();
275                                 break;
276                         }
277
278                         tmp_os << c;
279                 }
280                 expect_initial_quote = false;
281         }
282         if (is_json) {
283                 return deSerializeJsonString(tmp_os);
284         }
285
286         return tmp_os.str();
287 }
288