X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Futil%2Fserialize.cpp;h=5b276668d80a63e6b642abdca12eeee65337d483;hb=79414aa3e5591fdaffa0956a08610a2228042941;hp=069ec53859cd217192e70a025fa3ebdf7a88f504;hpb=d753d352f15ac3586a80b42dd4da6aca518c8ec8;p=minetest.git diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp index 069ec5385..5b276668d 100644 --- a/src/util/serialize.cpp +++ b/src/util/serialize.cpp @@ -21,151 +21,273 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "pointer.h" #include "porting.h" #include "util/string.h" -#include "../exceptions.h" -#include "../irrlichttypes.h" +#include "exceptions.h" +#include "irrlichttypes.h" #include #include #include -// Creates a string with the length as the first two bytes -std::string serializeString(const std::string &plain) +FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN; + +//// +//// BufReader +//// + +bool BufReader::getStringNoEx(std::string *val) { - //assert(plain.size() <= 65535); - if(plain.size() > 65535) - throw SerializationError("String too long for serializeString"); - char buf[2]; - writeU16((u8*)&buf[0], plain.size()); - std::string s; - s.append(buf, 2); - s.append(plain); - return s; + u16 num_chars; + if (!getU16NoEx(&num_chars)) + return false; + + if (pos + num_chars > size) { + pos -= sizeof(num_chars); + return false; + } + + val->assign((const char *)data + pos, num_chars); + pos += num_chars; + + return true; } -// Creates a string with the length as the first two bytes from wide string -std::string serializeWideString(const std::wstring &plain) +bool BufReader::getWideStringNoEx(std::wstring *val) +{ + u16 num_chars; + if (!getU16NoEx(&num_chars)) + return false; + + if (pos + num_chars * 2 > size) { + pos -= sizeof(num_chars); + return false; + } + + for (size_t i = 0; i != num_chars; i++) { + val->push_back(readU16(data + pos)); + pos += 2; + } + + return true; +} + +bool BufReader::getLongStringNoEx(std::string *val) +{ + u32 num_chars; + if (!getU32NoEx(&num_chars)) + return false; + + if (pos + num_chars > size) { + pos -= sizeof(num_chars); + return false; + } + + val->assign((const char *)data + pos, num_chars); + pos += num_chars; + + return true; +} + +bool BufReader::getRawDataNoEx(void *val, size_t len) +{ + if (pos + len > size) + return false; + + memcpy(val, data + pos, len); + pos += len; + + return true; +} + + +//// +//// String +//// + +std::string serializeString(const std::string &plain) { - //assert(plain.size() <= 65535); - if(plain.size() > 65535) - throw SerializationError("String too long for serializeString"); - char buf[2]; - writeU16((u8*)buf, plain.size()); std::string s; + char buf[2]; + + if (plain.size() > STRING_MAX_LEN) + throw SerializationError("String too long for serializeString"); + s.reserve(2 + plain.size()); + + writeU16((u8 *)&buf[0], plain.size()); s.append(buf, 2); - for(u32 i=0; i buf2(s_size); - is.read(&buf2[0], s_size); + + u16 s_size = readU16((u8 *)buf); + if (s_size == 0) + return s; + + s.resize(s_size); + is.read(&s[0], s_size); + if (is.gcount() != s_size) + throw SerializationError("deSerializeString: couldn't read all chars"); + + return s; +} + +//// +//// Wide String +//// + +std::string serializeWideString(const std::wstring &plain) +{ std::string s; - s.reserve(s_size); - s.append(&buf2[0], s_size); + char buf[2]; + + if (plain.size() > WIDE_STRING_MAX_LEN) + throw SerializationError("String too long for serializeWideString"); + s.reserve(2 + 2 * plain.size()); + + writeU16((u8 *)buf, plain.size()); + s.append(buf, 2); + + for (wchar_t i : plain) { + writeU16((u8 *)buf, i); + s.append(buf, 2); + } return s; } -// Reads a wide string with the length as the first two bytes std::wstring deSerializeWideString(std::istream &is) { + std::wstring s; char buf[2]; + is.read(buf, 2); - if(is.gcount() != 2) - throw SerializationError("deSerializeString: size not read"); - u16 s_size = readU16((u8*)buf); - if(s_size == 0) - return L""; - std::wstring s; + if (is.gcount() != 2) + throw SerializationError("deSerializeWideString: size not read"); + + u16 s_size = readU16((u8 *)buf); + if (s_size == 0) + return s; + s.reserve(s_size); - for(u32 i=0; i LONG_STRING_MAX_LEN) + throw SerializationError("String too long for serializeLongString"); + s.reserve(4 + plain.size()); + writeU32((u8*)&buf[0], plain.size()); - std::string s; s.append(buf, 4); s.append(plain); return s; } -// Reads a string with the length as the first four bytes std::string deSerializeLongString(std::istream &is) { + std::string s; char buf[4]; + is.read(buf, 4); - if(is.gcount() != 4) + if (is.gcount() != 4) throw SerializationError("deSerializeLongString: size not read"); - u32 s_size = readU32((u8*)buf); - if(s_size == 0) - return ""; - Buffer buf2(s_size); - is.read(&buf2[0], s_size); - std::string s; - s.reserve(s_size); - s.append(&buf2[0], s_size); + + u32 s_size = readU32((u8 *)buf); + if (s_size == 0) + return s; + + // We don't really want a remote attacker to force us to allocate 4GB... + if (s_size > LONG_STRING_MAX_LEN) { + throw SerializationError("deSerializeLongString: " + "string too long: " + itos(s_size) + " bytes"); + } + + s.resize(s_size); + is.read(&s[0], s_size); + if ((u32)is.gcount() != s_size) + throw SerializationError("deSerializeLongString: couldn't read all chars"); + return s; } -// Creates a string encoded in JSON format (almost equivalent to a C string literal) +//// +//// JSON +//// + std::string serializeJsonString(const std::string &plain) { std::ostringstream os(std::ios::binary); - os<<"\""; - for(size_t i = 0; i < plain.size(); i++) - { - char c = plain[i]; - switch(c) - { - case '"': os<<"\\\""; break; - case '\\': os<<"\\\\"; break; - case '/': os<<"\\/"; break; - case '\b': os<<"\\b"; break; - case '\f': os<<"\\f"; break; - case '\n': os<<"\\n"; break; - case '\r': os<<"\\r"; break; - case '\t': os<<"\\t"; break; - default: - { - if(c >= 32 && c <= 126) - { - os<= 32 && c <= 126) { + os << c; + } else { + u32 cnum = (u8)c; + os << "\\u" << std::hex << std::setw(4) + << std::setfill('0') << cnum; } break; } } } - os<<"\""; + + os << "\""; return os.str(); } -// Reads a string encoded in JSON format std::string deSerializeJsonString(std::istream &is) { std::ostringstream os(std::ios::binary); @@ -173,55 +295,118 @@ std::string deSerializeJsonString(std::istream &is) // Parse initial doublequote is >> c; - if(c != '"') + if (c != '"') throw SerializationError("JSON string must start with doublequote"); // Parse characters - for(;;) - { + for (;;) { c = is.get(); - if(is.eof()) + if (is.eof()) throw SerializationError("JSON string ended prematurely"); - if(c == '"') - { + + if (c == '"') { return os.str(); } - else if(c == '\\') - { + + if (c == '\\') { c2 = is.get(); - if(is.eof()) + if (is.eof()) throw SerializationError("JSON string ended prematurely"); - switch(c2) - { - default: os<> std::hex >> hexnumber; - os<<((char)hexnumber); + os << (char)hexnumber; break; } + default: + os << c2; + break; } - } - else - { - os<= 0x7f || s[i] == ' ' || s[i] == '\"') + return serializeJsonString(s); + } + return s; +} + +std::string deSerializeJsonStringIfNeeded(std::istream &is) +{ + std::ostringstream tmp_os; + bool expect_initial_quote = true; + bool is_json = false; + bool was_backslash = false; + for (;;) { + char c = is.get(); + if (is.eof()) + break; + + if (expect_initial_quote && c == '"') { + tmp_os << c; + is_json = true; + } else if(is_json) { + tmp_os << c; + if (was_backslash) + was_backslash = false; + else if (c == '\\') + was_backslash = true; + else if (c == '"') + break; // Found end of string + } else { + if (c == ' ') { + // Found end of word + is.unget(); + break; + } + + tmp_os << c; + } + expect_initial_quote = false; + } + if (is_json) { + std::istringstream tmp_is(tmp_os.str(), std::ios::binary); + return deSerializeJsonString(tmp_is); + } + + return tmp_os.str(); +} + +//// +//// String/Struct conversions +//// bool deSerializeStringToStruct(std::string valstr, std::string format, void *out, size_t olen) @@ -238,7 +423,7 @@ bool deSerializeStringToStruct(std::string valstr, char *fmtpos, *fmt = &format[0]; while ((f = strtok_r(fmt, ",", &fmtpos)) && s) { - fmt = NULL; + fmt = nullptr; bool is_unsigned = false; int width = 0; @@ -326,7 +511,7 @@ bool deSerializeStringToStruct(std::string valstr, bufpos += sizeof(std::string *); strs_alloced.push_back(str); - s = *snext ? snext + 1 : NULL; + s = *snext ? snext + 1 : nullptr; break; case 'v': while (*s == ' ' || *s == '\t') @@ -384,24 +569,23 @@ bool deSerializeStringToStruct(std::string valstr, return true; } +// Casts *buf to a signed or unsigned fixed-width integer of 'w' width +#define SIGN_CAST(w, buf) (is_unsigned ? *((u##w *) buf) : *((s##w *) buf)) -bool serializeStructToString(std::string *outstr, +bool serializeStructToString(std::string *out, std::string format, void *value) { - char sbuf[2048]; - int sbuflen = sizeof(sbuf) - 1; - sbuf[sbuflen] = 0; + std::ostringstream os; std::string str; - int pos = 0; - size_t fpos; char *f; + size_t strpos; - char *bufpos = (char *)value; + char *bufpos = (char *) value; char *fmtpos, *fmt = &format[0]; while ((f = strtok_r(fmt, ",", &fmtpos))) { - fmt = NULL; + fmt = nullptr; bool is_unsigned = false; - int width = 0, nprinted = 0; + int width = 0; char valtype = *f; width = (int)strtol(f + 1, &f, 10); @@ -415,79 +599,89 @@ bool serializeStructToString(std::string *outstr, case 'i': if (width == 16) { bufpos += PADDING(bufpos, u16); - nprinted = snprintf(sbuf + pos, sbuflen, - is_unsigned ? "%u, " : "%d, ", - *((u16 *)bufpos)); + os << SIGN_CAST(16, bufpos); bufpos += sizeof(u16); } else if (width == 32) { bufpos += PADDING(bufpos, u32); - nprinted = snprintf(sbuf + pos, sbuflen, - is_unsigned ? "%u, " : "%d, ", - *((u32 *)bufpos)); + os << SIGN_CAST(32, bufpos); bufpos += sizeof(u32); } else if (width == 64) { bufpos += PADDING(bufpos, u64); - nprinted = snprintf(sbuf + pos, sbuflen, - is_unsigned ? "%llu, " : "%lli, ", - (unsigned long long)*((u64 *)bufpos)); + os << SIGN_CAST(64, bufpos); bufpos += sizeof(u64); } break; case 'b': bufpos += PADDING(bufpos, bool); - nprinted = snprintf(sbuf + pos, sbuflen, "%s, ", - *((bool *)bufpos) ? "true" : "false"); + os << std::boolalpha << *((bool *) bufpos); bufpos += sizeof(bool); break; case 'f': bufpos += PADDING(bufpos, float); - nprinted = snprintf(sbuf + pos, sbuflen, "%f, ", - *((float *)bufpos)); + os << *((float *) bufpos); bufpos += sizeof(float); break; case 's': bufpos += PADDING(bufpos, std::string *); - str = **((std::string **)bufpos); + str = **((std::string **) bufpos); - fpos = 0; - while ((fpos = str.find('"', fpos)) != std::string::npos) { - str.insert(fpos, 1, '\\'); - fpos += 2; + strpos = 0; + while ((strpos = str.find('"', strpos)) != std::string::npos) { + str.insert(strpos, 1, '\\'); + strpos += 2; } - nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ", - (*((std::string **)bufpos))->c_str()); + os << str; bufpos += sizeof(std::string *); break; case 'v': if (width == 2) { bufpos += PADDING(bufpos, v2f); - v2f *v = (v2f *)bufpos; - nprinted = snprintf(sbuf + pos, sbuflen, - "(%f, %f), ", v->X, v->Y); + v2f *v = (v2f *) bufpos; + os << '(' << v->X << ", " << v->Y << ')'; bufpos += sizeof(v2f); } else { bufpos += PADDING(bufpos, v3f); - v3f *v = (v3f *)bufpos; - nprinted = snprintf(sbuf + pos, sbuflen, - "(%f, %f, %f), ", v->X, v->Y, v->Z); + v3f *v = (v3f *) bufpos; + os << '(' << v->X << ", " << v->Y << ", " << v->Z << ')'; bufpos += sizeof(v3f); } break; default: return false; } - if (nprinted < 0) //error, buffer too small - return false; - pos += nprinted; - sbuflen -= nprinted; + os << ", "; } + *out = os.str(); - // this is to trim off the trailing comma - if (pos >= 2) - sbuf[pos - 2] = 0; - - *outstr = sbuf; + // Trim off the trailing comma and space + if (out->size() >= 2) + out->resize(out->size() - 2); return true; } + +#undef SIGN_CAST + +//// +//// Other +//// + +std::string serializeHexString(const std::string &data, bool insert_spaces) +{ + std::string result; + result.reserve(data.size() * (2 + insert_spaces)); + + static const char hex_chars[] = "0123456789abcdef"; + + const size_t len = data.size(); + for (size_t i = 0; i != len; i++) { + u8 byte = data[i]; + result.push_back(hex_chars[(byte >> 4) & 0x0F]); + result.push_back(hex_chars[(byte >> 0) & 0x0F]); + if (insert_spaces && i != len - 1) + result.push_back(' '); + } + + return result; +}