From 4f47e396470a8ea05deff88ca574143b725eff18 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Thu, 30 May 2024 15:54:09 -0400 Subject: [PATCH 1/3] COMP: Bump jsonxx to latest From: https://github.com/hjiang/jsonxx/commit/1a3738d0062053b7fcdc1c0ea58b1675397f75ef --- include/jsonxx.h | 711 ++++++---------- src/jsonxx.cc | 2116 ++++++++++++++++++++-------------------------- 2 files changed, 1181 insertions(+), 1646 deletions(-) diff --git a/include/jsonxx.h b/include/jsonxx.h index f70e369..5bf4a45 100644 --- a/include/jsonxx.h +++ b/include/jsonxx.h @@ -19,429 +19,322 @@ // major = { number } // minor = { number } // extra = { 'a':alpha, 'b':beta, 'rc': release candidate, 'r': release, 's':stable } -#define JSONXX_MAJOR "0" -#define JSONXX_MINOR "22" -#define JSONXX_EXTRA "a" -#define JSONXX_VERSION JSONXX_MAJOR "." JSONXX_MINOR "-" JSONXX_EXTRA -#define JSONXX_XML_TAG "" +#define JSONXX_MAJOR "0" +#define JSONXX_MINOR "22" +#define JSONXX_EXTRA "a" +#define JSONXX_VERSION JSONXX_MAJOR "." JSONXX_MINOR "-" JSONXX_EXTRA +#define JSONXX_XML_TAG "" #if __cplusplus > 199711L -# define JSONXX_COMPILER_HAS_CXX11 1 +#define JSONXX_COMPILER_HAS_CXX11 1 #elif defined(_MSC_VER) && _MSC_VER > 1700 -# define JSONXX_COMPILER_HAS_CXX11 1 +#define JSONXX_COMPILER_HAS_CXX11 1 #else -# define JSONXX_COMPILER_HAS_CXX11 0 +#define JSONXX_COMPILER_HAS_CXX11 0 #endif #ifdef _MSC_VER -// disable the C4127 warning if using VC, see https://stackoverflow.com/a/12042515 -# define JSONXX_ASSERT(...) \ - do \ - { \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) if (jsonxx::Assertions) __pragma(warning(pop)) \ - jsonxx::assertion(__FILE__, __LINE__, #__VA_ARGS__, bool(__VA_ARGS__)); \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) \ - } while (0) __pragma(warning(pop)) +// disable the C4127 warning if using VC, see http://stackoverflow.com/a/12042515 +#define JSONXX_ASSERT(...) \ + do { \ + __pragma(warning(push)) __pragma(warning(disable:4127)) \ + if( jsonxx::Assertions ) \ + __pragma(warning(pop)) \ + jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); \ + __pragma(warning(push)) __pragma(warning(disable:4127)) \ + } while(0) \ + __pragma(warning(pop)) #else -# define JSONXX_ASSERT(...) \ - do \ - { \ - if (jsonxx::Assertions) \ - jsonxx::assertion(__FILE__, __LINE__, #__VA_ARGS__, bool(__VA_ARGS__)); \ - } while (0) +#define JSONXX_ASSERT(...) do { if( jsonxx::Assertions ) \ + jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); } while(0) #endif -namespace jsonxx -{ +namespace jsonxx { // FIXME(hjiang): Those should really be dynamic. // Settings -enum Settings -{ +enum Settings { // constants Enabled = true, Disabled = false, Permissive = true, Strict = false, // values - Parser = Permissive, // permissive or strict parsing + Parser = Permissive, // permissive or strict parsing UnquotedKeys = Disabled, // support of unquoted keys - Assertions = Enabled // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds) + Assertions = Enabled // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds) }; #ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4127) +#pragma warning(push) +#pragma warning(disable:4127) #endif -inline bool -parser_is_strict() -{ - return Parser == Strict; -} -inline bool -parser_is_permissive() -{ - return Parser == Permissive; -} -inline bool -unquoted_keys_are_enabled() -{ - return UnquotedKeys == Enabled; -} +inline bool parser_is_strict() { return Parser == Strict; } +inline bool parser_is_permissive() { return Parser == Permissive; } +inline bool unquoted_keys_are_enabled() { return UnquotedKeys == Enabled; } #ifdef _MSC_VER -# pragma warning(pop) +#pragma warning(pop) #endif // Constants for .write() and .xml() methods -enum Format -{ - JSON = 0, // JSON output - JSONx = 1, // XML output, JSONx format. see https://goo.gl/I3cxs - JXML = 2, // XML output, JXML format. see https://github.com/r-lyeh/JXML - JXMLex = 3, // XML output, JXMLex format. see https://github.com/r-lyeh/JXMLex - TaggedXML = 4 // XML output, tagged XML format. see https://github.com/hjiang/jsonxx/issues/12 +enum Format { + JSON = 0, // JSON output + JSONx = 1, // XML output, JSONx format. see http://goo.gl/I3cxs + JXML = 2, // XML output, JXML format. see https://github.com/r-lyeh/JXML + JXMLex = 3, // XML output, JXMLex format. see https://github.com/r-lyeh/JXMLex + TaggedXML = 4 // XML output, tagged XML format. see https://github.com/hjiang/jsonxx/issues/12 }; // Types typedef long double Number; -typedef bool Boolean; +typedef bool Boolean; typedef std::string String; -struct Null -{}; +struct Null {}; class Value; class Object; class Array; // Identity meta-function template -struct identity -{ +struct identity { typedef T type; }; // Tools -bool -validate(const std::string & input); -bool -validate(std::istream & input); -std::string -reformat(const std::string & input); -std::string -reformat(std::istream & input); -std::string -xml(const std::string & input, unsigned format = JSONx); -std::string -xml(std::istream & input, unsigned format = JSONx); +bool validate( const std::string &input ); +bool validate( std::istream &input ); +std::string reformat( const std::string &input ); +std::string reformat( std::istream &input ); +std::string xml( const std::string &input, unsigned format = JSONx ); +std::string xml( std::istream &input, unsigned format = JSONx ); // Detail -void -assertion(const char * file, int line, const char * expression, bool result); +void assertion( const char *file, int line, const char *expression, bool result ); // A JSON Object -class Object -{ -public: +class Object { + public: Object(); ~Object(); template - bool - has(const std::string & key) const; + bool has(const std::string& key) const; // Always call has<>() first. If the key doesn't exist, consider // the behavior undefined. template - T & - get(const std::string & key); + T& get(const std::string& key); template - const T & - get(const std::string & key) const; + const T& get(const std::string& key) const; template - const T & - get(const std::string & key, const typename identity::type & default_value) const; - - size_t - size() const; - bool - empty() const; - - const std::map & - kv_map() const; - std::string - json() const; - std::string - xml(unsigned format = JSONx, - const std::string & header = std::string(), - const std::string & attrib = std::string()) const; - std::string - write(unsigned format) const; - - void - reset(); - bool - parse(std::istream & input); - bool - parse(const std::string & input); - typedef std::map container; - void - import(const Object & other); - void - import(const std::string & key, const Value & value); - Object & - operator<<(const Value & value); - Object & - operator<<(const Object & value); - Object & - operator=(const Object & value); - Object(const Object & other); - Object(const std::string & key, const Value & value); - template - Object(const char (&key)[N], const Value & value) - { - import(key, value); + const T& get(const std::string& key, const typename identity::type& default_value) const; + + size_t size() const; + bool empty() const; + + const std::map& kv_map() const; + std::string json() const; + std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string() ) const; + std::string write( unsigned format ) const; + + void reset(); + bool parse(std::istream &input); + bool parse(const std::string &input); + typedef std::map container; + void import( const Object &other ); + void import( const std::string &key, const Value &value ); + Object &operator<<(const Value &value); + Object &operator<<(const Object &value); + Object &operator=(const Object &value); + Object(const Object &other); + Object(const std::string &key, const Value &value); + template + Object(const char (&key)[N], const Value &value) { + import(key,value); } - template - Object & - operator<<(const T & value); + template + Object &operator<<(const T &value); -protected: - static bool - parse(std::istream & input, Object & object); - container value_map_; + protected: + static bool parse(std::istream& input, Object& object); + container value_map_; std::string odd; }; -class Array -{ -public: +class Array { + public: Array(); ~Array(); - size_t - size() const; - bool - empty() const; + size_t size() const; + bool empty() const; template - bool - has(unsigned int i) const; + bool has(unsigned int i) const; template - T & - get(unsigned int i); + T& get(unsigned int i); template - const T & - get(unsigned int i) const; + const T& get(unsigned int i) const; template - const T & - get(unsigned int i, const typename identity::type & default_value) const; + const T& get(unsigned int i, const typename identity::type& default_value) const; - const std::vector & - values() const - { + const std::vector& values() const { return values_; } - std::string - json() const; - std::string - xml(unsigned format = JSONx, - const std::string & header = std::string(), - const std::string & attrib = std::string()) const; - - std::string - write(unsigned format) const - { - return format == JSON ? json() : xml(format); - } - void - reset(); - bool - parse(std::istream & input); - bool - parse(const std::string & input); - typedef std::vector container; - void - append(const Array & other); - void - append(const Value & value) - { - import(value); - } - void - import(const Array & other); - void - import(const Value & value); - Array & - operator<<(const Array & other); - Array & - operator<<(const Value & value); - Array & - operator=(const Array & other); - Array & - operator=(const Value & value); - Array(const Array & other); - Array(const Value & value); - -protected: - static bool - parse(std::istream & input, Array & array); + std::string json() const; + std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string() ) const; + + std::string write( unsigned format ) const { return format == JSON ? json() : xml(format); } + void reset(); + bool parse(std::istream &input); + bool parse(const std::string &input); + typedef std::vector container; + void append(const Array &other); + void append(const Value &value) { import(value); } + void import(const Array &other); + void import(const Value &value); + Array &operator<<(const Array &other); + Array &operator<<(const Value &value); + Array &operator=(const Array &other); + Array &operator=(const Value &value); + Array(const Array &other); + Array(const Value &value); + protected: + static bool parse(std::istream& input, Array& array); container values_; }; // A value could be a number, an array, a string, an object, a // boolean, or null -class Value -{ -public: +class Value { + public: + Value(); ~Value() { reset(); } - void - reset(); + void reset(); - template - void - import(const T &) - { + template + void import( const T & ) { reset(); type_ = INVALID_; // debug // std::cout << "[WARN] No support for " << typeid(t).name() << std::endl; } - void - import(const bool & b) - { + void import( const bool &b ) { reset(); type_ = BOOL_; bool_value_ = b; } -#define local_number(TYPE) \ - void import(const TYPE & n) \ - { \ - reset(); \ - type_ = NUMBER_; \ +#define $number(TYPE) \ + void import( const TYPE &n ) { \ + reset(); \ + type_ = NUMBER_; \ number_value_ = static_cast(n); \ } - local_number(char) local_number(int) local_number(long) local_number(long long) local_number(unsigned char) - local_number(unsigned int) local_number(unsigned long) local_number(unsigned long long) local_number(float) - local_number(double) local_number(long double) -#undef local_number + $number( char ) + $number( int ) + $number( long ) + $number( long long ) + $number( unsigned char ) + $number( unsigned int ) + $number( unsigned long ) + $number( unsigned long long ) + $number( float ) + $number( double ) + $number( long double ) +#undef $number #if JSONXX_COMPILER_HAS_CXX11 > 0 - void import(const std::nullptr_t &) - { + void import( const std::nullptr_t & ) { reset(); type_ = NULL_; } #endif - void - import(const Null &) - { + void import( const Null & ) { reset(); type_ = NULL_; } - void - import(const String & s) - { + void import( const String &s ) { + reset(); + type_ = STRING_; + *( string_value_ = new String() ) = s; + } + void import( const char* s ) { reset(); type_ = STRING_; - *(string_value_ = new String()) = s; + *( string_value_ = new String() ) = s; } - void - import(const Array & a) - { + void import( const Array &a ) { reset(); type_ = ARRAY_; - *(array_value_ = new Array()) = a; + *( array_value_ = new Array() ) = a; } - void - import(const Object & o) - { + void import( const Object &o ) { reset(); type_ = OBJECT_; - *(object_value_ = new Object()) = o; + *( object_value_ = new Object() ) = o; } - void - import(const Value & other) - { + void import( const Value &other ) { if (this != &other) - switch (other.type_) - { - case NULL_: - import(Null()); - break; - case BOOL_: - import(other.bool_value_); - break; - case NUMBER_: - import(other.number_value_); - break; - case STRING_: - import(*other.string_value_); - break; - case ARRAY_: - import(*other.array_value_); - break; - case OBJECT_: - import(*other.object_value_); - break; - case INVALID_: - type_ = INVALID_; - break; - default: - JSONXX_ASSERT(!"not implemented"); - } + switch (other.type_) { + case NULL_: + import( Null() ); + break; + case BOOL_: + import( other.bool_value_ ); + break; + case NUMBER_: + import( other.number_value_ ); + break; + case STRING_: + import( *other.string_value_ ); + break; + case ARRAY_: + import( *other.array_value_ ); + break; + case OBJECT_: + import( *other.object_value_ ); + break; + case INVALID_: + type_ = INVALID_; + break; + default: + JSONXX_ASSERT( !"not implemented" ); + } } - template - Value & - operator<<(const T & t) - { + template + Value &operator <<( const T &t ) { import(t); return *this; } - template - Value & - operator=(const T & t) - { + template + Value &operator =( const T &t ) { reset(); import(t); return *this; } - Value(const Value & other); - template - Value(const T & t) - : type_(INVALID_) - { - import(t); - } - template - Value(const char (&t)[N]) - : type_(INVALID_) - { - import(std::string(t)); - } - - bool - parse(std::istream & input); - bool - parse(const std::string & input); - - template - bool - is() const; - template - T & - get(); - template - const T & - get() const; - - bool - empty() const; - -public: - enum - { + Value(const Value &other); + template + Value( const T&t ) : type_(INVALID_) { import(t); } + template + Value( const char (&t)[N] ) : type_(INVALID_) { import( std::string(t) ); } + + bool parse(std::istream &input); + bool parse(const std::string &input); + + template + bool is() const; + template + T& get(); + template + const T& get() const; + + bool empty() const; + + public: + enum { NUMBER_, STRING_, BOOL_, @@ -450,262 +343,192 @@ class Value OBJECT_, INVALID_ } type_; - union - { - Number number_value_; - String * string_value_; - Boolean bool_value_; - Array * array_value_; - Object * object_value_; + union { + Number number_value_; + String* string_value_; + Boolean bool_value_; + Array* array_value_; + Object* object_value_; }; protected: - static bool - parse(std::istream & input, Value & value); + static bool parse(std::istream& input, Value& value); }; template -bool -Array::has(unsigned int i) const -{ - if (i >= size()) - { +bool Array::has(unsigned int i) const { + if (i >= size()) { return false; - } - else - { - Value * v = values_.at(i); + } else { + Value* v = values_.at(i); return v->is(); } } template -T & -Array::get(unsigned int i) -{ +T& Array::get(unsigned int i) { JSONXX_ASSERT(i < size()); - Value * v = values_.at(i); + Value* v = values_.at(i); return v->get(); } template -const T & -Array::get(unsigned int i) const -{ +const T& Array::get(unsigned int i) const { JSONXX_ASSERT(i < size()); - const Value * v = values_.at(i); + const Value* v = values_.at(i); return v->get(); } template -const T & -Array::get(unsigned int i, const typename identity::type & default_value) const -{ - if (has(i)) - { - const Value * v = values_.at(i); +const T& Array::get(unsigned int i, const typename identity::type& default_value) const { + if(has(i)) { + const Value* v = values_.at(i); return v->get(); - } - else - { + } else { return default_value; } } template -bool -Object::has(const std::string & key) const -{ +bool Object::has(const std::string& key) const { container::const_iterator it(value_map_.find(key)); return it != value_map_.end() && it->second->is(); } template -T & -Object::get(const std::string & key) -{ +T& Object::get(const std::string& key) { JSONXX_ASSERT(has(key)); return value_map_.find(key)->second->get(); } template -const T & -Object::get(const std::string & key) const -{ +const T& Object::get(const std::string& key) const { JSONXX_ASSERT(has(key)); return value_map_.find(key)->second->get(); } template -const T & -Object::get(const std::string & key, const typename identity::type & default_value) const -{ - if (has(key)) - { +const T& Object::get(const std::string& key, const typename identity::type& default_value) const { + if (has(key)) { return value_map_.find(key)->second->get(); - } - else - { + } else { return default_value; } } -template <> -inline bool -Value::is() const -{ - return true; +template<> +inline bool Value::is() const { + return true; } -template <> -inline bool -Value::is() const -{ +template<> +inline bool Value::is() const { return type_ == NULL_; } -template <> -inline bool -Value::is() const -{ +template<> +inline bool Value::is() const { return type_ == BOOL_; } -template <> -inline bool -Value::is() const -{ +template<> +inline bool Value::is() const { return type_ == STRING_; } -template <> -inline bool -Value::is() const -{ +template<> +inline bool Value::is() const { return type_ == NUMBER_; } -template <> -inline bool -Value::is() const -{ +template<> +inline bool Value::is() const { return type_ == ARRAY_; } -template <> -inline bool -Value::is() const -{ +template<> +inline bool Value::is() const { return type_ == OBJECT_; } -template <> -inline Value & -Value::get() -{ - return *this; +template<> +inline Value& Value::get() { + return *this; } -template <> -inline const Value & -Value::get() const -{ - return *this; +template<> +inline const Value& Value::get() const { + return *this; } -template <> -inline bool & -Value::get() -{ +template<> +inline bool& Value::get() { JSONXX_ASSERT(is()); return bool_value_; } -template <> -inline std::string & -Value::get() -{ +template<> +inline std::string& Value::get() { JSONXX_ASSERT(is()); return *string_value_; } -template <> -inline Number & -Value::get() -{ +template<> +inline Number& Value::get() { JSONXX_ASSERT(is()); return number_value_; } -template <> -inline Array & -Value::get() -{ +template<> +inline Array& Value::get() { JSONXX_ASSERT(is()); return *array_value_; } -template <> -inline Object & -Value::get() -{ +template<> +inline Object& Value::get() { JSONXX_ASSERT(is()); return *object_value_; } -template <> -inline const Boolean & -Value::get() const -{ +template<> +inline const Boolean& Value::get() const { JSONXX_ASSERT(is()); return bool_value_; } -template <> -inline const String & -Value::get() const -{ +template<> +inline const String& Value::get() const { JSONXX_ASSERT(is()); return *string_value_; } -template <> -inline const Number & -Value::get() const -{ +template<> +inline const Number& Value::get() const { JSONXX_ASSERT(is()); return number_value_; } -template <> -inline const Array & -Value::get() const -{ +template<> +inline const Array& Value::get() const { JSONXX_ASSERT(is()); return *array_value_; } -template <> -inline const Object & -Value::get() const -{ +template<> +inline const Object& Value::get() const { JSONXX_ASSERT(is()); return *object_value_; } -template -inline Object & -Object::operator<<(const T & value) -{ +template +inline Object &Object::operator<<(const T &value) { *this << Value(value); return *this; } -} // namespace jsonxx +} // namespace jsonxx -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Value & v); -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Object & v); -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Array & v); +std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v); +std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v); +std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v); diff --git a/src/jsonxx.cc b/src/jsonxx.cc index f259e95..a984f89 100644 --- a/src/jsonxx.cc +++ b/src/jsonxx.cc @@ -18,1462 +18,1174 @@ // Snippet that creates an assertion function that works both in DEBUG & RELEASE mode. // JSONXX_ASSERT(...) macro will redirect to this. assert() macro is kept untouched. #if defined(NDEBUG) || defined(_NDEBUG) -# define JSONXX_REENABLE_NDEBUG -# undef NDEBUG -# undef _NDEBUG +# define JSONXX_REENABLE_NDEBUG +# undef NDEBUG +# undef _NDEBUG #endif #include #include -void -jsonxx::assertion(const char * file, int line, const char * expression, bool result) -{ - if (!result) - { - fprintf(stderr, "[JSONXX] expression '%s' failed at %s:%d -> ", expression, file, line); - assert(0); - } +void jsonxx::assertion( const char *file, int line, const char *expression, bool result ) { + if( !result ) { + fprintf( stderr, "[JSONXX] expression '%s' failed at %s:%d -> ", expression, file, line ); + assert( 0 ); + } } #if defined(JSONXX_REENABLE_NDEBUG) -# define NDEBUG -# define _NDEBUG +# define NDEBUG +# define _NDEBUG #endif #include -namespace jsonxx -{ - -// static_assert( sizeof(unsigned long long) < sizeof(long double), "'long double' cannot hold 64bit values in this -// compiler :("); - -bool -match(const char * pattern, std::istream & input); -bool -parse_array(std::istream & input, Array & array); -bool -parse_bool(std::istream & input, Boolean & value); -bool -parse_comment(std::istream & input); -bool -parse_null(std::istream & input); -bool -parse_number(std::istream & input, Number & value); -bool -parse_object(std::istream & input, Object & object); -bool -parse_string(std::istream & input, String & value); -bool -parse_identifier(std::istream & input, String & value); -bool -parse_value(std::istream & input, Value & value); +namespace jsonxx { + +//static_assert( sizeof(unsigned long long) < sizeof(long double), "'long double' cannot hold 64bit values in this compiler :("); + +bool match(const char* pattern, std::istream& input); +bool parse_array(std::istream& input, Array& array); +bool parse_bool(std::istream& input, Boolean& value); +bool parse_comment(std::istream &input); +bool parse_null(std::istream& input); +bool parse_number(std::istream& input, Number& value); +bool parse_object(std::istream& input, Object& object); +bool parse_string(std::istream& input, String& value); +bool parse_identifier(std::istream& input, String& value); +bool parse_value(std::istream& input, Value& value); // Try to consume characters from the input stream and match the // pattern string. -bool -match(const char * pattern, std::istream & input) -{ - input >> std::ws; - const char * cur(pattern); - char ch(0); - while (input && !input.eof() && *cur != 0) - { - input.get(ch); - if (ch != *cur) - { - input.putback(ch); - if (parse_comment(input)) - continue; - while (cur > pattern) - { - cur--; - input.putback(*cur); - } - return false; - } - else - { - cur++; +bool match(const char* pattern, std::istream& input) { + input >> std::ws; + const char* cur(pattern); + char ch(0); + while(input && !input.eof() && *cur != 0) { + input.get(ch); + if (ch != *cur) { + input.putback(ch); + if( parse_comment(input) ) + continue; + while (cur > pattern) { + cur--; + input.putback(*cur); + } + return false; + } else { + cur++; + } } - } - return *cur == 0; + return *cur == 0; } -bool -parse_string(std::istream & input, String & value) -{ - char ch = '\0', delimiter = '"'; - if (!match("\"", input)) - { - if (parser_is_strict()) - { - return false; - } - delimiter = '\''; - if (input.peek() != delimiter) - { - return false; - } - input.get(ch); - } - while (!input.eof() && input.good()) - { - input.get(ch); - if (ch == delimiter) - { - break; +bool parse_string(std::istream& input, String& value) { + char ch = '\0', delimiter = '"'; + if (!match("\"", input)) { + if (parser_is_strict()) { + return false; + } + delimiter = '\''; + if (input.peek() != delimiter) { + return false; + } + input.get(ch); } - if (ch == '\\') - { - input.get(ch); - switch (ch) - { - case '\\': - case '/': - value.push_back(ch); - break; - case 'b': - value.push_back('\b'); - break; - case 'f': - value.push_back('\f'); - break; - case 'n': - value.push_back('\n'); - break; - case 'r': - value.push_back('\r'); - break; - case 't': - value.push_back('\t'); - break; - case 'u': - { - int i; - std::stringstream ss; - for (i = 0; (!input.eof() && input.good()) && i < 4; ++i) - { - input.get(ch); - ss << std::hex << ch; - } - if (input.good() && (ss >> i)) - value.push_back(static_cast(i)); + while(!input.eof() && input.good()) { + input.get(ch); + if (ch == delimiter) { + break; } - break; - default: - if (ch != delimiter) - { - value.push_back('\\'); - value.push_back(ch); - } - else + if (ch == '\\') { + input.get(ch); + switch(ch) { + case '\\': + case '/': + value.push_back(ch); + break; + case 'b': + value.push_back('\b'); + break; + case 'f': + value.push_back('\f'); + break; + case 'n': + value.push_back('\n'); + break; + case 'r': + value.push_back('\r'); + break; + case 't': + value.push_back('\t'); + break; + case 'u': { + int i; + std::stringstream ss; + for( i = 0; (!input.eof() && input.good()) && i < 4; ++i ) { + input.get(ch); + ss << std::hex << ch; + } + if( input.good() && (ss >> i) ) + value.push_back(static_cast(i)); + } + break; + default: + if (ch != delimiter) { + value.push_back('\\'); + value.push_back(ch); + } else value.push_back(ch); + break; + } + } else { value.push_back(ch); - break; - } + } } - else - { - value.push_back(ch); + if (input && ch == delimiter) { + return true; + } else { + return false; } - } - if (input && ch == delimiter) - { - return true; - } - else - { - return false; - } } -bool -parse_identifier(std::istream & input, String & value) -{ - input >> std::ws; +bool parse_identifier(std::istream& input, String& value) { + input >> std::ws; - char ch = '\0', delimiter = ':'; - bool first = true; + char ch = '\0', delimiter = ':'; + bool first = true; - while (!input.eof() && input.good()) - { - input.get(ch); + while(!input.eof() && input.good()) { + input.get(ch); - if (ch == delimiter) - { - input.unget(); - break; - } + if (ch == delimiter) { + input.unget(); + break; + } - if (first) - { - if ((ch != '_' && ch != '$') && (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')) - { + if(first) { + if ((ch != '_' && ch != '$') && + (ch < 'a' || ch > 'z') && + (ch < 'A' || ch > 'Z')) { + return false; + } + first = false; + } + if(ch == '_' || ch == '$' || + (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9')) { + value.push_back(ch); + } + else if(ch == '\t' || ch == ' ') { + input >> std::ws; + } + } + if (input && ch == delimiter) { + return true; + } else { return false; - } - first = false; } - if (ch == '_' || ch == '$' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) - { - value.push_back(ch); +} + +class IOStateMasker { + public: + explicit IOStateMasker(std::istream& input): stream(input) { + mask = input.exceptions(); + input.exceptions(std::istream::goodbit); } - else if (ch == '\t' || ch == ' ') - { - input >> std::ws; + + ~IOStateMasker() { + stream.exceptions(mask); + } + + private: + std::istream& stream; + std::istream::iostate mask; +}; + +bool parse_number(std::istream& input, Number& value) { + input >> std::ws; + std::streampos rollback = input.tellg(); + IOStateMasker masker(input); + input >> value; + if (input.fail()) { + input.clear(); + input.seekg(rollback); + return false; } - } - if (input && ch == delimiter) - { return true; - } - else - { - return false; - } } -bool -parse_number(std::istream & input, Number & value) -{ - input >> std::ws; - std::streampos rollback = input.tellg(); - input >> value; - if (input.fail()) - { - input.clear(); - input.seekg(rollback); +bool parse_bool(std::istream& input, Boolean& value) { + if (match("true", input)) { + value = true; + return true; + } + if (match("false", input)) { + value = false; + return true; + } return false; - } - return true; -} - -bool -parse_bool(std::istream & input, Boolean & value) -{ - if (match("true", input)) - { - value = true; - return true; - } - if (match("false", input)) - { - value = false; - return true; - } - return false; } -bool -parse_null(std::istream & input) -{ - if (match("null", input)) - { - return true; - } - if (parser_is_strict()) - { - return false; - } - return (input.peek() == ','); +bool parse_null(std::istream& input) { + if (match("null", input)) { + return true; + } + if (parser_is_strict()) { + return false; + } + return (input.peek()==','); } -bool -parse_array(std::istream & input, Array & array) -{ - return array.parse(input); +bool parse_array(std::istream& input, Array& array) { + return array.parse(input); } -bool -parse_object(std::istream & input, Object & object) -{ - return object.parse(input); +bool parse_object(std::istream& input, Object& object) { + return object.parse(input); } -bool -parse_comment(std::istream & input) -{ - if (parser_is_permissive()) - if (!input.eof() && input.peek() == '/') +bool parse_comment(std::istream &input) { + if( parser_is_permissive() ) + if( !input.eof() && input.peek() == '/' ) { - char ch0(0); - input.get(ch0); - - if (!input.eof()) - { - char ch1(0); - input.get(ch1); + char ch0(0); + input.get(ch0); - if (ch0 == '/' && ch1 == '/') + if( !input.eof() ) { - // trim chars till \r or \n - for (char ch(0); !input.eof() && (input.peek() != '\r' && input.peek() != '\n');) - input.get(ch); - - // consume spaces, tabs, \r or \n, in case no eof is found - if (!input.eof()) - input >> std::ws; - return true; + char ch1(0); + input.get(ch1); + + if( ch0 == '/' && ch1 == '/' ) + { + // trim chars till \r or \n + for( char ch(0); !input.eof() && (input.peek() != '\r' && input.peek() != '\n'); ) + input.get(ch); + + // consume spaces, tabs, \r or \n, in case no eof is found + if( !input.eof() ) + input >> std::ws; + return true; + } + + input.unget(); + input.clear(); } input.unget(); input.clear(); - } - - input.unget(); - input.clear(); } - return false; + return false; } -bool -parse_value(std::istream & input, Value & value) -{ - return value.parse(input); +bool parse_value(std::istream& input, Value& value) { + return value.parse(input); } -Object::Object() - : value_map_() -{} +Object::Object() : value_map_() {} -Object::~Object() -{ - reset(); +Object::~Object() { + reset(); } -bool -Object::parse(std::istream & input, Object & object) -{ - object.reset(); +bool Object::parse(std::istream& input, Object& object) { + object.reset(); - if (!match("{", input)) - { - return false; - } - if (match("}", input)) - { - return true; - } - - do - { - std::string key; - if (unquoted_keys_are_enabled()) - { - if (!parse_identifier(input, key)) - { - if (parser_is_permissive()) - { - if (input.peek() == '}') - break; - } + if (!match("{", input)) { return false; - } } - else - { - if (!parse_string(input, key)) - { - if (parser_is_permissive()) - { - if (input.peek() == '}') + if (match("}", input)) { + return true; + } + + do { + std::string key; + if (unquoted_keys_are_enabled()) { + if (!parse_identifier(input, key)) { + if (parser_is_permissive()) { + if (input.peek() == '}') + break; + } + return false; + } + } + else { + if (!parse_string(input, key)) { + if (parser_is_permissive()) { + if (input.peek() == '}') + break; + } + return false; + } + } + if (!match(":", input)) { + return false; + } + Value* v = new Value(); + if (!parse_value(input, *v)) { + delete v; break; } - return false; - } - } - if (!match(":", input)) - { - return false; - } - Value * v = new Value(); - if (!parse_value(input, *v)) - { - delete v; - break; - } - // TODO(hjiang): Add an option to allow duplicated keys? - if (object.value_map_.find(key) == object.value_map_.end()) - { - object.value_map_[key] = v; - } - else - { - if (parser_is_permissive()) - { - delete object.value_map_[key]; - object.value_map_[key] = v; - } - else - { - delete v; - return false; - } - } - } while (match(",", input)); + // TODO(hjiang): Add an option to allow duplicated keys? + if (object.value_map_.find(key) == object.value_map_.end()) { + object.value_map_[key] = v; + } else { + if (parser_is_permissive()) { + delete object.value_map_[key]; + object.value_map_[key] = v; + } else { + delete v; + return false; + } + } + } while (match(",", input)); - if (!match("}", input)) - { - return false; - } + if (!match("}", input)) { + return false; + } - return true; + return true; } -Value::Value() - : type_(INVALID_) -{} +Value::Value() : type_(INVALID_) {} -void -Value::reset() -{ - if (type_ == STRING_) - { - delete string_value_; - string_value_ = 0; - } - else if (type_ == OBJECT_) - { - delete object_value_; - object_value_ = 0; - } - else if (type_ == ARRAY_) - { - delete array_value_; - array_value_ = 0; - } +void Value::reset() { + if (type_ == STRING_) { + delete string_value_; + string_value_ = 0; + } + else if (type_ == OBJECT_) { + delete object_value_; + object_value_ = 0; + } + else if (type_ == ARRAY_) { + delete array_value_; + array_value_ = 0; + } } -bool -Value::parse(std::istream & input, Value & value) -{ - value.reset(); +bool Value::parse(std::istream& input, Value& value) { + value.reset(); - std::string string_value; - if (parse_string(input, string_value)) - { - value.string_value_ = new std::string(); - value.string_value_->swap(string_value); - value.type_ = STRING_; - return true; - } - if (parse_number(input, value.number_value_)) - { - value.type_ = NUMBER_; - return true; - } + std::string string_value; + if (parse_string(input, string_value)) { + value.string_value_ = new std::string(); + value.string_value_->swap(string_value); + value.type_ = STRING_; + return true; + } + if (parse_number(input, value.number_value_)) { + value.type_ = NUMBER_; + return true; + } - if (parse_bool(input, value.bool_value_)) - { - value.type_ = BOOL_; - return true; - } - if (parse_null(input)) - { - value.type_ = NULL_; - return true; - } - if (input.peek() == '[') - { - value.array_value_ = new Array(); - if (parse_array(input, *value.array_value_)) - { - value.type_ = ARRAY_; - return true; + if (parse_bool(input, value.bool_value_)) { + value.type_ = BOOL_; + return true; } - delete value.array_value_; - value.array_value_ = 0; - } - value.object_value_ = new Object(); - if (parse_object(input, *value.object_value_)) - { - value.type_ = OBJECT_; - return true; - } - delete value.object_value_; - value.object_value_ = 0; - return false; + if (parse_null(input)) { + value.type_ = NULL_; + return true; + } + if (input.peek() == '[') { + value.array_value_ = new Array(); + if (parse_array(input, *value.array_value_)) { + value.type_ = ARRAY_; + return true; + } + delete value.array_value_; + value.array_value_ = 0; + } + value.object_value_ = new Object(); + if (parse_object(input, *value.object_value_)) { + value.type_ = OBJECT_; + return true; + } + delete value.object_value_; + value.object_value_ = 0; + return false; } -Array::Array() - : values_() -{} +Array::Array() : values_() {} -Array::~Array() -{ - reset(); +Array::~Array() { + reset(); } -bool -Array::parse(std::istream & input, Array & array) -{ - array.reset(); +bool Array::parse(std::istream& input, Array& array) { + array.reset(); - if (!match("[", input)) - { - return false; - } - if (match("]", input)) - { - return true; - } - - do - { - Value * v = new Value(); - if (!parse_value(input, *v)) - { - delete v; - break; + if (!match("[", input)) { + return false; + } + if (match("]", input)) { + return true; } - array.values_.push_back(v); - } while (match(",", input)); - - if (!match("]", input)) - { - return false; - } - return true; -} -static std::ostream & -stream_string(std::ostream & stream, const std::string & string) -{ - stream << '"'; - for (std::string::const_iterator i = string.begin(), e = string.end(); i != e; ++i) - { - switch (*i) - { - case '"': - stream << "\\\""; - break; - case '\\': - stream << "\\\\"; - break; - case '/': - stream << "\\/"; - break; - case '\b': - stream << "\\b"; - break; - case '\f': - stream << "\\f"; - break; - case '\n': - stream << "\\n"; - break; - case '\r': - stream << "\\r"; - break; - case '\t': - stream << "\\t"; - break; - default: - if (*i < 32) - { - stream << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(*i) << std::dec - << std::setw(0); - } - else - { - stream << *i; + do { + Value* v = new Value(); + if (!parse_value(input, *v)) { + delete v; + break; } + array.values_.push_back(v); + } while (match(",", input)); + + if (!match("]", input)) { + return false; } - } - stream << '"'; - return stream; + return true; } -} // namespace jsonxx - -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Value & v) -{ - using namespace jsonxx; - if (v.is()) - { - return stream << v.get(); - } - else if (v.is()) - { - return stream_string(stream, v.get()); - } - else if (v.is()) - { - if (v.get()) - { - return stream << "true"; +static std::ostream& stream_string(std::ostream& stream, + const std::string& string) { + stream << '"'; + for (std::string::const_iterator i = string.begin(), + e = string.end(); i != e; ++i) { + switch (*i) { + case '"': + stream << "\\\""; + break; + case '\\': + stream << "\\\\"; + break; + case '/': + stream << "\\/"; + break; + case '\b': + stream << "\\b"; + break; + case '\f': + stream << "\\f"; + break; + case '\n': + stream << "\\n"; + break; + case '\r': + stream << "\\r"; + break; + case '\t': + stream << "\\t"; + break; + default: + if (*i < 32) { + stream << "\\u" << std::hex << std::setw(4) << + std::setfill('0') << static_cast(*i) << std::dec << + std::setw(0); + } else { + stream << *i; + } + } } - else - { - return stream << "false"; + stream << '"'; + return stream; +} + +} // namespace jsonxx + +std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v) { + using namespace jsonxx; + if (v.is()) { + return stream << v.get(); + } else if (v.is()) { + return stream_string(stream, v.get()); + } else if (v.is()) { + if (v.get()) { + return stream << "true"; + } else { + return stream << "false"; + } + } else if (v.is()) { + return stream << "null"; + } else if (v.is()) { + return stream << v.get(); + } else if (v.is()){ + return stream << v.get(); } - } - else if (v.is()) - { - return stream << "null"; - } - else if (v.is()) - { - return stream << v.get(); - } - else if (v.is()) - { - return stream << v.get(); - } - // Shouldn't reach here. - return stream; -} - -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Array & v) -{ - stream << "["; - jsonxx::Array::container::const_iterator it = v.values().begin(), end = v.values().end(); - while (it != end) - { - stream << *(*it); - ++it; - if (it != end) - { - stream << ", "; + // Shouldn't reach here. + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v) { + stream << "["; + jsonxx::Array::container::const_iterator + it = v.values().begin(), + end = v.values().end(); + while (it != end) { + stream << *(*it); + ++it; + if (it != end) { + stream << ", "; + } } - } - return stream << "]"; -} - -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Object & v) -{ - stream << "{"; - jsonxx::Object::container::const_iterator it = v.kv_map().begin(), end = v.kv_map().end(); - while (it != end) - { - jsonxx::stream_string(stream, it->first); - stream << ": " << *(it->second); - ++it; - if (it != end) - { - stream << ", "; + return stream << "]"; +} + +std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v) { + stream << "{"; + jsonxx::Object::container::const_iterator + it = v.kv_map().begin(), + end = v.kv_map().end(); + while (it != end) { + jsonxx::stream_string(stream, it->first); + stream << ": " << *(it->second); + ++it; + if (it != end) { + stream << ", "; + } } - } - return stream << "}"; + return stream << "}"; } -namespace jsonxx -{ -namespace -{ +namespace jsonxx { +namespace { typedef unsigned char byte; -// template -std::string -escape_string(const std::string & input, const bool quote = false) -{ - static std::string map[256], *once = 0; - static std::mutex mutex; - if (!once) - { - // The problem here is that, once is initializing at the end of job, but if multithreaded application is starting - // this for the first time it will jump into this part with several threads and cause double free or corruptions. To - // prevent it, it is required to have mutex, to lock other threads while only one of them is constructing the static - // map. - mutex.lock(); - if (!once) - { - // base - for (int i = 0; i < 256; ++i) - { - map[i] = std::string() + char(i); - } - // non-printable - for (int i = 0; i < 32; ++i) - { - std::stringstream str; - str << "\\u" << std::hex << std::setw(4) << std::setfill('0') << i; - map[i] = str.str(); - } - // exceptions - map[byte('"')] = "\\\""; - map[byte('\\')] = "\\\\"; - map[byte('/')] = "\\/"; - map[byte('\b')] = "\\b"; - map[byte('\f')] = "\\f"; - map[byte('\n')] = "\\n"; - map[byte('\r')] = "\\r"; - map[byte('\t')] = "\\t"; - - once = map; +//template +std::string escape_string( const std::string &input, const bool quote = false ) { + static std::string map[256], *once = 0; + static std::mutex mutex; + if( !once ) { + // The problem here is that, once is initializing at the end of job, but if multithreaded application is starting this for the first time + // it will jump into this part with several threads and cause double free or corruptions. + // To prevent it, it is required to have mutex, to lock other threads while only one of them is constructing the static map. + mutex.lock(); + if (!once) { + // base + for( int i = 0; i < 256; ++i ) { + map[ i ] = std::string() + char(i); + } + // non-printable + for( int i = 0; i < 32; ++i ) { + std::stringstream str; + str << "\\u" << std::hex << std::setw(4) << std::setfill('0') << i; + map[ i ] = str.str(); + } + // exceptions + map[ byte('"') ] = "\\\""; + map[ byte('\\') ] = "\\\\"; + map[ byte('/') ] = "\\/"; + map[ byte('\b') ] = "\\b"; + map[ byte('\f') ] = "\\f"; + map[ byte('\n') ] = "\\n"; + map[ byte('\r') ] = "\\r"; + map[ byte('\t') ] = "\\t"; + + once = map; + } + mutex.unlock(); } - mutex.unlock(); - } - std::string output; - output.reserve(input.size() * 2 + 2); // worst scenario - if (quote) - output += '"'; - for (std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it) - output += map[byte(*it)]; - if (quote) - output += '"'; - return output; -} - - -namespace json -{ - -std::string -remove_last_comma(const std::string & _input) -{ - std::string input(_input); - size_t size = input.size(); - if (size > 2) - if (input[size - 2] == ',') - input[size - 2] = ' '; - return input; -} - -std::string -tag(unsigned format, unsigned depth, const std::string & name, const jsonxx::Value & t) -{ - std::stringstream ss; - const std::string tab(depth, '\t'); - - if (!name.empty()) - ss << tab << '\"' << escape_string(name) << '\"' << ':' << ' '; - else - ss << tab; - - switch (t.type_) - { - default: - case jsonxx::Value::NULL_: - ss << "null"; - return ss.str() + ",\n"; - - case jsonxx::Value::BOOL_: - ss << (t.bool_value_ ? "true" : "false"); - return ss.str() + ",\n"; - - case jsonxx::Value::ARRAY_: - ss << "[\n"; - for (Array::container::const_iterator it = t.array_value_->values().begin(), end = t.array_value_->values().end(); - it != end; - ++it) - ss << tag(format, depth + 1, std::string(), **it); - return remove_last_comma(ss.str()) + tab + - "]" - ",\n"; - - case jsonxx::Value::STRING_: - ss << '\"' << escape_string(*t.string_value_) << '\"'; - return ss.str() + ",\n"; - - case jsonxx::Value::OBJECT_: - ss << "{\n"; - for (Object::container::const_iterator it = t.object_value_->kv_map().begin(), - end = t.object_value_->kv_map().end(); - it != end; - ++it) - ss << tag(format, depth + 1, it->first, *it->second); - return remove_last_comma(ss.str()) + tab + - "}" - ",\n"; - - case jsonxx::Value::NUMBER_: - // max precision - ss << std::setprecision(std::numeric_limits::digits10 + 1); - ss << t.number_value_; - return ss.str() + ",\n"; - } + std::string output; + output.reserve( input.size() * 2 + 2 ); // worst scenario + if( quote ) output += '"'; + for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it ) + output += map[ byte(*it) ]; + if( quote ) output += '"'; + return output; } -} // namespace json - -namespace xml -{ - -std::string -escape_attrib(const std::string & input) -{ - static std::string map[256], *once = 0; - if (!once) - { - for (int i = 0; i < 256; ++i) - map[i] = "_"; - for (int i = int('a'); i <= int('z'); ++i) - map[i] = std::string() + char(i); - for (int i = int('A'); i <= int('Z'); ++i) - map[i] = std::string() + char(i); - for (int i = int('0'); i <= int('9'); ++i) - map[i] = std::string() + char(i); - once = map; - } - std::string output; - output.reserve(input.size()); // worst scenario - for (std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it) - output += map[byte(*it)]; - return output; -} - -std::string -escape_tag(const std::string & input, unsigned format) -{ - static std::string map[256], *once = 0; - if (!once) - { - for (int i = 0; i < 256; ++i) - map[i] = std::string() + char(i); - map[byte('<')] = "<"; - map[byte('>')] = ">"; - - switch (format) - { - default: - break; - - case jsonxx::JXML: - case jsonxx::JXMLex: - case jsonxx::JSONx: - case jsonxx::TaggedXML: - map[byte('&')] = "&"; - break; + + +namespace json { + + std::string remove_last_comma( const std::string &_input ) { + std::string input( _input ); + size_t size = input.size(); + if( size > 2 ) + if( input[ size - 2 ] == ',' ) + input[ size - 2 ] = ' '; + return input; } - once = map; - } - std::string output; - output.reserve(input.size() * 5); // worst scenario - for (std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it) - output += map[byte(*it)]; - return output; -} - -std::string -open_tag(unsigned format, - char type, - const std::string & name, - const std::string & attr = std::string(), - const std::string & text = std::string()) -{ - std::string tagname; - switch (format) - { - default: - return std::string(); - - case jsonxx::JXML: - if (name.empty()) - tagname = std::string("j son=\"") + type + '\"'; - else - tagname = std::string("j son=\"") + type + ':' + escape_string(name) + '\"'; - break; - - case jsonxx::JXMLex: - if (name.empty()) - tagname = std::string("j son=\"") + type + '\"'; - else - tagname = std::string("j son=\"") + type + ':' + escape_string(name) + "\" " + escape_attrib(name) + "=\"" + - escape_string(text) + "\""; - break; - - case jsonxx::JSONx: - if (!name.empty()) - tagname = std::string(" name=\"") + escape_string(name) + "\""; - switch (type) - { - default: - case '0': - tagname = "json:null" + tagname; - break; - case 'b': - tagname = "json:boolean" + tagname; - break; - case 'a': - tagname = "json:array" + tagname; - break; - case 's': - tagname = "json:string" + tagname; - break; - case 'o': - tagname = "json:object" + tagname; - break; - case 'n': - tagname = "json:number" + tagname; - break; - } - break; - - case jsonxx::TaggedXML: // @TheMadButcher - if (!name.empty()) - tagname = escape_attrib(name); - else - tagname = "JsonItem"; - switch (type) - { - default: - case '0': - tagname += " type=\"json:null\""; - break; - case 'b': - tagname += " type=\"json:boolean\""; - break; - case 'a': - tagname += " type=\"json:array\""; - break; - case 's': - tagname += " type=\"json:string\""; - break; - case 'o': - tagname += " type=\"json:object\""; - break; - case 'n': - tagname += " type=\"json:number\""; - break; - } + std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t) { + std::stringstream ss; + const std::string tab(depth, '\t'); - if (!name.empty()) - tagname += std::string(" name=\"") + escape_string(name) + "\""; + if( !name.empty() ) + ss << tab << '\"' << escape_string( name ) << '\"' << ':' << ' '; + else + ss << tab; - break; - } + switch( t.type_ ) + { + default: + case jsonxx::Value::NULL_: + ss << "null"; + return ss.str() + ",\n"; + + case jsonxx::Value::BOOL_: + ss << ( t.bool_value_ ? "true" : "false" ); + return ss.str() + ",\n"; + + case jsonxx::Value::ARRAY_: + ss << "[\n"; + for(Array::container::const_iterator it = t.array_value_->values().begin(), + end = t.array_value_->values().end(); it != end; ++it ) + ss << tag( format, depth+1, std::string(), **it ); + return remove_last_comma( ss.str() ) + tab + "]" ",\n"; + + case jsonxx::Value::STRING_: + ss << '\"' << escape_string( *t.string_value_ ) << '\"'; + return ss.str() + ",\n"; + + case jsonxx::Value::OBJECT_: + ss << "{\n"; + for(Object::container::const_iterator it=t.object_value_->kv_map().begin(), + end = t.object_value_->kv_map().end(); it != end ; ++it) + ss << tag( format, depth+1, it->first, *it->second ); + return remove_last_comma( ss.str() ) + tab + "}" ",\n"; + + case jsonxx::Value::NUMBER_: + // max precision + ss << std::setprecision(std::numeric_limits::digits10 + 1); + ss << t.number_value_; + return ss.str() + ",\n"; + } + } +} // namespace jsonxx::anon::json + +namespace xml { + +std::string escape_attrib( const std::string &input ) { + static std::string map[256], *once = 0; + if( !once ) { + for( int i = 0; i < 256; ++i ) + map[ i ] = "_"; + for( int i = int('a'); i <= int('z'); ++i ) + map[ i ] = std::string() + char(i); + for( int i = int('A'); i <= int('Z'); ++i ) + map[ i ] = std::string() + char(i); + for( int i = int('0'); i <= int('9'); ++i ) + map[ i ] = std::string() + char(i); + once = map; + } + std::string output; + output.reserve( input.size() ); // worst scenario + for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it ) + output += map[ byte(*it) ]; + return output; +} + +std::string escape_tag( const std::string &input, unsigned format ) { + static std::string map[256], *once = 0; + if( !once ) { + for( int i = 0; i < 256; ++i ) + map[ i ] = std::string() + char(i); + map[ byte('<') ] = "<"; + map[ byte('>') ] = ">"; + + switch( format ) + { + default: + break; + + case jsonxx::JXML: + case jsonxx::JXMLex: + case jsonxx::JSONx: + case jsonxx::TaggedXML: + map[ byte('&') ] = "&"; + break; + } - return std::string("<") + tagname + attr + ">"; + once = map; + } + std::string output; + output.reserve( input.size() * 5 ); // worst scenario + for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it ) + output += map[ byte(*it) ]; + return output; } -std::string -close_tag(unsigned format, char type, const std::string & name) -{ - switch (format) - { - default: - return std::string(); +std::string open_tag( unsigned format, char type, const std::string &name, const std::string &attr = std::string(), const std::string &text = std::string() ) { + std::string tagname; + switch( format ) + { + default: + return std::string(); + + case jsonxx::JXML: + if( name.empty() ) + tagname = std::string("j son=\"") + type + '\"'; + else + tagname = std::string("j son=\"") + type + ':' + escape_string(name) + '\"'; + break; + + case jsonxx::JXMLex: + if( name.empty() ) + tagname = std::string("j son=\"") + type + '\"'; + else + tagname = std::string("j son=\"") + type + ':' + escape_string(name) + "\" " + escape_attrib(name) + "=\"" + escape_string(text) + "\""; + break; + + case jsonxx::JSONx: + if( !name.empty() ) + tagname = std::string(" name=\"") + escape_string(name) + "\""; + switch( type ) { + default: + case '0': tagname = "json:null" + tagname; break; + case 'b': tagname = "json:boolean" + tagname; break; + case 'a': tagname = "json:array" + tagname; break; + case 's': tagname = "json:string" + tagname; break; + case 'o': tagname = "json:object" + tagname; break; + case 'n': tagname = "json:number" + tagname; break; + } + break; - case jsonxx::JXML: - case jsonxx::JXMLex: - return ""; + case jsonxx::TaggedXML: // @TheMadButcher + if( !name.empty() ) + tagname = escape_attrib(name); + else + tagname = "JsonItem"; + switch( type ) { + default: + case '0': tagname += " type=\"json:null\""; break; + case 'b': tagname += " type=\"json:boolean\""; break; + case 'a': tagname += " type=\"json:array\""; break; + case 's': tagname += " type=\"json:string\""; break; + case 'o': tagname += " type=\"json:object\""; break; + case 'n': tagname += " type=\"json:number\""; break; + } + + if( !name.empty() ) + tagname += std::string(" name=\"") + escape_string(name) + "\""; - case jsonxx::JSONx: - switch (type) - { + break; + } + + return std::string("<") + tagname + attr + ">"; +} + +std::string close_tag( unsigned format, char type, const std::string &name ) { + switch( format ) + { default: - case '0': - return ""; - case 'b': - return ""; - case 'a': - return ""; - case 'o': - return ""; - case 's': - return ""; - case 'n': - return ""; - } - break; + return std::string(); + + case jsonxx::JXML: + case jsonxx::JXMLex: + return ""; + + case jsonxx::JSONx: + switch( type ) { + default: + case '0': return ""; + case 'b': return ""; + case 'a': return ""; + case 'o': return ""; + case 's': return ""; + case 'n': return ""; + } + break; - case jsonxx::TaggedXML: // @TheMadButcher - if (!name.empty()) - return ""; - else - return ""; - } + case jsonxx::TaggedXML: // @TheMadButcher + if( !name.empty() ) + return ""; + else + return ""; + } } -std::string -tag(unsigned format, - unsigned depth, - const std::string & name, - const jsonxx::Value & t, - const std::string & attr = std::string()) -{ - std::stringstream ss; - const std::string tab(depth, '\t'); - - switch (t.type_) - { - default: - case jsonxx::Value::NULL_: - return tab + open_tag(format, '0', name, " /") + '\n'; - - case jsonxx::Value::BOOL_: - ss << (t.bool_value_ ? "true" : "false"); - return tab + open_tag(format, 'b', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string()) + - ss.str() + close_tag(format, 'b', name) + '\n'; - - case jsonxx::Value::ARRAY_: - for (Array::container::const_iterator it = t.array_value_->values().begin(), end = t.array_value_->values().end(); - it != end; - ++it) - ss << tag(format, depth + 1, std::string(), **it); - return tab + open_tag(format, 'a', name, attr) + '\n' + ss.str() + tab + close_tag(format, 'a', name) + '\n'; - - case jsonxx::Value::STRING_: - ss << escape_tag(*t.string_value_, format); - return tab + open_tag(format, 's', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string()) + - ss.str() + close_tag(format, 's', name) + '\n'; - - case jsonxx::Value::OBJECT_: - for (Object::container::const_iterator it = t.object_value_->kv_map().begin(), - end = t.object_value_->kv_map().end(); - it != end; - ++it) - ss << tag(format, depth + 1, it->first, *it->second); - return tab + open_tag(format, 'o', name, attr) + '\n' + ss.str() + tab + close_tag(format, 'o', name) + '\n'; - - case jsonxx::Value::NUMBER_: - // max precision - ss << std::setprecision(std::numeric_limits::digits10 + 1); - ss << t.number_value_; - return tab + open_tag(format, 'n', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string()) + - ss.str() + close_tag(format, 'n', name) + '\n'; - } +std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t, const std::string &attr = std::string() ) { + std::stringstream ss; + const std::string tab(depth, '\t'); + + switch( t.type_ ) + { + default: + case jsonxx::Value::NULL_: + return tab + open_tag( format, '0', name, " /" ) + '\n'; + + case jsonxx::Value::BOOL_: + ss << ( t.bool_value_ ? "true" : "false" ); + return tab + open_tag( format, 'b', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() ) + + ss.str() + + close_tag( format, 'b', name ) + '\n'; + + case jsonxx::Value::ARRAY_: + for(Array::container::const_iterator it = t.array_value_->values().begin(), + end = t.array_value_->values().end(); it != end; ++it ) + ss << tag( format, depth+1, std::string(), **it ); + return tab + open_tag( format, 'a', name, attr ) + '\n' + + ss.str() + + tab + close_tag( format, 'a', name ) + '\n'; + + case jsonxx::Value::STRING_: + ss << escape_tag( *t.string_value_, format ); + return tab + open_tag( format, 's', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() ) + + ss.str() + + close_tag( format, 's', name ) + '\n'; + + case jsonxx::Value::OBJECT_: + for(Object::container::const_iterator it=t.object_value_->kv_map().begin(), + end = t.object_value_->kv_map().end(); it != end ; ++it) + ss << tag( format, depth+1, it->first, *it->second ); + return tab + open_tag( format, 'o', name, attr ) + '\n' + + ss.str() + + tab + close_tag( format, 'o', name ) + '\n'; + + case jsonxx::Value::NUMBER_: + // max precision + ss << std::setprecision(std::numeric_limits::digits10 + 1); + ss << t.number_value_; + return tab + open_tag( format, 'n', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() ) + + ss.str() + + close_tag( format, 'n', name ) + '\n'; + } } // order here matches jsonxx::Format enum -const char * defheader[] = { "", +const char *defheader[] = { + "", - "" JSONXX_XML_TAG "\n", + "" + JSONXX_XML_TAG "\n", - "" JSONXX_XML_TAG "\n", + "" + JSONXX_XML_TAG "\n", - "" JSONXX_XML_TAG "\n", + "" + JSONXX_XML_TAG "\n", - "" JSONXX_XML_TAG "\n" }; + "" + JSONXX_XML_TAG "\n" +}; // order here matches jsonxx::Format enum -const char * defrootattrib[] = { "", +const char *defrootattrib[] = { + "", - " xsi:schemaLocation=\"http://www.datapower.com/schemas/json jsonx.xsd\"" - " xmlns:xsi=\"https://www.w3.org/2001/XMLSchema-instance\"" - " xmlns:json=\"https://www.ibm.com/xmlns/prod/2009/jsonx\"", + " xsi:schemaLocation=\"http://www.datapower.com/schemas/json jsonx.xsd\"" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + " xmlns:json=\"http://www.ibm.com/xmlns/prod/2009/jsonx\"", - "", + "", - "", + "", - "" }; + "" +}; -} // namespace xml +} // namespace jsonxx::anon::xml -} // namespace +} // namespace jsonxx::anon -std::string -Object::json() const -{ - using namespace json; +std::string Object::json() const { + using namespace json; - jsonxx::Value v; - v.object_value_ = const_cast(this); - v.type_ = jsonxx::Value::OBJECT_; + jsonxx::Value v; + v.object_value_ = const_cast(this); + v.type_ = jsonxx::Value::OBJECT_; - std::string result = tag(jsonxx::JSON, 0, std::string(), v); + std::string result = tag( jsonxx::JSON, 0, std::string(), v ); - v.object_value_ = 0; - return remove_last_comma(result); + v.object_value_ = 0; + return remove_last_comma( result ); } -std::string -Object::xml(unsigned format, const std::string & header, const std::string & attrib) const -{ - using namespace xml; - JSONXX_ASSERT(format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || - format == jsonxx::TaggedXML); +std::string Object::xml( unsigned format, const std::string &header, const std::string &attrib ) const { + using namespace xml; + JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML ); - jsonxx::Value v; - v.object_value_ = const_cast(this); - v.type_ = jsonxx::Value::OBJECT_; + jsonxx::Value v; + v.object_value_ = const_cast(this); + v.type_ = jsonxx::Value::OBJECT_; - std::string result = tag(format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib); + std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib ); - v.object_value_ = 0; - return (header.empty() ? std::string(defheader[format]) : header) + result; + v.object_value_ = 0; + return ( header.empty() ? std::string(defheader[format]) : header ) + result; } -std::string -Array::json() const -{ - using namespace json; +std::string Array::json() const { + using namespace json; - jsonxx::Value v; - v.array_value_ = const_cast(this); - v.type_ = jsonxx::Value::ARRAY_; + jsonxx::Value v; + v.array_value_ = const_cast(this); + v.type_ = jsonxx::Value::ARRAY_; - std::string result = tag(jsonxx::JSON, 0, std::string(), v); + std::string result = tag( jsonxx::JSON, 0, std::string(), v ); - v.array_value_ = 0; - return remove_last_comma(result); + v.array_value_ = 0; + return remove_last_comma( result ); } -std::string -Array::xml(unsigned format, const std::string & header, const std::string & attrib) const -{ - using namespace xml; - JSONXX_ASSERT(format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || - format == jsonxx::TaggedXML); +std::string Array::xml( unsigned format, const std::string &header, const std::string &attrib ) const { + using namespace xml; + JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML ); - jsonxx::Value v; - v.array_value_ = const_cast(this); - v.type_ = jsonxx::Value::ARRAY_; + jsonxx::Value v; + v.array_value_ = const_cast(this); + v.type_ = jsonxx::Value::ARRAY_; - std::string result = tag(format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib); + std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib ); - v.array_value_ = 0; - return (header.empty() ? std::string(defheader[format]) : header) + result; + v.array_value_ = 0; + return ( header.empty() ? std::string(defheader[format]) : header ) + result; } -bool -validate(std::istream & input) -{ +bool validate( std::istream &input ) { - // trim non-printable chars - for (char ch(0); !input.eof() && input.peek() <= 32;) - input.get(ch); + // trim non-printable chars + for( char ch(0); !input.eof() && input.peek() <= 32; ) + input.get(ch); - // validate json - if (input.peek() == '{') - { - jsonxx::Object o; - if (parse_object(input, o)) - return true; - } - else if (input.peek() == '[') - { - jsonxx::Array a; - if (parse_array(input, a)) - return true; - } + // validate json + if( input.peek() == '{' ) + { + jsonxx::Object o; + if( parse_object( input, o ) ) + return true; + } + else + if( input.peek() == '[' ) + { + jsonxx::Array a; + if( parse_array( input, a ) ) + return true; + } - // bad json input - return false; + // bad json input + return false; } -bool -validate(const std::string & input) -{ - std::istringstream is(input); - return jsonxx::validate(is); +bool validate( const std::string &input ) { + std::istringstream is( input ); + return jsonxx::validate( is ); } -std::string -reformat(std::istream & input) -{ +std::string reformat( std::istream &input ) { - // trim non-printable chars - for (char ch(0); !input.eof() && input.peek() <= 32;) - input.get(ch); + // trim non-printable chars + for( char ch(0); !input.eof() && input.peek() <= 32; ) + input.get(ch); - // validate json - if (input.peek() == '{') - { - jsonxx::Object o; - if (parse_object(input, o)) - return o.json(); - } - else if (input.peek() == '[') - { - jsonxx::Array a; - if (parse_array(input, a)) - return a.json(); - } + // validate json + if( input.peek() == '{' ) + { + jsonxx::Object o; + if( parse_object( input, o ) ) + return o.json(); + } + else + if( input.peek() == '[' ) + { + jsonxx::Array a; + if( parse_array( input, a ) ) + return a.json(); + } - // bad json input - return std::string(); + // bad json input + return std::string(); } -std::string -reformat(const std::string & input) -{ - std::istringstream is(input); - return jsonxx::reformat(is); -} - -std::string -xml(std::istream & input, unsigned format) -{ - using namespace xml; - JSONXX_ASSERT(format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || - format == jsonxx::TaggedXML); - - // trim non-printable chars - for (char ch(0); !input.eof() && input.peek() <= 32;) - input.get(ch); - - // validate json, then transform - if (input.peek() == '{') - { - jsonxx::Object o; - if (parse_object(input, o)) - return o.xml(format); - } - else if (input.peek() == '[') - { - jsonxx::Array a; - if (parse_array(input, a)) - return a.xml(format); - } +std::string reformat( const std::string &input ) { + std::istringstream is( input ); + return jsonxx::reformat( is ); +} + +std::string xml( std::istream &input, unsigned format ) { + using namespace xml; + JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML ); + + // trim non-printable chars + for( char ch(0); !input.eof() && input.peek() <= 32; ) + input.get(ch); + + // validate json, then transform + if( input.peek() == '{' ) + { + jsonxx::Object o; + if( parse_object( input, o ) ) + return o.xml(format); + } + else + if( input.peek() == '[' ) + { + jsonxx::Array a; + if( parse_array( input, a ) ) + return a.xml(format); + } - // bad json, return empty xml - return defheader[format]; + // bad json, return empty xml + return defheader[format]; } -std::string -xml(const std::string & input, unsigned format) -{ - std::istringstream is(input); - return jsonxx::xml(is, format); +std::string xml( const std::string &input, unsigned format ) { + std::istringstream is( input ); + return jsonxx::xml( is, format ); } -Object::Object(const Object & other) -{ +Object::Object(const Object &other) { import(other); } -Object::Object(const std::string & key, const Value & value) -{ - import(key, value); +Object::Object(const std::string &key, const Value &value) { + import(key,value); } -void -Object::import(const Object & other) -{ +void Object::import( const Object &other ) { odd.clear(); - if (this != &other) - { + if (this != &other) { // default - container::const_iterator it = other.value_map_.begin(), end = other.value_map_.end(); - for (/**/; it != end; ++it) - { + container::const_iterator + it = other.value_map_.begin(), + end = other.value_map_.end(); + for (/**/ ; it != end ; ++it) { container::iterator found = value_map_.find(it->first); - if (found != value_map_.end()) - { + if( found != value_map_.end() ) { delete found->second; } - value_map_[it->first] = new Value(*it->second); + value_map_[ it->first ] = new Value( *it->second ); } - } - else - { + } else { // recursion is supported here - import(Object(*this)); + import( Object(*this) ); } } -void -Object::import(const std::string & key, const Value & value) -{ +void Object::import( const std::string &key, const Value &value ) { odd.clear(); container::iterator found = value_map_.find(key); - if (found != value_map_.end()) - { + if( found != value_map_.end() ) { delete found->second; } - value_map_[key] = new Value(value); + value_map_[ key ] = new Value( value ); } -Object & -Object::operator=(const Object & other) -{ +Object &Object::operator=(const Object &other) { odd.clear(); - if (this != &other) - { + if (this != &other) { reset(); import(other); } return *this; } -Object & -Object::operator<<(const Value & value) -{ - if (odd.empty()) - { +Object &Object::operator<<(const Value &value) { + if (odd.empty()) { odd = value.get(); - } - else - { - import(Object(odd, value)); + } else { + import( Object(odd, value) ); odd.clear(); } return *this; } -Object & -Object::operator<<(const Object & value) -{ - import(std::string(odd), value); +Object &Object::operator<<(const Object &value) { + import( std::string(odd),value); odd.clear(); return *this; } -size_t -Object::size() const -{ +size_t Object::size() const { return value_map_.size(); } -bool -Object::empty() const -{ +bool Object::empty() const { return value_map_.size() == 0; } -const std::map & -Object::kv_map() const -{ +const std::map &Object::kv_map() const { return value_map_; } -std::string -Object::write(unsigned format) const -{ +std::string Object::write( unsigned format ) const { return format == JSON ? json() : xml(format); } -void -Object::reset() -{ +void Object::reset() { container::iterator i; - for (i = value_map_.begin(); i != value_map_.end(); ++i) - { + for (i = value_map_.begin(); i != value_map_.end(); ++i) { delete i->second; } value_map_.clear(); } -bool -Object::parse(std::istream & input) -{ - return parse(input, *this); +bool Object::parse(std::istream &input) { + return parse(input,*this); } -bool -Object::parse(const std::string & input) -{ - std::istringstream is(input); - return parse(is, *this); +bool Object::parse(const std::string &input) { + std::istringstream is( input ); + return parse(is,*this); } -Array::Array(const Array & other) -{ +Array::Array(const Array &other) { import(other); } -Array::Array(const Value & value) -{ +Array::Array(const Value &value) { import(value); } -void -Array::append(const Array & other) -{ - if (this != &other) - { - values_.push_back(new Value(other)); - } - else - { - append(Array(*this)); - } +void Array::append(const Array &other) { + if (this != &other) { + values_.push_back( new Value(other) ); + } else { + append( Array(*this) ); + } } -void -Array::import(const Array & other) -{ - if (this != &other) - { +void Array::import(const Array &other) { + if (this != &other) { // default - container::const_iterator it = other.values_.begin(), end = other.values_.end(); - for (/**/; it != end; ++it) - { - values_.push_back(new Value(**it)); + container::const_iterator + it = other.values_.begin(), + end = other.values_.end(); + for (/**/ ; it != end; ++it) { + values_.push_back( new Value(**it) ); } - } - else - { + } else { // recursion is supported here - import(Array(*this)); + import( Array(*this) ); } } -void -Array::import(const Value & value) -{ - values_.push_back(new Value(value)); +void Array::import(const Value &value) { + values_.push_back( new Value(value) ); } -size_t -Array::size() const -{ +size_t Array::size() const { return values_.size(); } -bool -Array::empty() const -{ +bool Array::empty() const { return values_.size() == 0; } -void -Array::reset() -{ - for (container::iterator i = values_.begin(); i != values_.end(); ++i) - { +void Array::reset() { + for (container::iterator i = values_.begin(); i != values_.end(); ++i) { delete *i; } values_.clear(); } -bool -Array::parse(std::istream & input) -{ - return parse(input, *this); +bool Array::parse(std::istream &input) { + return parse(input,*this); } -bool -Array::parse(const std::string & input) -{ +bool Array::parse(const std::string &input) { std::istringstream is(input); - return parse(is, *this); + return parse(is,*this); } -Array & -Array::operator<<(const Array & other) -{ +Array &Array::operator<<(const Array &other) { import(other); return *this; } -Array & -Array::operator<<(const Value & value) -{ +Array &Array::operator<<(const Value &value) { import(value); return *this; } -Array & -Array::operator=(const Array & other) -{ - if (this != &other) - { +Array &Array::operator=(const Array &other) { + if( this != &other ) { reset(); import(other); } return *this; } -Array & -Array::operator=(const Value & value) -{ +Array &Array::operator=(const Value &value) { reset(); import(value); return *this; } -Value::Value(const Value & other) - : type_(INVALID_) -{ - import(other); +Value::Value(const Value &other) : type_(INVALID_) { + import( other ); } -bool -Value::empty() const -{ - if (type_ == INVALID_) - return true; - if (type_ == STRING_ && string_value_ == 0) - return true; - if (type_ == ARRAY_ && array_value_ == 0) - return true; - if (type_ == OBJECT_ && object_value_ == 0) - return true; +bool Value::empty() const { + if( type_ == INVALID_ ) return true; + if( type_ == STRING_ && string_value_ == 0 ) return true; + if( type_ == ARRAY_ && array_value_ == 0 ) return true; + if( type_ == OBJECT_ && object_value_ == 0 ) return true; return false; } -bool -Value::parse(std::istream & input) -{ - return parse(input, *this); +bool Value::parse(std::istream &input) { + return parse(input,*this); } -bool -Value::parse(const std::string & input) -{ - std::istringstream is(input); - return parse(is, *this); +bool Value::parse(const std::string &input) { + std::istringstream is( input ); + return parse(is,*this); } -} // namespace jsonxx +} // namespace jsonxx From e76fb362d4c21d08a1471f66facb8e9826a91027 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Thu, 30 May 2024 22:05:54 -0400 Subject: [PATCH 2/3] COMP: Update jsonxx From: https://github.com/TransitApp/jsonxx/commit/a27e393d4095b2d9b8d26ff62b0c8957622cbfde --- include/jsonxx.h | 1032 +++++++++++++++++++++++++--------------------- src/jsonxx.cc | 264 ++++++------ 2 files changed, 699 insertions(+), 597 deletions(-) diff --git a/include/jsonxx.h b/include/jsonxx.h index 5bf4a45..bec291e 100644 --- a/include/jsonxx.h +++ b/include/jsonxx.h @@ -5,14 +5,17 @@ // Sean Middleditch // rlyeh -#pragma once +#ifndef JSONXX_DEFINE_H +#define JSONXX_DEFINE_H #include #include #include +#include #include #include #include +#include #include // jsonxx versioning: major.minor-extra where @@ -33,502 +36,569 @@ #define JSONXX_COMPILER_HAS_CXX11 0 #endif -#ifdef _MSC_VER -// disable the C4127 warning if using VC, see http://stackoverflow.com/a/12042515 -#define JSONXX_ASSERT(...) \ - do { \ - __pragma(warning(push)) __pragma(warning(disable:4127)) \ - if( jsonxx::Assertions ) \ - __pragma(warning(pop)) \ - jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); \ - __pragma(warning(push)) __pragma(warning(disable:4127)) \ - } while(0) \ - __pragma(warning(pop)) -#else #define JSONXX_ASSERT(...) do { if( jsonxx::Assertions ) \ jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); } while(0) + +#ifndef JSONXX_HANDLE_INFINITY +#define JSONXX_HANDLE_INFINITY 1 #endif -namespace jsonxx { +#ifdef DEBUG +#define JSONXX_WARN(...) \ + std::cerr << "[WARN] " << __VA_ARGS__ << std::endl; +#else +#define JSONXX_WARN(...) ; +#endif -// FIXME(hjiang): Those should really be dynamic. -// Settings -enum Settings { - // constants - Enabled = true, - Disabled = false, - Permissive = true, - Strict = false, - // values - Parser = Permissive, // permissive or strict parsing - UnquotedKeys = Disabled, // support of unquoted keys - Assertions = Enabled // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds) -}; +namespace jsonxx { -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4127) + enum PrintMode { + Pretty, + Compact, + }; + + // Settings + enum Settings { + // constants + Enabled = true, + Disabled = false, + Permissive = true, + Strict = false, + // values + Parser = Permissive, // permissive or strict parsing + UnquotedKeys = Disabled, // support of unquoted keys +#if DEBUG + Assertions = Enabled // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds) +#else + Assertions = Disabled // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds) #endif -inline bool parser_is_strict() { return Parser == Strict; } -inline bool parser_is_permissive() { return Parser == Permissive; } -inline bool unquoted_keys_are_enabled() { return UnquotedKeys == Enabled; } -#ifdef _MSC_VER -#pragma warning(pop) + }; + + // Constants for .write() and .xml() methods + enum Format { + JSON = 0, // JSON output + JSONx = 1, // XML output, JSONx format. see http://goo.gl/I3cxs + JXML = 2, // XML output, JXML format. see https://github.com/r-lyeh/JXML + JXMLex = 3, // XML output, JXMLex format. see https://github.com/r-lyeh/JXMLex + TaggedXML = 4 // XML output, tagged XML format. see https://github.com/hjiang/jsonxx/issues/12 + }; + + // Types + typedef long double Number; + typedef bool Boolean; + typedef std::string String; + struct Null {}; + class Value; + class Object; + class Array; + + // Range of Number. Valid numbers outside the range as considered infinite + constexpr Number MaxNumberRange = std::numeric_limits::max(); + constexpr Number MinNumberRange = -std::numeric_limits::max(); + + // JSON representation of infinite numbers + constexpr const char* InfinityRepresentation = "1e500"; + + // Default value + static const String EmptyString = ""; + static const Number Zero = 0; + static const Number MinusOne = -1; + static const Number One = 1; + static const Boolean True = true; + static const Boolean False = false; + + // Identity meta-function + template + struct identity { + typedef T type; + }; + + // Tools + bool validate( const std::string &input ); + bool validate( std::istream &input ); + std::string reformat( const std::string &input ); + std::string reformat( std::istream &input ); + std::string xml( const std::string &input, unsigned format = JSONx ); + std::string xml( std::istream &input, unsigned format = JSONx ); + + // Detail + void assertion( const char *file, int line, const char *expression, bool result ); + + // A JSON Object + class Object { + public: + Object(); + ~Object(); + + template + bool has(const std::string& key) const; + + // Always call has<>() first. If the key doesn't exist, consider + // the behavior undefined. + template + T& get(const std::string& key); + template + const T& get(const std::string& key) const; + + template + const T& get(const std::string& key, const typename identity::type& default_value) const; + + // Delete unsafe operation + template + const T& get(const std::string& key, typename identity::type&& default_value) const = delete; + + size_t size() const; + bool empty() const; + + const std::map& kv_map() const; + std::string json(PrintMode printMode = PrintMode::Compact, int floatPrecision = std::numeric_limits::digits10 + 1) const; + std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string(), int floatPrecision = std::numeric_limits::digits10 + 1) const; + std::string write( unsigned format ) const; + + void reset(); + bool parse(std::istream &input); + bool parse(const std::string &input); + typedef std::map container; + void import( const Object &other ); + void import( const std::string &key, const Value &value ); + Object &operator<<(const Value &value); + Object &operator<<(const Object &value); + Object &operator=(const Object &value); + Object(const Object &other); + Object(const std::string &key, const Value &value); + template + Object(const char (&key)[N], const Value &value) { + import(key,value); + } + template + Object &operator<<(const T &value); + + protected: + static bool parse(std::istream& input, Object& object); + container value_map_; + std::string odd; + }; + + static const Object EmptyObject = {}; + + class Array { + public: + Array(); + ~Array(); + + size_t size() const; + bool empty() const; + + template + bool has(unsigned int i) const; + + template + T& get(unsigned int i); + template + const T& get(unsigned int i) const; + + template + const T& get(unsigned int i, const typename identity::type& default_value) const; + + // Delete unsafe operation + template + const T& get(unsigned int i, typename identity::type&& default_value) const = delete; + + const std::vector& values() const { + return values_; + } + std::string json(PrintMode printMode = PrintMode::Compact, int floatPrecision = std::numeric_limits::digits10 + 1) const; + std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string(), int floatPrecision = std::numeric_limits::digits10 + 1) const; + + std::string write( unsigned format ) const { return format == JSON ? json() : xml(format); } + void reset(); + bool parse(std::istream &input); + bool parse(const std::string &input); + typedef std::vector container; + void import(const Array &other); + void import(const Value &value); + Array &operator<<(const Array &other); + Array &operator<<(const Value &value); + Array &operator=(const Array &other); + Array &operator=(const Value &value); + Array(const Array &other); + Array(const Value &value); + protected: + static bool parse(std::istream& input, Array& array); + container values_; + }; + + static const Array EmptyArray = {}; + + // A value could be a number, an array, a string, an object, a + // boolean, or null + class Value { + public: + + Value(); + ~Value() { reset(); } + void reset(); + +#ifdef JSONXX_ALLOW_INVALID_TYPES + template + [[deprecated("this type is not natively supported by jsonxx, therefore its value will be converted to 'null'")]] + void import( const T& t ) { + reset(); + type_ = INVALID_; + JSONXX_WARN( "No JSONXX support for " << typeid(t).name() ); + } +#else + template void import( const T& t ) = delete; #endif -// Constants for .write() and .xml() methods -enum Format { - JSON = 0, // JSON output - JSONx = 1, // XML output, JSONx format. see http://goo.gl/I3cxs - JXML = 2, // XML output, JXML format. see https://github.com/r-lyeh/JXML - JXMLex = 3, // XML output, JXMLex format. see https://github.com/r-lyeh/JXMLex - TaggedXML = 4 // XML output, tagged XML format. see https://github.com/hjiang/jsonxx/issues/12 -}; - -// Types -typedef long double Number; -typedef bool Boolean; -typedef std::string String; -struct Null {}; -class Value; -class Object; -class Array; - -// Identity meta-function -template -struct identity { - typedef T type; -}; - -// Tools -bool validate( const std::string &input ); -bool validate( std::istream &input ); -std::string reformat( const std::string &input ); -std::string reformat( std::istream &input ); -std::string xml( const std::string &input, unsigned format = JSONx ); -std::string xml( std::istream &input, unsigned format = JSONx ); - -// Detail -void assertion( const char *file, int line, const char *expression, bool result ); - -// A JSON Object -class Object { - public: - Object(); - ~Object(); - - template - bool has(const std::string& key) const; - - // Always call has<>() first. If the key doesn't exist, consider - // the behavior undefined. - template - T& get(const std::string& key); - template - const T& get(const std::string& key) const; - - template - const T& get(const std::string& key, const typename identity::type& default_value) const; - - size_t size() const; - bool empty() const; - - const std::map& kv_map() const; - std::string json() const; - std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string() ) const; - std::string write( unsigned format ) const; - - void reset(); - bool parse(std::istream &input); - bool parse(const std::string &input); - typedef std::map container; - void import( const Object &other ); - void import( const std::string &key, const Value &value ); - Object &operator<<(const Value &value); - Object &operator<<(const Object &value); - Object &operator=(const Object &value); - Object(const Object &other); - Object(const std::string &key, const Value &value); - template - Object(const char (&key)[N], const Value &value) { - import(key,value); - } - template - Object &operator<<(const T &value); - - protected: - static bool parse(std::istream& input, Object& object); - container value_map_; - std::string odd; -}; - -class Array { - public: - Array(); - ~Array(); - - size_t size() const; - bool empty() const; - - template - bool has(unsigned int i) const; - - template - T& get(unsigned int i); - template - const T& get(unsigned int i) const; - - template - const T& get(unsigned int i, const typename identity::type& default_value) const; - - const std::vector& values() const { - return values_; - } - std::string json() const; - std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string() ) const; - - std::string write( unsigned format ) const { return format == JSON ? json() : xml(format); } - void reset(); - bool parse(std::istream &input); - bool parse(const std::string &input); - typedef std::vector container; - void append(const Array &other); - void append(const Value &value) { import(value); } - void import(const Array &other); - void import(const Value &value); - Array &operator<<(const Array &other); - Array &operator<<(const Value &value); - Array &operator=(const Array &other); - Array &operator=(const Value &value); - Array(const Array &other); - Array(const Value &value); - protected: - static bool parse(std::istream& input, Array& array); - container values_; -}; - -// A value could be a number, an array, a string, an object, a -// boolean, or null -class Value { - public: - - Value(); - ~Value() { reset(); } - void reset(); - - template - void import( const T & ) { - reset(); - type_ = INVALID_; - // debug - // std::cout << "[WARN] No support for " << typeid(t).name() << std::endl; - } - void import( const bool &b ) { - reset(); - type_ = BOOL_; - bool_value_ = b; - } + void import( const bool &b ) { + reset(); + type_ = BOOL_; + bool_value_ = b; + } #define $number(TYPE) \ - void import( const TYPE &n ) { \ - reset(); \ - type_ = NUMBER_; \ - number_value_ = static_cast(n); \ - } - $number( char ) - $number( int ) - $number( long ) - $number( long long ) - $number( unsigned char ) - $number( unsigned int ) - $number( unsigned long ) - $number( unsigned long long ) - $number( float ) - $number( double ) - $number( long double ) +void import( const TYPE &n ) { \ +reset(); \ +type_ = NUMBER_; \ +number_value_ = static_cast(n); \ +} + $number( char ) + $number( int ) + $number( long ) + $number( long long ) + $number( unsigned char ) + $number( unsigned int ) + $number( unsigned long ) + $number( unsigned long long ) + $number( float ) + $number( double ) + $number( long double ) #undef $number #if JSONXX_COMPILER_HAS_CXX11 > 0 - void import( const std::nullptr_t & ) { - reset(); - type_ = NULL_; - } + void import( const std::nullptr_t & ) { + reset(); + type_ = NULL_; + } +#endif + void import( const Null & ) { + reset(); + type_ = NULL_; + } + void import( const String &s ) { + reset(); + type_ = STRING_; + *( string_value_ = new String() ) = s; + } + void import( const std::string_view &sv ) { + reset(); + type_ = STRING_; + *( string_value_ = new String() ) = sv; + } + template + void import( const std::vector &array ) { + Array a; + for (const auto& elmt : array) { + a << elmt; + } + import(a); + } + void import( const Array &a ) { + reset(); + type_ = ARRAY_; + *( array_value_ = new Array() ) = a; + } + void import( const Object &o ) { + reset(); + type_ = OBJECT_; + *( object_value_ = new Object() ) = o; + } + void import( const Value &other ) { + if (this != &other) + switch (other.type_) { + case NULL_: + import( Null() ); + break; + case BOOL_: + import( other.bool_value_ ); + break; + case NUMBER_: + import( other.number_value_ ); + break; + case STRING_: + import( *other.string_value_ ); + break; + case ARRAY_: + import( *other.array_value_ ); + break; + case OBJECT_: + import( *other.object_value_ ); + break; + case INVALID_: + type_ = INVALID_; + break; + default: + JSONXX_ASSERT( !"not implemented" ); + } + } + template + Value &operator <<( const T &t ) { + import(t); + return *this; + } + template + Value &operator =( const T &t ) { + reset(); + import(t); + return *this; + } + Value(const Value &other); + +#define $Value(TYPE) Value( const TYPE &t ) : type_(INVALID_) { import(t); } + +#ifdef JSONXX_ALLOW_INVALID_TYPES + template $Value( T ) +#else + $Value( bool ) + $Value( char ) + $Value( int ) + $Value( long ) + $Value( long long ) + $Value( unsigned char ) + $Value( unsigned int ) + $Value( unsigned long ) + $Value( unsigned long long ) + $Value( float ) + $Value( double ) + $Value( long double ) +#if JSONXX_COMPILER_HAS_CXX11 > 0 + $Value( std::nullptr_t ) +#endif + $Value( Null ) + $Value( String ) + $Value( std::string_view ) + template $Value( std::vector ) + $Value( Array ) + $Value( Object ) #endif - void import( const Null & ) { - reset(); - type_ = NULL_; - } - void import( const String &s ) { - reset(); - type_ = STRING_; - *( string_value_ = new String() ) = s; - } - void import( const char* s ) { - reset(); - type_ = STRING_; - *( string_value_ = new String() ) = s; - } - void import( const Array &a ) { - reset(); - type_ = ARRAY_; - *( array_value_ = new Array() ) = a; - } - void import( const Object &o ) { - reset(); - type_ = OBJECT_; - *( object_value_ = new Object() ) = o; - } - void import( const Value &other ) { - if (this != &other) - switch (other.type_) { - case NULL_: - import( Null() ); - break; - case BOOL_: - import( other.bool_value_ ); - break; - case NUMBER_: - import( other.number_value_ ); - break; - case STRING_: - import( *other.string_value_ ); - break; - case ARRAY_: - import( *other.array_value_ ); - break; - case OBJECT_: - import( *other.object_value_ ); - break; - case INVALID_: - type_ = INVALID_; - break; - default: - JSONXX_ASSERT( !"not implemented" ); - } - } - template - Value &operator <<( const T &t ) { - import(t); - return *this; - } - template - Value &operator =( const T &t ) { - reset(); - import(t); - return *this; - } - Value(const Value &other); - template - Value( const T&t ) : type_(INVALID_) { import(t); } - template - Value( const char (&t)[N] ) : type_(INVALID_) { import( std::string(t) ); } - - bool parse(std::istream &input); - bool parse(const std::string &input); - - template - bool is() const; - template - T& get(); - template - const T& get() const; - - bool empty() const; - - public: - enum { - NUMBER_, - STRING_, - BOOL_, - NULL_, - ARRAY_, - OBJECT_, - INVALID_ - } type_; - union { - Number number_value_; - String* string_value_; - Boolean bool_value_; - Array* array_value_; - Object* object_value_; - }; - -protected: - static bool parse(std::istream& input, Value& value); -}; - -template -bool Array::has(unsigned int i) const { - if (i >= size()) { - return false; - } else { - Value* v = values_.at(i); - return v->is(); - } -} - -template -T& Array::get(unsigned int i) { - JSONXX_ASSERT(i < size()); - Value* v = values_.at(i); - return v->get(); -} - -template -const T& Array::get(unsigned int i) const { - JSONXX_ASSERT(i < size()); - const Value* v = values_.at(i); - return v->get(); -} - -template -const T& Array::get(unsigned int i, const typename identity::type& default_value) const { - if(has(i)) { - const Value* v = values_.at(i); - return v->get(); - } else { - return default_value; - } -} - -template -bool Object::has(const std::string& key) const { - container::const_iterator it(value_map_.find(key)); - return it != value_map_.end() && it->second->is(); -} - -template -T& Object::get(const std::string& key) { - JSONXX_ASSERT(has(key)); - return value_map_.find(key)->second->get(); -} - -template -const T& Object::get(const std::string& key) const { - JSONXX_ASSERT(has(key)); - return value_map_.find(key)->second->get(); -} - -template -const T& Object::get(const std::string& key, const typename identity::type& default_value) const { - if (has(key)) { - return value_map_.find(key)->second->get(); - } else { - return default_value; - } -} - -template<> -inline bool Value::is() const { - return true; -} - -template<> -inline bool Value::is() const { - return type_ == NULL_; -} - -template<> -inline bool Value::is() const { - return type_ == BOOL_; -} - -template<> -inline bool Value::is() const { - return type_ == STRING_; -} - -template<> -inline bool Value::is() const { - return type_ == NUMBER_; -} - -template<> -inline bool Value::is() const { - return type_ == ARRAY_; -} - -template<> -inline bool Value::is() const { - return type_ == OBJECT_; -} - -template<> -inline Value& Value::get() { - return *this; -} - -template<> -inline const Value& Value::get() const { - return *this; -} - -template<> -inline bool& Value::get() { - JSONXX_ASSERT(is()); - return bool_value_; -} - -template<> -inline std::string& Value::get() { - JSONXX_ASSERT(is()); - return *string_value_; -} - -template<> -inline Number& Value::get() { - JSONXX_ASSERT(is()); - return number_value_; -} - -template<> -inline Array& Value::get() { - JSONXX_ASSERT(is()); - return *array_value_; -} - -template<> -inline Object& Value::get() { - JSONXX_ASSERT(is()); - return *object_value_; -} - -template<> -inline const Boolean& Value::get() const { - JSONXX_ASSERT(is()); - return bool_value_; -} - -template<> -inline const String& Value::get() const { - JSONXX_ASSERT(is()); - return *string_value_; -} - -template<> -inline const Number& Value::get() const { - JSONXX_ASSERT(is()); - return number_value_; -} - -template<> -inline const Array& Value::get() const { - JSONXX_ASSERT(is()); - return *array_value_; -} - -template<> -inline const Object& Value::get() const { - JSONXX_ASSERT(is()); - return *object_value_; -} - -template -inline Object &Object::operator<<(const T &value) { - *this << Value(value); - return *this; -} +#undef $Value + + template + Value( const char (&t)[N] ) : type_(INVALID_) { import( std::string(t) ); } + + bool parse(std::istream &input); + bool parse(const std::string &input); + + template + bool is() const; + template + T& get(); + template + const T& get() const; + + bool empty() const; + + public: + enum { + NUMBER_, + STRING_, + BOOL_, + NULL_, + ARRAY_, + OBJECT_, + INVALID_ + } type_; + union { + Number number_value_; + String* string_value_; + Boolean bool_value_; + Array* array_value_; + Object* object_value_; + }; + + protected: + static bool parse(std::istream& input, Value& value); + }; + + static const Value EmptyValue = {}; + + template + bool Array::has(unsigned int i) const { + if (i >= size()) { + return false; + } else { + Value* v = values_.at(i); + return v->is(); + } + } + + template + T& Array::get(unsigned int i) { + JSONXX_ASSERT(i < size()); + Value* v = values_.at(i); + return v->get(); + } + + template + const T& Array::get(unsigned int i) const { + JSONXX_ASSERT(i < size()); + const Value* v = values_.at(i); + return v->get(); + } + + template + const T& Array::get(unsigned int i, const typename identity::type& default_value) const { + if(i < size()) { + const Value* v = values_.at(i); + if (v->is()) { + return v->get(); + } + } + return default_value; + } + + template + bool Object::has(const std::string& key) const { + container::const_iterator it(value_map_.find(key)); + return it != value_map_.end() && it->second->is(); + } + + template + T& Object::get(const std::string& key) { + JSONXX_ASSERT(has(key)); + return value_map_.find(key)->second->get(); + } + + template + const T& Object::get(const std::string& key) const { + JSONXX_ASSERT(has(key)); + return value_map_.find(key)->second->get(); + } + + template + const T& Object::get(const std::string& key, const typename identity::type& default_value) const { + auto iterator = value_map_.find(key); + if (iterator != value_map_.end() && iterator->second->is()) { + return iterator->second->get(); + } else { + return default_value; + } + } + + template<> + inline bool Value::is() const { + return true; + } + + template<> + inline bool Value::is() const { + return type_ == NULL_; + } + + template<> + inline bool Value::is() const { + return type_ == BOOL_; + } + + template<> + inline bool Value::is() const { + return type_ == STRING_; + } + + template<> + inline bool Value::is() const { + return type_ == NUMBER_; + } + + template<> + inline bool Value::is() const { + return type_ == ARRAY_; + } + + template<> + inline bool Value::is() const { + return type_ == OBJECT_; + } + + template<> + inline Value& Value::get() { + return *this; + } + + template<> + inline const Value& Value::get() const { + return *this; + } + + template<> + inline bool& Value::get() { + JSONXX_ASSERT(is()); + return bool_value_; + } + + template<> + inline std::string& Value::get() { + JSONXX_ASSERT(is()); + return *string_value_; + } + + template<> + inline Number& Value::get() { + JSONXX_ASSERT(is()); + return number_value_; + } + + template<> + inline Array& Value::get() { + JSONXX_ASSERT(is()); + return *array_value_; + } + + template<> + inline Object& Value::get() { + JSONXX_ASSERT(is()); + return *object_value_; + } + + template<> + inline const Boolean& Value::get() const { + JSONXX_ASSERT(is()); + return bool_value_; + } + + template<> + inline const String& Value::get() const { + JSONXX_ASSERT(is()); + return *string_value_; + } + + template<> + inline const Number& Value::get() const { + JSONXX_ASSERT(is()); + return number_value_; + } + + template<> + inline const Array& Value::get() const { + JSONXX_ASSERT(is()); + return *array_value_; + } + + template<> + inline const Object& Value::get() const { + JSONXX_ASSERT(is()); + return *object_value_; + } + + template + inline Object &Object::operator<<(const T &value) { + *this << Value(value); + return *this; + } } // namespace jsonxx std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v); std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v); std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v); + +#endif diff --git a/src/jsonxx.cc b/src/jsonxx.cc index a984f89..1edaad1 100644 --- a/src/jsonxx.cc +++ b/src/jsonxx.cc @@ -14,6 +14,7 @@ #include #include #include +#include // Snippet that creates an assertion function that works both in DEBUG & RELEASE mode. // JSONXX_ASSERT(...) macro will redirect to this. assert() macro is kept untouched. @@ -46,6 +47,7 @@ bool parse_bool(std::istream& input, Boolean& value); bool parse_comment(std::istream &input); bool parse_null(std::istream& input); bool parse_number(std::istream& input, Number& value); +bool parse_number_inf(std::istream& input, Number& value, std::streampos rollback); bool parse_object(std::istream& input, Object& object); bool parse_string(std::istream& input, String& value); bool parse_identifier(std::istream& input, String& value); @@ -78,7 +80,7 @@ bool match(const char* pattern, std::istream& input) { bool parse_string(std::istream& input, String& value) { char ch = '\0', delimiter = '"'; if (!match("\"", input)) { - if (parser_is_strict()) { + if (Parser == Strict) { return false; } delimiter = '\''; @@ -122,7 +124,7 @@ bool parse_string(std::istream& input, String& value) { ss << std::hex << ch; } if( input.good() && (ss >> i) ) - value.push_back(static_cast(i)); + value.push_back(i); } break; default: @@ -169,7 +171,7 @@ bool parse_identifier(std::istream& input, String& value) { (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) { - value.push_back(ch); + value.push_back(ch); } else if(ch == '\t' || ch == ' ') { input >> std::ws; @@ -182,35 +184,63 @@ bool parse_identifier(std::istream& input, String& value) { } } -class IOStateMasker { - public: - explicit IOStateMasker(std::istream& input): stream(input) { - mask = input.exceptions(); - input.exceptions(std::istream::goodbit); - } - - ~IOStateMasker() { - stream.exceptions(mask); - } - - private: - std::istream& stream; - std::istream::iostate mask; -}; - bool parse_number(std::istream& input, Number& value) { input >> std::ws; std::streampos rollback = input.tellg(); - IOStateMasker masker(input); input >> value; if (input.fail()) { + if (parse_number_inf(input, value, rollback)) { + return true; + } + input.clear(); input.seekg(rollback); return false; } + +#if JSONXX_HANDLE_INFINITY + if (value >= MaxNumberRange) { + value = std::numeric_limits::infinity(); + } + else if (value <= MinNumberRange) { + value = -std::numeric_limits::infinity(); + } +#endif return true; } +bool parse_number_inf(std::istream& input, Number& value, std::streampos rollback) { + input.clear(); + input.seekg(rollback); + + switch (input.peek()) { + case '-': + input.get(); + value = -std::numeric_limits::infinity(); + break; + case '+': + case '0'...'9': + input.get(); + value = std::numeric_limits::infinity(); + break; + default: + return false; + } + + int ch; + do { + ch = input.get(); + } while (isdigit(ch)); + + if (ch != 'E' && ch != 'e') { + return false; + } + + int exponent; + input >> exponent; + return !input.fail(); +} + bool parse_bool(std::istream& input, Boolean& value) { if (match("true", input)) { value = true; @@ -227,7 +257,7 @@ bool parse_null(std::istream& input) { if (match("null", input)) { return true; } - if (parser_is_strict()) { + if (Parser == Strict) { return false; } return (input.peek()==','); @@ -242,7 +272,7 @@ bool parse_object(std::istream& input, Object& object) { } bool parse_comment(std::istream &input) { - if( parser_is_permissive() ) + if( Parser == Permissive ) if( !input.eof() && input.peek() == '/' ) { char ch0(0); @@ -299,9 +329,9 @@ bool Object::parse(std::istream& input, Object& object) { do { std::string key; - if (unquoted_keys_are_enabled()) { + if(UnquotedKeys == Enabled) { if (!parse_identifier(input, key)) { - if (parser_is_permissive()) { + if (Parser == Permissive) { if (input.peek() == '}') break; } @@ -310,7 +340,7 @@ bool Object::parse(std::istream& input, Object& object) { } else { if (!parse_string(input, key)) { - if (parser_is_permissive()) { + if (Parser == Permissive) { if (input.peek() == '}') break; } @@ -325,18 +355,7 @@ bool Object::parse(std::istream& input, Object& object) { delete v; break; } - // TODO(hjiang): Add an option to allow duplicated keys? - if (object.value_map_.find(key) == object.value_map_.end()) { - object.value_map_[key] = v; - } else { - if (parser_is_permissive()) { - delete object.value_map_[key]; - object.value_map_[key] = v; - } else { - delete v; - return false; - } - } + object.value_map_[key] = v; } while (match(",", input)); @@ -394,7 +413,6 @@ bool Value::parse(std::istream& input, Value& value) { return true; } delete value.array_value_; - value.array_value_ = 0; } value.object_value_ = new Object(); if (parse_object(input, *value.object_value_)) { @@ -402,7 +420,6 @@ bool Value::parse(std::istream& input, Value& value) { return true; } delete value.object_value_; - value.object_value_ = 0; return false; } @@ -545,38 +562,30 @@ typedef unsigned char byte; //template std::string escape_string( const std::string &input, const bool quote = false ) { - static std::string map[256], *once = 0; - static std::mutex mutex; - if( !once ) { - // The problem here is that, once is initializing at the end of job, but if multithreaded application is starting this for the first time - // it will jump into this part with several threads and cause double free or corruptions. - // To prevent it, it is required to have mutex, to lock other threads while only one of them is constructing the static map. - mutex.lock(); - if (!once) { - // base - for( int i = 0; i < 256; ++i ) { - map[ i ] = std::string() + char(i); - } - // non-printable - for( int i = 0; i < 32; ++i ) { - std::stringstream str; - str << "\\u" << std::hex << std::setw(4) << std::setfill('0') << i; - map[ i ] = str.str(); - } - // exceptions - map[ byte('"') ] = "\\\""; - map[ byte('\\') ] = "\\\\"; - map[ byte('/') ] = "\\/"; - map[ byte('\b') ] = "\\b"; - map[ byte('\f') ] = "\\f"; - map[ byte('\n') ] = "\\n"; - map[ byte('\r') ] = "\\r"; - map[ byte('\t') ] = "\\t"; - - once = map; + static std::string map[256]; + static std::once_flag once; + std::call_once(once, []{ + // base + for( int i = 0; i < 256; ++i ) { + map[ i ] = std::string() + char(i); } - mutex.unlock(); - } + // non-printable + for( int i = 0; i < 32; ++i ) { + std::stringstream str; + str << "\\u" << std::hex << std::setw(4) << std::setfill('0') << i; + map[ i ] = str.str(); + } + // exceptions + map[ byte('"') ] = "\\\""; + map[ byte('\\') ] = "\\\\"; + map[ byte('/') ] = "\\/"; + map[ byte('\b') ] = "\\b"; + map[ byte('\f') ] = "\\f"; + map[ byte('\n') ] = "\\n"; + map[ byte('\r') ] = "\\r"; + map[ byte('\t') ] = "\\t"; + }); + std::string output; output.reserve( input.size() * 2 + 2 ); // worst scenario if( quote ) output += '"'; @@ -592,18 +601,33 @@ namespace json { std::string remove_last_comma( const std::string &_input ) { std::string input( _input ); size_t size = input.size(); - if( size > 2 ) + if( size > 2 ) { if( input[ size - 2 ] == ',' ) input[ size - 2 ] = ' '; + if( input[ size - 1 ] == ',' ) // compact case + input.resize(size - 1); + } return input; } - std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t) { + std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t, PrintMode printMode, int floatPrecision = std::numeric_limits::digits10 + 1) { std::stringstream ss; - const std::string tab(depth, '\t'); + std::string tab(depth, '\t'); + std::string newLine("\n"); + std::string space(" "); + + switch (printMode) { + case Compact: + tab = ""; + newLine = ""; + space = ""; + break; + case Pretty: + break; + } if( !name.empty() ) - ss << tab << '\"' << escape_string( name ) << '\"' << ':' << ' '; + ss << tab << '\"' << escape_string( name ) << '\"' << ':' << space; else ss << tab; @@ -612,35 +636,49 @@ namespace json { default: case jsonxx::Value::NULL_: ss << "null"; - return ss.str() + ",\n"; + return ss.str() + "," + newLine; case jsonxx::Value::BOOL_: ss << ( t.bool_value_ ? "true" : "false" ); - return ss.str() + ",\n"; + return ss.str() + "," + newLine; case jsonxx::Value::ARRAY_: - ss << "[\n"; + ss << "[" + newLine; for(Array::container::const_iterator it = t.array_value_->values().begin(), end = t.array_value_->values().end(); it != end; ++it ) - ss << tag( format, depth+1, std::string(), **it ); - return remove_last_comma( ss.str() ) + tab + "]" ",\n"; + ss << tag( format, depth+1, std::string(), **it, printMode, floatPrecision ); + return remove_last_comma( ss.str() ) + tab + "]" "," + newLine; case jsonxx::Value::STRING_: ss << '\"' << escape_string( *t.string_value_ ) << '\"'; - return ss.str() + ",\n"; + return ss.str() + "," + newLine; case jsonxx::Value::OBJECT_: - ss << "{\n"; + ss << "{" + newLine; for(Object::container::const_iterator it=t.object_value_->kv_map().begin(), end = t.object_value_->kv_map().end(); it != end ; ++it) - ss << tag( format, depth+1, it->first, *it->second ); - return remove_last_comma( ss.str() ) + tab + "}" ",\n"; + ss << tag( format, depth+1, it->first, *it->second, printMode, floatPrecision); + return remove_last_comma( ss.str() ) + tab + "}" "," + newLine; case jsonxx::Value::NUMBER_: - // max precision - ss << std::setprecision(std::numeric_limits::digits10 + 1); - ss << t.number_value_; - return ss.str() + ",\n"; + if (isfinite(t.number_value_)) { + // max precision + ss << std::setprecision(floatPrecision); + ss << t.number_value_; + } +#if JSONXX_HANDLE_INFINITY + else if (t.number_value_ > MaxNumberRange) { + ss << InfinityRepresentation; + } + else if (t.number_value_ < MinNumberRange) { + ss << '-' << InfinityRepresentation; + } +#endif + else { + JSONXX_WARN( "No JSONXX support for number value " << t.number_value_ ); + ss << "null"; // NaN or other stuff we cannot represent + } + return ss.str() + "," + newLine; } } } // namespace jsonxx::anon::json @@ -648,8 +686,9 @@ namespace json { namespace xml { std::string escape_attrib( const std::string &input ) { - static std::string map[256], *once = 0; - if( !once ) { + static std::string map[256]; + static std::once_flag once; + std::call_once(once, [] { for( int i = 0; i < 256; ++i ) map[ i ] = "_"; for( int i = int('a'); i <= int('z'); ++i ) @@ -658,8 +697,8 @@ std::string escape_attrib( const std::string &input ) { map[ i ] = std::string() + char(i); for( int i = int('0'); i <= int('9'); ++i ) map[ i ] = std::string() + char(i); - once = map; - } + }); + std::string output; output.reserve( input.size() ); // worst scenario for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it ) @@ -668,18 +707,19 @@ std::string escape_attrib( const std::string &input ) { } std::string escape_tag( const std::string &input, unsigned format ) { - static std::string map[256], *once = 0; - if( !once ) { + static std::string map[256]; + static std::once_flag once; + std::call_once(once, [=] { for( int i = 0; i < 256; ++i ) map[ i ] = std::string() + char(i); map[ byte('<') ] = "<"; map[ byte('>') ] = ">"; - + switch( format ) { default: break; - + case jsonxx::JXML: case jsonxx::JXMLex: case jsonxx::JSONx: @@ -687,9 +727,8 @@ std::string escape_tag( const std::string &input, unsigned format ) { map[ byte('&') ] = "&"; break; } + }); - once = map; - } std::string output; output.reserve( input.size() * 5 ); // worst scenario for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it ) @@ -786,7 +825,7 @@ std::string close_tag( unsigned format, char type, const std::string &name ) { } } -std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t, const std::string &attr = std::string() ) { +std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t, const std::string &attr = std::string(), int floatPrecision = std::numeric_limits::digits10 + 1) { std::stringstream ss; const std::string tab(depth, '\t'); @@ -805,7 +844,7 @@ std::string tag( unsigned format, unsigned depth, const std::string &name, const case jsonxx::Value::ARRAY_: for(Array::container::const_iterator it = t.array_value_->values().begin(), end = t.array_value_->values().end(); it != end; ++it ) - ss << tag( format, depth+1, std::string(), **it ); + ss << tag( format, depth+1, std::string(), **it, std::string(), floatPrecision ); return tab + open_tag( format, 'a', name, attr ) + '\n' + ss.str() + tab + close_tag( format, 'a', name ) + '\n'; @@ -819,14 +858,14 @@ std::string tag( unsigned format, unsigned depth, const std::string &name, const case jsonxx::Value::OBJECT_: for(Object::container::const_iterator it=t.object_value_->kv_map().begin(), end = t.object_value_->kv_map().end(); it != end ; ++it) - ss << tag( format, depth+1, it->first, *it->second ); + ss << tag( format, depth+1, it->first, *it->second, std::string(), floatPrecision ); return tab + open_tag( format, 'o', name, attr ) + '\n' + ss.str() + tab + close_tag( format, 'o', name ) + '\n'; case jsonxx::Value::NUMBER_: // max precision - ss << std::setprecision(std::numeric_limits::digits10 + 1); + ss << std::setprecision(floatPrecision); ss << t.number_value_; return tab + open_tag( format, 'n', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() ) + ss.str() @@ -870,20 +909,20 @@ const char *defrootattrib[] = { } // namespace jsonxx::anon -std::string Object::json() const { +std::string Object::json(PrintMode printMode, int floatPrecision) const { using namespace json; jsonxx::Value v; v.object_value_ = const_cast(this); v.type_ = jsonxx::Value::OBJECT_; - std::string result = tag( jsonxx::JSON, 0, std::string(), v ); + std::string result = tag( jsonxx::JSON, 0, std::string(), v, printMode, floatPrecision ); v.object_value_ = 0; return remove_last_comma( result ); } -std::string Object::xml( unsigned format, const std::string &header, const std::string &attrib ) const { +std::string Object::xml( unsigned format, const std::string &header, const std::string &attrib, int floatPrecision ) const { using namespace xml; JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML ); @@ -891,26 +930,26 @@ std::string Object::xml( unsigned format, const std::string &header, const std:: v.object_value_ = const_cast(this); v.type_ = jsonxx::Value::OBJECT_; - std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib ); + std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib, floatPrecision ); v.object_value_ = 0; return ( header.empty() ? std::string(defheader[format]) : header ) + result; } -std::string Array::json() const { +std::string Array::json(PrintMode printMode, int floatPrecision) const { using namespace json; jsonxx::Value v; v.array_value_ = const_cast(this); v.type_ = jsonxx::Value::ARRAY_; - std::string result = tag( jsonxx::JSON, 0, std::string(), v ); + std::string result = tag( jsonxx::JSON, 0, std::string(), v, printMode, floatPrecision); v.array_value_ = 0; return remove_last_comma( result ); } -std::string Array::xml( unsigned format, const std::string &header, const std::string &attrib ) const { +std::string Array::xml( unsigned format, const std::string &header, const std::string &attrib, int floatPrecision ) const { using namespace xml; JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML ); @@ -918,7 +957,7 @@ std::string Array::xml( unsigned format, const std::string &header, const std::s v.array_value_ = const_cast(this); v.type_ = jsonxx::Value::ARRAY_; - std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib ); + std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib, floatPrecision ); v.array_value_ = 0; return ( header.empty() ? std::string(defheader[format]) : header ) + result; @@ -1106,13 +1145,6 @@ Array::Array(const Array &other) { Array::Array(const Value &value) { import(value); } -void Array::append(const Array &other) { - if (this != &other) { - values_.push_back( new Value(other) ); - } else { - append( Array(*this) ); - } -} void Array::import(const Array &other) { if (this != &other) { // default From 0b5ad2029366305506bfd45acfc550973dc758c2 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Thu, 30 May 2024 22:22:47 -0400 Subject: [PATCH 3/3] COMP: Avoid ... operator To address: D:\a\ITKPerformanceBenchmarking\ITKPerformanceBenchmarking\src\jsonxx.cc(222): error C2143: syntax error: missing ':' before '...' D:\a\ITKPerformanceBenchmarking\ITKPerformanceBenchmarking\src\jsonxx.cc(222): error C2059: syntax error: '...' --- src/jsonxx.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/jsonxx.cc b/src/jsonxx.cc index 1edaad1..7fec9ef 100644 --- a/src/jsonxx.cc +++ b/src/jsonxx.cc @@ -219,7 +219,16 @@ bool parse_number_inf(std::istream& input, Number& value, std::streampos rollbac value = -std::numeric_limits::infinity(); break; case '+': - case '0'...'9': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': input.get(); value = std::numeric_limits::infinity(); break;