Skip to content

Instantly share code, notes, and snippets.

@elliotwoods
Created July 10, 2012 09:28
Show Gist options
  • Save elliotwoods/3082283 to your computer and use it in GitHub Desktop.
Save elliotwoods/3082283 to your computer and use it in GitHub Desktop.
Variant.cpp
/*
* 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