#include "JSONSerializer.hpp" #include "../common/compat.hpp" #include <cmath> #include <stdlib.h> #ifndef SPRAWL_STRICT_JSON # define SPRAWL_STRICT_JSON 0 #endif #ifdef _WIN32 # define snprintf _snprintf #endif namespace sprawl { namespace serialization { JSONToken JSONToken::staticEmpty(JSONToken::JSONType::Empty); JSONToken const& JSONToken::operator[](sprawl::String const& key) const { if( m_holder->m_type != JSONType::Object ) { return staticEmpty; } StringData keyData(key.c_str(), key.length()); auto it = m_holder->m_objectChildren->find( keyData ); if( it == m_holder->m_objectChildren->end() ) { return staticEmpty; } return it.Value(); } JSONToken const& JSONToken::operator[](ssize_t index) const { if( m_holder->m_type != JSONType::Array || index >= m_holder->m_arrayChildren.Size() ) { return staticEmpty; } return m_holder->m_arrayChildren[index]; } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::operator[](sprawl::String const& key) { StringData keyData(key.c_str(), key.length()); if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } auto it = m_holder->m_objectChildren->find( keyData ); if( it == m_holder->m_objectChildren->end() ) { keyData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken( keyData, JSONType::Empty ) ); return it.Value(); } return it.Value(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::operator[](ssize_t index) { if( m_holder->m_type != JSONType::Array || index >= m_holder->m_arrayChildren.Size() ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } return m_holder->m_arrayChildren[index]; } size_t JSONToken::Size() { switch( m_holder->m_type ) { case JSONType::Object: { return m_holder->m_objectChildren->Size(); } case JSONType::Array: { return size_t(m_holder->m_arrayChildren.Size()); } case JSONType::Null: case JSONType::Empty: { return 0; } case JSONType::Boolean: case JSONType::Double: case JSONType::Integer: case JSONType::String: default: { return 1; } } } sprawl::String JSONToken::ToJSONString(bool pretty) { sprawl::StringBuilder outString; BuildJSONString(outString, pretty, 1); return outString.Str(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::PushBack(JSONToken const& token) { if( m_holder->m_type != JSONType::Array ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } m_holder->m_arrayChildren.EmplaceBack( token ); return m_holder->m_arrayChildren.Back(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::PushBack(unsigned long long value) { if( m_holder->m_type != JSONType::Array ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } m_holder->m_arrayChildren.EmplaceBack( JSONType::Integer, value ); return m_holder->m_arrayChildren.Back(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::PushBack(long long value) { if( m_holder->m_type != JSONType::Array ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } m_holder->m_arrayChildren.EmplaceBack( JSONType::Integer, value ); return m_holder->m_arrayChildren.Back(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::PushBack(long double value) { if( m_holder->m_type != JSONType::Array ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } m_holder->m_arrayChildren.EmplaceBack( JSONType::Double, value ); return m_holder->m_arrayChildren.Back(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::PushBack(bool value) { if( m_holder->m_type != JSONType::Array ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } m_holder->m_arrayChildren.EmplaceBack( JSONType::Boolean, value ); return m_holder->m_arrayChildren.Back(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::PushBack(const char* const value) { if( m_holder->m_type != JSONType::Array ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } m_holder->m_arrayChildren.EmplaceBack( JSONType::String, sprawl::String(value) ); return m_holder->m_arrayChildren.Back(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::PushBack(const char* const value, size_t length) { if( m_holder->m_type != JSONType::Array ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } m_holder->m_arrayChildren.EmplaceBack( JSONType::String, sprawl::String(value, length) ); return m_holder->m_arrayChildren.Back(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::PushBack(sprawl::String const& value) { if( m_holder->m_type != JSONType::Array ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } m_holder->m_arrayChildren.EmplaceBack( JSONType::String, value ); return m_holder->m_arrayChildren.Back(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::Insert(sprawl::String const& name, JSONToken const& token) { if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } StringData nameData(name.c_str(), name.length()); nameData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken(nameData, token) ); return it.Value(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::Insert(sprawl::String const& name, unsigned long long value) { if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } StringData nameData(name.c_str(), name.length()); nameData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken(nameData, JSONType::Integer, value) ); return it.Value(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::Insert(sprawl::String const& name, long long value) { if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } StringData nameData(name.c_str(), name.length()); nameData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken(nameData, JSONType::Integer, value) ); return it.Value(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::Insert(sprawl::String const& name, long double value) { if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } StringData nameData(name.c_str(), name.length()); nameData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken(nameData, JSONType::Double, value) ); return it.Value(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::Insert(sprawl::String const& name, bool value) { if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } StringData nameData(name.c_str(), name.length()); nameData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken(nameData, JSONType::Boolean, value) ); return it.Value(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::Insert(sprawl::String const& name, const char* const value) { if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } StringData nameData(name.c_str(), name.length()); nameData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken(nameData, JSONType::String, sprawl::String(value)) ); return it.Value(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::Insert(sprawl::String const& name, const char* const value, size_t length) { if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } StringData nameData(name.c_str(), name.length()); nameData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken(nameData, JSONType::String, sprawl::String(value, length)) ); return it.Value(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<JSONToken&> JSONToken::Insert(sprawl::String const& name, sprawl::String const& value) { if( m_holder->m_type != JSONType::Object ) { SPRAWL_THROW_EXCEPTION(sprawl::InvalidJsonData()); } StringData nameData(name.c_str(), name.length()); nameData.CommitStorage(); auto it = m_holder->m_objectChildren->Insert( JSONToken(nameData, JSONType::String, value) ); return it.Value(); } JSONToken::JSONToken(JSONToken::JSONType statedType, sprawl::String const& data) : m_holder(Holder::Create()) , m_key(nullptr, 0) { ::new(m_holder) Holder(statedType); m_holder->m_data = StringData(data.c_str(), data.length()); m_holder->m_data.CommitStorage(); } JSONToken::JSONToken(JSONToken::JSONType statedType, bool data) : m_holder(Holder::Create()) , m_key(nullptr, 0) { ::new(m_holder) Holder(statedType); if(data) { m_holder->m_data = StringData( "true", 4 ); } else { m_holder->m_data = StringData( "false", 5 ); } } JSONToken::JSONToken(JSONToken::JSONType statedType) : m_holder(Holder::Create()) , m_key(nullptr, 0) { ::new(m_holder) Holder(statedType); } JSONToken::JSONToken(JSONToken::JSONType statedType, long long data) : m_holder(Holder::Create()) , m_key(nullptr, 0) { ::new(m_holder) Holder(statedType); char buf[128]; snprintf( buf, 128, "%lld", data ); m_holder->m_data = StringData( buf, strlen( buf ) ); m_holder->m_data.CommitStorage(); } JSONToken::JSONToken(JSONToken::JSONType statedType, unsigned long long data) : m_holder(Holder::Create()) , m_key(nullptr, 0) { ::new(m_holder) Holder(statedType); char buf[128]; snprintf( buf, 128, "%llu", data ); m_holder->m_data = StringData( buf, strlen( buf ) ); m_holder->m_data.CommitStorage(); } JSONToken::JSONToken(JSONToken::JSONType statedType, long double data) : m_holder(Holder::Create()) , m_key(nullptr, 0) { ::new(m_holder) Holder(statedType); char buf[128]; snprintf( buf, 128, "%.20Lg", data ); m_holder->m_data = StringData( buf, strlen( buf ) ); m_holder->m_data.CommitStorage(); } JSONToken::JSONToken(StringData const& myKey, JSONToken::JSONType statedType, sprawl::String const& data) : m_holder(Holder::Create()) , m_key(myKey) { m_key.CommitStorage(); ::new(m_holder) Holder(statedType); m_holder->m_data = StringData(data.c_str(), data.length()); m_holder->m_data.CommitStorage(); } JSONToken::JSONToken(StringData const& myKey, JSONToken::JSONType statedType, bool data) : m_holder(Holder::Create()) , m_key(myKey) { m_key.CommitStorage(); ::new(m_holder) Holder(statedType); if(data) { m_holder->m_data = StringData( "true", 4 ); } else { m_holder->m_data = StringData( "false", 5 ); } } JSONToken::JSONToken(StringData const& myKey, JSONToken::JSONType statedType) : m_holder(Holder::Create()) , m_key(myKey) { m_key.CommitStorage(); ::new(m_holder) Holder(statedType); } JSONToken::JSONToken(StringData const& myKey, JSONToken::JSONType statedType, long long data) : m_holder(Holder::Create()) , m_key(myKey) { m_key.CommitStorage(); ::new(m_holder) Holder(statedType); char buf[128]; snprintf( buf, 128, "%lld", data ); m_holder->m_data = StringData( buf, strlen( buf ) ); m_holder->m_data.CommitStorage(); } JSONToken::JSONToken(StringData const& myKey, JSONToken::JSONType statedType, unsigned long long data) : m_holder(Holder::Create()) , m_key(myKey) { m_key.CommitStorage(); ::new(m_holder) Holder(statedType); char buf[128]; snprintf( buf, 128, "%llu", data ); m_holder->m_data = StringData( buf, strlen( buf ) ); m_holder->m_data.CommitStorage(); } JSONToken::JSONToken(StringData const& myKey, JSONToken::JSONType statedType, long double data) : m_holder(Holder::Create()) , m_key(myKey) { m_key.CommitStorage(); ::new(m_holder) Holder(statedType); char buf[128]; snprintf( buf, 128, "%.20Lg", data ); m_holder->m_data = StringData( buf, strlen( buf ) ); m_holder->m_data.CommitStorage(); } JSONToken* JSONToken::Create() { typedef memory::PoolAllocator<sizeof(JSONToken)> allocator; return (JSONToken*)allocator::alloc(); } void JSONToken::Free(JSONToken* token) { typedef memory::PoolAllocator<sizeof(JSONToken)> allocator; token->~JSONToken(); allocator::free(token); } void JSONToken::BuildJSONString(StringBuilder& outString, bool pretty, int tabDepth) { if( m_holder->m_type == JSONType::Object ) { outString << "{"; if( pretty ) { outString << "\n"; } else { outString << " "; } bool first = true; for( auto kvp : *m_holder->m_objectChildren ) { if(kvp.Value().IsEmpty()) { continue; } if( !first ) { outString << ","; if( pretty ) { outString << "\n"; } else { outString << " "; } } if( pretty ) { for( int i = 0; i < tabDepth; ++i ) { outString << "\t"; } } outString << "\""; outString << sprawl::String( sprawl::StringRef( kvp.Key().c_str(), kvp.Key().length() ) ); outString << "\" : "; kvp.Value().BuildJSONString( outString, pretty, tabDepth + 1 ); first = false; } if( pretty ) { outString << "\n"; for( int i = 0; i < tabDepth - 1; ++i ) { outString << "\t"; } } else { outString << " "; } outString << "}"; } else if( m_holder->m_type == JSONType::Array ) { outString << "["; if( pretty ) { outString << "\n"; } else { outString << " "; } bool first = true; for( auto& it : m_holder->m_arrayChildren ) { if(it.IsEmpty()) { continue; } if( !first ) { outString << ","; if( pretty ) { outString << "\n"; } else { outString << " "; } } if( pretty ) { for( int i = 0; i < tabDepth; ++i ) { outString << "\t"; } } it.BuildJSONString( outString, pretty, tabDepth + 1 ); first = false; } if( pretty ) { outString << "\n"; for( int i = 0; i < tabDepth - 1; ++i ) { outString << "\t"; } } else { outString << " "; } outString << "]"; } else if( m_holder->m_type == JSONType::Null ) { outString << "null"; } else if(m_holder->m_type == JSONType::String) { outString << "\""; outString << EscapeString(m_holder->m_data); outString << "\""; } else { outString << sprawl::String( sprawl::StringRef( m_holder->m_data.c_str(), m_holder->m_data.length() ) ); } } /// TODO PERFORMANCE: Replace sprawl::String here with a non-ref-counted string class that will simply store a pointer and a length with no allocations void JSONToken::SkipWhitespace(char const*& data) { while(*data == ' ' || *data == '\t' || *data == '\n' || *data == '\r') ++data; } void JSONToken::CollectString(char const*& data) { ++data; while(*data != '\"') { if(SPRAWL_UNLIKELY(*data == '\\')) { ++data; } ++data; } ++data; } void JSONToken::ParseString(char const*& data) { char const* startPoint = data + 1; CollectString(data); m_holder->m_data = StringData(startPoint, data - startPoint - 1); } void JSONToken::ParseNumber(char const*& data) { char const* startPoint = data; for(;;) { switch( *data ) { case '-': case '+': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { ++data; break; } case '.': case 'e': case 'E': { if( m_holder->m_type == JSONType::Integer ) { m_holder->m_type = JSONType::Double; } ++data; break; } default: { m_holder->m_data = StringData(startPoint, data - startPoint); return; } } } } void JSONToken::ParseBool(char const*& data) { if( *data == 't' #if SPRAWL_STRICT_JSON && *(data + 1) == 'r' && *(data + 2) == 'u' && *(data + 3) == 'e' #endif ) { m_holder->m_data = StringData(data, 4); data += 4; } else if( *data == 'f' #if SPRAWL_STRICT_JSON && *(data + 1) == 'a' && *(data + 2) == 'l' && *(data + 3) == 's' && *(data + 4) == 'e' #endif ) { m_holder->m_data = StringData(data, 5); data += 5; } #if SPRAWL_STRICT_JSON else { SPRAWL_THROW_EXCEPTION_OR_ABORT(sprawl::InvalidJsonData()); } #endif return; } void JSONToken::ParseArray(char const*& data) { ++data; for(;;) { SkipWhitespace(data); if(*data == ']') { ++data; return; } switch(*data) { case '{': { m_holder->m_arrayChildren.EmplaceBack( StringData(nullptr, 0), data, JSONType::Object ); break; } case '[': { m_holder->m_arrayChildren.EmplaceBack( StringData(nullptr, 0), data, JSONType::Array ); break; } case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { m_holder->m_arrayChildren.EmplaceBack( StringData(nullptr, 0), data, JSONType::Integer ); break; } case '\"': { m_holder->m_arrayChildren.EmplaceBack( StringData(nullptr, 0), data, JSONType::String ); break; } case 't': case 'f': { m_holder->m_arrayChildren.EmplaceBack( StringData(nullptr, 0), data, JSONType::Boolean ); break; } case 'n': { m_holder->m_arrayChildren.EmplaceBack( JSONType::Null ); data += 4; break; } #if SPRAWL_STRICT_JSON default: { SPRAWL_THROW_EXCEPTION_OR_ABORT(sprawl::InvalidJsonData()); } #endif } SkipWhitespace(data); #if SPRAWL_STRICT_JSON if(*data != ',' && *data != ']') { SPRAWL_THROW_EXCEPTION_OR_ABORT(sprawl::InvalidJsonData()); } #endif if(*data == ']') { ++data; break; } ++data; } } void JSONToken::ParseObject(char const*& data) { ++data; StringData key(nullptr, 0); for(;;) { SkipWhitespace(data); if(*data == '}') { ++data; return; } #if SPRAWL_STRICT_JSON if(*data != '\"') { SPRAWL_THROW_EXCEPTION_OR_ABORT(sprawl::InvalidJsonData()); } #endif char const* keyStart = nullptr; char const* keyEnd = nullptr; keyStart = data + 1; CollectString(data); keyEnd = data - 1; key = StringData( keyStart, keyEnd - keyStart ); SkipWhitespace(data); #if SPRAWL_STRICT_JSON if(*data != ':') { SPRAWL_THROW_EXCEPTION_OR_ABORT(sprawl::InvalidJsonData()); } #endif ++data; SkipWhitespace(data); switch(*data) { case '{': { m_holder->m_objectChildren->Insert( JSONToken( key, data, JSONType::Object ) ); break; } case '[': { m_holder->m_objectChildren->Insert( JSONToken( key, data, JSONType::Array ) ); break; } case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { m_holder->m_objectChildren->Insert( JSONToken( key, data, JSONType::Integer ) ); break; } case '\"': { m_holder->m_objectChildren->Insert( JSONToken( key, data, JSONType::String ) ); break; } case 't': case 'f': { m_holder->m_objectChildren->Insert( JSONToken( key, data, JSONType::Boolean ) ); break; } case 'n': { m_holder->m_objectChildren->Insert( JSONToken( JSONType::Null ) ); #if SPRAWL_STRICT_JSON if(*(data + 1) != 'u' || *(data + 2) != 'l' || *(data + 3) != 'l') { SPRAWL_THROW_EXCEPTION_OR_ABORT(sprawl::InvalidJsonData()); } #endif data += 4; break; } #if SPRAWL_STRICT_JSON default: { SPRAWL_THROW_EXCEPTION_OR_ABORT(sprawl::InvalidJsonData()); } #endif } SkipWhitespace(data); #if SPRAWL_STRICT_JSON if(*data != ',' && *data != '}') { SPRAWL_THROW_EXCEPTION_OR_ABORT(sprawl::InvalidJsonData()); } #endif if(*data == '}') { ++data; break; } ++data; } } JSONToken::JSONToken(StringData const& myKey, char const*& data, JSONToken::JSONType expectedType) : m_holder(Holder::Create()) , m_key(myKey) { ::new(m_holder) Holder(expectedType); switch(expectedType) { case JSONType::Boolean: ParseBool(data); break; case JSONType::Integer: ParseNumber(data); break; case JSONType::String: ParseString(data); break; case JSONType::Array: ParseArray(data); break; case JSONType::Object: ParseObject(data); break; // "Empty" and "Null" have no content to parse. // "Double" will never actually show up here - it will begin its life as "Integer" and grow into "Double" later! case JSONType::Empty: case JSONType::Double: case JSONType::Null: default: break; } } JSONToken::~JSONToken() { DecRef(); } JSONToken::JSONToken() : m_holder(Holder::Create()) , m_key(nullptr, 0) { ::new(m_holder) Holder(JSONType::Empty); } JSONToken::JSONToken(JSONToken const& other) : m_holder(other.m_holder) , m_key(other.m_key) { IncRef(); } JSONToken::JSONToken(StringData const& myKey, JSONToken const& other) : m_holder(other.m_holder) , m_key(myKey) { IncRef(); } JSONToken JSONToken::ShallowCopy() { JSONToken ret(m_key, m_holder->m_type); Holder* newHolder = ret.m_holder; //We do not want to copy object children directly. They need cleanup work. newHolder->m_data = m_holder->m_data; if(newHolder->m_type == JSONType::Array) { newHolder->m_arrayChildren = m_holder->m_arrayChildren; } if(newHolder->m_type == JSONType::Object) { for(auto kvp = m_holder->m_objectChildren->begin(); kvp; ++kvp) { newHolder->m_objectChildren->Insert( JSONToken(kvp.Key(), kvp.Value()) ); } } return std::move(ret); } JSONToken JSONToken::DeepCopy() { JSONToken ret(m_key, m_holder->m_type); Holder* newHolder = ret.m_holder; //We do not want to copy object children directly. They need cleanup work. newHolder->m_data = m_holder->m_data; if(newHolder->m_type == JSONType::Array) { for(int i = 0; i < m_holder->m_arrayChildren.Size(); ++i) { newHolder->m_arrayChildren.EmplaceBack(std::move(m_holder->m_arrayChildren[i].DeepCopy())); } } if(newHolder->m_type == JSONType::Object) { for(auto kvp = m_holder->m_objectChildren->begin(); kvp; ++kvp) { newHolder->m_objectChildren->Insert( kvp.Value().DeepCopy() ); } } return std::move(ret); } JSONToken& JSONToken::operator=(JSONToken const& other) { DecRef(); m_holder = other.m_holder; IncRef(); return *this; } JSONToken::iterator JSONToken::begin() { if(m_holder->m_type == JSONType::Array) { return iterator(m_holder->m_arrayChildren.begin()); } else if(m_holder->m_type == JSONType::Object) { return iterator(m_holder->m_objectChildren->begin()); } return iterator(nullptr); } JSONToken::iterator JSONToken::end() { if(m_holder->m_type == JSONType::Array) { return iterator(m_holder->m_arrayChildren.end()); } else if(m_holder->m_type == JSONType::Object) { return iterator(m_holder->m_objectChildren->end()); } return iterator(nullptr); } long long JSONToken::ToInt(StringData const& str) { long long result = 0; size_t index = 0; bool negative = false; char const* const data = str.c_str(); size_t const length = str.length(); if( *data == '-' ) { negative = true; ++index; } for( ; index < length; ++index ) { result *= 10; result += ( (int)( data[index] ) - 48 ); } if( negative ) { result *= -1; } return result; } unsigned long long JSONToken::ToUInt(StringData const& str) { unsigned long long result = 0; size_t index = 0; char const* const data = str.c_str(); size_t const length = str.length(); for( ; index < length; ++index ) { result *= 10; result += ( (int)( data[index] ) - 48 ); } return result; } long double JSONToken::ToDouble(StringData const& str) { long double result = 0; size_t index = 0; bool negative = false; double fractionSize = 1.0; bool inFraction = false; char const* const data = str.c_str(); size_t const length = str.length(); bool exp = false; double expVal = 0; bool expNegative = false; if( *data == '-' ) { negative = true; ++index; } for( ; index < length; ++index ) { char c = data[index]; if( c == '.' ) { inFraction = true; continue; } if(c == 'e' || c == 'E') { exp = true; if(index != length - 1) { if(data[index+1] == '-') { ++index; expNegative = true; } else if(data[index+1] == '+') { ++index; } continue; } continue; } if(exp) { expVal *= 10; expVal += ( (int)( data[index] ) - 48 ); } else { result *= 10; result += ( (int)( data[index] ) - 48 ); if( inFraction ) { fractionSize *= 10.0; } } } if( negative ) { result *= -1; } result /= fractionSize; if(exp) { double mult = pow(10.0, expVal); if(expNegative) { result /= mult; } else { result *= mult; } } return result; } bool JSONToken::ToBool(StringData const& str) { char const* const data = str.c_str(); size_t const length = str.length(); if( length == 4 && data[0] == 't' && data[1] == 'r' && data[2] == 'u' && data[3] == 'e' ) { return true; } return false; } sprawl::String JSONToken::EscapeString(StringData const& str) { sprawl::StringBuilder builder; const char* data = str.c_str(); const size_t length = str.length(); for(size_t index = 0; index < length; ++index) { switch(data[index]) { case '\"': builder << "\\\""; break; case '\\': builder << "\\\\"; break; case '\b': builder << "\\b"; break; case '\f': builder << "\\f"; break; case '\n': builder << "\\n"; break; case '\r': builder << "\\r"; break; case '\t': builder << "\\t"; break; default: builder << data[index]; break; } } return builder.Str(); } sprawl::String JSONToken::UnescapeString(StringData const& str) { sprawl::StringBuilder builder; bool inEscape = false; const char* data = str.c_str(); const size_t length = str.length(); for(size_t index = 0; index < length; ++index) { if(inEscape) { switch(data[index]) { case '\"': builder << '\"'; break; case '\\' : builder << '\\'; break; case '/' : builder << '/'; break; case 'b': builder << '\b'; break; case 'f': builder << '\f'; break; case 'n': builder << '\n'; break; case 'r': builder << '\r'; break; case 't': builder << '\t'; break; case 'u': { ++index; char digits[5]; memcpy(digits, &data[index], 5); digits[4] = '\0'; union UnicodeChar { int32_t asInt; char asChar[4]; } ch; ch.asInt= strtol(digits, nullptr, 16); if (ch.asInt < 0x80) { builder << ch.asChar[0]; } else if (ch.asInt < 0x800) { builder << char((ch.asInt>>6) | 0xC0); builder << char((ch.asInt & 0x3F) | 0x80); } else if (ch.asInt < 0x10000) { builder << char((ch.asInt>>12) | 0xE0); builder << char(((ch.asInt>>6) & 0x3F) | 0x80); builder << char((ch.asInt & 0x3F) | 0x80); } else if (ch.asInt < 0x110000) { builder << char((ch.asInt>>18) | 0xF0); builder << char(((ch.asInt>>12) & 0x3F) | 0x80); builder << char(((ch.asInt>>6) & 0x3F) | 0x80); builder << char((ch.asInt & 0x3F) | 0x80); } index += 3; break; } default: builder << '\\' << data[index]; break; } inEscape = false; } else { if(data[index] == '\\') { inEscape = true; } else { builder << data[index]; } } } return builder.Str(); } JSONToken::Holder* JSONToken::Holder::Create() { typedef memory::PoolAllocator<sizeof(JSONToken::Holder)> holderAlloc; return (JSONToken::Holder*)holderAlloc::alloc(); } void JSONToken::Holder::Free(JSONToken::Holder* holder) { typedef memory::PoolAllocator<sizeof(JSONToken::Holder)> holderAlloc; holder->~Holder(); holderAlloc::free(holder); } JSONToken::Holder::Holder(JSONType forType) : m_data(nullptr, 0) , m_type(forType) , m_objectChildren(nullptr) , m_arrayChildren(sprawl::collections::Capacity(0)) , refCount(1) { typedef memory::PoolAllocator<sizeof(JSONToken::TokenMap)> mapAlloc; if(forType == JSONType::Array) { //Give the array a little starting space so we have fewer reallocs of it. m_arrayChildren.Reserve(16); } else if(forType == JSONType::Object) { m_objectChildren = (TokenMap*)mapAlloc::alloc(); ::new(m_objectChildren) TokenMap(64); } } JSONToken::Holder::~Holder() { typedef memory::PoolAllocator<sizeof(JSONToken::TokenMap)> mapAlloc; if(m_objectChildren) { m_objectChildren->~TokenMap(); mapAlloc::free(m_objectChildren); } } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#9 | 16768 | ShadauxCat |
Improvements to error handling in builds with exceptions disabled: - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if Get() is called when an error flag is set. (In release buils or with SPRAWL_ERRORSTATE_PERMISSIVE defined, Get() will return junk memory in this case.) - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if its destructor is called without checking the errorstate if an error is present (equivalent to an exception terminating the application if no catch() block is present for it). - On linux builds and when running "Analyze" through visual studio, a warning will be issued if any function returning ErrorState has its return value ignored. (This only applies to builds with exceptions not enabled; when exceptions are enabled no warning is issued) - Many functions that could return ErrorState were having their return values silently ignored in internal sprawl code so the user would not find out about errors if exceptions are disabled; now anything in sprawl code that calls a function returning ErrorState will either handle the error, or (in most cases) surface it back up to the user. - As a positive side-effect of the warnings for ignoring ErrorState, several constructors that were capable of throwing exceptions are no longer capable of doing so. #review-16769 |
||
#8 | 16378 | ShadauxCat |
New exception framework, phase one. Added new class, ErrorState, for handling errors when exceptions are disabled. When exceptions are enabled, ErrorState<T> is an alias for T. When they're disabled, ErrorState<T> additionally encodes an error code, and will have junk data (and probably a crash) if an error is returned and not checked before the data is used. ErrorState<T> is implicitly convertible to and from T, so applications that don't care about errors can code like they don't exist... Phase two will involve overloading operators on ErrorState so that things that return ErrorState can still function as much as possible like they do when exceptions are enabled. #review-16379 |
||
#7 | 16171 | ShadauxCat |
- Created type traits header including macros to make checks for types having any operator or member function, and seeded it with every operator operating on the same type. For function and/or type combinations not already handled, creating a new type traits struct is just a single line of code. - Updated coroutine to use the new type traits header and removed the explicit operator() check - Added alternative to SPRAWL_THROW_EXCEPTION - SPRAWL_THROW_EXCEPTION_OR_ABORT, which as it sounds, will either throw the exception if exceptions are enabled, otherwise will print the message and abort. This is for cases where returning a value reliably isn't possible, such as in constructors or code that returns a template type that may not be default constructible. - Added a layer of safety around coroutines; trying to call the various functions that pause the current coroutine will now throw an exception (if enabled) or abort (if not) if you try to call the wrong pause function (i.e., calling an empty yield() on a coroutine that's supposed to return a value) - Added ability to determine what type a CoroutineBase object actually is underneath - Added function in logging to flush one specific category instead of flushing everything #review-16172 |
||
#6 | 16081 | ShadauxCat |
Addressing tasks from previous review. #review-16082 |
||
#5 | 16052 | ShadauxCat |
- Changed default block size for concurrent queue to a more reasonable value - Changed some memory orders to memory_order_seq_cst when they don't actually need to be that to get around a bug in visual studio 2013 - debug builds assert when memory_order_acq_rel is used for a compare_exchange_strong (this is a standard library bug and is fixed in VS2015) - Added Event API - events are an alternative to condition variables that do not require a mutex and are guaranteed not to miss any signals, even if the signal comes while the thread is not listening for it. Unlike condition variables, however, they do not support broadcasting (and in fact, in general, are not safe to use with multiple threads listening for the same event simultaneously - though notifying on the same event is fine) - Rewrote ThreadManager around ConcurrentQueue and Event API so it is now lock-free. Also improved some behaviors of the staged thread manager operation so it now supports tasks that can be run on multiple stages via a bitmask. - Fixed an issue where the Coroutine copy constructor was calling the std::function constructor instead and another where initializing with a stack might try to call the wrong constructor and vice-versa - Fixed Coroutine never calling munmap() on its stack in linux and causing a memory leak - Added default arguments to time functions - Attempted to fix some issues with BinaryTree. Fixed some but not all. It's currently not suitable for use, sadly. - Logging Improvements: - - Added thread ID to logging - - Fixed some issues with category handlers - - Added backtraces - - Added the following additional log macros: - - - LOG_IF - - - LOG_EVERY_N - - - LOG_FIRST_N - - - LOG_IF_EVERY_N - - - LOG_IF_FIRST_N - - - LOG_ASSERT - - Added the ability to set extra info callbacks to get data such as script backtraces - - Removed the thread-related handlers and replaced them with RunHandler_Threaded and RunHandler_ThreadManager, which will enable any passed-in handler to be run in a threaded fashion - Removed StaticPoolAllocator and renamed DynamicPoolAllocator to PoolAllocator; adjusted unit tests accordingly - PoolAllocator now allocates its pool with mmap and VirtualAlloc, rather than with malloc - Fixed a bug with Vector copy assignment operator - Improved performance of StringBuilder considerably for cases where there are no modifier strings - Removed Copy-On-Write behavior of JSONToken as it was broken; copies are now performed with explicit DeepCopy() and ShallowCopy() functions - Fixed some parser bugs with JSONToken - Added iteration to JSONToken to iterate its children - Fixed crash when reading a negative number of bytes from a file - Changed StringBuilder to favor speed instead of memory by default - Added some performance unit tests for JSON token #review-16053 |
||
#4 | 14783 | ShadauxCat |
Style corrections (placement of const) #review-14784 |
||
#3 | 14163 | ShadauxCat |
-Renamed HashMap functions to follow coding style. Only begin, end, find, and variants are left lowercase, in keeping with C++ algorithm and range-based for support. -Fixed some accounting issues with list and forwardlist; size wasn't properly being maintained. -Made a small pedantic change to ThreadManager to ensure that m_numThreadsSynced got reset to 0 before the NotifyAll() to eliminate the miniscule potential for deadlock it would cause if it happened after another thread had already woken up. #review-14164 |
||
#2 | 14066 | ShadauxCat |
-Improved iterating in hashmap - range-based for now gives the ability to access both key and value instead of just value -Slightly improved some of the template aliases -Mega-deprecated VC11 support. Probably doesn't compile anymore. Maintaining it is too much of a headache. #review-14067 |
||
#1 | 11496 | ShadauxCat | Initial checkin: Current states for csbuild and libSprawl |