Created
July 10, 2012 09:28
-
-
Save elliotwoods/3082283 to your computer and use it in GitHub Desktop.
Variant.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2010, | |
* Gavriloaie Eugen-Andrei (shiretu@gmail.com) | |
* | |
* This file is part of crtmpserver. | |
* crtmpserver is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* crtmpserver is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with crtmpserver. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#include "defines.h" | |
#include "utils/misc/variant.h" | |
#include "utils/misc/variantmap.h" | |
#include "utils/logging/logging.h" | |
#include "utils/misc/file.h" | |
#include "utils/misc/crypto.h" | |
#define TIXML_USE_STL | |
#include "tinyxml.h" | |
#ifdef LOG_VARIANT_MEMORY_MANAGEMENT | |
int Variant::_constructorCount = 0; | |
int Variant::_dynamicAllocationCount = 0; | |
#endif | |
Variant::Variant() { | |
CONSTRUCTOR; | |
_type = V_NULL; | |
memset(&_value, 0, sizeof (_value)); | |
} | |
Variant::Variant(const Variant &val) { | |
CONSTRUCTOR; | |
InternalCopy(val); | |
} | |
Variant::Variant(const bool &val) { | |
CONSTRUCTOR; | |
_type = V_BOOL; | |
memset(&_value, 0, sizeof (_value)); | |
_value.b = val; | |
} | |
Variant::Variant(const int8_t &val) { | |
CONSTRUCTOR; | |
_type = V_INT8; | |
memset(&_value, 0, sizeof (_value)); | |
_value.i8 = val; | |
} | |
Variant::Variant(const int16_t &val) { | |
CONSTRUCTOR; | |
_type = V_INT16; | |
memset(&_value, 0, sizeof (_value)); | |
_value.i16 = val; | |
} | |
Variant::Variant(const int32_t &val) { | |
CONSTRUCTOR; | |
_type = V_INT32; | |
memset(&_value, 0, sizeof (_value)); | |
_value.i32 = val; | |
} | |
Variant::Variant(const int64_t &val) { | |
CONSTRUCTOR; | |
_type = V_INT64; | |
memset(&_value, 0, sizeof (_value)); | |
_value.i64 = val; | |
} | |
Variant::Variant(const uint8_t &val) { | |
CONSTRUCTOR; | |
_type = V_UINT8; | |
memset(&_value, 0, sizeof (_value)); | |
_value.ui8 = val; | |
} | |
Variant::Variant(const uint16_t &val) { | |
CONSTRUCTOR; | |
_type = V_UINT16; | |
memset(&_value, 0, sizeof (_value)); | |
_value.ui16 = val; | |
} | |
Variant::Variant(const uint32_t &val) { | |
CONSTRUCTOR; | |
_type = V_UINT32; | |
memset(&_value, 0, sizeof (_value)); | |
_value.ui32 = val; | |
} | |
Variant::Variant(const uint64_t &val) { | |
CONSTRUCTOR; | |
_type = V_UINT64; | |
memset(&_value, 0, sizeof (_value)); | |
_value.ui64 = val; | |
} | |
Variant::Variant(const double &val) { | |
CONSTRUCTOR; | |
_type = V_DOUBLE; | |
memset(&_value, 0, sizeof (_value)); | |
_value.d = val; | |
} | |
Variant::Variant(const Timestamp &val) { | |
CONSTRUCTOR; | |
_type = V_TIMESTAMP; | |
memset(&_value, 0, sizeof (_value)); | |
DYNAMIC_ALLOC("_value.t"); | |
_value.t = new Timestamp; | |
*_value.t = val; | |
NormalizeTs(); | |
} | |
Variant::Variant(const uint16_t year, const uint8_t month, const uint8_t day) { | |
CONSTRUCTOR; | |
_type = V_DATE; | |
memset(&_value, 0, sizeof (_value)); | |
DYNAMIC_ALLOC("_value.t"); | |
_value.t = new Timestamp; | |
memset(_value.t, 0, sizeof (Timestamp)); | |
_value.t->tm_year = year - 1900; | |
_value.t->tm_mon = month - 1; | |
_value.t->tm_mday = day; | |
_value.t->tm_hour = 0; | |
_value.t->tm_min = 0; | |
_value.t->tm_sec = 0; | |
NormalizeTs(); | |
} | |
Variant::Variant(const uint8_t hour, const uint8_t min, const uint8_t sec, const uint16_t m) { | |
CONSTRUCTOR; | |
_type = V_TIME; | |
memset(&_value, 0, sizeof (_value)); | |
DYNAMIC_ALLOC("_value.t"); | |
_value.t = new Timestamp; | |
memset(_value.t, 0, sizeof (Timestamp)); | |
_value.t->tm_year = 70; | |
_value.t->tm_mon = 0; | |
_value.t->tm_mday = 1; | |
_value.t->tm_hour = hour; | |
_value.t->tm_min = min; | |
_value.t->tm_sec = sec; | |
NormalizeTs(); | |
} | |
Variant::Variant(const uint16_t year, const uint8_t month, const uint8_t day, | |
const uint8_t hour, const uint8_t min, const uint8_t sec, const uint16_t m) { | |
CONSTRUCTOR; | |
_type = V_TIMESTAMP; | |
memset(&_value, 0, sizeof (_value)); | |
DYNAMIC_ALLOC("_value.t"); | |
_value.t = new Timestamp; | |
memset(_value.t, 0, sizeof (Timestamp)); | |
_value.t->tm_year = year - 1900; | |
_value.t->tm_mon = month - 1; | |
_value.t->tm_mday = day; | |
_value.t->tm_hour = hour; | |
_value.t->tm_min = min; | |
_value.t->tm_sec = sec; | |
NormalizeTs(); | |
} | |
Variant::Variant(const char *pVal) { | |
CONSTRUCTOR; | |
_type = V_STRING; | |
memset(&_value, 0, sizeof (_value)); | |
DYNAMIC_ALLOC("_value.s"); | |
_value.s = new string(pVal); | |
} | |
Variant::Variant(const string &val) { | |
CONSTRUCTOR; | |
_type = V_STRING; | |
memset(&_value, 0, sizeof (_value)); | |
DYNAMIC_ALLOC("_value.s"); | |
_value.s = new string(val); | |
} | |
Variant::~Variant() { | |
DESTRUCTOR; | |
Reset(); | |
} | |
void Variant::Reset(bool isUndefined) { | |
switch (_type) { | |
case V_DATE: | |
case V_TIME: | |
case V_TIMESTAMP: | |
{ | |
DYNAMIC_FREE("_value.t"); | |
delete _value.t; | |
break; | |
} | |
case V_BYTEARRAY: | |
case V_STRING: | |
{ | |
DYNAMIC_FREE("_value.s"); | |
delete _value.s; | |
break; | |
} | |
case V_MAP: | |
case V_TYPED_MAP: | |
{ | |
DYNAMIC_FREE("_value.m"); | |
delete _value.m; | |
break; | |
} | |
default: | |
{ | |
break; | |
} | |
} | |
if (isUndefined) | |
_type = V_UNDEFINED; | |
else | |
_type = V_NULL; | |
memset(&_value, 0, sizeof (_value)); | |
} | |
string Variant::ToString(string name, uint32_t indent) { | |
string result = ""; | |
string strIndent = string(indent * 4, ' '); | |
switch (_type) { | |
case V_NULL: | |
{ | |
result += format("%s<NULL name=\"%s\"></NULL>", | |
STR(strIndent), STR(name)); | |
break; | |
} | |
case V_UNDEFINED: | |
{ | |
result += format("%s<UNDEFINED name=\"%s\"></UNDEFINED>", | |
STR(strIndent), STR(name)); | |
break; | |
} | |
case V_BOOL: | |
{ | |
result += format("%s<BOOL name=\"%s\">%s</BOOL>", | |
STR(strIndent), STR(name), _value.b ? "true" : "false"); | |
break; | |
} | |
case V_INT8: | |
{ | |
result += format("%s<INT8 name=\"%s\">%hhd</INT8>", | |
STR(strIndent), STR(name), _value.i8); | |
break; | |
} | |
case V_INT16: | |
{ | |
result += format("%s<INT16 name=\"%s\">%hd</INT16>", | |
STR(strIndent), STR(name), _value.i16); | |
break; | |
} | |
case V_INT32: | |
{ | |
result += format("%s<INT32 name=\"%s\">%d</INT32>", | |
STR(strIndent), STR(name), _value.i32); | |
break; | |
} | |
case V_INT64: | |
{ | |
result += format("%s<INT64 name=\"%s\">%"PRId64"</INT64>", | |
STR(strIndent), STR(name), _value.i64); | |
break; | |
} | |
case V_UINT8: | |
{ | |
result += format("%s<UINT8 name=\"%s\">%hhu</UINT8>", | |
STR(strIndent), STR(name), _value.ui8); | |
break; | |
} | |
case V_UINT16: | |
{ | |
result += format("%s<UINT16 name=\"%s\">%hu</UINT16>", | |
STR(strIndent), STR(name), _value.ui16); | |
break; | |
} | |
case V_UINT32: | |
{ | |
result += format("%s<UINT32 name=\"%s\">%u</UINT32>", | |
STR(strIndent), STR(name), _value.ui32); | |
break; | |
} | |
case V_UINT64: | |
{ | |
result += format("%s<UINT64 name=\"%s\">%"PRIu64"</UINT64>", | |
STR(strIndent), STR(name), _value.ui64); | |
break; | |
} | |
case V_DOUBLE: | |
{ | |
result += format("%s<DOUBLE name=\"%s\">%.03f</DOUBLE>", | |
STR(strIndent), STR(name), _value.d); | |
break; | |
} | |
case V_TIMESTAMP: | |
{ | |
result += format("%s<TIMESTAMP name=\"%s\">%s</TIMESTAMP>", | |
STR(strIndent), STR(name), STR(*this)); | |
break; | |
} | |
case V_DATE: | |
{ | |
result += format("%s<DATE name=\"%s\">%s</DATE>", | |
STR(strIndent), STR(name), STR(*this)); | |
break; | |
} | |
case V_TIME: | |
{ | |
result += format("%s<TIME name=\"%s\">%s</TIME>", | |
STR(strIndent), STR(name), STR(*this)); | |
break; | |
} | |
case V_STRING: | |
{ | |
result += format("%s<STR name=\"%s\">%s</STR>", | |
STR(strIndent), STR(name), STR(*_value.s)); | |
break; | |
} | |
case V_BYTEARRAY: | |
{ | |
result += format("%s<BYTEARRAY name=\"%s\">%"PRIz"u bytes</BYTEARRAY>", | |
STR(strIndent), STR(name), _value.s->length()); | |
break; | |
} | |
case V_TYPED_MAP: | |
{ | |
result += format("%s<TYPED_MAP name=\"%s\" typename=\"%s\" isArray=\"%s\">\n", | |
STR(strIndent), STR(name), STR(_value.m->typeName), | |
_value.m->isArray ? "true" : "false"); | |
FOR_MAP(_value.m->children, string, Variant, i) { | |
result += MAP_VAL(i).ToString(MAP_KEY(i), indent + 1) + "\n"; | |
} | |
result += strIndent + "</TYPED_MAP>"; | |
break; | |
} | |
case V_MAP: | |
{ | |
result += format("%s<MAP name=\"%s\" isArray=\"%s\">\n", | |
STR(strIndent), STR(name), | |
_value.m->isArray ? "true" : "false"); | |
FOR_MAP(_value.m->children, string, Variant, i) { | |
result += MAP_VAL(i).ToString(MAP_KEY(i), indent + 1) + "\n"; | |
} | |
result += strIndent + "</MAP>"; | |
break; | |
} | |
default: | |
{ | |
FATAL("Invalid type: %hhu", _type); | |
assert(false); | |
} | |
} | |
return result; | |
} | |
Variant& Variant::operator=(const Variant &val) { | |
Reset(); | |
InternalCopy(val); | |
return *this; | |
} | |
Variant& Variant::operator=(const bool &val) { | |
Reset(); | |
_type = V_BOOL; | |
_value.b = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const int8_t &val) { | |
Reset(); | |
_type = V_INT8; | |
_value.i8 = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const int16_t &val) { | |
Reset(); | |
_type = V_INT16; | |
_value.i16 = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const int32_t &val) { | |
Reset(); | |
_type = V_INT32; | |
_value.i32 = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const int64_t &val) { | |
Reset(); | |
_type = V_INT64; | |
_value.i64 = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const uint8_t &val) { | |
Reset(); | |
_type = V_UINT8; | |
_value.ui8 = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const uint16_t &val) { | |
Reset(); | |
_type = V_UINT16; | |
_value.ui16 = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const uint32_t &val) { | |
Reset(); | |
_type = V_UINT32; | |
_value.ui32 = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const uint64_t &val) { | |
Reset(); | |
_type = V_UINT64; | |
_value.ui64 = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const double &val) { | |
Reset(); | |
_type = V_DOUBLE; | |
_value.d = val; | |
return *this; | |
} | |
Variant& Variant::operator=(const Timestamp &val) { | |
Reset(); | |
_type = V_TIMESTAMP; | |
DYNAMIC_ALLOC("_value.t"); | |
_value.t = new Timestamp; | |
*_value.t = val; | |
NormalizeTs(); | |
return *this; | |
} | |
Variant& Variant::operator=(const char *pVal) { | |
Reset(); | |
_type = V_STRING; | |
DYNAMIC_ALLOC("_value.s"); | |
_value.s = new string(pVal); | |
return *this; | |
} | |
Variant& Variant::operator=(const string &val) { | |
Reset(); | |
_type = V_STRING; | |
DYNAMIC_ALLOC("_value.s"); | |
_value.s = new string(val); | |
return *this; | |
} | |
Variant::operator VariantType() { | |
return _type; | |
} | |
Variant::operator bool() { | |
switch (_type) { | |
case V_NULL: | |
case V_UNDEFINED: | |
{ | |
return false; | |
} | |
case V_BOOL: | |
{ | |
return _value.b; | |
} | |
case V_INT8: | |
case V_INT16: | |
case V_INT32: | |
case V_INT64: | |
case V_UINT8: | |
case V_UINT16: | |
case V_UINT32: | |
case V_UINT64: | |
case V_DOUBLE: | |
{ | |
bool result = false; | |
result |= (_value.i8 != 0); | |
result |= (_value.i16 != 0); | |
result |= (_value.i32 != 0); | |
result |= (_value.i64 != 0); | |
result |= (_value.ui8 != 0); | |
result |= (_value.ui16 != 0); | |
result |= (_value.ui32 != 0); | |
result |= (_value.ui64 != 0); | |
return result; | |
} | |
case V_TIMESTAMP: | |
case V_DATE: | |
case V_TIME: | |
case V_STRING: | |
case V_TYPED_MAP: | |
case V_MAP: | |
default: | |
{ | |
ASSERT("Cast to bool failed: %s", STR(ToString())); | |
return false; | |
} | |
} | |
} | |
#define OPERATOR_DEF(ctype) \ | |
Variant::operator ctype() {\ | |
switch (_type) {\ | |
case V_NULL:\ | |
case V_UNDEFINED:\ | |
{\ | |
return 0;\ | |
}\ | |
case V_BOOL:\ | |
{\ | |
return (ctype) _value.b;\ | |
}\ | |
case V_INT8:\ | |
{\ | |
return (ctype) _value.i8;\ | |
}\ | |
case V_INT16:\ | |
{\ | |
return (ctype) _value.i16;\ | |
}\ | |
case V_INT32:\ | |
{\ | |
return (ctype) _value.i32;\ | |
}\ | |
case V_INT64:\ | |
{\ | |
return (ctype) _value.i64;\ | |
}\ | |
case V_UINT8:\ | |
{\ | |
return (ctype) _value.ui8;\ | |
}\ | |
case V_UINT16:\ | |
{\ | |
return (ctype) _value.ui16;\ | |
}\ | |
case V_UINT32:\ | |
{\ | |
return (ctype) _value.ui32;\ | |
}\ | |
case V_UINT64:\ | |
{\ | |
return (ctype) _value.ui64;\ | |
}\ | |
case V_DOUBLE:\ | |
{\ | |
return (ctype) _value.d;\ | |
}\ | |
case V_TIMESTAMP:\ | |
case V_DATE:\ | |
case V_TIME:\ | |
case V_STRING:\ | |
case V_TYPED_MAP:\ | |
case V_MAP:\ | |
default:\ | |
{\ | |
ASSERT("Cast failed: %s", STR(ToString()));\ | |
return 0;\ | |
}\ | |
}\ | |
} | |
OPERATOR_DEF(int8_t); | |
OPERATOR_DEF(int16_t); | |
OPERATOR_DEF(int32_t); | |
OPERATOR_DEF(int64_t); | |
OPERATOR_DEF(uint8_t); | |
OPERATOR_DEF(uint16_t); | |
OPERATOR_DEF(uint32_t); | |
OPERATOR_DEF(uint64_t); | |
OPERATOR_DEF(double); | |
Variant::operator Timestamp() { | |
if (_type == V_DATE || | |
_type == V_TIME || | |
_type == V_TIMESTAMP) { | |
return *_value.t; | |
} else { | |
ASSERT("Cast to struct tm failed: %s", STR(ToString())); | |
Timestamp temp = Timestamp_init; | |
return temp; | |
} | |
} | |
Variant::operator string() { | |
switch (_type) { | |
case V_BOOL: | |
{ | |
return _value.b ? "true" : "false"; | |
} | |
case V_INT8: | |
case V_INT16: | |
case V_INT32: | |
{ | |
return format("%d", this->operator int32_t()); | |
} | |
case V_INT64: | |
{ | |
return format("%"PRId64, this->operator int64_t()); | |
} | |
case V_UINT8: | |
case V_UINT16: | |
case V_UINT32: | |
{ | |
return format("%u", this->operator uint32_t()); | |
} | |
case V_UINT64: | |
{ | |
return format("%"PRIu64, this->operator uint64_t()); | |
} | |
case V_DOUBLE: | |
{ | |
return format("%.3f", this->operator double()); | |
} | |
case V_TIMESTAMP: | |
{ | |
char tempBuff[24] = {0}; | |
return string(tempBuff, strftime(tempBuff, 24, "%Y-%m-%dT%H:%M:%S.000", _value.t)); | |
} | |
case V_DATE: | |
{ | |
char tempBuff[24] = {0}; | |
return string(tempBuff, strftime(tempBuff, 24, "%Y-%m-%d", _value.t)); | |
} | |
case V_TIME: | |
{ | |
char tempBuff[24] = {0}; | |
return string(tempBuff, strftime(tempBuff, 24, "%H:%M:%S.000", _value.t)); | |
} | |
case V_BYTEARRAY: | |
case V_STRING: | |
{ | |
return *_value.s; | |
} | |
case V_NULL: | |
case V_UNDEFINED: | |
case V_TYPED_MAP: | |
case V_MAP: | |
default: | |
{ | |
ASSERT("Cast to string failed: %s", STR(ToString())); | |
return ""; | |
} | |
} | |
return ""; | |
} | |
Variant& Variant::operator[](const string &key) { | |
if ((_type != V_TYPED_MAP) && | |
(_type != V_MAP) && | |
(_type != V_NULL) && | |
(_type != V_UNDEFINED)) { | |
ASSERT("Subscript operator applied on a incorrect Variant type: %s", | |
STR(ToString())); | |
} | |
if ((_type == V_NULL) || (_type == V_UNDEFINED)) { | |
_type = V_MAP; | |
DYNAMIC_ALLOC("_value.m"); | |
_value.m = new VariantMap; | |
} | |
if (!MAP_HAS1(_value.m->children, key)) { | |
_value.m->children[key] = Variant(); | |
} | |
return _value.m->children[key]; | |
} | |
Variant& Variant::operator[](const char *key) { | |
return operator[](string(key)); | |
} | |
Variant& Variant::operator[](const double &key) { | |
stringstream ss; | |
ss << VAR_INDEX_VALUE << key; | |
return operator[](ss.str()); | |
} | |
Variant& Variant::operator[](const uint32_t &key) { | |
stringstream ss; | |
ss << VAR_INDEX_VALUE << key; | |
return operator[](ss.str()); | |
} | |
Variant& Variant::operator[](Variant &key) { | |
stringstream ss; | |
switch (key._type) { | |
case V_BOOL: | |
case V_INT8: | |
case V_INT16: | |
case V_INT32: | |
case V_INT64: | |
case V_UINT8: | |
case V_UINT16: | |
case V_UINT32: | |
case V_UINT64: | |
case V_DOUBLE: | |
{ | |
ss << VAR_INDEX_VALUE << STR(key); | |
break; | |
} | |
case V_STRING: | |
{ | |
ss << *key._value.s; | |
break; | |
} | |
case V_NULL: | |
case V_UNDEFINED: | |
case V_DATE: | |
case V_TIME: | |
case V_TIMESTAMP: | |
case V_MAP: | |
case V_TYPED_MAP: | |
default: | |
{ | |
ASSERT("Variant has invalid type to be used as an index: %s", STR(key.ToString())); | |
break; | |
} | |
} | |
return operator[](ss.str()); | |
} | |
Variant &Variant::GetValue(string key, bool caseSensitive) { | |
if (caseSensitive) { | |
return (*this)[key]; | |
} else { | |
FOR_MAP(*this, string, Variant, i) { | |
if (lowerCase(MAP_KEY(i)) == lowerCase(key)) | |
return MAP_VAL(i); | |
} | |
return (*this)[key]; | |
} | |
} | |
bool Variant::operator==(Variant variant) { | |
return ToString() == variant.ToString(); | |
} | |
bool Variant::operator!=(Variant variant) { | |
return !operator==(variant); | |
} | |
bool Variant::operator==(VariantType type) { | |
if (type == _V_NUMERIC) | |
return _type == V_INT8 || | |
_type == V_INT8 || | |
_type == V_INT16 || | |
_type == V_INT32 || | |
_type == V_INT64 || | |
_type == V_UINT8 || | |
_type == V_UINT16 || | |
_type == V_UINT32 || | |
_type == V_UINT64 || | |
_type == V_DOUBLE; | |
else | |
return _type == type; | |
} | |
bool Variant::operator!=(VariantType type) { | |
return !operator ==(type); | |
} | |
string Variant::GetTypeName() { | |
if (_type != V_TYPED_MAP) { | |
ASSERT("GetMapName failed: %s", STR(ToString())); | |
return ""; | |
} | |
return _value.m->typeName; | |
} | |
void Variant::SetTypeName(string name) { | |
if ((_type != V_TYPED_MAP) && (_type != V_MAP) && | |
(_type != V_UNDEFINED) && (_type != V_NULL)) { | |
ASSERT("SetMapName failed: %s", STR(ToString())); | |
return; | |
} | |
if (_type == V_UNDEFINED || _type == V_NULL) { | |
DYNAMIC_ALLOC("_value.m"); | |
_value.m = new VariantMap; | |
} | |
_type = V_TYPED_MAP; | |
_value.m->typeName = name; | |
} | |
bool Variant::HasKey(const string &key, bool caseSensitive) { | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
ASSERT("HasKey failed: %s", STR(ToString())); | |
return false; | |
} | |
if (caseSensitive) { | |
return MAP_HAS1(_value.m->children, key); | |
} else { | |
FOR_MAP(*this, string, Variant, i) { | |
if (lowerCase(MAP_KEY(i)) == lowerCase(key)) | |
return true; | |
} | |
return false; | |
} | |
} | |
bool Variant::HasKeyChain(VariantType end, bool caseSensitive, uint32_t depth, ...) { | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
return false; | |
} | |
va_list arguments; | |
va_start(arguments, depth); | |
Variant *pCurrent = this; | |
for (uint8_t i = 0; i < depth; i++) { | |
const char *pPathElement = va_arg(arguments, const char *); | |
if (!pCurrent->HasKey(pPathElement, caseSensitive)) { | |
va_end(arguments); | |
return false; | |
} | |
Variant *pValue = &pCurrent->GetValue(pPathElement, caseSensitive); | |
if (i == depth - 1) { | |
va_end(arguments); | |
return *pValue == end; | |
} else { | |
if ((*pValue != V_MAP) && (*pValue != V_TYPED_MAP)) { | |
va_end(arguments); | |
return false; | |
} | |
} | |
pCurrent = pValue; | |
} | |
return false; | |
} | |
void Variant::RemoveKey(const string &key) { | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
ASSERT("RemoveKey failed: %s", STR(ToString())); | |
return; | |
} | |
_value.m->children.erase(key); | |
} | |
void Variant::RemoveAt(const uint32_t index) { | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
ASSERT("RemoveKey failed: %s", STR(ToString())); | |
return; | |
} | |
_value.m->children.erase(format(VAR_INDEX_VALUE"%u", index)); | |
} | |
void Variant::RemoveAllKeys() { | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
ASSERT("RemoveAllKeys failed: %s", STR(ToString())); | |
return; | |
} | |
_value.m->children.clear(); | |
} | |
uint32_t Variant::MapSize() { | |
if (_type == V_NULL || _type == V_UNDEFINED) | |
return 0; | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
ASSERT("MapSize failed: %s", STR(ToString())); | |
return 0; | |
} | |
return (uint32_t) _value.m->children.size(); | |
} | |
uint32_t Variant::MapDenseSize() { | |
if (_type == V_NULL || _type == V_UNDEFINED) | |
return 0; | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
ASSERT("MapSize failed: %s", STR(ToString())); | |
return 0; | |
} | |
uint32_t denseCount = 0; | |
for (denseCount = 0; denseCount < MapSize(); denseCount++) { | |
if (!MAP_HAS1(_value.m->children, format(VAR_INDEX_VALUE"%u", denseCount))) | |
break; | |
} | |
return denseCount; | |
} | |
void Variant::PushToArray(Variant value) { | |
if (_type != V_NULL && _type != V_MAP) | |
ASSERT("This is not an array and it can't be converted to array"); | |
IsArray(true); | |
(*this)[(uint32_t)this->MapDenseSize()] = value; | |
} | |
map<string, Variant>::iterator Variant::begin() { | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
ASSERT("This is not a map-like variant: %s", STR(ToString())); | |
map<string, Variant> temp; | |
return temp.begin(); | |
} | |
return _value.m->children.begin(); | |
} | |
map<string, Variant>::iterator Variant::end() { | |
if (_type != V_TYPED_MAP && _type != V_MAP) { | |
ASSERT("This is not a map-like variant: %s", STR(ToString())); | |
map<string, Variant> temp; | |
return temp.end(); | |
} | |
return _value.m->children.end(); | |
} | |
bool Variant::IsTimestamp(VariantType &type) { | |
Variant &temp = *this; | |
if ((VariantType) temp != V_MAP) | |
return false; | |
bool hasDate = temp.HasKey("year") && temp.HasKey("month") && temp.HasKey("day"); | |
bool hasLongTime = temp.HasKey("hour") && temp.HasKey("min") && temp.HasKey("sec"); | |
bool hasShortTime = false; | |
if (!hasLongTime) | |
hasShortTime = temp.HasKey("hour") && temp.HasKey("min"); | |
bool hasIsdst = temp.HasKey("isdst"); | |
bool hasType = temp.HasKey("type"); | |
if (hasDate) { | |
hasDate = hasDate && (temp["year"] == _V_NUMERIC); | |
hasDate = hasDate && (temp["month"] == _V_NUMERIC); | |
hasDate = hasDate && (temp["day"] == _V_NUMERIC); | |
} | |
if (hasLongTime) { | |
hasLongTime = hasLongTime && (temp["hour"] == _V_NUMERIC); | |
hasLongTime = hasLongTime && (temp["min"] == _V_NUMERIC); | |
hasLongTime = hasLongTime && (temp["sec"] == _V_NUMERIC); | |
} else if (hasShortTime) { | |
hasShortTime = hasShortTime && (temp["hour"] == _V_NUMERIC); | |
hasShortTime = hasShortTime && (temp["min"] == _V_NUMERIC); | |
} | |
bool hasTime = hasLongTime || hasShortTime; | |
if (hasIsdst) | |
hasIsdst = hasIsdst && (temp["isdst"] == V_BOOL); | |
if ((!hasDate) && (!hasTime)) | |
return false; | |
uint32_t size = 0; | |
if (hasDate) | |
size += 3; | |
if (hasLongTime) | |
size += 3; | |
else if (hasShortTime) | |
size += 2; | |
if (hasType) | |
size += 1; | |
if (hasIsdst) | |
size += 1; | |
if (hasType) { | |
if (temp["type"] == "date") { | |
hasDate = true; | |
hasTime = false; | |
} | |
if (temp["type"] == "time") { | |
hasDate = false; | |
hasTime = true; | |
} | |
if (temp["type"] == "timestamp") { | |
hasDate = true; | |
hasTime = true; | |
} | |
} | |
if (hasDate && hasTime) | |
type = V_TIMESTAMP; | |
else if (hasDate) | |
type = V_DATE; | |
else | |
type = V_TIME; | |
return temp.MapSize() == size; | |
} | |
bool Variant::IsNumeric() { | |
return _type == V_DOUBLE || | |
_type == V_INT16 || | |
_type == V_INT32 || | |
_type == V_INT64 || | |
_type == V_INT8 || | |
_type == V_UINT16 || | |
_type == V_UINT32 || | |
_type == V_UINT64 || | |
_type == V_UINT8; | |
} | |
bool Variant::IsArray() { | |
if (_type == V_MAP) | |
return _value.m->isArray; | |
return false; | |
} | |
void Variant::IsArray(bool isArray) { | |
if (_type == V_NULL) { | |
_type = V_MAP; | |
DYNAMIC_ALLOC("_value.m"); | |
_value.m = new VariantMap; | |
} | |
if (_type == V_MAP) | |
_value.m->isArray = isArray; | |
} | |
bool Variant::IsByteArray() { | |
return _type == V_BYTEARRAY; | |
} | |
void Variant::IsByteArray(bool isByteArray) { | |
if (isByteArray) { | |
if (_type == V_STRING) { | |
_type = V_BYTEARRAY; | |
} | |
} else { | |
if (_type == V_BYTEARRAY) { | |
_type = V_STRING; | |
} | |
} | |
} | |
bool Variant::ConvertToTimestamp() { | |
VariantType detectedType = V_NULL; | |
if (!IsTimestamp(detectedType)) | |
return false; | |
Timestamp temp = Timestamp_init; | |
if (detectedType == V_DATE || detectedType == V_TIMESTAMP) { | |
temp.tm_year = (int) ((int32_t) (*this)["year"] - 1900); | |
temp.tm_mon = (int) ((int32_t) (*this)["month"]) - 1; | |
temp.tm_mday = (int) ((int32_t) (*this)["day"]); | |
} else { | |
temp.tm_year = 70; | |
temp.tm_mon = 0; | |
temp.tm_mday = 1; | |
} | |
if (detectedType == V_TIME || detectedType == V_TIMESTAMP) { | |
temp.tm_hour = (int) ((int32_t) (*this)["hour"]); | |
temp.tm_min = (int) ((int32_t) (*this)["min"]); | |
temp.tm_sec = (int) (HasKey("sec") ? | |
(int32_t) (*this)["sec"] : 0); | |
temp.tm_isdst = HasKey("isdst") ? (bool) ((*this)["isdst"]) : false; | |
} | |
if (mktime(&temp) < 0) { | |
FATAL("mktime failed"); | |
return false; | |
} | |
Reset(); | |
DYNAMIC_ALLOC("_value.t"); | |
_value.t = new Timestamp; | |
*_value.t = temp; | |
_type = detectedType; | |
return true; | |
} | |
void Variant::Compact() { | |
switch (_type) { | |
case V_DOUBLE: | |
{ | |
if ((((double) (*this)) < INT32_MIN) | |
|| (((double) (*this)) > UINT32_MAX)) | |
break; | |
Variant &variant = *this; | |
double doubleVal = (double) variant; | |
if ((int64_t) doubleVal != doubleVal) | |
break; | |
variant = (int64_t) doubleVal; | |
variant.Compact(); | |
break; | |
} | |
case V_INT64: | |
{ | |
Variant &variant = *this; | |
int64_t val = (int64_t) variant; | |
if ((val < INT32_MIN) || (val > UINT32_MAX)) | |
break; | |
if (val < 0) | |
variant = (int32_t) variant; | |
else | |
variant = (uint32_t) variant; | |
Compact(); | |
break; | |
} | |
case V_INT32: | |
{ | |
Variant &variant = *this; | |
int32_t val = (int32_t) variant; | |
if ((val < INT16_MIN) || (val > (int32_t) (UINT16_MAX))) | |
break; | |
if (val < 0) | |
variant = (int16_t) variant; | |
else | |
variant = (uint16_t) variant; | |
Compact(); | |
break; | |
} | |
case V_INT16: | |
{ | |
Variant &variant = *this; | |
int16_t val = (int16_t) variant; | |
if ((val < INT8_MIN) || (val > (int16_t) (UINT8_MAX))) | |
break; | |
if (val < 0) | |
variant = (int8_t) variant; | |
else | |
variant = (uint8_t) variant; | |
Compact(); | |
break; | |
} | |
case V_UINT64: | |
{ | |
Variant &variant = *this; | |
if ((uint64_t) variant <= INT64_MAX) { | |
variant = (int64_t) variant; | |
Compact(); | |
} | |
break; | |
} | |
case V_UINT32: | |
{ | |
Variant &variant = *this; | |
if ((uint32_t) variant <= INT32_MAX) { | |
variant = (int32_t) variant; | |
Compact(); | |
} | |
break; | |
} | |
case V_UINT16: | |
{ | |
Variant &variant = *this; | |
if ((uint16_t) variant <= INT16_MAX) { | |
variant = (int16_t) variant; | |
Compact(); | |
} | |
break; | |
} | |
case V_UINT8: | |
{ | |
Variant &variant = *this; | |
if ((uint8_t) variant <= INT8_MAX) { | |
variant = (int8_t) variant; | |
Compact(); | |
} | |
break; | |
} | |
case V_MAP: | |
case V_TYPED_MAP: | |
{ | |
FOR_MAP(*this, string, Variant, i) { | |
MAP_VAL(i).Compact(); | |
} | |
break; | |
} | |
default: | |
{ | |
break; | |
} | |
} | |
} | |
bool Variant::DeserializeFromBin(uint8_t *pBuffer, uint32_t bufferLength, | |
Variant &variant) { | |
uint32_t cursor = 0; | |
variant.Reset(); | |
return DeserializeFromBin(pBuffer, bufferLength, variant, cursor); | |
} | |
bool Variant::DeserializeFromBin(string &data, Variant &variant) { | |
return DeserializeFromBin((uint8_t *) data.c_str(), data.size(), variant); | |
} | |
bool Variant::SerializeToBin(string &result) { | |
result += string(1, (char) _type); | |
switch (_type) { | |
case V_NULL: | |
case V_UNDEFINED: | |
{ | |
return true; | |
} | |
case V_BOOL: | |
{ | |
result += string(1, (char) _value.b); | |
return true; | |
} | |
case V_INT8: | |
{ | |
result += string(1, (char) _value.i8); | |
return true; | |
} | |
case V_INT16: | |
{ | |
int16_t val = EHTONS(_value.i16); //----MARKED-SHORT---- | |
result += string((char *) & val, sizeof (int16_t)); | |
return true; | |
} | |
case V_INT32: | |
{ | |
int32_t val = EHTONL(_value.i32); //----MARKED-LONG--- | |
result += string((char *) & val, sizeof (int32_t)); | |
return true; | |
} | |
case V_INT64: | |
{ | |
int64_t val = EHTONLL(_value.i64); | |
result += string((char *) & val, sizeof (int64_t)); | |
return true; | |
} | |
case V_UINT8: | |
{ | |
result += string((char *) & _value.ui8, sizeof (uint8_t)); | |
return true; | |
} | |
case V_UINT16: | |
{ | |
uint16_t val = EHTONS(_value.ui16); //----MARKED-SHORT---- | |
result += string((char *) & val, sizeof (uint16_t)); | |
return true; | |
} | |
case V_UINT32: | |
{ | |
uint32_t val = EHTONL(_value.ui32); //----MARKED-LONG--- | |
result += string((char *) & val, sizeof (uint32_t)); | |
return true; | |
} | |
case V_UINT64: | |
{ | |
uint64_t val = EHTONLL(_value.ui64); | |
result += string((char *) & val, sizeof (uint64_t)); | |
return true; | |
} | |
case V_DOUBLE: | |
{ | |
uint64_t val = 0; | |
EHTOND(_value.d, val); | |
result += string((char *) & val, sizeof (uint64_t)); | |
return true; | |
} | |
case V_TIMESTAMP: | |
case V_DATE: | |
case V_TIME: | |
{ | |
uint64_t temp = EHTONLL((uint64_t) timegm(_value.t)); | |
result += string((char *) & temp, sizeof (uint64_t)); | |
return true; | |
} | |
case V_BYTEARRAY: | |
case V_STRING: | |
{ | |
uint32_t length = EHTONL((uint32_t) _value.s->size()); //----MARKED-LONG--- | |
result += string((char *) & length, sizeof (uint32_t)); | |
result += *(_value.s); | |
return true; | |
} | |
case V_MAP: | |
case V_TYPED_MAP: | |
{ | |
bool isArray = IsArray(); | |
result += string(1, (char) isArray); | |
uint32_t length = 0; | |
if (_type == V_TYPED_MAP) { | |
length = EHTONL((uint32_t) _value.m->typeName.size()); //----MARKED-LONG--- | |
result += string((char *) & length, sizeof (uint32_t)); | |
result += _value.m->typeName; | |
} | |
length = EHTONL(MapSize()); //----MARKED-LONG--- | |
result += string((char *) & length, sizeof (uint32_t)); | |
FOR_MAP(*this, string, Variant, i) { | |
length = EHTONL((uint32_t) MAP_KEY(i).size()); //----MARKED-LONG--- | |
result += string((char *) & length, sizeof (uint32_t)); | |
result += MAP_KEY(i); | |
string temp = ""; | |
if (!MAP_VAL(i).SerializeToBin(temp)) { | |
FATAL("Unable to serialize variant"); | |
result = ""; | |
return false; | |
} else { | |
result += temp; | |
} | |
} | |
return true; | |
} | |
default: | |
{ | |
result = ""; | |
FATAL("Invalid variant type: %hhu", _type); | |
return false; | |
} | |
} | |
return true; | |
} | |
bool Variant::DeserializeFromXml(const uint8_t *pBuffer, uint32_t bufferLength, | |
Variant &variant) { | |
variant.Reset(); | |
if (bufferLength == 0) { | |
return true; | |
} | |
uint8_t *pTemp = NULL; | |
if (pBuffer[bufferLength - 1] != 0) { | |
pTemp = new uint8_t[bufferLength + 1]; | |
memcpy(pTemp, pBuffer, bufferLength); | |
pTemp[bufferLength] = 0; | |
pBuffer = pTemp; | |
} | |
TiXmlDocument document; | |
document.Parse((char *) pBuffer); | |
if (document.Error()) { | |
FATAL("Invalid XML file: Error id: %d; Error desc: %s; Row: %d; Col: %d", | |
document.ErrorId(), | |
document.ErrorDesc(), | |
document.ErrorRow(), | |
document.ErrorCol()); | |
if (pTemp != NULL) | |
delete[] pTemp; | |
return false; | |
} | |
if (!DeserializeFromXml(document.RootElement(), variant)) { | |
variant.Reset(); | |
if (pTemp != NULL) | |
delete[] pTemp; | |
return false; | |
} | |
if (pTemp != NULL) | |
delete[] pTemp; | |
return true; | |
} | |
bool Variant::DeserializeFromXml(string data, Variant &result) { | |
return DeserializeFromXml((const uint8_t *) data.c_str(), data.size(), result); | |
} | |
bool Variant::SerializeToXml(string &result, bool prettyPrint) { | |
result = ""; | |
string name = ""; | |
TiXmlElement *pElement = SerializeToXmlElement(name); | |
if (pElement == NULL) { | |
FATAL("Unable to serialize variant to xml element"); | |
return false; | |
} | |
TiXmlDocument document; | |
TiXmlDeclaration * pXmlDeclaration = new TiXmlDeclaration("1.0", "", ""); | |
document.LinkEndChild(pXmlDeclaration); | |
document.LinkEndChild(pElement); | |
if (prettyPrint) { | |
TiXmlPrinter printer; | |
document.Accept(&printer); | |
result = printer.Str(); | |
} else { | |
stringstream ss; | |
ss << document; | |
result = ss.str(); | |
} | |
return true; | |
} | |
bool Variant::DeserializeFromBinFile(string path, Variant &variant) { | |
//1. Open the file | |
File file; | |
if (!file.Initialize(path)) { | |
FATAL("Unable to open file %s", STR(path)); | |
return false; | |
} | |
if (file.Size() >= 0x100000000LL) { | |
FATAL("File too big"); | |
return false; | |
} | |
//2. Allocate memory | |
uint8_t *pBuffer = new uint8_t[(uint32_t) file.Size()]; | |
//3. Read the content | |
if (!file.ReadBuffer((uint8_t *) pBuffer, file.Size())) { | |
FATAL("Unable to read the file"); | |
return false; | |
} | |
//4. Prepare the string | |
string raw = string((char *) pBuffer, (uint32_t) file.Size()); | |
//8. Dispose the buffer | |
delete[] pBuffer; | |
//9. read the variant from the buffer | |
variant.Reset(); | |
return DeserializeFromBin(raw, variant); | |
} | |
bool Variant::SerializeToBinFile(string fileName) { | |
string rawContent = ""; | |
if (!SerializeToBin(rawContent)) { | |
FATAL("Unable to serialize to XML"); | |
return false; | |
} | |
File file; | |
if (!file.Initialize(fileName, FILE_OPEN_MODE_TRUNCATE)) { | |
FATAL("Unable to open file %s", STR(fileName)); | |
return false; | |
} | |
if (!file.WriteString(rawContent)) { | |
FATAL("Unable to write content"); | |
return false; | |
} | |
return true; | |
} | |
bool Variant::DeserializeFromXmlFile(string path, Variant &variant) { | |
//1. Open the file | |
File file; | |
if (!file.Initialize(path)) { | |
FATAL("Unable to open file %s", STR(path)); | |
return false; | |
} | |
//2. Check his size | |
if (file.Size() == 0) { | |
variant.Reset(); | |
return true; | |
} | |
if (file.Size() > 1024 * 1024 * 4) { | |
FATAL("File too large"); | |
return false; | |
} | |
//3. Allocate memory | |
uint8_t *pBuffer = new uint8_t[(uint32_t) file.Size() + 1]; | |
//4. Read the content | |
if (!file.ReadBuffer((uint8_t *) pBuffer, file.Size())) { | |
FATAL("Unable to read the file"); | |
delete[] pBuffer; | |
return false; | |
} | |
pBuffer[file.Size()] = 0; | |
//5. read the variant from the buffer | |
variant.Reset(); | |
bool result = DeserializeFromXml(pBuffer, (uint32_t) file.Size(), variant); | |
//8. Dispose the buffer | |
delete[] pBuffer; | |
return result; | |
} | |
bool Variant::SerializeToXmlFile(string fileName) { | |
string rawContent = ""; | |
if (!SerializeToXml(rawContent, true)) { | |
FATAL("Unable to serialize to XML"); | |
return false; | |
} | |
File file; | |
if (!file.Initialize(fileName, FILE_OPEN_MODE_TRUNCATE)) { | |
FATAL("Unable to open file %s", STR(fileName)); | |
return false; | |
} | |
if (!file.WriteString(rawContent)) { | |
FATAL("Unable to write content"); | |
return false; | |
} | |
return true; | |
} | |
bool Variant::DeserializeFromJSON(string &raw, Variant &result, uint32_t &start) { | |
result.Reset(); | |
if (start >= raw.size()) { | |
return false; | |
} | |
if (!ReadJSONWhiteSpace(raw, start)) { | |
FATAL("Invalid JSON string"); | |
return false; | |
} | |
switch (raw[start]) { | |
case '\"': | |
{ | |
return ReadJSONString(raw, result, start); | |
} | |
case '-': | |
case '0': | |
case '1': | |
case '2': | |
case '3': | |
case '4': | |
case '5': | |
case '6': | |
case '7': | |
case '8': | |
case '9': | |
{ | |
return ReadJSONNumber(raw, result, start); | |
} | |
case '{': | |
{ | |
return ReadJSONObject(raw, result, start); | |
} | |
case '[': | |
{ | |
return ReadJSONArray(raw, result, start); | |
} | |
case 't': | |
case 'T': | |
{ | |
return ReadJSONBool(raw, result, start, "true"); | |
} | |
case 'f': | |
case 'F': | |
{ | |
return ReadJSONBool(raw, result, start, "false"); | |
} | |
case 'n': | |
case 'N': | |
{ | |
return ReadJSONNull(raw, result, start); | |
} | |
default: | |
{ | |
result.Reset(); | |
return false; | |
} | |
} | |
} | |
bool Variant::SerializeToJSON(string &result) { | |
switch (_type) { | |
case V_NULL: | |
case V_UNDEFINED: | |
{ | |
result += "null"; | |
break; | |
} | |
case V_BOOL: | |
{ | |
result += ((bool)(*this)) ? "true" : "false"; | |
break; | |
} | |
case V_INT8: | |
case V_INT16: | |
case V_INT32: | |
case V_INT64: | |
{ | |
int64_t value = (int64_t) (*this); | |
result += format("%"PRId64, value); | |
break; | |
} | |
case V_UINT8: | |
case V_UINT16: | |
case V_UINT32: | |
case V_UINT64: | |
{ | |
uint64_t value = (uint64_t) (*this); | |
result += format("%"PRIu64, value); | |
break; | |
} | |
case V_DOUBLE: | |
{ | |
result += format("%.4f", (double) (*this)); | |
break; | |
} | |
case V_TIMESTAMP: | |
case V_DATE: | |
case V_TIME: | |
case V_TYPED_MAP: | |
case V_BYTEARRAY: | |
{ | |
result += "\"V_TIMESTAMP,V_DATE,V_TIME,V_TYPED_MAP and V_BYTEARRAY not supported by JSON\""; | |
break; | |
} | |
case V_STRING: | |
{ | |
string value = (string) (*this); | |
EscapeJSON(value); | |
result += value; | |
break; | |
} | |
case V_MAP: | |
{ | |
result += IsArray() ? "[" : "{"; | |
FOR_MAP(_value.m->children, string, Variant, i) { | |
if (!IsArray()) { | |
string key = MAP_KEY(i); | |
EscapeJSON(key); | |
result += key + ":"; | |
} | |
if (!MAP_VAL(i).SerializeToJSON(result)) { | |
FATAL("Unable to serialize to JSON"); | |
return false; | |
} | |
result += ","; | |
} | |
if (_value.m->children.size() > 0) { | |
result[result.size() - 1] = IsArray() ? ']' : '}'; | |
} else { | |
result += IsArray() ? "]" : "}"; | |
} | |
break; | |
} | |
default: | |
{ | |
ASSERT("Invalid type %hhu", _type); | |
break; | |
} | |
} | |
return true; | |
} | |
bool Variant::DeserializeFromCmdLineArgs(uint32_t count, char **pArguments, | |
Variant &result) { | |
if (count < 1) { | |
FATAL("Inavlid parameters count"); | |
return false; | |
} | |
result.Reset(); | |
result["program"] = pArguments[0]; | |
result["arguments"].IsArray(false); | |
for (uint32_t i = 1; i < count; i++) { | |
string keyValue = pArguments[i]; | |
string::size_type separatorPos = string::npos; | |
if ((separatorPos = keyValue.find('=')) == string::npos) { | |
result["arguments"][keyValue] = (bool)true; | |
} else { | |
string key = keyValue.substr(0, separatorPos); | |
string value = keyValue.substr(separatorPos + 1, | |
keyValue.size() - separatorPos); | |
result["arguments"][key] = value; | |
} | |
} | |
return true; | |
} | |
TiXmlElement *Variant::SerializeToXmlElement(string &name) { | |
TiXmlElement *pResult = NULL; | |
switch (_type) { | |
case V_NULL: | |
{ | |
pResult = new TiXmlElement("NULL"); | |
break; | |
} | |
case V_UNDEFINED: | |
{ | |
pResult = new TiXmlElement("UNDEFINED"); | |
break; | |
} | |
case V_BOOL: | |
{ | |
pResult = new TiXmlElement("BOOL"); | |
pResult->LinkEndChild(new TiXmlText(_value.b ? "true" : "false")); | |
break; | |
} | |
case V_INT8: | |
{ | |
pResult = new TiXmlElement("INT8"); | |
pResult->LinkEndChild(new TiXmlText(format("%hhd", _value.i8))); | |
break; | |
} | |
case V_INT16: | |
{ | |
pResult = new TiXmlElement("INT16"); | |
pResult->LinkEndChild(new TiXmlText(format("%hd", _value.i16))); | |
break; | |
} | |
case V_INT32: | |
{ | |
pResult = new TiXmlElement("INT32"); | |
pResult->LinkEndChild(new TiXmlText(format("%d", _value.i32))); | |
break; | |
} | |
case V_INT64: | |
{ | |
pResult = new TiXmlElement("INT64"); | |
pResult->LinkEndChild(new TiXmlText(format("%"PRId64, _value.i64))); | |
break; | |
} | |
case V_UINT8: | |
{ | |
pResult = new TiXmlElement("UINT8"); | |
pResult->LinkEndChild(new TiXmlText(format("%hhu", _value.ui8))); | |
break; | |
} | |
case V_UINT16: | |
{ | |
pResult = new TiXmlElement("UINT16"); | |
pResult->LinkEndChild(new TiXmlText(format("%hu", _value.ui16))); | |
break; | |
} | |
case V_UINT32: | |
{ | |
pResult = new TiXmlElement("UINT32"); | |
pResult->LinkEndChild(new TiXmlText(format("%u", _value.ui32))); | |
break; | |
} | |
case V_UINT64: | |
{ | |
pResult = new TiXmlElement("UINT64"); | |
pResult->LinkEndChild(new TiXmlText(format("%"PRIu64, _value.ui64))); | |
break; | |
} | |
case V_DOUBLE: | |
{ | |
pResult = new TiXmlElement("DOUBLE"); | |
pResult->LinkEndChild(new TiXmlText(format("%.03f", _value.d))); | |
break; | |
} | |
case V_TIMESTAMP: | |
{ | |
pResult = new TiXmlElement("TIMESTAMP"); | |
pResult->LinkEndChild(new TiXmlText(STR(*this))); | |
break; | |
} | |
case V_DATE: | |
{ | |
pResult = new TiXmlElement("DATE"); | |
pResult->LinkEndChild(new TiXmlText(STR(*this))); | |
break; | |
} | |
case V_TIME: | |
{ | |
pResult = new TiXmlElement("TIME"); | |
pResult->LinkEndChild(new TiXmlText(STR(*this))); | |
break; | |
} | |
case V_STRING: | |
{ | |
pResult = new TiXmlElement("STR"); | |
pResult->LinkEndChild(new TiXmlText(STR(*_value.s))); | |
break; | |
} | |
case V_BYTEARRAY: | |
{ | |
pResult = new TiXmlElement("BYTEARRAY"); | |
pResult->LinkEndChild(new TiXmlText(STR(b64(*_value.s)))); | |
break; | |
} | |
case V_TYPED_MAP: | |
case V_MAP: | |
{ | |
if (_type == V_MAP) { | |
pResult = new TiXmlElement("MAP"); | |
} else { | |
pResult = new TiXmlElement("TYPED_MAP"); | |
pResult->SetAttribute("typeName", _value.m->typeName); | |
} | |
pResult->SetAttribute("isArray", _value.m->isArray ? "true" : "false"); | |
FOR_MAP(_value.m->children, string, Variant, i) { | |
string key = MAP_KEY(i); | |
TiXmlElement *pElement = MAP_VAL(i).SerializeToXmlElement(key); | |
if (pElement == NULL) { | |
delete pResult; | |
pResult = NULL; | |
break; | |
} | |
pResult->LinkEndChild(pElement); | |
} | |
break; | |
} | |
default: | |
{ | |
ASSERT("Invalid type: %hhu", _type); | |
return NULL; | |
} | |
} | |
if (pResult != NULL) { | |
pResult->SetAttribute("name", name); | |
} | |
return pResult; | |
} | |
#define VARIANT_CHECK_BOUNDS(s) \ | |
do {\ | |
if(s>bufferSize-cursor) \ | |
{ \ | |
FATAL("Not enough data. Wanted: %u; Got: %u",(uint32_t)s,bufferSize-cursor);\ | |
return false; \ | |
} \ | |
}\ | |
while(0) | |
#define PTR (pBuffer+cursor) | |
bool Variant::DeserializeFromBin(uint8_t *pBuffer, uint32_t bufferSize, | |
Variant &variant, uint32_t &cursor) { | |
VARIANT_CHECK_BOUNDS(1); | |
VariantType type = (VariantType) PTR[0]; | |
cursor += 1; | |
switch (type) { | |
case V_NULL: | |
{ | |
variant.Reset(); | |
return true; | |
} | |
case V_UNDEFINED: | |
{ | |
variant.Reset(true); | |
return true; | |
} | |
case V_BOOL: | |
{ | |
VARIANT_CHECK_BOUNDS(1); | |
variant = (bool)(PTR[0] != 0); | |
cursor += 1; | |
return true; | |
} | |
case V_INT8: | |
{ | |
VARIANT_CHECK_BOUNDS(1); | |
variant = *((int8_t *) PTR); | |
cursor += 1; | |
return true; | |
} | |
case V_INT16: | |
{ | |
VARIANT_CHECK_BOUNDS(2); | |
uint16_t val = ENTOHSP(PTR); //----MARKED-SHORT---- | |
cursor += 2; | |
variant = *((int16_t *) & val); | |
return true; | |
break; | |
} | |
case V_INT32: | |
{ | |
VARIANT_CHECK_BOUNDS(4); | |
uint32_t val = ENTOHLP(PTR); //----MARKED-LONG--- | |
cursor += 4; | |
variant = *((int32_t *) & val); | |
return true; | |
} | |
case V_INT64: | |
{ | |
VARIANT_CHECK_BOUNDS(8); | |
uint64_t val = ENTOHLLP(PTR); //----MARKED-LONG--- | |
cursor += 8; | |
variant = *((int64_t *) & val); | |
return true; | |
} | |
case V_UINT8: | |
{ | |
VARIANT_CHECK_BOUNDS(1); | |
variant = *((uint8_t *) PTR); | |
cursor += 1; | |
return true; | |
} | |
case V_UINT16: | |
{ | |
VARIANT_CHECK_BOUNDS(2); | |
variant = ENTOHSP(PTR); //----MARKED-SHORT---- | |
cursor += 2; | |
return true; | |
} | |
case V_UINT32: | |
{ | |
VARIANT_CHECK_BOUNDS(4); | |
variant = ENTOHLP(PTR); //----MARKED-LONG--- | |
cursor += 4; | |
return true; | |
} | |
case V_UINT64: | |
{ | |
VARIANT_CHECK_BOUNDS(8); | |
variant = (uint64_t) ENTOHLLP(PTR); //----MARKED-LONG--- | |
cursor += 8; | |
return true; | |
} | |
case V_DOUBLE: | |
{ | |
VARIANT_CHECK_BOUNDS(8); | |
double temp = 0; | |
ENTOHDP(PTR, temp); | |
cursor += 8; | |
variant = (double) temp; | |
return true; | |
} | |
case V_TIMESTAMP: | |
case V_DATE: | |
case V_TIME: | |
{ | |
VARIANT_CHECK_BOUNDS(8); | |
time_t val = (time_t) ENTOHLLP(PTR); //----MARKED-LONG--- | |
cursor += 8; | |
variant = *((Timestamp *) gmtime(&val)); | |
variant._type = type; | |
return true; | |
} | |
case V_BYTEARRAY: | |
case V_STRING: | |
{ | |
VARIANT_CHECK_BOUNDS(4); | |
uint32_t length = ENTOHLP(PTR); //----MARKED-LONG--- | |
cursor += 4; | |
VARIANT_CHECK_BOUNDS(length); | |
if (length > 1024 * 128) { | |
FATAL("string too large"); | |
return false; | |
} | |
variant = string((char *) PTR, length); | |
cursor += length; | |
variant.IsByteArray(type == V_BYTEARRAY); | |
return true; | |
} | |
case V_MAP: | |
case V_TYPED_MAP: | |
{ | |
VARIANT_CHECK_BOUNDS(1); | |
bool isArray = (PTR[0] != 0); | |
cursor += 1; | |
variant.IsArray(isArray); | |
uint32_t length = 0; | |
if (type == V_TYPED_MAP) { | |
VARIANT_CHECK_BOUNDS(4); | |
length = ENTOHLP(PTR); //----MARKED-LONG--- | |
cursor += 4; | |
VARIANT_CHECK_BOUNDS(length); | |
if (length > 1024 * 128) { | |
FATAL("string too large"); | |
return false; | |
} | |
string name = string((char *) PTR, length); | |
cursor += length; | |
variant.SetTypeName(name); | |
} | |
VARIANT_CHECK_BOUNDS(4); | |
length = ENTOHLP(PTR); //----MARKED-LONG--- | |
if (length > 1024) { | |
FATAL("Length too large"); | |
return false; | |
} | |
cursor += 4; | |
for (uint32_t i = 0; i < length; i++) { | |
string key; | |
uint32_t keyLength; | |
VARIANT_CHECK_BOUNDS(4); | |
keyLength = ENTOHLP(PTR); //----MARKED-LONG--- | |
cursor += 4; | |
VARIANT_CHECK_BOUNDS(keyLength); | |
if (keyLength > 1024 * 128) { | |
FATAL("string too large"); | |
return false; | |
} | |
key = string((char *) PTR, keyLength); | |
cursor += keyLength; | |
if (!DeserializeFromBin(pBuffer, bufferSize, variant[key], cursor)) { | |
FATAL("Unable to deserialize variant"); | |
return false; | |
} | |
} | |
return true; | |
} | |
default: | |
{ | |
FATAL("Invalid variant type: %hhu", type); | |
return false; | |
} | |
} | |
} | |
bool Variant::DeserializeFromXml(TiXmlElement *pNode, Variant &variant) { | |
string nodeName = lowerCase(pNode->ValueStr()); | |
union { | |
int64_t i64; | |
uint64_t ui64; | |
double d; | |
Timestamp t; | |
} val; | |
const char *pText = pNode->GetText(); | |
string text = pText == NULL ? "" : pText; | |
if (nodeName == "bool") { | |
variant = (bool)(lowerCase(text) == "true"); | |
return true; | |
} else if (nodeName == "null") { | |
variant.Reset(); | |
return true; | |
} else if (nodeName == "undefined") { | |
variant.Reset(true); | |
return true; | |
} else if (nodeName == "int8") { | |
if (sscanf(STR(text), "%"PRId64, &val.i64) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (int8_t) val.i64; | |
return true; | |
} else if (nodeName == "int16") { | |
if (sscanf(STR(text), "%"PRId64, &val.i64) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (int16_t) val.i64; | |
return true; | |
} else if (nodeName == "int32") { | |
if (sscanf(STR(text), "%"PRId64, &val.i64) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (int32_t) val.i64; | |
return true; | |
} else if (nodeName == "int64") { | |
if (sscanf(STR(text), "%"PRId64, &val.i64) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (int64_t) val.i64; | |
return true; | |
} else if (nodeName == "uint8") { | |
if (sscanf(STR(text), "%"PRIu64, &val.ui64) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (uint8_t) val.ui64; | |
return true; | |
} else if (nodeName == "uint16") { | |
if (sscanf(STR(text), "%"PRIu64, &val.ui64) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (uint16_t) val.ui64; | |
return true; | |
} else if (nodeName == "uint32") { | |
if (sscanf(STR(text), "%"PRIu64, &val.ui64) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (uint32_t) val.ui64; | |
return true; | |
} else if (nodeName == "uint64") { | |
if (sscanf(STR(text), "%"PRIu64, &val.ui64) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (uint64_t) val.ui64; | |
return true; | |
} else if (nodeName == "double") { | |
if (sscanf(STR(text), "%lf", &val.d) != 1) { | |
FATAL("Invalid number"); | |
return false; | |
} | |
variant = (double) val.d; | |
return true; | |
} else if (nodeName == "timestamp") { | |
if (strptime(STR(text), "%Y-%m-%dT%H:%M:%S.000", &val.t) == NULL) { | |
FATAL("Invalid timestamp (date, time or timestamp)"); | |
return false; | |
} | |
variant = (Timestamp) val.t; | |
variant._type = V_TIMESTAMP; | |
return true; | |
} else if (nodeName == "date") { | |
if (strptime(STR(text), "%Y-%m-%u", &val.t) == NULL) { | |
FATAL("Invalid timestamp (date, time or timestamp)"); | |
return false; | |
} | |
variant = (Timestamp) val.t; | |
variant._type = V_DATE; | |
return true; | |
} else if (nodeName == "time") { | |
if (strptime(STR(text), "%H:%M:%S.000", &val.t) == NULL) { | |
FATAL("Invalid timestamp (date, time or timestamp)"); | |
return false; | |
} | |
variant = (Timestamp) val.t; | |
variant._type = V_TIME; | |
return true; | |
} else if (nodeName == "str") { | |
variant = text; | |
return true; | |
} else if (nodeName == "bytearray") { | |
variant = unb64(text); | |
variant.IsByteArray(true); | |
return true; | |
} else if (nodeName == "map" || nodeName == "typed_map") { | |
TiXmlAttribute *pAttribute = pNode->FirstAttribute(); | |
//isArray and typename | |
variant.IsArray(false); | |
for (TiXmlAttribute *pI = pAttribute; pI != NULL; pI = pI->Next()) { | |
if (lowerCase(pI->NameTStr()) == "isarray") { | |
variant.IsArray(lowerCase(pI->ValueStr()) == "true"); | |
} else if (lowerCase(pI->NameTStr()) == "typename") { | |
variant.SetTypeName(pI->Value()); | |
} | |
} | |
//childs | |
for (TiXmlElement *pE = pNode->FirstChildElement(); pE != NULL; pE = pE->NextSiblingElement()) { | |
//search for the name | |
string key = ""; | |
for (TiXmlAttribute *pA = pE->FirstAttribute(); pA != NULL; pA = pA->Next()) { | |
if (lowerCase(pA->NameTStr()) == "name") { | |
key = pA->ValueStr(); | |
break; | |
} | |
} | |
if (!DeserializeFromXml(pE, variant[key])) { | |
FATAL("Unable to deserialize element: %s", STR(key)); | |
return false; | |
} | |
} | |
return true; | |
} else { | |
FATAL("Invalid node name: %s", STR(nodeName)); | |
return false; | |
} | |
} | |
void Variant::InternalCopy(const Variant &val) { | |
_type = val._type; | |
memset(&_value, 0, sizeof (_value)); | |
switch (val._type) { | |
case V_DATE: | |
case V_TIME: | |
case V_TIMESTAMP: | |
{ | |
DYNAMIC_ALLOC("_value.t"); | |
_value.t = new Timestamp(*val._value.t); | |
break; | |
} | |
case V_BYTEARRAY: | |
case V_STRING: | |
{ | |
DYNAMIC_ALLOC("_value.s"); | |
_value.s = new string(*val._value.s); | |
break; | |
} | |
case V_MAP: | |
case V_TYPED_MAP: | |
{ | |
DYNAMIC_ALLOC("_value.m"); | |
_value.m = new VariantMap(*val._value.m); | |
break; | |
} | |
default: | |
{ | |
memcpy(&_value, &val._value, sizeof (_value)); | |
break; | |
} | |
} | |
} | |
void Variant::NormalizeTs() { | |
time_t val = timegm(_value.t); | |
if (val < 0) { | |
val = 0; | |
} | |
gmtime_r(&val, _value.t); | |
} | |
void Variant::EscapeJSON(string &value) { | |
replace(value, "\\", "\\\\"); | |
replace(value, "/", "\\/"); | |
replace(value, "\"", "\\\""); | |
replace(value, "\b", "\\b"); | |
replace(value, "\f", "\\f"); | |
replace(value, "\n", "\\n"); | |
replace(value, "\r", "\\r"); | |
replace(value, "\t", "\\t"); | |
value = "\"" + value + "\""; | |
} | |
void Variant::UnEscapeJSON(string &value) { | |
replace(value, "\\/", "/"); | |
replace(value, "\\\"", "\""); | |
replace(value, "\\b", "\b"); | |
replace(value, "\\f", "\f"); | |
replace(value, "\\n", "\n"); | |
replace(value, "\\r", "\r"); | |
replace(value, "\\t", "\t"); | |
replace(value, "\\\\", "\\"); | |
} | |
bool Variant::ReadJSONWhiteSpace(string &raw, uint32_t &start) { | |
for (; start < raw.length(); start++) { | |
if ((raw[start] != ' ') | |
&& (raw[start] != '\t') | |
&& (raw[start] != '\r') | |
&& (raw[start] != '\n')) | |
break; | |
} | |
return true; | |
} | |
bool Variant::ReadJSONDelimiter(string &raw, uint32_t &start, char &c) { | |
if (!ReadJSONWhiteSpace(raw, start)) { | |
FATAL("Invalid JSON object"); | |
return false; | |
} | |
if ((raw.size() - start) < 1) { | |
FATAL("Invalid JSON delimiter"); | |
return false; | |
} | |
c = raw[start]; | |
start++; | |
return ReadJSONWhiteSpace(raw, start); | |
} | |
bool Variant::ReadJSONString(string &raw, Variant &result, uint32_t &start) { | |
if ((raw.size() - start) < 2) { | |
FATAL("Invalid JSON string"); | |
return false; | |
} | |
if (raw[start] != '\"') { | |
FATAL("Invalid JSON string: %u", start); | |
return false; | |
} | |
start++; | |
string::size_type pos = start; | |
while (true) { | |
pos = raw.find('\"', pos); | |
if (pos == string::npos) { | |
FATAL("Invalid JSON string"); | |
return false; | |
} | |
if (raw[pos - 1] == '\\') { | |
pos++; | |
} else { | |
string value = raw.substr(start, pos - start); | |
UnEscapeJSON(value); | |
result = value; | |
start = pos + 1; | |
return true; | |
} | |
} | |
} | |
bool Variant::ReadJSONNumber(string &raw, Variant &result, uint32_t &start) { | |
string str = ""; | |
for (; start < raw.length(); start++) { | |
if ((raw[start] < '0') | |
|| (raw[start] > '9')) { | |
break; | |
} | |
str += raw[start]; | |
} | |
if (str == "") { | |
FATAL("Invalid JSON number"); | |
return false; | |
} | |
result = (int64_t) atoll(STR(str)); | |
return true; | |
} | |
bool Variant::ReadJSONObject(string &raw, Variant &result, uint32_t &start) { | |
result.Reset(); | |
result.IsArray(false); | |
if ((raw.size() - start) < 2) { | |
FATAL("Invalid JSON array"); | |
return false; | |
} | |
if (raw[start] != '{') { | |
FATAL("Invalid JSON object"); | |
return false; | |
} | |
start++; | |
char c; | |
while (start < raw.length()) { | |
if (raw[start] == '}') { | |
start++; | |
return true; | |
} | |
Variant key; | |
if (!Variant::DeserializeFromJSON(raw, key, start)) { | |
FATAL("Invalid JSON object"); | |
return false; | |
} | |
if (!ReadJSONDelimiter(raw, start, c)) { | |
FATAL("Invalid JSON object"); | |
return false; | |
} | |
if (c != ':') { | |
FATAL("Invalid JSON object"); | |
return false; | |
} | |
Variant value; | |
if (!Variant::DeserializeFromJSON(raw, value, start)) { | |
FATAL("Invalid JSON object"); | |
return false; | |
} | |
result[key] = value; | |
if (!ReadJSONDelimiter(raw, start, c)) { | |
FATAL("Invalid JSON object"); | |
return false; | |
} | |
if (c == '}') { | |
return true; | |
} else if (c == ',') { | |
continue; | |
} else { | |
FATAL("Invalid JSON object"); | |
return false; | |
} | |
} | |
return false; | |
} | |
bool Variant::ReadJSONArray(string &raw, Variant &result, uint32_t &start) { | |
result.Reset(); | |
result.IsArray(true); | |
if ((raw.size() - start) < 2) { | |
FATAL("Invalid JSON array"); | |
return false; | |
} | |
if (raw[start] != '[') { | |
FATAL("Invalid JSON array"); | |
return false; | |
} | |
start++; | |
char c; | |
while (start < raw.length()) { | |
if (raw[start] == ']') { | |
start++; | |
return true; | |
} | |
Variant value; | |
if (!Variant::DeserializeFromJSON(raw, value, start)) { | |
FATAL("Invalid JSON array"); | |
return false; | |
} | |
result.PushToArray(value); | |
if (!ReadJSONDelimiter(raw, start, c)) { | |
FATAL("Invalid JSON array"); | |
return false; | |
} | |
if (c == ']') { | |
return true; | |
} else if (c == ',') { | |
continue; | |
} else { | |
FATAL("Invalid JSON array"); | |
return false; | |
} | |
} | |
return false; | |
} | |
bool Variant::ReadJSONBool(string &raw, Variant &result, uint32_t &start, string wanted) { | |
if ((raw.size() - start) < wanted.size()) { | |
FATAL("Invalid JSON bool"); | |
return false; | |
} | |
string temp = lowerCase(raw.substr(start, wanted.size())); | |
if (temp != wanted) { | |
FATAL("Invalid JSON bool"); | |
return false; | |
} | |
start += wanted.size(); | |
result = (bool)(wanted == "true"); | |
return true; | |
} | |
bool Variant::ReadJSONNull(string &raw, Variant &result, uint32_t &start) { | |
if ((raw.size() - start) < 4) { | |
FATAL("Invalid JSON null"); | |
return false; | |
} | |
string temp = lowerCase(raw.substr(start, 4)); | |
if (temp != "null") { | |
FATAL("Invalid JSON null"); | |
return false; | |
} | |
start += 4; | |
result.Reset(); | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment