From 0eca806d46ca254b4d8ad9fae94b250964947d72 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 11:42:17 +0100 Subject: [PATCH] Now using the PushNotifier SDK and submodules --- .gitmodules | 6 + makefile | 29 +- src/curlhandler.cpp | 71 - src/curlhandler.h | 17 - src/json/json.hpp | 10183 ----------------------------------- src/main.cpp | 54 +- src/pushhandler.cpp | 188 - src/pushhandler.h | 64 - src/pushnotifier-sdk-cpp | 1 + src/simpleini | 1 + src/simpleini/ConvertUTF.c | 539 -- src/simpleini/ConvertUTF.h | 149 - src/simpleini/SimpleIni.h | 3435 ------------ 13 files changed, 34 insertions(+), 14703 deletions(-) create mode 100644 .gitmodules delete mode 100644 src/curlhandler.cpp delete mode 100644 src/curlhandler.h delete mode 100644 src/json/json.hpp delete mode 100644 src/pushhandler.cpp delete mode 100644 src/pushhandler.h create mode 160000 src/pushnotifier-sdk-cpp create mode 160000 src/simpleini delete mode 100644 src/simpleini/ConvertUTF.c delete mode 100644 src/simpleini/ConvertUTF.h delete mode 100644 src/simpleini/SimpleIni.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..34c1c3b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "src/pushnotifier-sdk-cpp"] + path = src/pushnotifier-sdk-cpp + url = https://github.com/HackHerz/pushnotifier-sdk-cpp +[submodule "src/simpleini"] + path = src/simpleini + url = https://github.com/brofield/simpleini diff --git a/makefile b/makefile index e81d13c..5be79b4 100644 --- a/makefile +++ b/makefile @@ -5,16 +5,18 @@ TARGET = pusher INSTALL_DIR = /usr/local/bin CXX = g++ CPPFLAGS = -std=c++11 -BUILDCOMMAND = $(CXX) $(CPPFLAGS) +CDEFS = -DAPI_TOKEN=\"8E7D8B2DDE7DDE7D6C3V52VB52VBD4DDETBTTTKFFB11\" +CDEFS += -DAPP_PACKAGE=\"com.hackherz.pusher\" +BUILDCOMMAND = $(CXX) $(CDEFS) $(CPPFLAGS) LIBS = `pkg-config libcurl --cflags --libs` #============================================================================= # Build -all: pusher +all: $(TARGET) -$(TARGET): simpleini curlhandler pushhandler main - $(BUILDCOMMAND) src/simpleini/ConvertUTF.o src/curlhandler.o src/pushhandler.o src/main.o $(LIBS) -o $(TARGET) +$(TARGET): simpleini pushnotifier main + $(BUILDCOMMAND) src/simpleini/ConvertUTF.o src/pushnotifier-sdk-cpp/pushnotifier.o src/main.o $(LIBS) -o $(TARGET) # simpleini @@ -25,20 +27,12 @@ src/simpleini/ConvertUTF.o: src/simpleini/ConvertUTF.c $(BUILDCOMMAND) -c src/simpleini/ConvertUTF.c -o src/simpleini/ConvertUTF.o -# curlhandler -.PHONY: curlhandler -curlhandler: src/curlhandler.o +# pushnotifier +.PHONY: pushnotifier +pushnotifier: src/pushnotifier-sdk-cpp/pushnotifier.o -src/curlhandler.o: src/curlhandler.cpp - $(BUILDCOMMAND) -c src/curlhandler.cpp -o src/curlhandler.o - - -# pushhandler -.PHONY: pushhandler -pushhandler: src/pushhandler.o - -src/pushhandler.o: src/pushhandler.cpp - $(BUILDCOMMAND) -c src/pushhandler.cpp -o src/pushhandler.o +src/pushnotifier-sdk-cpp/pushnotifier.o: src/pushnotifier-sdk-cpp/PushNotifier.cpp + $(BUILDCOMMAND) -c src/pushnotifier-sdk-cpp/PushNotifier.cpp -o src/pushnotifier-sdk-cpp/pushnotifier.o # main @@ -54,6 +48,7 @@ src/main.o: src/main.cpp clean: rm -f src/*.o rm -f src/simpleini/*.o + rm -f src/pushnotifier-sdk-cpp/pushnotifier.o rm -f $(TARGET) diff --git a/src/curlhandler.cpp b/src/curlhandler.cpp deleted file mode 100644 index 8bb5fbc..0000000 --- a/src/curlhandler.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "curlhandler.h" - -using namespace std; - - -// urlDecode matches -string matches[][2] = { - {"$", "%24"}, - {"&", "%26"}, - {"+", "%2B"}, - {",", "%2C"}, - {"/", "%2F"}, - {":", "%3A"}, - {";", "%3B"}, - {"=", "%3D"}, - {"?", "%3F"}, - {"@", "%40"} -}; - - - -// needed for handling curl output -static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) -{ - ((std::string*)userp)->append((char*)contents, size * nmemb); - return size * nmemb; -} - - -string CurlHandler::request(string data, const char* url) -{ - CURL *curl; - CURLcode res; - string readBuffer; - curl = curl_easy_init(); - - if(curl) - { - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); - res = curl_easy_perform(curl); - curl_easy_cleanup(curl); - - if(res != CURLE_OK) - { - //throw PusherError("Network Error"); - } - } - - return readBuffer; -} - - -string CurlHandler::urlDecode(string url) -{ - - for(unsigned int i = 0; i < (sizeof(matches)/sizeof(matches[0])); i++) - { - size_t start_pos = 0; - while((start_pos = url.find(matches[i][0], start_pos)) != string::npos) - { - url.replace(start_pos, matches[i][0].length(), matches[i][1]); - start_pos += matches[i][1].length(); - } - } - - return url; -} diff --git a/src/curlhandler.h b/src/curlhandler.h deleted file mode 100644 index 34e95da..0000000 --- a/src/curlhandler.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef H_CURLHANDLER -#define H_CURLHANDLER - -#include -#include - -#define USER_AGENT "pusher/0.2" - - -class CurlHandler -{ -public: - static std::string request(std::string data, const char* url); - static std::string urlDecode(std::string url); -}; - -#endif diff --git a/src/json/json.hpp b/src/json/json.hpp deleted file mode 100644 index 9d6687d..0000000 --- a/src/json/json.hpp +++ /dev/null @@ -1,10183 +0,0 @@ -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.0 -|_____|_____|_____|_|___| https://github.com/nlohmann/json - -Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#ifndef NLOHMANN_JSON_HPP -#define NLOHMANN_JSON_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// disable float-equal warnings on GCC/clang -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ - - -/*! -@brief unnamed namespace with internal helper functions -@since version 1.0.0 -*/ -namespace -{ -/*! -@brief Helper to determine whether there's a key_type for T. -@sa http://stackoverflow.com/a/7728728/266378 -*/ -template -struct has_mapped_type -{ - private: - template static char test(typename C::mapped_type*); - template static char (&test(...))[2]; - public: - static constexpr bool value = sizeof(test(0)) == 1; -}; - -/*! -@brief helper class to create locales with decimal point -@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315 -*/ -class DecimalSeparator : public std::numpunct -{ - protected: - char do_decimal_point() const - { - return '.'; - } -}; - -} - -/*! -@brief a class to store JSON values - -@tparam ObjectType type for JSON objects (`std::map` by default; will be used -in @ref object_t) -@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used -in @ref array_t) -@tparam StringType type for JSON strings and object keys (`std::string` by -default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (`bool` by default; will be used -in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by -default; will be used in @ref number_integer_t) -@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c -`uint64_t` by default; will be used in @ref number_unsigned_t) -@tparam NumberFloatType type for JSON floating-point numbers (`double` by -default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (`std::allocator` by -default) - -@requirement The class satisfies the following concept requirements: -- Basic - - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null value. - - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): - A JSON value can be constructed from an rvalue argument. - - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): - A JSON value can be copy-constructed from an lvalue expression. - - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): - A JSON value van be assigned from an rvalue argument. - - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): - A JSON value can be copy-assigned from an lvalue expression. - - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): - JSON values can be destructed. -- Layout - - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): - JSON values have - [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the class - has no virtual functions or (virtual) base classes. -- Library-wide - - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): - JSON values can be compared with `==`, see @ref - operator==(const_reference,const_reference). - - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): - JSON values can be compared with `<`, see @ref - operator<(const_reference,const_reference). - - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): - Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of - other compatible types, using unqualified function call @ref swap(). - - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): - JSON values can be compared against `std::nullptr_t` objects which are used - to model the `null` value. -- Container - - [Container](http://en.cppreference.com/w/cpp/concept/Container): - JSON values can be used like STL containers and provide iterator access. - - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); - JSON values can be used like STL containers and provide reverse iterator - access. - -@internal -@note ObjectType trick from http://stackoverflow.com/a/9860911 -@endinternal - -@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange -Format](http://rfc7159.net/rfc7159) - -@since version 1.0.0 - -@nosubgrouping -*/ -template < - template class ObjectType = std::map, - template class ArrayType = std::vector, - class StringType = std::string, - class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator - > -class basic_json -{ - private: - /// workaround type for MSVC - using basic_json_t = basic_json; - - public: - // forward declarations - template class json_reverse_iterator; - class json_pointer; - - ///////////////////// - // container types // - ///////////////////// - - /// @name container types - /// @{ - - /// the type of elements in a basic_json container - using value_type = basic_json; - - /// the type of an element reference - using reference = value_type&; - /// the type of an element const reference - using const_reference = const value_type&; - - /// a type to represent differences between iterators - using difference_type = std::ptrdiff_t; - /// a type to represent container sizes - using size_type = std::size_t; - - /// the allocator type - using allocator_type = AllocatorType; - - /// the type of an element pointer - using pointer = typename std::allocator_traits::pointer; - /// the type of an element const pointer - using const_pointer = typename std::allocator_traits::const_pointer; - - /// an iterator for a basic_json container - class iterator; - /// a const iterator for a basic_json container - class const_iterator; - /// a reverse iterator for a basic_json container - using reverse_iterator = json_reverse_iterator; - /// a const reverse iterator for a basic_json container - using const_reverse_iterator = json_reverse_iterator; - - /// @} - - - /*! - @brief returns the allocator associated with the container - */ - static allocator_type get_allocator() - { - return allocator_type(); - } - - - /////////////////////////// - // JSON value data types // - /////////////////////////// - - /// @name JSON value data types - /// @{ - - /*! - @brief a type for an object - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: - > An object is an unordered collection of zero or more name/value pairs, - > where a name is a string and a value is a string, number, boolean, null, - > object, or array. - - To store objects in C++, a type is defined by the template parameters - described below. - - @tparam ObjectType the container to store objects (e.g., `std::map` or - `std::unordered_map`) - @tparam StringType the type of the keys or names (e.g., `std::string`). - The comparison function `std::less` is used to order elements - inside the container. - @tparam AllocatorType the allocator to use for objects (e.g., - `std::allocator`) - - #### Default type - - With the default values for @a ObjectType (`std::map`), @a StringType - (`std::string`), and @a AllocatorType (`std::allocator`), the default - value for @a object_t is: - - @code {.cpp} - std::map< - std::string, // key_type - basic_json, // value_type - std::less, // key_compare - std::allocator> // allocator_type - > - @endcode - - #### Behavior - - The choice of @a object_t influences the behavior of the JSON class. With - the default type, objects have the following behavior: - - - When all names are unique, objects will be interoperable in the sense - that all software implementations receiving that object will agree on - the name-value mappings. - - When the names within an object are not unique, later stored name/value - pairs overwrite previously stored name/value pairs, leaving the used - names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will - be treated as equal and both stored as `{"key": 1}`. - - Internally, name/value pairs are stored in lexicographical order of the - names. Objects will also be serialized (see @ref dump) in this order. - For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored - and serialized as `{"a": 2, "b": 1}`. - - When comparing objects, the order of the name/value pairs is irrelevant. - This makes objects interoperable in the sense that they will not be - affected by these differences. For instance, `{"b": 1, "a": 2}` and - `{"a": 2, "b": 1}` will be treated as equal. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. - - In this class, the object's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON object. - - #### Storage - - Objects are stored as pointers in a @ref basic_json type. That is, for any - access to object values, a pointer of type `object_t*` must be - dereferenced. - - @sa @ref array_t -- type for an array value - - @since version 1.0.0 - - @note The order name/value pairs are added to the object is *not* - preserved by the library. Therefore, iterating an object may return - name/value pairs in a different order than they were originally stored. In - fact, keys will be traversed in alphabetical order as `std::map` with - `std::less` is used by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the - specified "unordered" nature of JSON objects. - */ - using object_t = ObjectType, - AllocatorType>>; - - /*! - @brief a type for an array - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: - > An array is an ordered sequence of zero or more values. - - To store objects in C++, a type is defined by the template parameters - explained below. - - @tparam ArrayType container type to store arrays (e.g., `std::vector` or - `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) - - #### Default type - - With the default values for @a ArrayType (`std::vector`) and @a - AllocatorType (`std::allocator`), the default value for @a array_t is: - - @code {.cpp} - std::vector< - basic_json, // value_type - std::allocator // allocator_type - > - @endcode - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. - - In this class, the array's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON array. - - #### Storage - - Arrays are stored as pointers in a @ref basic_json type. That is, for any - access to array values, a pointer of type `array_t*` must be dereferenced. - - @sa @ref object_t -- type for an object value - - @since version 1.0.0 - */ - using array_t = ArrayType>; - - /*! - @brief a type for a string - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: - > A string is a sequence of zero or more Unicode characters. - - To store objects in C++, a type is defined by the template parameter - described below. Unicode values are split by the JSON class into - byte-sized characters during deserialization. - - @tparam StringType the container to store strings (e.g., `std::string`). - Note this container is used for keys/names in objects, see @ref object_t. - - #### Default type - - With the default values for @a StringType (`std::string`), the default - value for @a string_t is: - - @code {.cpp} - std::string - @endcode - - #### String comparison - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > Software implementations are typically required to test names of object - > members for equality. Implementations that transform the textual - > representation into sequences of Unicode code units and then perform the - > comparison numerically, code unit by code unit, are interoperable in the - > sense that implementations will agree in all cases on equality or - > inequality of two strings. For example, implementations that compare - > strings with escaped characters unconverted may incorrectly find that - > `"a\\b"` and `"a\u005Cb"` are not equal. - - This implementation is interoperable as it does compare strings code unit - by code unit. - - #### Storage - - String values are stored as pointers in a @ref basic_json type. That is, - for any access to string values, a pointer of type `string_t*` must be - dereferenced. - - @since version 1.0.0 - */ - using string_t = StringType; - - /*! - @brief a type for a boolean - - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a - type which differentiates the two literals `true` and `false`. - - To store objects in C++, a type is defined by the template parameter @a - BooleanType which chooses the type to use. - - #### Default type - - With the default values for @a BooleanType (`bool`), the default value for - @a boolean_t is: - - @code {.cpp} - bool - @endcode - - #### Storage - - Boolean values are stored directly inside a @ref basic_json type. - - @since version 1.0.0 - */ - using boolean_t = BooleanType; - - /*! - @brief a type for a number (integer) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store integer numbers in C++, a type is defined by the template - parameter @a NumberIntegerType which chooses the type to use. - - #### Default type - - With the default values for @a NumberIntegerType (`int64_t`), the default - value for @a number_integer_t is: - - @code {.cpp} - int64_t - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. - - When the default type is used, the maximal integer number that can be - stored is `9223372036854775807` (INT64_MAX) and the minimal integer number - that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers - that are out of range will yield over/underflow when used in a - constructor. During deserialization, too large or small integer numbers - will be automatically be stored as @ref number_unsigned_t or @ref - number_float_t. - - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. - - As this range is a subrange of the exactly supported range [INT64_MIN, - INT64_MAX], this class's integer type is interoperable. - - #### Storage - - Integer number values are stored directly inside a @ref basic_json type. - - @sa @ref number_float_t -- type for number values (floating-point) - - @sa @ref number_unsigned_t -- type for number values (unsigned integer) - - @since version 1.0.0 - */ - using number_integer_t = NumberIntegerType; - - /*! - @brief a type for a number (unsigned) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store unsigned integer numbers in C++, a type is defined by the - template parameter @a NumberUnsignedType which chooses the type to use. - - #### Default type - - With the default values for @a NumberUnsignedType (`uint64_t`), the - default value for @a number_unsigned_t is: - - @code {.cpp} - uint64_t - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. - - When the default type is used, the maximal integer number that can be - stored is `18446744073709551615` (UINT64_MAX) and the minimal integer - number that can be stored is `0`. Integer numbers that are out of range - will yield over/underflow when used in a constructor. During - deserialization, too large or small integer numbers will be automatically - be stored as @ref number_integer_t or @ref number_float_t. - - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. - - As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], this - class's integer type is interoperable. - - #### Storage - - Integer number values are stored directly inside a @ref basic_json type. - - @sa @ref number_float_t -- type for number values (floating-point) - - @sa @ref number_integer_t -- type for number values (integer) - - @since version 2.0.0 - */ - using number_unsigned_t = NumberUnsignedType; - - /*! - @brief a type for a number (floating-point) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store floating-point numbers in C++, a type is defined by the template - parameter @a NumberFloatType which chooses the type to use. - - #### Default type - - With the default values for @a NumberFloatType (`double`), the default - value for @a number_float_t is: - - @code {.cpp} - double - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in floating-point literals will be ignored. Internally, - the value will be stored as decimal number. For instance, the C++ - floating-point literal `01.2` will be serialized to `1.2`. During - deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > This specification allows implementations to set limits on the range and - > precision of numbers accepted. Since software that implements IEEE - > 754-2008 binary64 (double precision) numbers is generally available and - > widely used, good interoperability can be achieved by implementations - > that expect no more precision or range than these provide, in the sense - > that implementations will approximate JSON numbers within the expected - > precision. - - This implementation does exactly follow this approach, as it uses double - precision floating-point numbers. Note values smaller than - `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` - will be stored as NaN internally and be serialized to `null`. - - #### Storage - - Floating-point number values are stored directly inside a @ref basic_json - type. - - @sa @ref number_integer_t -- type for number values (integer) - - @sa @ref number_unsigned_t -- type for number values (unsigned integer) - - @since version 1.0.0 - */ - using number_float_t = NumberFloatType; - - /// @} - - - /////////////////////////// - // JSON type enumeration // - /////////////////////////// - - /*! - @brief the JSON type enumeration - - This enumeration collects the different JSON types. It is internally used - to distinguish the stored values, and the functions @ref is_null(), @ref - is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref - is_number(), and @ref is_discarded() rely on it. - - @since version 1.0.0 - */ - enum class value_t : uint8_t - { - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function - }; - - - private: - - /*! - @brief a type to hold JSON type information - - This bitfield type holds information about JSON types. It is internally - used to hold the basic JSON type enumeration, as well as additional - information in the case of values that have been parsed from a string - including whether of not it was created directly or parsed, and in the - case of floating point numbers the number of significant figures in the - original representaiton and if it was in exponential form, if a '+' was - included in the exponent and the capitilization of the exponent marker. - The sole purpose of this information is to permit accurate round trips. - - @since version 2.0.0 - */ - union type_data_t - { - struct - { - /// the type of the value (@ref value_t) - uint16_t type : 4; - /// whether the number was parsed from a string - uint16_t parsed : 1; - /// whether parsed number contained an exponent ('e'/'E') - uint16_t has_exp : 1; - /// whether parsed number contained a plus in the exponent - uint16_t exp_plus : 1; - /// whether parsed number's exponent was capitalized ('E') - uint16_t exp_cap : 1; - /// the number of figures for a parsed number - uint16_t precision : 8; - } bits; - uint16_t data; - - /// return the type as value_t - operator value_t() const - { - return static_cast(bits.type); - } - - /// test type for equality (ignore other fields) - bool operator==(const value_t& rhs) const - { - return static_cast(bits.type) == rhs; - } - - /// assignment - type_data_t& operator=(value_t rhs) - { - bits.type = static_cast(rhs) & 15; // avoid overflow - return *this; - } - - /// construct from value_t - type_data_t(value_t t) noexcept - { - *reinterpret_cast(this) = 0; - bits.type = static_cast(t) & 15; // avoid overflow - } - - /// default constructor - type_data_t() noexcept - { - data = 0; - bits.type = reinterpret_cast(value_t::null); - } - }; - - /// helper for exception-safe object creation - template - static T* create(Args&& ... args) - { - AllocatorType alloc; - auto deleter = [&](T * object) - { - alloc.deallocate(object, 1); - }; - std::unique_ptr object(alloc.allocate(1), deleter); - alloc.construct(object.get(), std::forward(args)...); - return object.release(); - } - - //////////////////////// - // JSON value storage // - //////////////////////// - - /*! - @brief a JSON value - - The actual storage for a JSON value of the @ref basic_json class. - - @since version 1.0.0 - */ - union json_value - { - /// object (stored with pointer to save storage) - object_t* object; - /// array (stored with pointer to save storage) - array_t* array; - /// string (stored with pointer to save storage) - string_t* string; - /// boolean - boolean_t boolean; - /// number (integer) - number_integer_t number_integer; - /// number (unsigned integer) - number_unsigned_t number_unsigned; - /// number (floating-point) - number_float_t number_float; - - /// default constructor (for null values) - json_value() = default; - /// constructor for booleans - json_value(boolean_t v) noexcept : boolean(v) {} - /// constructor for numbers (integer) - json_value(number_integer_t v) noexcept : number_integer(v) {} - /// constructor for numbers (unsigned) - json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} - /// constructor for numbers (floating-point) - json_value(number_float_t v) noexcept : number_float(v) {} - /// constructor for empty values of a given type - json_value(value_t t) - { - switch (t) - { - case value_t::object: - { - object = create(); - break; - } - - case value_t::array: - { - array = create(); - break; - } - - case value_t::string: - { - string = create(""); - break; - } - - case value_t::boolean: - { - boolean = boolean_t(false); - break; - } - - case value_t::number_integer: - { - number_integer = number_integer_t(0); - break; - } - - case value_t::number_unsigned: - { - number_unsigned = number_unsigned_t(0); - break; - } - - case value_t::number_float: - { - number_float = number_float_t(0.0); - break; - } - - default: - { - break; - } - } - } - - /// constructor for strings - json_value(const string_t& value) - { - string = create(value); - } - - /// constructor for objects - json_value(const object_t& value) - { - object = create(value); - } - - /// constructor for arrays - json_value(const array_t& value) - { - array = create(value); - } - }; - - - public: - ////////////////////////// - // JSON parser callback // - ////////////////////////// - - /*! - @brief JSON callback events - - This enumeration lists the parser events that can trigger calling a - callback function of type @ref parser_callback_t during parsing. - - @since version 1.0.0 - */ - enum class parse_event_t : uint8_t - { - /// the parser read `{` and started to process a JSON object - object_start, - /// the parser read `}` and finished processing a JSON object - object_end, - /// the parser read `[` and started to process a JSON array - array_start, - /// the parser read `]` and finished processing a JSON array - array_end, - /// the parser read a key of a value in an object - key, - /// the parser finished reading a JSON value - value - }; - - /*! - @brief per-element parser callback type - - With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t), it is called on certain - events (passed as @ref parse_event_t via parameter @a event) with a set - recursion depth @a depth and context JSON value @a parsed. The return - value of the callback function is a boolean indicating whether the element - that emitted the callback shall be kept or not. - - We distinguish six scenarios (determined by the event type) in which the - callback function can be called. The following table describes the values - of the parameters @a depth, @a event, and @a parsed. - - parameter @a event | description | parameter @a depth | parameter @a parsed - ------------------ | ----------- | ------------------ | ------------------- - parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded - parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key - parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object - parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded - parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array - parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value - - Discarding a value (i.e., returning `false`) has different effects - depending on the context in which function was called: - - - Discarded values in structured types are skipped. That is, the parser - will behave as if the discarded value was never read. - - In case a value outside a structured type is skipped, it is replaced - with `null`. This case happens if the top-level element is skipped. - - @param[in] depth the depth of the recursion during parsing - - @param[in] event an event of type parse_event_t indicating the context in - the callback function has been called - - @param[in,out] parsed the current intermediate parse result; note that - writing to this value has no effect for parse_event_t::key events - - @return Whether the JSON value which called the function during parsing - should be kept (`true`) or not (`false`). In the latter case, it is either - skipped completely or replaced by an empty discarded object. - - @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t) for examples - - @since version 1.0.0 - */ - using parser_callback_t = std::function; - - - ////////////////// - // constructors // - ////////////////// - - /// @name constructors and destructors - /// @{ - - /*! - @brief create an empty value with a given type - - Create an empty JSON value with a given type. The value will be default - initialized with an empty value which depends on the type: - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @param[in] value_type the type of the value to create - - @complexity Constant. - - @throw std::bad_alloc if allocation for object, array, or string value - fails - - @liveexample{The following code shows the constructor for different @ref - value_t values,basic_json__value_t} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - @sa @ref basic_json(boolean_t value) -- create a boolean value - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const object_t&) -- create a object value - @sa @ref basic_json(const array_t&) -- create a array value - @sa @ref basic_json(const number_float_t) -- create a number - (floating-point) value - @sa @ref basic_json(const number_integer_t) -- create a number (integer) - value - @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned) - value - - @since version 1.0.0 - */ - basic_json(const value_t value_type) - : m_type(value_type), m_value(value_type) - {} - - /*! - @brief create a null object (implicitly) - - Create a `null` JSON value. This is the implicit version of the `null` - value constructor as it takes no parameters. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - As postcondition, it holds: `basic_json().empty() == true`. - - @liveexample{The following code shows the constructor for a `null` JSON - value.,basic_json} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - - @since version 1.0.0 - */ - basic_json() = default; - - /*! - @brief create a null object (explicitly) - - Create a `null` JSON value. This is the explicitly version of the `null` - value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a `nullptr` to a JSON value. - The passed null pointer itself is not read -- it is only used to choose - the right constructor. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @liveexample{The following code shows the constructor with null pointer - parameter.,basic_json__nullptr_t} - - @sa @ref basic_json() -- default constructor (implicitly creating a `null` - value) - - @since version 1.0.0 - */ - basic_json(std::nullptr_t) noexcept - : basic_json(value_t::null) - {} - - /*! - @brief create an object (explicit) - - Create an object JSON value with a given content. - - @param[in] val a value for the object - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for object value fails - - @liveexample{The following code shows the constructor with an @ref - object_t parameter.,basic_json__object_t} - - @sa @ref basic_json(const CompatibleObjectType&) -- create an object value - from a compatible STL container - - @since version 1.0.0 - */ - basic_json(const object_t& val) - : m_type(value_t::object), m_value(val) - {} - - /*! - @brief create an object (implicit) - - Create an object JSON value with a given content. This constructor allows - any type @a CompatibleObjectType that can be used to construct values of - type @ref object_t. - - @tparam CompatibleObjectType An object type whose `key_type` and - `value_type` is compatible to @ref object_t. Examples include `std::map`, - `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with - a `key_type` of `std::string`, and a `value_type` from which a @ref - basic_json value can be constructed. - - @param[in] val a value for the object - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for object value fails - - @liveexample{The following code shows the constructor with several - compatible object type parameters.,basic_json__CompatibleObjectType} - - @sa @ref basic_json(const object_t&) -- create an object value - - @since version 1.0.0 - */ - template ::value and - std::is_constructible::value, int>::type - = 0> - basic_json(const CompatibleObjectType& val) - : m_type(value_t::object) - { - using std::begin; - using std::end; - m_value.object = create(begin(val), end(val)); - } - - /*! - @brief create an array (explicit) - - Create an array JSON value with a given content. - - @param[in] val a value for the array - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for array value fails - - @liveexample{The following code shows the constructor with an @ref array_t - parameter.,basic_json__array_t} - - @sa @ref basic_json(const CompatibleArrayType&) -- create an array value - from a compatible STL containers - - @since version 1.0.0 - */ - basic_json(const array_t& val) - : m_type(value_t::array), m_value(val) - {} - - /*! - @brief create an array (implicit) - - Create an array JSON value with a given content. This constructor allows - any type @a CompatibleArrayType that can be used to construct values of - type @ref array_t. - - @tparam CompatibleArrayType An object type whose `value_type` is - compatible to @ref array_t. Examples include `std::vector`, `std::deque`, - `std::list`, `std::forward_list`, `std::array`, `std::set`, - `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a - `value_type` from which a @ref basic_json value can be constructed. - - @param[in] val a value for the array - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for array value fails - - @liveexample{The following code shows the constructor with several - compatible array type parameters.,basic_json__CompatibleArrayType} - - @sa @ref basic_json(const array_t&) -- create an array value - - @since version 1.0.0 - */ - template ::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> - basic_json(const CompatibleArrayType& val) - : m_type(value_t::array) - { - using std::begin; - using std::end; - m_value.array = create(begin(val), end(val)); - } - - /*! - @brief create a string (explicit) - - Create an string JSON value with a given content. - - @param[in] val a value for the string - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the constructor with an @ref - string_t parameter.,basic_json__string_t} - - @sa @ref basic_json(const typename string_t::value_type*) -- create a - string value from a character pointer - @sa @ref basic_json(const CompatibleStringType&) -- create a string value - from a compatible string container - - @since version 1.0.0 - */ - basic_json(const string_t& val) - : m_type(value_t::string), m_value(val) - {} - - /*! - @brief create a string (explicit) - - Create a string JSON value with a given content. - - @param[in] val a literal value for the string - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the constructor with string literal - parameter.,basic_json__string_t_value_type} - - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const CompatibleStringType&) -- create a string value - from a compatible string container - - @since version 1.0.0 - */ - basic_json(const typename string_t::value_type* val) - : basic_json(string_t(val)) - {} - - /*! - @brief create a string (implicit) - - Create a string JSON value with a given content. - - @param[in] val a value for the string - - @tparam CompatibleStringType an string type which is compatible to @ref - string_t, for instance `std::string`. - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the construction of a string value - from a compatible type.,basic_json__CompatibleStringType} - - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const typename string_t::value_type*) -- create a - string value from a character pointer - - @since version 1.0.0 - */ - template ::value, int>::type - = 0> - basic_json(const CompatibleStringType& val) - : basic_json(string_t(val)) - {} - - /*! - @brief create a boolean (explicit) - - Creates a JSON boolean type from a given value. - - @param[in] val a boolean value to store - - @complexity Constant. - - @liveexample{The example below demonstrates boolean - values.,basic_json__boolean_t} - - @since version 1.0.0 - */ - basic_json(boolean_t val) noexcept - : m_type(value_t::boolean), m_value(val) - {} - - /*! - @brief create an integer number (explicit) - - Create an integer number JSON value with a given content. - - @tparam T A helper type to remove this function via SFINAE in case @ref - number_integer_t is the same as `int`. In this case, this constructor - would have the same signature as @ref basic_json(const int value). Note - the helper type @a T is not visible in this constructor's interface. - - @param[in] val an integer to create a JSON number from - - @complexity Constant. - - @liveexample{The example below shows the construction of an integer - number value.,basic_json__number_integer_t} - - @sa @ref basic_json(const int) -- create a number value (integer) - @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number - value (integer) from a compatible number type - - @since version 1.0.0 - */ - template::value) - and std::is_same::value - , int>::type - = 0> - basic_json(const number_integer_t val) noexcept - : m_type(value_t::number_integer), m_value(val) - {} - - /*! - @brief create an integer number from an enum type (explicit) - - Create an integer number JSON value with a given content. - - @param[in] val an integer to create a JSON number from - - @note This constructor allows to pass enums directly to a constructor. As - C++ has no way of specifying the type of an anonymous enum explicitly, we - can only rely on the fact that such values implicitly convert to int. As - int may already be the same type of number_integer_t, we may need to - switch off the constructor @ref basic_json(const number_integer_t). - - @complexity Constant. - - @liveexample{The example below shows the construction of an integer - number value from an anonymous enum.,basic_json__const_int} - - @sa @ref basic_json(const number_integer_t) -- create a number value - (integer) - @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number - value (integer) from a compatible number type - - @since version 1.0.0 - */ - basic_json(const int val) noexcept - : m_type(value_t::number_integer), - m_value(static_cast(val)) - {} - - /*! - @brief create an integer number (implicit) - - Create an integer number JSON value with a given content. This constructor - allows any type @a CompatibleNumberIntegerType that can be used to - construct values of type @ref number_integer_t. - - @tparam CompatibleNumberIntegerType An integer type which is compatible to - @ref number_integer_t. Examples include the types `int`, `int32_t`, - `long`, and `short`. - - @param[in] val an integer to create a JSON number from - - @complexity Constant. - - @liveexample{The example below shows the construction of several integer - number values from compatible - types.,basic_json__CompatibleIntegerNumberType} - - @sa @ref basic_json(const number_integer_t) -- create a number value - (integer) - @sa @ref basic_json(const int) -- create a number value (integer) - - @since version 1.0.0 - */ - template::value and - std::numeric_limits::is_integer and - std::numeric_limits::is_signed, - CompatibleNumberIntegerType>::type - = 0> - basic_json(const CompatibleNumberIntegerType val) noexcept - : m_type(value_t::number_integer), - m_value(static_cast(val)) - {} - - /*! - @brief create an unsigned integer number (explicit) - - Create an unsigned integer number JSON value with a given content. - - @tparam T helper type to compare number_unsigned_t and unsigned int - (not visible in) the interface. - - @param[in] val an integer to create a JSON number from - - @complexity Constant. - - @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number - value (unsigned integer) from a compatible number type - - @since version 2.0.0 - */ - template::value) - and std::is_same::value - , int>::type - = 0> - basic_json(const number_unsigned_t val) noexcept - : m_type(value_t::number_unsigned), m_value(val) - {} - - /*! - @brief create an unsigned number (implicit) - - Create an unsigned number JSON value with a given content. This - constructor allows any type @a CompatibleNumberUnsignedType that can be - used to construct values of type @ref number_unsigned_t. - - @tparam CompatibleNumberUnsignedType An integer type which is compatible - to @ref number_unsigned_t. Examples may include the types `unsigned int`, - `uint32_t`, or `unsigned short`. - - @param[in] val an unsigned integer to create a JSON number from - - @complexity Constant. - - @sa @ref basic_json(const number_unsigned_t) -- create a number value - (unsigned) - - @since version 2.0.0 - */ - template ::value and - std::numeric_limits::is_integer and - not std::numeric_limits::is_signed, - CompatibleNumberUnsignedType>::type - = 0> - basic_json(const CompatibleNumberUnsignedType val) noexcept - : m_type(value_t::number_unsigned), - m_value(static_cast(val)) - {} - - /*! - @brief create a floating-point number (explicit) - - Create a floating-point number JSON value with a given content. - - @param[in] val a floating-point value to create a JSON number from - - @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6 - disallows NaN values: - > Numeric values that cannot be represented in the grammar below (such as - > Infinity and NaN) are not permitted. - In case the parameter @a val is not a number, a JSON null value is - created instead. - - @complexity Constant. - - @liveexample{The following example creates several floating-point - values.,basic_json__number_float_t} - - @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number - value (floating-point) from a compatible number type - - @since version 1.0.0 - */ - basic_json(const number_float_t val) noexcept - : m_type(value_t::number_float), m_value(val) - { - // replace infinity and NAN by null - if (not std::isfinite(val)) - { - m_type = value_t::null; - m_value = json_value(); - } - } - - /*! - @brief create an floating-point number (implicit) - - Create an floating-point number JSON value with a given content. This - constructor allows any type @a CompatibleNumberFloatType that can be used - to construct values of type @ref number_float_t. - - @tparam CompatibleNumberFloatType A floating-point type which is - compatible to @ref number_float_t. Examples may include the types `float` - or `double`. - - @param[in] val a floating-point to create a JSON number from - - @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6 - disallows NaN values: - > Numeric values that cannot be represented in the grammar below (such as - > Infinity and NaN) are not permitted. - In case the parameter @a val is not a number, a JSON null value is - created instead. - - @complexity Constant. - - @liveexample{The example below shows the construction of several - floating-point number values from compatible - types.,basic_json__CompatibleNumberFloatType} - - @sa @ref basic_json(const number_float_t) -- create a number value - (floating-point) - - @since version 1.0.0 - */ - template::value and - std::is_floating_point::value>::type - > - basic_json(const CompatibleNumberFloatType val) noexcept - : basic_json(number_float_t(val)) - {} - - /*! - @brief create a container (array or object) from an initializer list - - Creates a JSON value of type array or object from the passed initializer - list @a init. In case @a type_deduction is `true` (default), the type of - the JSON value to be created is deducted from the initializer list @a init - according to the following rules: - - 1. If the list is empty, an empty JSON object value `{}` is created. - 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are treated - as keys and the second elements are as values. - 3. In all other cases, an array is created. - - The rules aim to create the best fit between a C++ initializer list and - JSON values. The rationale is as follows: - - 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. - 2. C++ has now way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them as - an object. - 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. - - With the rules described above, the following JSON values cannot be - expressed by an initializer list: - - - the empty array (`[]`): use @ref array(std::initializer_list) - with an empty initializer list in this case - - arrays whose elements satisfy rule 2: use @ref - array(std::initializer_list) with the same initializer list - in this case - - @note When used without parentheses around an empty initializer list, @ref - basic_json() is called instead of this function, yielding the JSON null - value. - - @param[in] init initializer list with JSON values - - @param[in] type_deduction internal parameter; when set to `true`, the type - of the JSON value is deducted from the initializer list @a init; when set - to `false`, the type provided via @a manual_type is forced. This mode is - used by the functions @ref array(std::initializer_list) and - @ref object(std::initializer_list). - - @param[in] manual_type internal parameter; when @a type_deduction is set - to `false`, the created JSON value will use the provided type (only @ref - value_t::array and @ref value_t::object are valid); when @a type_deduction - is set to `true`, this parameter has no effect - - @throw std::domain_error if @a type_deduction is `false`, @a manual_type - is `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string; example: `"cannot create object from - initializer list"` - - @complexity Linear in the size of the initializer list @a init. - - @liveexample{The example below shows how JSON values are created from - initializer lists.,basic_json__list_init_t} - - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list - - @since version 1.0.0 - */ - basic_json(std::initializer_list init, - bool type_deduction = true, - value_t manual_type = value_t::array) - { - // the initializer list could describe an object - bool is_an_object = true; - - // check if each element is an array with two elements whose first - // element is a string - for (const auto& element : init) - { - if (not element.is_array() or element.size() != 2 - or not element[0].is_string()) - { - // we found an element that makes it impossible to use the - // initializer list as object - is_an_object = false; - break; - } - } - - // adjust type if type deduction is not wanted - if (not type_deduction) - { - // if array is wanted, do not create an object though possible - if (manual_type == value_t::array) - { - is_an_object = false; - } - - // if object is wanted but impossible, throw an exception - if (manual_type == value_t::object and not is_an_object) - { - throw std::domain_error("cannot create object from initializer list"); - } - } - - if (is_an_object) - { - // the initializer list is a list of pairs -> create object - m_type = value_t::object; - m_value = value_t::object; - - assert(m_value.object != nullptr); - - for (auto& element : init) - { - m_value.object->emplace(*(element[0].m_value.string), element[1]); - } - } - else - { - // the initializer list describes an array -> create array - m_type = value_t::array; - m_value.array = create(init); - } - } - - /*! - @brief explicitly create an array from an initializer list - - Creates a JSON array value from a given initializer list. That is, given a - list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the - initializer list is empty, the empty array `[]` is created. - - @note This function is only needed to express two edge cases that cannot - be realized with the initializer list constructor (@ref - basic_json(std::initializer_list, bool, value_t)). These cases - are: - 1. creating an array whose elements are all pairs whose first element is a - string -- in this case, the initializer list constructor would create an - object, taking the first elements as keys - 2. creating an empty array -- passing the empty initializer list to the - initializer list constructor yields an empty object - - @param[in] init initializer list with JSON values to create an array from - (optional) - - @return JSON array value - - @complexity Linear in the size of @a init. - - @liveexample{The following code shows an example for the `array` - function.,array} - - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list - - @since version 1.0.0 - */ - static basic_json array(std::initializer_list init = - std::initializer_list()) - { - return basic_json(init, false, value_t::array); - } - - /*! - @brief explicitly create an object from an initializer list - - Creates a JSON object value from a given initializer list. The initializer - lists elements must be pairs, and their first elements must be strings. If - the initializer list is empty, the empty object `{}` is created. - - @note This function is only added for symmetry reasons. In contrast to the - related function @ref array(std::initializer_list), there are - no cases which can only be expressed by this function. That is, any - initializer list @a init can also be passed to the initializer list - constructor @ref basic_json(std::initializer_list, bool, - value_t). - - @param[in] init initializer list to create an object from (optional) - - @return JSON object value - - @throw std::domain_error if @a init is not a pair whose first elements are - strings; thrown by - @ref basic_json(std::initializer_list, bool, value_t) - - @complexity Linear in the size of @a init. - - @liveexample{The following code shows an example for the `object` - function.,object} - - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list - - @since version 1.0.0 - */ - static basic_json object(std::initializer_list init = - std::initializer_list()) - { - return basic_json(init, false, value_t::object); - } - - /*! - @brief construct an array with count copies of given value - - Constructs a JSON array value by creating @a cnt copies of a passed value. - In case @a cnt is `0`, an empty array is created. As postcondition, - `std::distance(begin(),end()) == cnt` holds. - - @param[in] cnt the number of JSON copies of @a val to create - @param[in] val the JSON value to copy - - @complexity Linear in @a cnt. - - @liveexample{The following code shows examples for the @ref - basic_json(size_type\, const basic_json&) - constructor.,basic_json__size_type_basic_json} - - @since version 1.0.0 - */ - basic_json(size_type cnt, const basic_json& val) - : m_type(value_t::array) - { - m_value.array = create(cnt, val); - } - - /*! - @brief construct a JSON container given an iterator range - - Constructs the JSON value with the contents of the range `[first, last)`. - The semantics depends on the different types a JSON value can have: - - In case of primitive types (number, boolean, or string), @a first must - be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, std::out_of_range is thrown. - - In case of structured types (array, object), the constructor behaves as - similar versions for `std::vector`. - - In case of a null type, std::domain_error is thrown. - - @tparam InputIT an input iterator type (@ref iterator or @ref - const_iterator) - - @param[in] first begin of the range to copy from (included) - @param[in] last end of the range to copy from (excluded) - - @throw std::domain_error if iterators are not compatible; that is, do not - belong to the same JSON value; example: `"iterators are not compatible"` - @throw std::out_of_range if iterators are for a primitive type (number, - boolean, or string) where an out of range error can be detected easily; - example: `"iterators out of range"` - @throw std::bad_alloc if allocation for object, array, or string fails - @throw std::domain_error if called with a null value; example: `"cannot - use construct with iterators from null"` - - @complexity Linear in distance between @a first and @a last. - - @liveexample{The example below shows several ways to create JSON values by - specifying a subrange with iterators.,basic_json__InputIt_InputIt} - - @since version 1.0.0 - */ - template ::value or - std::is_same::value - , int>::type - = 0> - basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type) - { - // make sure iterator fits the current value - if (first.m_object != last.m_object) - { - throw std::domain_error("iterators are not compatible"); - } - - // check if iterator range is complete for primitive values - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - throw std::out_of_range("iterators out of range"); - } - break; - } - - default: - { - break; - } - } - - switch (m_type) - { - case value_t::number_integer: - { - assert(first.m_object != nullptr); - m_value.number_integer = first.m_object->m_value.number_integer; - break; - } - - case value_t::number_unsigned: - { - assert(first.m_object != nullptr); - m_value.number_unsigned = first.m_object->m_value.number_unsigned; - break; - } - - case value_t::number_float: - { - assert(first.m_object != nullptr); - m_value.number_float = first.m_object->m_value.number_float; - break; - } - - case value_t::boolean: - { - assert(first.m_object != nullptr); - m_value.boolean = first.m_object->m_value.boolean; - break; - } - - case value_t::string: - { - assert(first.m_object != nullptr); - m_value = *first.m_object->m_value.string; - break; - } - - case value_t::object: - { - m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); - break; - } - - case value_t::array: - { - m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); - break; - } - - default: - { - assert(first.m_object != nullptr); - throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()); - } - } - } - - /*! - @brief construct a JSON value given an input stream - - @param[in,out] i stream to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates constructing a JSON value from - a `std::stringstream` with and without callback - function.,basic_json__istream} - - @since version 2.0.0 - */ - explicit basic_json(std::istream& i, parser_callback_t cb = nullptr) - { - *this = parser(i, cb).parse(); - } - - /////////////////////////////////////// - // other constructors and destructor // - /////////////////////////////////////// - - /*! - @brief copy constructor - - Creates a copy of a given JSON value. - - @param[in] other the JSON value to copy - - @complexity Linear in the size of @a other. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - As postcondition, it holds: `other == basic_json(other)`. - - @throw std::bad_alloc if allocation for object, array, or string fails. - - @liveexample{The following code shows an example for the copy - constructor.,basic_json__basic_json} - - @since version 1.0.0 - */ - basic_json(const basic_json& other) - : m_type(other.m_type) - { - switch (m_type) - { - case value_t::object: - { - assert(other.m_value.object != nullptr); - m_value = *other.m_value.object; - break; - } - - case value_t::array: - { - assert(other.m_value.array != nullptr); - m_value = *other.m_value.array; - break; - } - - case value_t::string: - { - assert(other.m_value.string != nullptr); - m_value = *other.m_value.string; - break; - } - - case value_t::boolean: - { - m_value = other.m_value.boolean; - break; - } - - case value_t::number_integer: - { - m_value = other.m_value.number_integer; - break; - } - - case value_t::number_unsigned: - { - m_value = other.m_value.number_unsigned; - break; - } - - case value_t::number_float: - { - m_value = other.m_value.number_float; - break; - } - - default: - { - break; - } - } - } - - /*! - @brief move constructor - - Move constructor. Constructs a JSON value with the contents of the given - value @a other using move semantics. It "steals" the resources from @a - other and leaves it as JSON null value. - - @param[in,out] other value to move to this object - - @post @a other is a JSON null value - - @complexity Constant. - - @liveexample{The code below shows the move constructor explicitly called - via std::move.,basic_json__moveconstructor} - - @since version 1.0.0 - */ - basic_json(basic_json&& other) noexcept - : m_type(std::move(other.m_type)), - m_value(std::move(other.m_value)) - { - // invalidate payload - other.m_type = value_t::null; - other.m_value = {}; - } - - /*! - @brief copy assignment - - Copy assignment operator. Copies a JSON value via the "copy and swap" - strategy: It is expressed in terms of the copy constructor, destructor, - and the swap() member function. - - @param[in] other value to copy from - - @complexity Linear. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - @liveexample{The code below shows and example for the copy assignment. It - creates a copy of value `a` which is then swapped with `b`. Finally\, the - copy of `a` (which is the null value after the swap) is - destroyed.,basic_json__copyassignment} - - @since version 1.0.0 - */ - reference& operator=(basic_json other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - using std::swap; - swap(m_type, other.m_type); - swap(m_value, other.m_value); - return *this; - } - - /*! - @brief destructor - - Destroys the JSON value and frees all allocated memory. - - @complexity Linear. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - All stored elements are destroyed and all memory is freed. - - @since version 1.0.0 - */ - ~basic_json() - { - switch (m_type) - { - case value_t::object: - { - AllocatorType alloc; - alloc.destroy(m_value.object); - alloc.deallocate(m_value.object, 1); - break; - } - - case value_t::array: - { - AllocatorType alloc; - alloc.destroy(m_value.array); - alloc.deallocate(m_value.array, 1); - break; - } - - case value_t::string: - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - break; - } - - default: - { - // all other types need no specific destructor - break; - } - } - } - - /// @} - - public: - /////////////////////// - // object inspection // - /////////////////////// - - /// @name object inspection - /// @{ - - /*! - @brief serialization - - Serialization function for JSON values. The function tries to mimic - Python's @p json.dumps() function, and currently supports its @p indent - parameter. - - @param[in] indent if indent is nonnegative, then array elements and object - members will be pretty-printed with that indent level. An indent level of - 0 will only insert newlines. -1 (the default) selects the most compact - representation - - @return string containing the serialization of the JSON value - - @complexity Linear. - - @liveexample{The following example shows the effect of different @a indent - parameters to the result of the serialization.,dump} - - @see https://docs.python.org/2/library/json.html#json.dump - - @since version 1.0.0 - */ - string_t dump(const int indent = -1) const - { - std::stringstream ss; - - if (indent >= 0) - { - dump(ss, true, static_cast(indent)); - } - else - { - dump(ss, false, 0); - } - - return ss.str(); - } - - /*! - @brief return the type of the JSON value (explicit) - - Return the type of the JSON value as a value from the @ref value_t - enumeration. - - @return the type of the JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `type()` for all JSON - types.,type} - - @since version 1.0.0 - */ - constexpr value_t type() const noexcept - { - return m_type; - } - - /*! - @brief return whether type is primitive - - This function returns true iff the JSON type is primitive (string, number, - boolean, or null). - - @return `true` if type is primitive (string, number, boolean, or null), - `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_primitive()` for all JSON - types.,is_primitive} - - @sa @ref is_structured() -- returns whether JSON value is structured - @sa @ref is_null() -- returns whether JSON value is `null` - @sa @ref is_string() -- returns whether JSON value is a string - @sa @ref is_boolean() -- returns whether JSON value is a boolean - @sa @ref is_number() -- returns whether JSON value is a number - - @since version 1.0.0 - */ - constexpr bool is_primitive() const noexcept - { - return is_null() or is_string() or is_boolean() or is_number(); - } - - /*! - @brief return whether type is structured - - This function returns true iff the JSON type is structured (array or - object). - - @return `true` if type is structured (array or object), `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_structured()` for all JSON - types.,is_structured} - - @sa @ref is_primitive() -- returns whether value is primitive - @sa @ref is_array() -- returns whether value is an array - @sa @ref is_object() -- returns whether value is an object - - @since version 1.0.0 - */ - constexpr bool is_structured() const noexcept - { - return is_array() or is_object(); - } - - /*! - @brief return whether value is null - - This function returns true iff the JSON value is null. - - @return `true` if type is null, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_null()` for all JSON - types.,is_null} - - @since version 1.0.0 - */ - constexpr bool is_null() const noexcept - { - return m_type == value_t::null; - } - - /*! - @brief return whether value is a boolean - - This function returns true iff the JSON value is a boolean. - - @return `true` if type is boolean, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_boolean()` for all JSON - types.,is_boolean} - - @since version 1.0.0 - */ - constexpr bool is_boolean() const noexcept - { - return m_type == value_t::boolean; - } - - /*! - @brief return whether value is a number - - This function returns true iff the JSON value is a number. This includes - both integer and floating-point values. - - @return `true` if type is number (regardless whether integer, unsigned - integer or floating-type), `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number()` for all JSON - types.,is_number} - - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 1.0.0 - */ - constexpr bool is_number() const noexcept - { - return is_number_integer() or is_number_float(); - } - - /*! - @brief return whether value is an integer number - - This function returns true iff the JSON value is an integer or unsigned - integer number. This excludes floating-point values. - - @return `true` if type is an integer or unsigned integer number, `false` - otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_integer()` for all - JSON types.,is_number_integer} - - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 1.0.0 - */ - constexpr bool is_number_integer() const noexcept - { - return m_type == value_t::number_integer or m_type == value_t::number_unsigned; - } - - /*! - @brief return whether value is an unsigned integer number - - This function returns true iff the JSON value is an unsigned integer - number. This excludes floating-point and (signed) integer values. - - @return `true` if type is an unsigned integer number, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_unsigned()` for all - JSON types.,is_number_unsigned} - - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 2.0.0 - */ - constexpr bool is_number_unsigned() const noexcept - { - return m_type == value_t::number_unsigned; - } - - /*! - @brief return whether value is a floating-point number - - This function returns true iff the JSON value is a floating-point number. - This excludes integer and unsigned integer values. - - @return `true` if type is a floating-point number, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_float()` for all - JSON types.,is_number_float} - - @sa @ref is_number() -- check if value is number - @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - - @since version 1.0.0 - */ - constexpr bool is_number_float() const noexcept - { - return m_type == value_t::number_float; - } - - /*! - @brief return whether value is an object - - This function returns true iff the JSON value is an object. - - @return `true` if type is object, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_object()` for all JSON - types.,is_object} - - @since version 1.0.0 - */ - constexpr bool is_object() const noexcept - { - return m_type == value_t::object; - } - - /*! - @brief return whether value is an array - - This function returns true iff the JSON value is an array. - - @return `true` if type is array, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_array()` for all JSON - types.,is_array} - - @since version 1.0.0 - */ - constexpr bool is_array() const noexcept - { - return m_type == value_t::array; - } - - /*! - @brief return whether value is a string - - This function returns true iff the JSON value is a string. - - @return `true` if type is string, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_string()` for all JSON - types.,is_string} - - @since version 1.0.0 - */ - constexpr bool is_string() const noexcept - { - return m_type == value_t::string; - } - - /*! - @brief return whether value is discarded - - This function returns true iff the JSON value was discarded during parsing - with a callback function (see @ref parser_callback_t). - - @note This function will always be `false` for JSON values after parsing. - That is, discarded values can only occur during parsing, but will be - removed when inside a structured value or replaced by null in other cases. - - @return `true` if type is discarded, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_discarded()` for all JSON - types.,is_discarded} - - @since version 1.0.0 - */ - constexpr bool is_discarded() const noexcept - { - return m_type == value_t::discarded; - } - - /*! - @brief return the type of the JSON value (implicit) - - Implicitly return the type of the JSON value as a value from the @ref - value_t enumeration. - - @return the type of the JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies the @ref value_t operator for - all JSON types.,operator__value_t} - - @since version 1.0.0 - */ - constexpr operator value_t() const noexcept - { - return m_type; - } - - /// @} - - private: - ////////////////// - // value access // - ////////////////// - - /// get an object (explicit) - template ::value and - std::is_convertible::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_object()) - { - assert(m_value.object != nullptr); - return T(m_value.object->begin(), m_value.object->end()); - } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } - } - - /// get an object (explicit) - object_t get_impl(object_t*) const - { - if (is_object()) - { - assert(m_value.object != nullptr); - return *(m_value.object); - } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_array()) - { - T to_vector; - assert(m_value.array != nullptr); - std::transform(m_value.array->begin(), m_value.array->end(), - std::inserter(to_vector, to_vector.end()), [](basic_json i) - { - return i.get(); - }); - return to_vector; - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not std::is_same::value - , int>::type = 0> - std::vector get_impl(std::vector*) const - { - if (is_array()) - { - std::vector to_vector; - assert(m_value.array != nullptr); - to_vector.reserve(m_value.array->size()); - std::transform(m_value.array->begin(), m_value.array->end(), - std::inserter(to_vector, to_vector.end()), [](basic_json i) - { - return i.get(); - }); - return to_vector; - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not has_mapped_type::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_array()) - { - assert(m_value.array != nullptr); - return T(m_value.array->begin(), m_value.array->end()); - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - array_t get_impl(array_t*) const - { - if (is_array()) - { - assert(m_value.array != nullptr); - return *(m_value.array); - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get a string (explicit) - template ::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_string()) - { - assert(m_value.string != nullptr); - return *m_value.string; - } - else - { - throw std::domain_error("type must be string, but is " + type_name()); - } - } - - /// get a number (explicit) - template::value - , int>::type = 0> - T get_impl(T*) const - { - switch (m_type) - { - case value_t::number_integer: - { - return static_cast(m_value.number_integer); - } - - case value_t::number_unsigned: - { - return static_cast(m_value.number_unsigned); - } - - case value_t::number_float: - { - return static_cast(m_value.number_float); - } - - default: - { - throw std::domain_error("type must be number, but is " + type_name()); - } - } - } - - /// get a boolean (explicit) - constexpr boolean_t get_impl(boolean_t*) const - { - return is_boolean() - ? m_value.boolean - : throw std::domain_error("type must be boolean, but is " + type_name()); - } - - /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t*) noexcept - { - return is_object() ? m_value.object : nullptr; - } - - /// get a pointer to the value (object) - constexpr const object_t* get_impl_ptr(const object_t*) const noexcept - { - return is_object() ? m_value.object : nullptr; - } - - /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t*) noexcept - { - return is_array() ? m_value.array : nullptr; - } - - /// get a pointer to the value (array) - constexpr const array_t* get_impl_ptr(const array_t*) const noexcept - { - return is_array() ? m_value.array : nullptr; - } - - /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t*) noexcept - { - return is_string() ? m_value.string : nullptr; - } - - /// get a pointer to the value (string) - constexpr const string_t* get_impl_ptr(const string_t*) const noexcept - { - return is_string() ? m_value.string : nullptr; - } - - /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t*) noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } - - /// get a pointer to the value (boolean) - constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } - - /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t*) noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } - - /// get a pointer to the value (integer number) - constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } - - /// get a pointer to the value (unsigned number) - number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept - { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; - } - - /// get a pointer to the value (unsigned number) - constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept - { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; - } - - /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t*) noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } - - /// get a pointer to the value (floating-point number) - constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } - - /*! - @brief helper function to implement get_ref() - - This funcion helps to implement get_ref() without code duplication for - const and non-const overloads - - @tparam ThisType will be deduced as `basic_json` or `const basic_json` - - @throw std::domain_error if ReferenceType does not match underlying value - type of the current JSON - */ - template - static ReferenceType get_ref_impl(ThisType& obj) - { - // delegate the call to get_ptr<>() - using PointerType = typename std::add_pointer::type; - auto ptr = obj.template get_ptr(); - - if (ptr != nullptr) - { - return *ptr; - } - else - { - throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + - obj.type_name()); - } - } - - public: - - /// @name value access - /// @{ - - /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value. - - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays - - @return copy of the JSON value, converted to type @a ValueType - - @throw std::domain_error in case passed type @a ValueType is incompatible - to JSON; example: `"type must be object, but is null"` - - @complexity Linear in the size of the JSON value. - - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get__ValueType_const} - - @internal - The idea of using a casted null pointer to choose the correct - implementation is from . - @endinternal - - @sa @ref operator ValueType() const for implicit conversion - @sa @ref get() for pointer-member access - - @since version 1.0.0 - */ - template::value - , int>::type = 0> - ValueType get() const - { - return get_impl(static_cast(nullptr)); - } - - /*! - @brief get a pointer value (explicit) - - Explicit pointer access to the internally stored JSON value. No copies are - made. - - @warning The pointer becomes invalid if the underlying JSON object changes. - - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - - @complexity Constant. - - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get__PointerType} - - @sa @ref get_ptr() for explicit pointer-member access - - @since version 1.0.0 - */ - template::value - , int>::type = 0> - PointerType get() noexcept - { - // delegate the call to get_ptr - return get_ptr(); - } - - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template::value - , int>::type = 0> - constexpr const PointerType get() const noexcept - { - // delegate the call to get_ptr - return get_ptr(); - } - - /*! - @brief get a pointer value (implicit) - - Implicit pointer access to the internally stored JSON value. No copies are - made. - - @warning Writing data to the pointee of the result yields an undefined - state. - - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - - @complexity Constant. - - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} - - @since version 1.0.0 - */ - template::value - , int>::type = 0> - PointerType get_ptr() noexcept - { - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); - } - - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() - */ - template::value - and std::is_const::type>::value - , int>::type = 0> - constexpr const PointerType get_ptr() const noexcept - { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); - } - - /*! - @brief get a reference value (implicit) - - Implict reference access to the internally stored JSON value. No copies - are made. - - @warning Writing data to the referee of the result yields an undefined - state. - - @tparam ReferenceType reference type; must be a reference to @ref array_t, - @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. - - @return reference to the internally stored JSON value if the requested - reference type @a ReferenceType fits to the JSON value; throws - std::domain_error otherwise - - @throw std::domain_error in case passed type @a ReferenceType is - incompatible with the stored JSON value - - @complexity Constant. - - @liveexample{The example shows several calls to `get_ref()`.,get_ref} - - @since version 1.1.0 - */ - template::value - , int>::type = 0> - ReferenceType get_ref() - { - // delegate call to get_ref_impl - return get_ref_impl(*this); - } - - /*! - @brief get a reference value (implicit) - @copydoc get_ref() - */ - template::value - and std::is_const::type>::value - , int>::type = 0> - ReferenceType get_ref() const - { - // delegate call to get_ref_impl - return get_ref_impl(*this); - } - - /*! - @brief get a value (implicit) - - Implicit type conversion between the JSON value and a compatible value. - The call is realized by calling @ref get() const. - - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays. The character type of @ref string_t - as well as an initializer list of this type is excluded to avoid - ambiguities as these types implicitly convert to `std::string`. - - @return copy of the JSON value, converted to type @a ValueType - - @throw std::domain_error in case passed type @a ValueType is incompatible - to JSON, thrown by @ref get() const - - @complexity Linear in the size of the JSON value. - - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,operator__ValueType} - - @since version 1.0.0 - */ - template < typename ValueType, typename - std::enable_if < - not std::is_pointer::value - and not std::is_same::value -#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 - and not std::is_same>::value -#endif - , int >::type = 0 > - operator ValueType() const - { - // delegate the call to get<>() const - return get(); - } - - /// @} - - - //////////////////// - // element access // - //////////////////// - - /// @name element access - /// @{ - - /*! - @brief access specified array element with bounds checking - - Returns a reference to the element at specified location @a idx, with - bounds checking. - - @param[in] idx index of the element to access - - @return reference to the element at index @a idx - - @throw std::domain_error if the JSON value is not an array; example: - `"cannot use at() with string"` - @throw std::out_of_range if the index @a idx is out of range of the array; - that is, `idx >= size()`; example: `"array index 7 is out of range"` - - @complexity Constant. - - @liveexample{The example below shows how array elements can be read and - written using `at()`.,at__size_type} - - @since version 1.0.0 - */ - reference at(size_type idx) - { - // at only works for arrays - if (is_array()) - { - try - { - assert(m_value.array != nullptr); - return m_value.array->at(idx); - } - catch (std::out_of_range&) - { - // create better exception explanation - throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); - } - } - else - { - throw std::domain_error("cannot use at() with " + type_name()); - } - } - - /*! - @brief access specified array element with bounds checking - - Returns a const reference to the element at specified location @a idx, - with bounds checking. - - @param[in] idx index of the element to access - - @return const reference to the element at index @a idx - - @throw std::domain_error if the JSON value is not an array; example: - `"cannot use at() with string"` - @throw std::out_of_range if the index @a idx is out of range of the array; - that is, `idx >= size()`; example: `"array index 7 is out of range"` - - @complexity Constant. - - @liveexample{The example below shows how array elements can be read using - `at()`.,at__size_type_const} - - @since version 1.0.0 - */ - const_reference at(size_type idx) const - { - // at only works for arrays - if (is_array()) - { - try - { - assert(m_value.array != nullptr); - return m_value.array->at(idx); - } - catch (std::out_of_range&) - { - // create better exception explanation - throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); - } - } - else - { - throw std::domain_error("cannot use at() with " + type_name()); - } - } - - /*! - @brief access specified object element with bounds checking - - Returns a reference to the element at with specified key @a key, with - bounds checking. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw std::domain_error if the JSON value is not an object; example: - `"cannot use at() with boolean"` - @throw std::out_of_range if the key @a key is is not stored in the object; - that is, `find(key) == end()`; example: `"key "the fast" not found"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using `at()`.,at__object_t_key_type} - - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - reference at(const typename object_t::key_type& key) - { - // at only works for objects - if (is_object()) - { - try - { - assert(m_value.object != nullptr); - return m_value.object->at(key); - } - catch (std::out_of_range&) - { - // create better exception explanation - throw std::out_of_range("key '" + key + "' not found"); - } - } - else - { - throw std::domain_error("cannot use at() with " + type_name()); - } - } - - /*! - @brief access specified object element with bounds checking - - Returns a const reference to the element at with specified key @a key, - with bounds checking. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw std::domain_error if the JSON value is not an object; example: - `"cannot use at() with boolean"` - @throw std::out_of_range if the key @a key is is not stored in the object; - that is, `find(key) == end()`; example: `"key "the fast" not found"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - `at()`.,at__object_t_key_type_const} - - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - const_reference at(const typename object_t::key_type& key) const - { - // at only works for objects - if (is_object()) - { - try - { - assert(m_value.object != nullptr); - return m_value.object->at(key); - } - catch (std::out_of_range&) - { - // create better exception explanation - throw std::out_of_range("key '" + key + "' not found"); - } - } - else - { - throw std::domain_error("cannot use at() with " + type_name()); - } - } - - /*! - @brief access specified array element - - Returns a reference to the element at specified location @a idx. - - @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), - then the array is silently filled up with `null` values to make `idx` a - valid reference to the last stored element. - - @param[in] idx index of the element to access - - @return reference to the element at index @a idx - - @throw std::domain_error if JSON is not an array or null; example: - `"cannot use operator[] with string"` - - @complexity Constant if @a idx is in the range of the array. Otherwise - linear in `idx - size()`. - - @liveexample{The example below shows how array elements can be read and - written using `[]` operator. Note the addition of `null` - values.,operatorarray__size_type} - - @since version 1.0.0 - */ - reference operator[](size_type idx) - { - // implicitly convert null value to an empty array - if (is_null()) - { - m_type = value_t::array; - m_value.array = create(); - } - - // operator[] only works for arrays - if (is_array()) - { - // fill up array with null values until given idx is reached - assert(m_value.array != nullptr); - for (size_t i = m_value.array->size(); i <= idx; ++i) - { - m_value.array->push_back(basic_json()); - } - - return m_value.array->operator[](idx); - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief access specified array element - - Returns a const reference to the element at specified location @a idx. - - @param[in] idx index of the element to access - - @return const reference to the element at index @a idx - - @throw std::domain_error if JSON is not an array; example: `"cannot use - operator[] with null"` - - @complexity Constant. - - @liveexample{The example below shows how array elements can be read using - the `[]` operator.,operatorarray__size_type_const} - - @since version 1.0.0 - */ - const_reference operator[](size_type idx) const - { - // const operator[] only works for arrays - if (is_array()) - { - assert(m_value.array != nullptr); - return m_value.array->operator[](idx); - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - reference operator[](const typename object_t::key_type& key) - { - // implicitly convert null value to an empty object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create(); - } - - // operator[] only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - return m_value.object->operator[](key); - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - const_reference operator[](const typename object_t::key_type& key) const - { - // const operator[] only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - template - reference operator[](T * (&key)[n]) - { - return operator[](static_cast(key)); - } - - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @note This function is required for compatibility reasons with Clang. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - template - const_reference operator[](T * (&key)[n]) const - { - return operator[](static_cast(key)); - } - - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.1.0 - */ - template - reference operator[](T* key) - { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - } - - // at only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - return m_value.object->operator[](key); - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.1.0 - */ - template - const_reference operator[](T* key) const - { - // at only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief access specified object element with default value - - Returns either a copy of an object's element at the specified key @a key or - a given default value if no element with key @a key exists. - - The function is basically equivalent to executing - @code {.cpp} - try { - return at(key); - } catch(std::out_of_range) { - return default_value; - } - @endcode - - @note Unlike @ref at(const typename object_t::key_type&), this function - does not throw if the given key @a key was not found. - - @note Unlike @ref operator[](const typename object_t::key_type& key), this - function does not implicitly add an element to the position defined by @a - key. This function is furthermore also applicable to const objects. - - @param[in] key key of the element to access - @param[in] default_value the value to return if @a key is not found - - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. - - @return copy of the element at key @a key or @a default_value if @a key - is not found - - @throw std::domain_error if JSON is not an object; example: `"cannot use - value() with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - - @since version 1.0.0 - */ - template ::value - , int>::type = 0> - ValueType value(const typename object_t::key_type& key, ValueType default_value) const - { - // at only works for objects - if (is_object()) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return *it; - } - else - { - return default_value; - } - } - else - { - throw std::domain_error("cannot use value() with " + type_name()); - } - } - - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value() - */ - string_t value(const typename object_t::key_type& key, const char* default_value) const - { - return value(key, string_t(default_value)); - } - - /*! - @brief access the first element - - Returns a reference to the first element in the container. For a JSON - container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - - @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). - @post The JSON value remains unchanged. - - @throw std::out_of_range when called on `null` value - - @liveexample{The following code shows an example for `front()`.,front} - - @sa @ref back() -- access the last element - - @since version 1.0.0 - */ - reference front() - { - return *begin(); - } - - /*! - @copydoc basic_json::front() - */ - const_reference front() const - { - return *cbegin(); - } - - /*! - @brief access the last element - - Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to - @code {.cpp} - auto tmp = c.end(); - --tmp; - return *tmp; - @endcode - - @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). - @post The JSON value remains unchanged. - - @throw std::out_of_range when called on `null` value. - - @liveexample{The following code shows an example for `back()`.,back} - - @sa @ref front() -- access the first element - - @since version 1.0.0 - */ - reference back() - { - auto tmp = end(); - --tmp; - return *tmp; - } - - /*! - @copydoc basic_json::back() - */ - const_reference back() const - { - auto tmp = cend(); - --tmp; - return *tmp; - } - - /*! - @brief remove element given an iterator - - Removes the element specified by iterator @a pos. The iterator @a pos must - be valid and dereferenceable. Thus the `end()` iterator (which is valid, - but is not dereferenceable) cannot be used as a value for @a pos. - - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. - - @param[in] pos iterator to the element to remove - @return Iterator following the last removed element. If the iterator @a - pos refers to the last element, the `end()` iterator is returned. - - @tparam InteratorType an @ref iterator or @ref const_iterator - - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. - - @throw std::domain_error if called on a `null` value; example: `"cannot - use erase() with null"` - @throw std::domain_error if called on an iterator which does not belong to - the current JSON value; example: `"iterator does not fit current value"` - @throw std::out_of_range if called on a primitive type with invalid - iterator (i.e., any iterator which is not `begin()`); example: `"iterator - out of range"` - - @complexity The complexity depends on the type: - - objects: amortized constant - - arrays: linear in distance between pos and the end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType} - - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ - template ::value or - std::is_same::value - , int>::type - = 0> - InteratorType erase(InteratorType pos) - { - // make sure iterator fits the current value - if (this != pos.m_object) - { - throw std::domain_error("iterator does not fit current value"); - } - - InteratorType result = end(); - - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not pos.m_it.primitive_iterator.is_begin()) - { - throw std::out_of_range("iterator out of range"); - } - - if (is_string()) - { - delete m_value.string; - m_value.string = nullptr; - } - - m_type = value_t::null; - break; - } - - case value_t::object: - { - assert(m_value.object != nullptr); - result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); - break; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); - break; - } - - default: - { - throw std::domain_error("cannot use erase() with " + type_name()); - } - } - - return result; - } - - /*! - @brief remove elements given an iterator range - - Removes the element specified by the range `[first; last)`. The iterator - @a first does not need to be dereferenceable if `first == last`: erasing - an empty range is a no-op. - - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. - - @param[in] first iterator to the beginning of the range to remove - @param[in] last iterator past the end of the range to remove - @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the `end()` iterator is returned. - - @tparam InteratorType an @ref iterator or @ref const_iterator - - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. - - @throw std::domain_error if called on a `null` value; example: `"cannot - use erase() with null"` - @throw std::domain_error if called on iterators which does not belong to - the current JSON value; example: `"iterators do not fit current value"` - @throw std::out_of_range if called on a primitive type with invalid - iterators (i.e., if `first != begin()` and `last != end()`); example: - `"iterators out of range"` - - @complexity The complexity depends on the type: - - objects: `log(size()) + std::distance(first, last)` - - arrays: linear in the distance between @a first and @a last, plus linear - in the distance between @a last and end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType_IteratorType} - - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ - template ::value or - std::is_same::value - , int>::type - = 0> - InteratorType erase(InteratorType first, InteratorType last) - { - // make sure iterator fits the current value - if (this != first.m_object or this != last.m_object) - { - throw std::domain_error("iterators do not fit current value"); - } - - InteratorType result = end(); - - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - throw std::out_of_range("iterators out of range"); - } - - if (is_string()) - { - delete m_value.string; - m_value.string = nullptr; - } - - m_type = value_t::null; - break; - } - - case value_t::object: - { - assert(m_value.object != nullptr); - result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } - - default: - { - throw std::domain_error("cannot use erase() with " + type_name()); - } - } - - return result; - } - - /*! - @brief remove element from a JSON object given a key - - Removes elements from a JSON object with the key value @a key. - - @param[in] key value of the elements to remove - - @return Number of elements removed. If @a ObjectType is the default - `std::map` type, the return value will always be `0` (@a key was not - found) or `1` (@a key was found). - - @post References and iterators to the erased elements are invalidated. - Other references and iterators are not affected. - - @throw std::domain_error when called on a type other than JSON object; - example: `"cannot use erase() with null"` - - @complexity `log(size()) + count(key)` - - @liveexample{The example shows the effect of `erase()`.,erase__key_type} - - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in - the given range - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ - size_type erase(const typename object_t::key_type& key) - { - // this erase only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - return m_value.object->erase(key); - } - else - { - throw std::domain_error("cannot use erase() with " + type_name()); - } - } - - /*! - @brief remove element from a JSON array given an index - - Removes element from a JSON array at the index @a idx. - - @param[in] idx index of the element to remove - - @throw std::domain_error when called on a type other than JSON array; - example: `"cannot use erase() with null"` - @throw std::out_of_range when `idx >= size()`; example: `"array index 17 - is out of range"` - - @complexity Linear in distance between @a idx and the end of the container. - - @liveexample{The example shows the effect of `erase()`.,erase__size_type} - - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - - @since version 1.0.0 - */ - void erase(const size_type idx) - { - // this erase only works for arrays - if (is_array()) - { - if (idx >= size()) - { - throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); - } - - assert(m_value.array != nullptr); - m_value.array->erase(m_value.array->begin() + static_cast(idx)); - } - else - { - throw std::domain_error("cannot use erase() with " + type_name()); - } - } - - /// @} - - - //////////// - // lookup // - //////////// - - /// @name lookup - /// @{ - - /*! - @brief find an element in a JSON object - - Finds an element in a JSON object with key equivalent to @a key. If the - element is not found or the JSON value is not an object, end() is - returned. - - @param[in] key key value of the element to search for - - @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The example shows how `find()` is used.,find__key_type} - - @since version 1.0.0 - */ - iterator find(typename object_t::key_type key) - { - auto result = end(); - - if (is_object()) - { - assert(m_value.object != nullptr); - result.m_it.object_iterator = m_value.object->find(key); - } - - return result; - } - - /*! - @brief find an element in a JSON object - @copydoc find(typename object_t::key_type) - */ - const_iterator find(typename object_t::key_type key) const - { - auto result = cend(); - - if (is_object()) - { - assert(m_value.object != nullptr); - result.m_it.object_iterator = m_value.object->find(key); - } - - return result; - } - - /*! - @brief returns the number of occurrences of a key in a JSON object - - Returns the number of elements with key @a key. If ObjectType is the - default `std::map` type, the return value will always be `0` (@a key was - not found) or `1` (@a key was found). - - @param[in] key key value of the element to count - - @return Number of elements with key @a key. If the JSON value is not an - object, the return value will be `0`. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The example shows how `count()` is used.,count} - - @since version 1.0.0 - */ - size_type count(typename object_t::key_type key) const - { - // return 0 for all nonobject types - assert(not is_object() or m_value.object != nullptr); - return is_object() ? m_value.object->count(key) : 0; - } - - /// @} - - - /////////////// - // iterators // - /////////////// - - /// @name iterators - /// @{ - - /*! - @brief returns an iterator to the first element - - Returns an iterator to the first element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator to the first element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for `begin()`.,begin} - - @sa @ref cbegin() -- returns a const iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end - - @since version 1.0.0 - */ - iterator begin() noexcept - { - iterator result(this); - result.set_begin(); - return result; - } - - /*! - @copydoc basic_json::cbegin() - */ - const_iterator begin() const noexcept - { - return cbegin(); - } - - /*! - @brief returns a const iterator to the first element - - Returns a const iterator to the first element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator to the first element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).begin()`. - - @liveexample{The following code shows an example for `cbegin()`.,cbegin} - - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end - - @since version 1.0.0 - */ - const_iterator cbegin() const noexcept - { - const_iterator result(this); - result.set_begin(); - return result; - } - - /*! - @brief returns an iterator to one past the last element - - Returns an iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator one past the last element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for `end()`.,end} - - @sa @ref cend() -- returns a const iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning - - @since version 1.0.0 - */ - iterator end() noexcept - { - iterator result(this); - result.set_end(); - return result; - } - - /*! - @copydoc basic_json::cend() - */ - const_iterator end() const noexcept - { - return cend(); - } - - /*! - @brief returns a const iterator to one past the last element - - Returns a const iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator one past the last element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).end()`. - - @liveexample{The following code shows an example for `cend()`.,cend} - - @sa @ref end() -- returns an iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning - - @since version 1.0.0 - */ - const_iterator cend() const noexcept - { - const_iterator result(this); - result.set_end(); - return result; - } - - /*! - @brief returns an iterator to the reverse-beginning - - Returns an iterator to the reverse-beginning; that is, the last element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(end())`. - - @liveexample{The following code shows an example for `rbegin()`.,rbegin} - - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end - - @since version 1.0.0 - */ - reverse_iterator rbegin() noexcept - { - return reverse_iterator(end()); - } - - /*! - @copydoc basic_json::crbegin() - */ - const_reverse_iterator rbegin() const noexcept - { - return crbegin(); - } - - /*! - @brief returns an iterator to the reverse-end - - Returns an iterator to the reverse-end; that is, one before the first - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(begin())`. - - @liveexample{The following code shows an example for `rend()`.,rend} - - @sa @ref crend() -- returns a const reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - - @since version 1.0.0 - */ - reverse_iterator rend() noexcept - { - return reverse_iterator(begin()); - } - - /*! - @copydoc basic_json::crend() - */ - const_reverse_iterator rend() const noexcept - { - return crend(); - } - - /*! - @brief returns a const reverse iterator to the last element - - Returns a const iterator to the reverse-beginning; that is, the last - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rbegin()`. - - @liveexample{The following code shows an example for `crbegin()`.,crbegin} - - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end - - @since version 1.0.0 - */ - const_reverse_iterator crbegin() const noexcept - { - return const_reverse_iterator(cend()); - } - - /*! - @brief returns a const reverse iterator to one before the first - - Returns a const reverse iterator to the reverse-end; that is, one before - the first element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rend()`. - - @liveexample{The following code shows an example for `crend()`.,crend} - - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - - @since version 1.0.0 - */ - const_reverse_iterator crend() const noexcept - { - return const_reverse_iterator(cbegin()); - } - - private: - // forward declaration - template class iteration_proxy; - - public: - /*! - @brief wrapper to access iterator member functions in range-based for - - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a - reference to the JSON values is returned, so there is no access to the - underlying iterator. - - @note The name of this function is not yet final and may change in the - future. - */ - static iteration_proxy iterator_wrapper(reference cont) - { - return iteration_proxy(cont); - } - - /*! - @copydoc iterator_wrapper(reference) - */ - static iteration_proxy iterator_wrapper(const_reference cont) - { - return iteration_proxy(cont); - } - - /// @} - - - ////////////// - // capacity // - ////////////// - - /// @name capacity - /// @{ - - /*! - @brief checks whether the container is empty - - Checks if a JSON value has no elements. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `true` - boolean | `false` - string | `false` - number | `false` - object | result of function `object_t::empty()` - array | result of function `array_t::empty()` - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `empty()` functions have constant - complexity. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `begin() == end()`. - - @liveexample{The following code uses `empty()` to check if a JSON - object contains any elements.,empty} - - @sa @ref size() -- returns the number of elements - - @since version 1.0.0 - */ - bool empty() const noexcept - { - switch (m_type) - { - case value_t::null: - { - // null values are empty - return true; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - return m_value.array->empty(); - } - - case value_t::object: - { - assert(m_value.object != nullptr); - return m_value.object->empty(); - } - - default: - { - // all other types are nonempty - return false; - } - } - } - - /*! - @brief returns the number of elements - - Returns the number of elements in a JSON value. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` - boolean | `1` - string | `1` - number | `1` - object | result of function object_t::size() - array | result of function array_t::size() - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their size() functions have constant - complexity. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `std::distance(begin(), end())`. - - @liveexample{The following code calls `size()` on the different value - types.,size} - - @sa @ref empty() -- checks whether the container is empty - @sa @ref max_size() -- returns the maximal number of elements - - @since version 1.0.0 - */ - size_type size() const noexcept - { - switch (m_type) - { - case value_t::null: - { - // null values are empty - return 0; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - return m_value.array->size(); - } - - case value_t::object: - { - assert(m_value.object != nullptr); - return m_value.object->size(); - } - - default: - { - // all other types have size 1 - return 1; - } - } - } - - /*! - @brief returns the maximum possible number of elements - - Returns the maximum number of elements a JSON value is able to hold due to - system or library implementation limitations, i.e. `std::distance(begin(), - end())` for the JSON value. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` (same as `size()`) - boolean | `1` (same as `size()`) - string | `1` (same as `size()`) - number | `1` (same as `size()`) - object | result of function `object_t::max_size()` - array | result of function `array_t::max_size()` - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `max_size()` functions have constant - complexity. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of returning `b.size()` where `b` is the largest - possible JSON value. - - @liveexample{The following code calls `max_size()` on the different value - types. Note the output is implementation specific.,max_size} - - @sa @ref size() -- returns the number of elements - - @since version 1.0.0 - */ - size_type max_size() const noexcept - { - switch (m_type) - { - case value_t::array: - { - assert(m_value.array != nullptr); - return m_value.array->max_size(); - } - - case value_t::object: - { - assert(m_value.object != nullptr); - return m_value.object->max_size(); - } - - default: - { - // all other types have max_size() == size() - return size(); - } - } - } - - /// @} - - - /////////////// - // modifiers // - /////////////// - - /// @name modifiers - /// @{ - - /*! - @brief clears the contents - - Clears the content of a JSON value and resets it to the default value as - if @ref basic_json(value_t) would have been called: - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - - @complexity Linear in the size of the JSON value. - - @liveexample{The example below shows the effect of `clear()` to different - JSON types.,clear} - - @since version 1.0.0 - */ - void clear() noexcept - { - switch (m_type) - { - case value_t::number_integer: - { - m_value.number_integer = 0; - break; - } - - case value_t::number_unsigned: - { - m_value.number_unsigned = 0; - break; - } - - case value_t::number_float: - { - m_value.number_float = 0.0; - break; - } - - case value_t::boolean: - { - m_value.boolean = false; - break; - } - - case value_t::string: - { - assert(m_value.string != nullptr); - m_value.string->clear(); - break; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - m_value.array->clear(); - break; - } - - case value_t::object: - { - assert(m_value.object != nullptr); - m_value.object->clear(); - break; - } - - default: - { - break; - } - } - } - - /*! - @brief add an object to an array - - Appends the given element @a val to the end of the JSON value. If the - function is called on a JSON null value, an empty array is created before - appending @a val. - - @param[in] val the value to add to the JSON array - - @throw std::domain_error when called on a type other than JSON array or - null; example: `"cannot use push_back() with number"` - - @complexity Amortized constant. - - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON array. Note how the `null` value was silently - converted to a JSON array.,push_back} - - @since version 1.0.0 - */ - void push_back(basic_json&& val) - { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - throw std::domain_error("cannot use push_back() with " + type_name()); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - } - - // add element to array (move semantics) - assert(m_value.array != nullptr); - m_value.array->push_back(std::move(val)); - // invalidate object - val.m_type = value_t::null; - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(basic_json&& val) - { - push_back(std::move(val)); - return *this; - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - void push_back(const basic_json& val) - { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - throw std::domain_error("cannot use push_back() with " + type_name()); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - } - - // add element to array - assert(m_value.array != nullptr); - m_value.array->push_back(val); - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(const basic_json& val) - { - push_back(val); - return *this; - } - - /*! - @brief add an object to an object - - Inserts the given element @a val to the JSON object. If the function is - called on a JSON null value, an empty object is created before inserting - @a val. - - @param[in] val the value to add to the JSON object - - @throw std::domain_error when called on a type other than JSON object or - null; example: `"cannot use push_back() with number"` - - @complexity Logarithmic in the size of the container, O(log(`size()`)). - - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON object. Note how the `null` value was silently - converted to a JSON object.,push_back__object_t__value} - - @since version 1.0.0 - */ - void push_back(const typename object_t::value_type& val) - { - // push_back only works for null objects or objects - if (not(is_null() or is_object())) - { - throw std::domain_error("cannot use push_back() with " + type_name()); - } - - // transform null object into an object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - } - - // add element to array - assert(m_value.object != nullptr); - m_value.object->insert(val); - } - - /*! - @brief add an object to an object - @copydoc push_back(const typename object_t::value_type&) - */ - reference operator+=(const typename object_t::value_type& val) - { - push_back(val); - return *this; - } - - /*! - @brief add an object to an object - - This function allows to use `push_back` with an initializer list. In case - - 1. the current value is an object, - 2. the initializer list @a init contains only two elements, and - 3. the first element of @a init is a string, - - @a init is converted into an object element and added using - @ref push_back(const typename object_t::value_type&). Otherwise, @a init - is converted to a JSON value and added using @ref push_back(basic_json&&). - - @param init an initializer list - - @complexity Linear in the size of the initializer list @a init. - - @note This function is required to resolve an ambiguous overload error, - because pairs like `{"key", "value"}` can be both interpreted as - `object_t::value_type` or `std::initializer_list`, see - https://github.com/nlohmann/json/issues/235 for more information. - - @liveexample{The example shows how initializer lists are treated as - objects when possible.,push_back__initializer_list} - */ - void push_back(std::initializer_list init) - { - if (is_object() and init.size() == 2 and init.begin()->is_string()) - { - const string_t key = *init.begin(); - push_back(typename object_t::value_type(key, *(init.begin() + 1))); - } - else - { - push_back(basic_json(init)); - } - } - - /*! - @brief add an object to an object - @copydoc push_back(std::initializer_list) - */ - reference operator+=(std::initializer_list init) - { - push_back(init); - return *this; - } - - /*! - @brief inserts element - - Inserts element @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] val element to insert - @return iterator pointing to the inserted @a val. - - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - - @complexity Constant plus linear in the distance between pos and end of the - container. - - @liveexample{The example shows how `insert()` is used.,insert} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, const basic_json& val) - { - // insert only works for arrays - if (is_array()) - { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - throw std::domain_error("iterator does not fit current value"); - } - - // insert to array and return iterator - iterator result(this); - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); - return result; - } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } - } - - /*! - @brief inserts element - @copydoc insert(const_iterator, const basic_json&) - */ - iterator insert(const_iterator pos, basic_json&& val) - { - return insert(pos, val); - } - - /*! - @brief inserts elements - - Inserts @a cnt copies of @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] cnt number of copies of @a val to insert - @param[in] val element to insert - @return iterator pointing to the first element inserted, or @a pos if - `cnt==0` - - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - - @complexity Linear in @a cnt plus linear in the distance between @a pos - and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__count} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, size_type cnt, const basic_json& val) - { - // insert only works for arrays - if (is_array()) - { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - throw std::domain_error("iterator does not fit current value"); - } - - // insert to array and return iterator - iterator result(this); - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); - return result; - } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } - } - - /*! - @brief inserts elements - - Inserts elements from range `[first, last)` before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - @throw std::domain_error if @a first and @a last do not belong to the same - JSON value; example: `"iterators do not fit"` - @throw std::domain_error if @a first or @a last are iterators into - container for which insert is called; example: `"passed iterators may not - belong to container"` - - @return iterator pointing to the first element inserted, or @a pos if - `first==last` - - @complexity Linear in `std::distance(first, last)` plus linear in the - distance between @a pos and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__range} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, const_iterator first, const_iterator last) - { - // insert only works for arrays - if (not is_array()) - { - throw std::domain_error("cannot use insert() with " + type_name()); - } - - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - throw std::domain_error("iterator does not fit current value"); - } - - // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) - { - throw std::domain_error("iterators do not fit"); - } - - if (first.m_object == this or last.m_object == this) - { - throw std::domain_error("passed iterators may not belong to container"); - } - - // insert to array and return iterator - iterator result(this); - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->insert( - pos.m_it.array_iterator, - first.m_it.array_iterator, - last.m_it.array_iterator); - return result; - } - - /*! - @brief inserts elements - - Inserts elements from initializer list @a ilist before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] ilist initializer list to insert the values from - - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - - @return iterator pointing to the first element inserted, or @a pos if - `ilist` is empty - - @complexity Linear in `ilist.size()` plus linear in the distance between - @a pos and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__ilist} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, std::initializer_list ilist) - { - // insert only works for arrays - if (not is_array()) - { - throw std::domain_error("cannot use insert() with " + type_name()); - } - - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - throw std::domain_error("iterator does not fit current value"); - } - - // insert to array and return iterator - iterator result(this); - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); - return result; - } - - /*! - @brief exchanges the values - - Exchanges the contents of the JSON value with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other JSON value to exchange the contents with - - @complexity Constant. - - @liveexample{The example below shows how JSON values can be swapped with - `swap()`.,swap__reference} - - @since version 1.0.0 - */ - void swap(reference other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); - } - - /*! - @brief exchanges the values - - Exchanges the contents of a JSON array with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other array to exchange the contents with - - @throw std::domain_error when JSON value is not an array; example: `"cannot - use swap() with string"` - - @complexity Constant. - - @liveexample{The example below shows how arrays can be swapped with - `swap()`.,swap__array_t} - - @since version 1.0.0 - */ - void swap(array_t& other) - { - // swap only works for arrays - if (is_array()) - { - assert(m_value.array != nullptr); - std::swap(*(m_value.array), other); - } - else - { - throw std::domain_error("cannot use swap() with " + type_name()); - } - } - - /*! - @brief exchanges the values - - Exchanges the contents of a JSON object with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other object to exchange the contents with - - @throw std::domain_error when JSON value is not an object; example: - `"cannot use swap() with string"` - - @complexity Constant. - - @liveexample{The example below shows how objects can be swapped with - `swap()`.,swap__object_t} - - @since version 1.0.0 - */ - void swap(object_t& other) - { - // swap only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - std::swap(*(m_value.object), other); - } - else - { - throw std::domain_error("cannot use swap() with " + type_name()); - } - } - - /*! - @brief exchanges the values - - Exchanges the contents of a JSON string with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other string to exchange the contents with - - @throw std::domain_error when JSON value is not a string; example: `"cannot - use swap() with boolean"` - - @complexity Constant. - - @liveexample{The example below shows how strings can be swapped with - `swap()`.,swap__string_t} - - @since version 1.0.0 - */ - void swap(string_t& other) - { - // swap only works for strings - if (is_string()) - { - assert(m_value.string != nullptr); - std::swap(*(m_value.string), other); - } - else - { - throw std::domain_error("cannot use swap() with " + type_name()); - } - } - - /// @} - - - ////////////////////////////////////////// - // lexicographical comparison operators // - ////////////////////////////////////////// - - /// @name lexicographical comparison operators - /// @{ - - private: - /*! - @brief comparison operator for JSON types - - Returns an ordering that is similar to Python: - - order: null < boolean < number < object < array < string - - furthermore, each type is not smaller than itself - - @since version 1.0.0 - */ - friend bool operator<(const value_t lhs, const value_t rhs) noexcept - { - static constexpr std::array order = {{ - 0, // null - 3, // object - 4, // array - 5, // string - 1, // boolean - 2, // integer - 2, // unsigned - 2, // float - } - }; - - // discarded values are not comparable - if (lhs == value_t::discarded or rhs == value_t::discarded) - { - return false; - } - - return order[static_cast(lhs)] < order[static_cast(rhs)]; - } - - public: - /*! - @brief comparison: equal - - Compares two JSON values for equality according to the following rules: - - Two JSON values are equal if (1) they are from the same type and (2) - their stored values are the same. - - Integer and floating-point numbers are automatically converted before - comparison. Floating-point numbers are compared indirectly: two - floating-point numbers `f1` and `f2` are considered equal if neither - `f1 > f2` nor `f2 > f1` holds. - - Two JSON null values are equal. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are equal - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__equal} - - @since version 1.0.0 - */ - friend bool operator==(const_reference lhs, const_reference rhs) noexcept - { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); - return *lhs.m_value.array == *rhs.m_value.array; - } - case value_t::object: - { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); - return *lhs.m_value.object == *rhs.m_value.object; - } - case value_t::null: - { - return true; - } - case value_t::string: - { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); - return *lhs.m_value.string == *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean == rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer == rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float == rhs.m_value.number_float; - } - default: - { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); - } - - return false; - } - - /*! - @brief comparison: equal - - The functions compares the given JSON value against a null pointer. As the - null pointer can be used to initialize a JSON value to null, a comparison - of JSON value @a v with a null pointer should be equivalent to call - `v.is_null()`. - - @param[in] v JSON value to consider - @return whether @a v is null - - @complexity Constant. - - @liveexample{The example compares several JSON types to the null pointer. - ,operator__equal__nullptr_t} - - @since version 1.0.0 - */ - friend bool operator==(const_reference v, std::nullptr_t) noexcept - { - return v.is_null(); - } - - /*! - @brief comparison: equal - @copydoc operator==(const_reference, std::nullptr_t) - */ - friend bool operator==(std::nullptr_t, const_reference v) noexcept - { - return v.is_null(); - } - - /*! - @brief comparison: not equal - - Compares two JSON values for inequality by calculating `not (lhs == rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are not equal - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__notequal} - - @since version 1.0.0 - */ - friend bool operator!=(const_reference lhs, const_reference rhs) noexcept - { - return not (lhs == rhs); - } - - /*! - @brief comparison: not equal - - The functions compares the given JSON value against a null pointer. As the - null pointer can be used to initialize a JSON value to null, a comparison - of JSON value @a v with a null pointer should be equivalent to call - `not v.is_null()`. - - @param[in] v JSON value to consider - @return whether @a v is not null - - @complexity Constant. - - @liveexample{The example compares several JSON types to the null pointer. - ,operator__notequal__nullptr_t} - - @since version 1.0.0 - */ - friend bool operator!=(const_reference v, std::nullptr_t) noexcept - { - return not v.is_null(); - } - - /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, std::nullptr_t) - */ - friend bool operator!=(std::nullptr_t, const_reference v) noexcept - { - return not v.is_null(); - } - - /*! - @brief comparison: less than - - Compares whether one JSON value @a lhs is less than another JSON value @a - rhs according to the following rules: - - If @a lhs and @a rhs have the same type, the values are compared using - the default `<` operator. - - Integer and floating-point numbers are automatically converted before - comparison - - In case @a lhs and @a rhs have different types, the values are ignored - and the order of the types is considered, see - @ref operator<(const value_t, const value_t). - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than @a rhs - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__less} - - @since version 1.0.0 - */ - friend bool operator<(const_reference lhs, const_reference rhs) noexcept - { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); - return *lhs.m_value.array < *rhs.m_value.array; - } - case value_t::object: - { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); - return *lhs.m_value.object < *rhs.m_value.object; - } - case value_t::null: - { - return false; - } - case value_t::string: - { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); - return *lhs.m_value.string < *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean < rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer < rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float < rhs.m_value.number_float; - } - default: - { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; - } - - // We only reach this line if we cannot compare values. In that case, - // we compare types. Note we have to call the operator explicitly, - // because MSVC has problems otherwise. - return operator<(lhs_type, rhs_type); - } - - /*! - @brief comparison: less than or equal - - Compares whether one JSON value @a lhs is less than or equal to another - JSON value by calculating `not (rhs < lhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than or equal to @a rhs - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__greater} - - @since version 1.0.0 - */ - friend bool operator<=(const_reference lhs, const_reference rhs) noexcept - { - return not (rhs < lhs); - } - - /*! - @brief comparison: greater than - - Compares whether one JSON value @a lhs is greater than another - JSON value by calculating `not (lhs <= rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than to @a rhs - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__lessequal} - - @since version 1.0.0 - */ - friend bool operator>(const_reference lhs, const_reference rhs) noexcept - { - return not (lhs <= rhs); - } - - /*! - @brief comparison: greater than or equal - - Compares whether one JSON value @a lhs is greater than or equal to another - JSON value by calculating `not (lhs < rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than or equal to @a rhs - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__greaterequal} - - @since version 1.0.0 - */ - friend bool operator>=(const_reference lhs, const_reference rhs) noexcept - { - return not (lhs < rhs); - } - - /// @} - - - /////////////////// - // serialization // - /////////////////// - - /// @name serialization - /// @{ - - /*! - @brief serialize to stream - - Serialize the given JSON value @a j to the output stream @a o. The JSON - value will be serialized using the @ref dump member function. The - indentation of the output can be controlled with the member variable - `width` of the output stream @a o. For instance, using the manipulator - `std::setw(4)` on @a o sets the indentation level to `4` and the - serialization result is the same as calling `dump(4)`. - - @param[in,out] o stream to serialize to - @param[in] j JSON value to serialize - - @return the stream @a o - - @complexity Linear. - - @liveexample{The example below shows the serialization with different - parameters to `width` to adjust the indentation level.,operator_serialize} - - @since version 1.0.0 - */ - friend std::ostream& operator<<(std::ostream& o, const basic_json& j) - { - // read width member and use it as indentation parameter if nonzero - const bool pretty_print = (o.width() > 0); - const auto indentation = (pretty_print ? o.width() : 0); - - // reset width to 0 for subsequent calls to this stream - o.width(0); - - // do the actual serialization - j.dump(o, pretty_print, static_cast(indentation)); - return o; - } - - /*! - @brief serialize to stream - @copydoc operator<<(std::ostream&, const basic_json&) - */ - friend std::ostream& operator>>(const basic_json& j, std::ostream& o) - { - return o << j; - } - - /// @} - - - ///////////////////// - // deserialization // - ///////////////////// - - /// @name deserialization - /// @{ - - /*! - @brief deserialize from string - - @param[in] s string to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__string__parser_callback_t} - - @sa @ref parse(std::istream&, parser_callback_t) for a version that reads - from an input stream - - @since version 1.0.0 - */ - static basic_json parse(const string_t& s, parser_callback_t cb = nullptr) - { - return parser(s, cb).parse(); - } - - /*! - @brief deserialize from stream - - @param[in,out] i stream to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__istream__parser_callback_t} - - @sa @ref parse(const string_t&, parser_callback_t) for a version that - reads from a string - - @since version 1.0.0 - */ - static basic_json parse(std::istream& i, parser_callback_t cb = nullptr) - { - return parser(i, cb).parse(); - } - - /*! - @copydoc parse(std::istream&, parser_callback_t) - */ - static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr) - { - return parser(i, cb).parse(); - } - - /*! - @brief deserialize from stream - - Deserializes an input stream to a JSON value. - - @param[in,out] i input stream to read a serialized JSON value from - @param[in,out] j JSON value to write the deserialized input to - - @throw std::invalid_argument in case of parse errors - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below shows how a JSON value is constructed by - reading a serialization from a stream.,operator_deserialize} - - @sa parse(std::istream&, parser_callback_t) for a variant with a parser - callback function to filter values while parsing - - @since version 1.0.0 - */ - friend std::istream& operator<<(basic_json& j, std::istream& i) - { - j = parser(i).parse(); - return i; - } - - /*! - @brief deserialize from stream - @copydoc operator<<(basic_json&, std::istream&) - */ - friend std::istream& operator>>(std::istream& i, basic_json& j) - { - j = parser(i).parse(); - return i; - } - - /// @} - - - private: - /////////////////////////// - // convenience functions // - /////////////////////////// - - /// return the type as string - string_t type_name() const noexcept - { - switch (m_type) - { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; - default: - return "number"; - } - } - - /*! - @brief calculates the extra space to escape a JSON string - - @param[in] s the string to escape - @return the number of characters required to escape string @a s - - @complexity Linear in the length of string @a s. - */ - static std::size_t extra_space(const string_t& s) noexcept - { - std::size_t result = 0; - - for (const auto& c : s) - { - switch (c) - { - case '"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - { - // from c (1 byte) to \x (2 bytes) - result += 1; - break; - } - - default: - { - if (c >= 0x00 and c <= 0x1f) - { - // from c (1 byte) to \uxxxx (6 bytes) - result += 5; - } - break; - } - } - } - - return result; - } - - /*! - @brief escape a string - - Escape a string by replacing certain special characters by a sequence of - an escape character (backslash) and another character and other control - characters by a sequence of "\u" followed by a four-digit hex - representation. - - @param[in] s the string to escape - @return the escaped string - - @complexity Linear in the length of string @a s. - */ - static string_t escape_string(const string_t& s) - { - const auto space = extra_space(s); - if (space == 0) - { - return s; - } - - // create a result string of necessary size - string_t result(s.size() + space, '\\'); - std::size_t pos = 0; - - for (const auto& c : s) - { - switch (c) - { - // quotation mark (0x22) - case '"': - { - result[pos + 1] = '"'; - pos += 2; - break; - } - - // reverse solidus (0x5c) - case '\\': - { - // nothing to change - pos += 2; - break; - } - - // backspace (0x08) - case '\b': - { - result[pos + 1] = 'b'; - pos += 2; - break; - } - - // formfeed (0x0c) - case '\f': - { - result[pos + 1] = 'f'; - pos += 2; - break; - } - - // newline (0x0a) - case '\n': - { - result[pos + 1] = 'n'; - pos += 2; - break; - } - - // carriage return (0x0d) - case '\r': - { - result[pos + 1] = 'r'; - pos += 2; - break; - } - - // horizontal tab (0x09) - case '\t': - { - result[pos + 1] = 't'; - pos += 2; - break; - } - - default: - { - if (c >= 0x00 and c <= 0x1f) - { - // convert a number 0..15 to its hex representation - // (0..f) - const auto hexify = [](const int v) -> char - { - return (v < 10) - ? ('0' + static_cast(v)) - : ('a' + static_cast((v - 10) & 0x1f)); - }; - - // print character c as \uxxxx - for (const char m : - { 'u', '0', '0', hexify(c >> 4), hexify(c & 0x0f) - }) - { - result[++pos] = m; - } - - ++pos; - } - else - { - // all other characters are added as-is - result[pos++] = c; - } - break; - } - } - } - - return result; - } - - /*! - @brief internal implementation of the serialization function - - This function is called by the public member function dump and organizes - the serialization internally. The indentation level is propagated as - additional parameter. In case of arrays and objects, the function is - called recursively. Note that - - - strings and object keys are escaped using `escape_string()` - - integer numbers are converted implicitly via `operator<<` - - floating-point numbers are converted to a string using `"%g"` format - - @param[out] o stream to write to - @param[in] pretty_print whether the output shall be pretty-printed - @param[in] indent_step the indent level - @param[in] current_indent the current indent level (only used internally) - */ - void dump(std::ostream& o, - const bool pretty_print, - const unsigned int indent_step, - const unsigned int current_indent = 0) const - { - // variable to hold indentation for recursive calls - unsigned int new_indent = current_indent; - - switch (m_type) - { - case value_t::object: - { - assert(m_value.object != nullptr); - - if (m_value.object->empty()) - { - o << "{}"; - return; - } - - o << "{"; - - // increase indentation - if (pretty_print) - { - new_indent += indent_step; - o << "\n"; - } - - for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) - { - if (i != m_value.object->cbegin()) - { - o << (pretty_print ? ",\n" : ","); - } - o << string_t(new_indent, ' ') << "\"" - << escape_string(i->first) << "\":" - << (pretty_print ? " " : ""); - i->second.dump(o, pretty_print, indent_step, new_indent); - } - - // decrease indentation - if (pretty_print) - { - new_indent -= indent_step; - o << "\n"; - } - - o << string_t(new_indent, ' ') + "}"; - return; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - - if (m_value.array->empty()) - { - o << "[]"; - return; - } - - o << "["; - - // increase indentation - if (pretty_print) - { - new_indent += indent_step; - o << "\n"; - } - - for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) - { - if (i != m_value.array->cbegin()) - { - o << (pretty_print ? ",\n" : ","); - } - o << string_t(new_indent, ' '); - i->dump(o, pretty_print, indent_step, new_indent); - } - - // decrease indentation - if (pretty_print) - { - new_indent -= indent_step; - o << "\n"; - } - - o << string_t(new_indent, ' ') << "]"; - return; - } - - case value_t::string: - { - assert(m_value.string != nullptr); - o << string_t("\"") << escape_string(*m_value.string) << "\""; - return; - } - - case value_t::boolean: - { - o << (m_value.boolean ? "true" : "false"); - return; - } - - case value_t::number_integer: - { - o << m_value.number_integer; - return; - } - - case value_t::number_unsigned: - { - o << m_value.number_unsigned; - return; - } - - case value_t::number_float: - { - // check if number was parsed from a string - if (m_type.bits.parsed) - { - // check if parsed number had an exponent given - if (m_type.bits.has_exp) - { - // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1) - char buf[263]; - int len; - - // handle capitalization of the exponent - if (m_type.bits.exp_cap) - { - len = snprintf(buf, sizeof(buf), "%.*E", - m_type.bits.precision, m_value.number_float) + 1; - } - else - { - len = snprintf(buf, sizeof(buf), "%.*e", - m_type.bits.precision, m_value.number_float) + 1; - } - - // remove '+' sign from the exponent if necessary - if (not m_type.bits.exp_plus) - { - if (len > static_cast(sizeof(buf))) - { - len = sizeof(buf); - } - for (int i = 0; i < len; i++) - { - if (buf[i] == '+') - { - for (; i + 1 < len; i++) - { - buf[i] = buf[i + 1]; - } - } - } - } - - o << buf; - } - else - { - // no exponent - output as a decimal - std::stringstream ss; - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems - ss << std::setprecision(m_type.bits.precision) - << std::fixed << m_value.number_float; - o << ss.str(); - } - } - else - { - if (m_value.number_float == 0) - { - // special case for zero to get "0.0"/"-0.0" - o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0"); - } - else - { - // Otherwise 6, 15 or 16 digits of precision allows - // round-trip IEEE 754 string->float->string, - // string->double->string or string->long - // double->string; to be safe, we read this value from - // std::numeric_limits::digits10 - std::stringstream ss; - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems - ss << std::setprecision(std::numeric_limits::digits10) - << m_value.number_float; - o << ss.str(); - } - } - return; - } - - case value_t::discarded: - { - o << ""; - return; - } - - case value_t::null: - { - o << "null"; - return; - } - } - } - - private: - ////////////////////// - // member variables // - ////////////////////// - - /// the type of the current element - type_data_t m_type = value_t::null; - - /// the value of the current element - json_value m_value = {}; - - - private: - /////////////// - // iterators // - /////////////// - - /*! - @brief an iterator for primitive JSON types - - This class models an iterator for primitive JSON types (boolean, number, - string). It's only purpose is to allow the iterator/const_iterator classes - to "iterate" over primitive values. Internally, the iterator is modeled by - a `difference_type` variable. Value begin_value (`0`) models the begin, - end_value (`1`) models past the end. - */ - class primitive_iterator_t - { - public: - /// set iterator to a defined beginning - void set_begin() noexcept - { - m_it = begin_value; - } - - /// set iterator to a defined past the end - void set_end() noexcept - { - m_it = end_value; - } - - /// return whether the iterator can be dereferenced - constexpr bool is_begin() const noexcept - { - return (m_it == begin_value); - } - - /// return whether the iterator is at end - constexpr bool is_end() const noexcept - { - return (m_it == end_value); - } - - /// return reference to the value to change and compare - operator difference_type& () noexcept - { - return m_it; - } - - /// return value to compare - constexpr operator difference_type () const noexcept - { - return m_it; - } - - private: - static constexpr difference_type begin_value = 0; - static constexpr difference_type end_value = begin_value + 1; - - /// iterator as signed integer type - difference_type m_it = std::numeric_limits::denorm_min(); - }; - - /*! - @brief an iterator value - - @note This structure could easily be a union, but MSVC currently does not - allow unions members with complex constructors, see - https://github.com/nlohmann/json/pull/105. - */ - struct internal_iterator - { - /// iterator for JSON objects - typename object_t::iterator object_iterator; - /// iterator for JSON arrays - typename array_t::iterator array_iterator; - /// generic iterator for all other types - primitive_iterator_t primitive_iterator; - - /// create an uninitialized internal_iterator - internal_iterator() noexcept - : object_iterator(), array_iterator(), primitive_iterator() - {} - }; - - /// proxy class for the iterator_wrapper functions - template - class iteration_proxy - { - private: - /// helper class for iteration - class iteration_proxy_internal - { - private: - /// the iterator - IteratorType anchor; - /// an index for arrays (used to create key names) - size_t array_index = 0; - - public: - explicit iteration_proxy_internal(IteratorType it) noexcept - : anchor(it) - {} - - /// dereference operator (needed for range-based for) - iteration_proxy_internal& operator*() - { - return *this; - } - - /// increment operator (needed for range-based for) - iteration_proxy_internal& operator++() - { - ++anchor; - ++array_index; - - return *this; - } - - /// inequality operator (needed for range-based for) - bool operator!= (const iteration_proxy_internal& o) const - { - return anchor != o.anchor; - } - - /// return key of the iterator - typename basic_json::string_t key() const - { - assert(anchor.m_object != nullptr); - - switch (anchor.m_object->type()) - { - // use integer array index as key - case value_t::array: - { - return std::to_string(array_index); - } - - // use key from the object - case value_t::object: - { - return anchor.key(); - } - - // use an empty key for all primitive types - default: - { - return ""; - } - } - } - - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } - }; - - /// the container to iterate - typename IteratorType::reference container; - - public: - /// construct iteration proxy from a container - explicit iteration_proxy(typename IteratorType::reference cont) - : container(cont) - {} - - /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() noexcept - { - return iteration_proxy_internal(container.begin()); - } - - /// return iterator end (needed for range-based for) - iteration_proxy_internal end() noexcept - { - return iteration_proxy_internal(container.end()); - } - }; - - public: - /*! - @brief a const random access iterator for the @ref basic_json class - - This class implements a const iterator for the @ref basic_json class. From - this class, the @ref iterator class is derived. - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - @since version 1.0.0 - */ - class const_iterator : public std::iterator - { - /// allow basic_json to access private members - friend class basic_json; - - public: - /// the type of the values when the iterator is dereferenced - using value_type = typename basic_json::value_type; - /// a type to represent differences between iterators - using difference_type = typename basic_json::difference_type; - /// defines a pointer to the type iterated over (value_type) - using pointer = typename basic_json::const_pointer; - /// defines a reference to the type iterated over (value_type) - using reference = typename basic_json::const_reference; - /// the category of the iterator - using iterator_category = std::bidirectional_iterator_tag; - - /// default constructor - const_iterator() = default; - - /// constructor for a given JSON instance - explicit const_iterator(pointer object) noexcept - : m_object(object) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } - - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } - } - } - - /// copy constructor given a nonconst iterator - explicit const_iterator(const iterator& other) noexcept - : m_object(other.m_object) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = other.m_it.object_iterator; - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } - - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; - } - } - } - - /// copy constructor - const_iterator(const const_iterator& other) noexcept - : m_object(other.m_object), m_it(other.m_it) - {} - - /// copy assignment - const_iterator& operator=(const_iterator other) noexcept( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - std::swap(m_object, other.m_object); - std::swap(m_it, other.m_it); - return *this; - } - - private: - /// set the iterator to the first value - void set_begin() noexcept - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_object->m_value.object != nullptr); - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } - - case basic_json::value_t::array: - { - assert(m_object->m_value.array != nullptr); - m_it.array_iterator = m_object->m_value.array->begin(); - break; - } - - case basic_json::value_t::null: - { - // set to end so begin()==end() is true: null is empty - m_it.primitive_iterator.set_end(); - break; - } - - default: - { - m_it.primitive_iterator.set_begin(); - break; - } - } - } - - /// set the iterator past the last value - void set_end() noexcept - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_object->m_value.object != nullptr); - m_it.object_iterator = m_object->m_value.object->end(); - break; - } - - case basic_json::value_t::array: - { - assert(m_object->m_value.array != nullptr); - m_it.array_iterator = m_object->m_value.array->end(); - break; - } - - default: - { - m_it.primitive_iterator.set_end(); - break; - } - } - } - - public: - /// return a reference to the value pointed to by the iterator - reference operator*() const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_object->m_value.object); - assert(m_it.object_iterator != m_object->m_value.object->end()); - return m_it.object_iterator->second; - } - - case basic_json::value_t::array: - { - assert(m_object->m_value.array); - assert(m_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } - - case basic_json::value_t::null: - { - throw std::out_of_range("cannot get value"); - } - - default: - { - if (m_it.primitive_iterator.is_begin()) - { - return *m_object; - } - else - { - throw std::out_of_range("cannot get value"); - } - } - } - } - - /// dereference the iterator - pointer operator->() const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_object->m_value.object); - assert(m_it.object_iterator != m_object->m_value.object->end()); - return &(m_it.object_iterator->second); - } - - case basic_json::value_t::array: - { - assert(m_object->m_value.array); - assert(m_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; - } - - default: - { - if (m_it.primitive_iterator.is_begin()) - { - return m_object; - } - else - { - throw std::out_of_range("cannot get value"); - } - } - } - } - - /// post-increment (it++) - const_iterator operator++(int) - { - auto result = *this; - ++(*this); - return result; - } - - /// pre-increment (++it) - const_iterator& operator++() - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - ++m_it.object_iterator; - break; - } - - case basic_json::value_t::array: - { - ++m_it.array_iterator; - break; - } - - default: - { - ++m_it.primitive_iterator; - break; - } - } - - return *this; - } - - /// post-decrement (it--) - const_iterator operator--(int) - { - auto result = *this; - --(*this); - return result; - } - - /// pre-decrement (--it) - const_iterator& operator--() - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - --m_it.object_iterator; - break; - } - - case basic_json::value_t::array: - { - --m_it.array_iterator; - break; - } - - default: - { - --m_it.primitive_iterator; - break; - } - } - - return *this; - } - - /// comparison: equal - bool operator==(const const_iterator& other) const - { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { - throw std::domain_error("cannot compare iterators of different containers"); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - return (m_it.object_iterator == other.m_it.object_iterator); - } - - case basic_json::value_t::array: - { - return (m_it.array_iterator == other.m_it.array_iterator); - } - - default: - { - return (m_it.primitive_iterator == other.m_it.primitive_iterator); - } - } - } - - /// comparison: not equal - bool operator!=(const const_iterator& other) const - { - return not operator==(other); - } - - /// comparison: smaller - bool operator<(const const_iterator& other) const - { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { - throw std::domain_error("cannot compare iterators of different containers"); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - throw std::domain_error("cannot compare order of object iterators"); - } - - case basic_json::value_t::array: - { - return (m_it.array_iterator < other.m_it.array_iterator); - } - - default: - { - return (m_it.primitive_iterator < other.m_it.primitive_iterator); - } - } - } - - /// comparison: less than or equal - bool operator<=(const const_iterator& other) const - { - return not other.operator < (*this); - } - - /// comparison: greater than - bool operator>(const const_iterator& other) const - { - return not operator<=(other); - } - - /// comparison: greater than or equal - bool operator>=(const const_iterator& other) const - { - return not operator<(other); - } - - /// add to iterator - const_iterator& operator+=(difference_type i) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - throw std::domain_error("cannot use offsets with object iterators"); - } - - case basic_json::value_t::array: - { - m_it.array_iterator += i; - break; - } - - default: - { - m_it.primitive_iterator += i; - break; - } - } - - return *this; - } - - /// subtract from iterator - const_iterator& operator-=(difference_type i) - { - return operator+=(-i); - } - - /// add to iterator - const_iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - const_iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const const_iterator& other) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - throw std::domain_error("cannot use offsets with object iterators"); - } - - case basic_json::value_t::array: - { - return m_it.array_iterator - other.m_it.array_iterator; - } - - default: - { - return m_it.primitive_iterator - other.m_it.primitive_iterator; - } - } - } - - /// access to successor - reference operator[](difference_type n) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - throw std::domain_error("cannot use operator[] for object iterators"); - } - - case basic_json::value_t::array: - { - return *(m_it.array_iterator + n); - } - - case basic_json::value_t::null: - { - throw std::out_of_range("cannot get value"); - } - - default: - { - if (m_it.primitive_iterator == -n) - { - return *m_object; - } - else - { - throw std::out_of_range("cannot get value"); - } - } - } - } - - /// return the key of an object iterator - typename object_t::key_type key() const - { - assert(m_object != nullptr); - - if (m_object->is_object()) - { - return m_it.object_iterator->first; - } - else - { - throw std::domain_error("cannot use key() for non-object iterators"); - } - } - - /// return the value of an iterator - reference value() const - { - return operator*(); - } - - private: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - internal_iterator m_it = internal_iterator(); - }; - - /*! - @brief a mutable random access iterator for the @ref basic_json class - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element. - - @since version 1.0.0 - */ - class iterator : public const_iterator - { - public: - using base_iterator = const_iterator; - using pointer = typename basic_json::pointer; - using reference = typename basic_json::reference; - - /// default constructor - iterator() = default; - - /// constructor for a given JSON instance - explicit iterator(pointer object) noexcept - : base_iterator(object) - {} - - /// copy constructor - iterator(const iterator& other) noexcept - : base_iterator(other) - {} - - /// copy assignment - iterator& operator=(iterator other) noexcept( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - base_iterator::operator=(other); - return *this; - } - - /// return a reference to the value pointed to by the iterator - reference operator*() const - { - return const_cast(base_iterator::operator*()); - } - - /// dereference the iterator - pointer operator->() const - { - return const_cast(base_iterator::operator->()); - } - - /// post-increment (it++) - iterator operator++(int) - { - iterator result = *this; - base_iterator::operator++(); - return result; - } - - /// pre-increment (++it) - iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - iterator operator--(int) - { - iterator result = *this; - base_iterator::operator--(); - return result; - } - - /// pre-decrement (--it) - iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// subtract from iterator - iterator& operator-=(difference_type i) - { - base_iterator::operator-=(i); - return *this; - } - - /// add to iterator - iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const iterator& other) const - { - return base_iterator::operator-(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return const_cast(base_iterator::operator[](n)); - } - - /// return the value of an iterator - reference value() const - { - return const_cast(base_iterator::value()); - } - }; - - /*! - @brief a template for a reverse iterator class - - @tparam Base the base iterator type to reverse. Valid types are @ref - iterator (to create @ref reverse_iterator) and @ref const_iterator (to - create @ref const_reverse_iterator). - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element (only if @a Base is - @ref iterator). - - @since version 1.0.0 - */ - template - class json_reverse_iterator : public std::reverse_iterator - { - public: - /// shortcut to the reverse iterator adaptor - using base_iterator = std::reverse_iterator; - /// the reference type for the pointed-to element - using reference = typename Base::reference; - - /// create reverse iterator from iterator - json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept - : base_iterator(it) - {} - - /// create reverse iterator from base class - json_reverse_iterator(const base_iterator& it) noexcept - : base_iterator(it) - {} - - /// post-increment (it++) - json_reverse_iterator operator++(int) - { - return base_iterator::operator++(1); - } - - /// pre-increment (++it) - json_reverse_iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - json_reverse_iterator operator--(int) - { - return base_iterator::operator--(1); - } - - /// pre-decrement (--it) - json_reverse_iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - json_reverse_iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// add to iterator - json_reverse_iterator operator+(difference_type i) const - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - json_reverse_iterator operator-(difference_type i) const - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const json_reverse_iterator& other) const - { - return this->base() - other.base(); - } - - /// access to successor - reference operator[](difference_type n) const - { - return *(this->operator+(n)); - } - - /// return the key of an object iterator - typename object_t::key_type key() const - { - auto it = --this->base(); - return it.key(); - } - - /// return the value of an iterator - reference value() const - { - auto it = --this->base(); - return it.operator * (); - } - }; - - - private: - ////////////////////// - // lexer and parser // - ////////////////////// - - /*! - @brief lexical analysis - - This class organizes the lexical analysis during JSON deserialization. The - core of it is a scanner generated by [re2c](http://re2c.org) that - processes a buffer and recognizes tokens according to RFC 7159. - */ - class lexer - { - public: - /// token types for the parser - enum class token_type - { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_number, ///< a number -- use get_number() for actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` - name_separator, ///< the name separator `:` - value_separator, ///< the value separator `,` - parse_error, ///< indicating a parse error - end_of_input ///< indicating the end of the input buffer - }; - - /// the char type to use in the lexer - using lexer_char_t = unsigned char; - - /// constructor with a given buffer - explicit lexer(const string_t& s) noexcept - : m_stream(nullptr), m_buffer(s) - { - m_content = reinterpret_cast(s.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + s.size(); - } - - /// constructor with a given stream - explicit lexer(std::istream* s) noexcept - : m_stream(s), m_buffer() - { - assert(m_stream != nullptr); - getline(*m_stream, m_buffer); - m_content = reinterpret_cast(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + m_buffer.size(); - } - - /// default constructor - lexer() = default; - - // switch off unwanted functions - lexer(const lexer&) = delete; - lexer operator=(const lexer&) = delete; - - /*! - @brief create a string from a Unicode code point - - @param[in] codepoint1 the code point (can be high surrogate) - @param[in] codepoint2 the code point (can be low surrogate or 0) - - @return string representation of the code point - - @throw std::out_of_range if code point is > 0x10ffff; example: `"code - points above 0x10FFFF are invalid"` - @throw std::invalid_argument if the low surrogate is invalid; example: - `""missing or wrong low surrogate""` - - @see - */ - static string_t to_unicode(const std::size_t codepoint1, - const std::size_t codepoint2 = 0) - { - // calculate the codepoint from the given code points - std::size_t codepoint = codepoint1; - - // check if codepoint1 is a high surrogate - if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) - { - // check if codepoint2 is a low surrogate - if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) - { - codepoint = - // high surrogate occupies the most significant 22 bits - (codepoint1 << 10) - // low surrogate occupies the least significant 15 bits - + codepoint2 - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00; - } - else - { - throw std::invalid_argument("missing or wrong low surrogate"); - } - } - - string_t result; - - if (codepoint < 0x80) - { - // 1-byte characters: 0xxxxxxx (ASCII) - result.append(1, static_cast(codepoint)); - } - else if (codepoint <= 0x7ff) - { - // 2-byte characters: 110xxxxx 10xxxxxx - result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0xffff) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); - result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0x10ffff) - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); - result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); - result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else - { - throw std::out_of_range("code points above 0x10FFFF are invalid"); - } - - return result; - } - - /// return name of values of type token_type (only used for errors) - static std::string token_type_name(token_type t) - { - switch (t) - { - case token_type::uninitialized: - return ""; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case token_type::value_number: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return ""; - case token_type::end_of_input: - return "end of input"; - default: - { - // catch non-enum values - return "unknown token"; // LCOV_EXCL_LINE - } - } - } - - /*! - This function implements a scanner for JSON. It is specified using - regular expressions that try to follow RFC 7159 as close as possible. - These regular expressions are then translated into a minimized - deterministic finite automaton (DFA) by the tool - [re2c](http://re2c.org). As a result, the translated code for this - function consists of a large block of code with `goto` jumps. - - @return the class of the next token read from the buffer - */ - token_type scan() noexcept - { - // pointer for backtracking information - m_marker = nullptr; - - // remember the begin of the token - m_start = m_cursor; - assert(m_start != nullptr); - - - { - lexer_char_t yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = - { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 32, 0, 0, 32, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 160, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 0, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - }; - if ((m_limit - m_cursor) < 5) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_6; - } - if (yych <= '\\') - { - if (yych <= '-') - { - if (yych <= '"') - { - if (yych <= 0x00) - { - goto basic_json_parser_2; - } - if (yych <= '!') - { - goto basic_json_parser_4; - } - goto basic_json_parser_9; - } - else - { - if (yych <= '+') - { - goto basic_json_parser_4; - } - if (yych <= ',') - { - goto basic_json_parser_10; - } - goto basic_json_parser_12; - } - } - else - { - if (yych <= '9') - { - if (yych <= '/') - { - goto basic_json_parser_4; - } - if (yych <= '0') - { - goto basic_json_parser_13; - } - goto basic_json_parser_15; - } - else - { - if (yych <= ':') - { - goto basic_json_parser_17; - } - if (yych == '[') - { - goto basic_json_parser_19; - } - goto basic_json_parser_4; - } - } - } - else - { - if (yych <= 't') - { - if (yych <= 'f') - { - if (yych <= ']') - { - goto basic_json_parser_21; - } - if (yych <= 'e') - { - goto basic_json_parser_4; - } - goto basic_json_parser_23; - } - else - { - if (yych == 'n') - { - goto basic_json_parser_24; - } - if (yych <= 's') - { - goto basic_json_parser_4; - } - goto basic_json_parser_25; - } - } - else - { - if (yych <= '|') - { - if (yych == '{') - { - goto basic_json_parser_26; - } - goto basic_json_parser_4; - } - else - { - if (yych <= '}') - { - goto basic_json_parser_28; - } - if (yych == 0xEF) - { - goto basic_json_parser_30; - } - goto basic_json_parser_4; - } - } - } -basic_json_parser_2: - ++m_cursor; - { - return token_type::end_of_input; - } -basic_json_parser_4: - ++m_cursor; -basic_json_parser_5: - { - return token_type::parse_error; - } -basic_json_parser_6: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_6; - } - { - return scan(); - } -basic_json_parser_9: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x0F) - { - goto basic_json_parser_5; - } - goto basic_json_parser_32; -basic_json_parser_10: - ++m_cursor; - { - return token_type::value_separator; - } -basic_json_parser_12: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_5; - } - if (yych <= '0') - { - goto basic_json_parser_13; - } - if (yych <= '9') - { - goto basic_json_parser_15; - } - goto basic_json_parser_5; -basic_json_parser_13: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') - { - goto basic_json_parser_37; - } - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_38; - } - if (yych == 'e') - { - goto basic_json_parser_38; - } - } -basic_json_parser_14: - { - return token_type::value_number; - } -basic_json_parser_15: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yybm[0 + yych] & 64) - { - goto basic_json_parser_15; - } - if (yych <= 'D') - { - if (yych == '.') - { - goto basic_json_parser_37; - } - goto basic_json_parser_14; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_38; - } - if (yych == 'e') - { - goto basic_json_parser_38; - } - goto basic_json_parser_14; - } -basic_json_parser_17: - ++m_cursor; - { - return token_type::name_separator; - } -basic_json_parser_19: - ++m_cursor; - { - return token_type::begin_array; - } -basic_json_parser_21: - ++m_cursor; - { - return token_type::end_array; - } -basic_json_parser_23: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') - { - goto basic_json_parser_39; - } - goto basic_json_parser_5; -basic_json_parser_24: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'u') - { - goto basic_json_parser_40; - } - goto basic_json_parser_5; -basic_json_parser_25: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'r') - { - goto basic_json_parser_41; - } - goto basic_json_parser_5; -basic_json_parser_26: - ++m_cursor; - { - return token_type::begin_object; - } -basic_json_parser_28: - ++m_cursor; - { - return token_type::end_object; - } -basic_json_parser_30: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) - { - goto basic_json_parser_42; - } - goto basic_json_parser_5; -basic_json_parser_31: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; -basic_json_parser_32: - if (yybm[0 + yych] & 128) - { - goto basic_json_parser_31; - } - if (yych <= 0x0F) - { - goto basic_json_parser_33; - } - if (yych <= '"') - { - goto basic_json_parser_34; - } - goto basic_json_parser_36; -basic_json_parser_33: - m_cursor = m_marker; - if (yyaccept == 0) - { - goto basic_json_parser_5; - } - else - { - goto basic_json_parser_14; - } -basic_json_parser_34: - ++m_cursor; - { - return token_type::value_string; - } -basic_json_parser_36: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'e') - { - if (yych <= '/') - { - if (yych == '"') - { - goto basic_json_parser_31; - } - if (yych <= '.') - { - goto basic_json_parser_33; - } - goto basic_json_parser_31; - } - else - { - if (yych <= '\\') - { - if (yych <= '[') - { - goto basic_json_parser_33; - } - goto basic_json_parser_31; - } - else - { - if (yych == 'b') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - } - } - else - { - if (yych <= 'q') - { - if (yych <= 'f') - { - goto basic_json_parser_31; - } - if (yych == 'n') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 's') - { - if (yych <= 'r') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 't') - { - goto basic_json_parser_31; - } - if (yych <= 'u') - { - goto basic_json_parser_43; - } - goto basic_json_parser_33; - } - } - } -basic_json_parser_37: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_44; - } - goto basic_json_parser_33; -basic_json_parser_38: - yych = *++m_cursor; - if (yych <= ',') - { - if (yych == '+') - { - goto basic_json_parser_46; - } - goto basic_json_parser_33; - } - else - { - if (yych <= '-') - { - goto basic_json_parser_46; - } - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_47; - } - goto basic_json_parser_33; - } -basic_json_parser_39: - yych = *++m_cursor; - if (yych == 'l') - { - goto basic_json_parser_49; - } - goto basic_json_parser_33; -basic_json_parser_40: - yych = *++m_cursor; - if (yych == 'l') - { - goto basic_json_parser_50; - } - goto basic_json_parser_33; -basic_json_parser_41: - yych = *++m_cursor; - if (yych == 'u') - { - goto basic_json_parser_51; - } - goto basic_json_parser_33; -basic_json_parser_42: - yych = *++m_cursor; - if (yych == 0xBF) - { - goto basic_json_parser_52; - } - goto basic_json_parser_33; -basic_json_parser_43: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_54; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_54; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych <= 'f') - { - goto basic_json_parser_54; - } - goto basic_json_parser_33; - } -basic_json_parser_44: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'D') - { - if (yych <= '/') - { - goto basic_json_parser_14; - } - if (yych <= '9') - { - goto basic_json_parser_44; - } - goto basic_json_parser_14; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_38; - } - if (yych == 'e') - { - goto basic_json_parser_38; - } - goto basic_json_parser_14; - } -basic_json_parser_46: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } -basic_json_parser_47: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '/') - { - goto basic_json_parser_14; - } - if (yych <= '9') - { - goto basic_json_parser_47; - } - goto basic_json_parser_14; -basic_json_parser_49: - yych = *++m_cursor; - if (yych == 's') - { - goto basic_json_parser_55; - } - goto basic_json_parser_33; -basic_json_parser_50: - yych = *++m_cursor; - if (yych == 'l') - { - goto basic_json_parser_56; - } - goto basic_json_parser_33; -basic_json_parser_51: - yych = *++m_cursor; - if (yych == 'e') - { - goto basic_json_parser_58; - } - goto basic_json_parser_33; -basic_json_parser_52: - ++m_cursor; - { - return scan(); - } -basic_json_parser_54: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_60; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_60; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych <= 'f') - { - goto basic_json_parser_60; - } - goto basic_json_parser_33; - } -basic_json_parser_55: - yych = *++m_cursor; - if (yych == 'e') - { - goto basic_json_parser_61; - } - goto basic_json_parser_33; -basic_json_parser_56: - ++m_cursor; - { - return token_type::literal_null; - } -basic_json_parser_58: - ++m_cursor; - { - return token_type::literal_true; - } -basic_json_parser_60: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_63; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_63; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych <= 'f') - { - goto basic_json_parser_63; - } - goto basic_json_parser_33; - } -basic_json_parser_61: - ++m_cursor; - { - return token_type::literal_false; - } -basic_json_parser_63: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_31; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych <= 'f') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - } - - } - - /// append data from the stream to the internal buffer - void yyfill() noexcept - { - if (m_stream == nullptr or not * m_stream) - { - return; - } - - const auto offset_start = m_start - m_content; - const auto offset_marker = m_marker - m_start; - const auto offset_cursor = m_cursor - m_start; - - m_buffer.erase(0, static_cast(offset_start)); - std::string line; - assert(m_stream != nullptr); - std::getline(*m_stream, line); - m_buffer += "\n" + line; // add line with newline symbol - - m_content = reinterpret_cast(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_content; - m_marker = m_start + offset_marker; - m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; - } - - /// return string representation of last read token - string_t get_token() const - { - assert(m_start != nullptr); - return string_t(reinterpret_cast(m_start), - static_cast(m_cursor - m_start)); - } - - /*! - @brief return string value for string tokens - - The function iterates the characters between the opening and closing - quotes of the string value. The complete string is the range - [m_start,m_cursor). Consequently, we iterate from m_start+1 to - m_cursor-1. - - We differentiate two cases: - - 1. Escaped characters. In this case, a new character is constructed - according to the nature of the escape. Some escapes create new - characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied - as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape - `"\\uxxxx"` need special care. In this case, to_unicode takes care - of the construction of the values. - 2. Unescaped characters are copied as is. - - @return string value of current token without opening and closing - quotes - @throw std::out_of_range if to_unicode fails - */ - string_t get_string() const - { - string_t result; - result.reserve(static_cast(m_cursor - m_start - 2)); - - // iterate the result between the quotes - for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) - { - // process escaped characters - if (*i == '\\') - { - // read next character - ++i; - - switch (*i) - { - // the default escapes - case 't': - { - result += "\t"; - break; - } - case 'b': - { - result += "\b"; - break; - } - case 'f': - { - result += "\f"; - break; - } - case 'n': - { - result += "\n"; - break; - } - case 'r': - { - result += "\r"; - break; - } - case '\\': - { - result += "\\"; - break; - } - case '/': - { - result += "/"; - break; - } - case '"': - { - result += "\""; - break; - } - - // unicode - case 'u': - { - // get code xxxx from uxxxx - auto codepoint = std::strtoul(std::string(reinterpret_cast(i + 1), - 4).c_str(), nullptr, 16); - - // check if codepoint is a high surrogate - if (codepoint >= 0xD800 and codepoint <= 0xDBFF) - { - // make sure there is a subsequent unicode - if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') - { - throw std::invalid_argument("missing low surrogate"); - } - - // get code yyyy from uxxxx\uyyyy - auto codepoint2 = std::strtoul(std::string(reinterpret_cast - (i + 7), 4).c_str(), nullptr, 16); - result += to_unicode(codepoint, codepoint2); - // skip the next 10 characters (xxxx\uyyyy) - i += 10; - } - else - { - // add unicode character(s) - result += to_unicode(codepoint); - // skip the next four characters (xxxx) - i += 4; - } - break; - } - } - } - else - { - // all other characters are just copied to the end of the - // string - result.append(1, static_cast(*i)); - } - } - - return result; - } - - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). - - @param[in] type the @ref number_float_t in use - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - - @bug This function uses `std::strtof`, `std::strtod`, or `std::strtold` - which use the current C locale to determine which character is used as - decimal point character. This may yield to parse errors if the locale - does not used `.`. - */ - long double str_to_float_t(long double* /* type */, char** endptr) const - { - return std::strtold(reinterpret_cast(m_start), endptr); - } - - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). - - @param[in] type the @ref number_float_t in use - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - */ - double str_to_float_t(double* /* type */, char** endptr) const - { - return std::strtod(reinterpret_cast(m_start), endptr); - } - - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). - - @param[in] type the @ref number_float_t in use - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - */ - float str_to_float_t(float* /* type */, char** endptr) const - { - return std::strtof(reinterpret_cast(m_start), endptr); - } - - /*! - @brief return number value for number tokens - - This function translates the last token into the most appropriate - number type (either integer, unsigned integer or floating point), - which is passed back to the caller via the result parameter. - - This function parses the integer component up to the radix point or - exponent while collecting information about the 'floating point - representation', which it stores in the result parameter. If there is - no radix point or exponent, and the number can fit into a @ref - number_integer_t or @ref number_unsigned_t then it sets the result - parameter accordingly. - - The 'floating point representation' includes the number of significant - figures after the radix point, whether the number is in exponential or - decimal form, the capitalization of the exponent marker, and if the - optional '+' is present in the exponent. This information is necessary - to perform accurate round trips of floating point numbers. - - If the number is a floating point number the number is then parsed - using @a std:strtod (or @a std:strtof or @a std::strtold). - - @param[out] result @ref basic_json object to receive the number, or - NAN if the conversion read past the current token. The latter case - needs to be treated by the caller function. - */ - void get_number(basic_json& result) const - { - assert(m_start != nullptr); - - const lexer::lexer_char_t* curptr = m_start; - - // remember this number was parsed (for later serialization) - result.m_type.bits.parsed = true; - - // 'found_radix_point' will be set to 0xFF upon finding a radix - // point and later used to mask in/out the precision depending - // whether a radix is found i.e. 'precision &= found_radix_point' - uint8_t found_radix_point = 0; - uint8_t precision = 0; - - // accumulate the integer conversion result (unsigned for now) - number_unsigned_t value = 0; - - // maximum absolute value of the relevant integer type - number_unsigned_t max; - - // temporarily store the type to avoid unecessary bitfield access - value_t type; - - // look for sign - if (*curptr == '-') - { - type = value_t::number_integer; - max = static_cast((std::numeric_limits::max)()) + 1; - curptr++; - } - else - { - type = value_t::number_unsigned; - max = static_cast((std::numeric_limits::max)()); - } - - // count the significant figures - for (; curptr < m_cursor; curptr++) - { - // quickly skip tests if a digit - if (*curptr < '0' || *curptr > '9') - { - if (*curptr == '.') - { - // don't count '.' but change to float - type = value_t::number_float; - - // reset precision count - precision = 0; - found_radix_point = 0xFF; - continue; - } - // assume exponent (if not then will fail parse): change to - // float, stop counting and record exponent details - type = value_t::number_float; - result.m_type.bits.has_exp = true; - - // exponent capitalization - result.m_type.bits.exp_cap = (*curptr == 'E'); - - // exponent '+' sign - result.m_type.bits.exp_plus = (*(++curptr) == '+'); - break; - } - - // skip if definitely not an integer - if (type != value_t::number_float) - { - // multiply last value by ten and add the new digit - auto temp = value * 10 + *curptr - 0x30; - - // test for overflow - if (temp < value || temp > max) - { - // overflow - type = value_t::number_float; - } - else - { - // no overflow - save it - value = temp; - } - } - ++precision; - } - - // If no radix point was found then precision would now be set to - // the number of digits, which is wrong - clear it. - result.m_type.bits.precision = precision & found_radix_point; - - // save the value (if not a float) - if (type == value_t::number_unsigned) - { - result.m_value.number_unsigned = value; - } - else if (type == value_t::number_integer) - { - result.m_value.number_integer = -static_cast(value); - } - else - { - // parse with strtod - result.m_value.number_float = str_to_float_t(static_cast(nullptr), NULL); - } - - // save the type - result.m_type = type; - } - - private: - /// optional input stream - std::istream* m_stream = nullptr; - /// the buffer - string_t m_buffer; - /// the buffer pointer - const lexer_char_t* m_content = nullptr; - /// pointer to the beginning of the current symbol - const lexer_char_t* m_start = nullptr; - /// pointer for backtracking information - const lexer_char_t* m_marker = nullptr; - /// pointer to the current symbol - const lexer_char_t* m_cursor = nullptr; - /// pointer to the end of the buffer - const lexer_char_t* m_limit = nullptr; - }; - - /*! - @brief syntax analysis - - This class implements a recursive decent parser. - */ - class parser - { - public: - /// constructor for strings - parser(const string_t& s, parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(s) - { - // read first token - get_token(); - } - - /// a parser reading from an input stream - parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(&_is) - { - // read first token - get_token(); - } - - /// public parser interface - basic_json parse() - { - basic_json result = parse_internal(true); - - expect(lexer::token_type::end_of_input); - - // return parser result and replace it with null in case the - // top-level value was discarded by the callback function - return result.is_discarded() ? basic_json() : result; - } - - private: - /// the actual parser - basic_json parse_internal(bool keep) - { - auto result = basic_json(value_t::discarded); - - switch (last_token) - { - case lexer::token_type::begin_object: - { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) - { - // explicitly set result to object to cope with {} - result.m_type = value_t::object; - result.m_value = json_value(value_t::object); - } - - // read next token - get_token(); - - // closing } -> we are done - if (last_token == lexer::token_type::end_object) - { - get_token(); - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - // no comma is expected here - unexpect(lexer::token_type::value_separator); - - // otherwise: parse key-value pairs - do - { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) - { - get_token(); - } - - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); - - bool keep_tag = false; - if (keep) - { - if (callback) - { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); - } - else - { - keep_tag = true; - } - } - - // parse separator (:) - get_token(); - expect(lexer::token_type::name_separator); - - // parse and add value - get_token(); - auto value = parse_internal(keep); - if (keep and keep_tag and not value.is_discarded()) - { - result[key] = std::move(value); - } - } - while (last_token == lexer::token_type::value_separator); - - // closing } - expect(lexer::token_type::end_object); - get_token(); - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } - - return result; - } - - case lexer::token_type::begin_array: - { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) - { - // explicitly set result to object to cope with [] - result.m_type = value_t::array; - result.m_value = json_value(value_t::array); - } - - // read next token - get_token(); - - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) - { - get_token(); - if (callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - // no comma is expected here - unexpect(lexer::token_type::value_separator); - - // otherwise: parse values - do - { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) - { - get_token(); - } - - // parse value - auto value = parse_internal(keep); - if (keep and not value.is_discarded()) - { - result.push_back(std::move(value)); - } - } - while (last_token == lexer::token_type::value_separator); - - // closing ] - expect(lexer::token_type::end_array); - get_token(); - if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } - - return result; - } - - case lexer::token_type::literal_null: - { - get_token(); - result.m_type = value_t::null; - break; - } - - case lexer::token_type::value_string: - { - const auto s = m_lexer.get_string(); - get_token(); - result = basic_json(s); - break; - } - - case lexer::token_type::literal_true: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = true; - break; - } - - case lexer::token_type::literal_false: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = false; - break; - } - - case lexer::token_type::value_number: - { - m_lexer.get_number(result); - get_token(); - break; - } - - default: - { - // the last token was unexpected - unexpect(last_token); - } - } - - if (keep and callback and not callback(depth, parse_event_t::value, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - /// get next token from lexer - typename lexer::token_type get_token() noexcept - { - last_token = m_lexer.scan(); - return last_token; - } - - void expect(typename lexer::token_type t) const - { - if (t != last_token) - { - std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : - lexer::token_type_name(last_token)); - error_msg += "; expected " + lexer::token_type_name(t); - throw std::invalid_argument(error_msg); - } - } - - void unexpect(typename lexer::token_type t) const - { - if (t == last_token) - { - std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : - lexer::token_type_name(last_token)); - throw std::invalid_argument(error_msg); - } - } - - private: - /// current level of recursion - int depth = 0; - /// callback function - parser_callback_t callback; - /// the type of the last read token - typename lexer::token_type last_token = lexer::token_type::uninitialized; - /// the lexer - lexer m_lexer; - }; - - public: - /*! - @brief JSON Pointer - - A JSON pointer defines a string syntax for identifying a specific value - within a JSON document. It can be used with functions `at` and - `operator[]`. Furthermore, JSON pointers are the base for JSON patches. - - @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) - - @since version 2.0.0 - */ - class json_pointer - { - /// allow basic_json to access private members - friend class basic_json; - - public: - /*! - @brief create JSON pointer - - Create a JSON pointer according to the syntax described in - [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). - - @param[in] s string representing the JSON pointer; if omitted, the - empty string is assumed which references the whole JSON - value - - @throw std::domain_error if reference token is nonempty and does not - begin with a slash (`/`); example: `"JSON pointer must be empty or - begin with /"` - @throw std::domain_error if a tilde (`~`) is not followed by `0` - (representing `~`) or `1` (representing `/`); example: `"escape error: - ~ must be followed with 0 or 1"` - - @liveexample{The example shows the construction several valid JSON - pointers as well as the exceptional behavior.,json_pointer} - - @since version 2.0.0 - */ - explicit json_pointer(const std::string& s = "") - : reference_tokens(split(s)) - {} - - /*! - @brief return a string representation of the JSON pointer - - @invariant For each JSON pointer `ptr`, it holds: - @code {.cpp} - ptr == json_pointer(ptr.to_string()); - @endcode - - @return a string representation of the JSON pointer - - @liveexample{The example shows the result of `to_string`., - json_pointer__to_string} - - @since version 2.0.0 - */ - std::string to_string() const noexcept - { - std::string result; - - for (const auto& reference_token : reference_tokens) - { - result += "/" + escape(reference_token); - } - - return result; - } - - /// @copydoc to_string() - operator std::string() const - { - return to_string(); - } - - private: - /// remove and return last reference pointer - std::string pop_back() - { - if (is_root()) - { - throw std::domain_error("JSON pointer has no parent"); - } - - auto last = reference_tokens.back(); - reference_tokens.pop_back(); - return last; - } - - /// return whether pointer points to the root document - bool is_root() const - { - return reference_tokens.empty(); - } - - json_pointer top() const - { - if (is_root()) - { - throw std::domain_error("JSON pointer has no parent"); - } - - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; - } - - /*! - @brief create and return a reference to the pointed to value - */ - reference get_and_create(reference j) const - { - pointer result = &j; - - // in case no reference tokens exist, return a reference to the - // JSON value j which will be overwritten by a primitive value - for (const auto& reference_token : reference_tokens) - { - switch (result->m_type) - { - case value_t::null: - { - if (reference_token == "0") - { - // start a new array if reference token is 0 - result = &result->operator[](0); - } - else - { - // start a new object otherwise - result = &result->operator[](reference_token); - } - break; - } - - case value_t::object: - { - // create an entry in the object - result = &result->operator[](reference_token); - break; - } - - case value_t::array: - { - // create an entry in the array - result = &result->operator[](static_cast(std::stoi(reference_token))); - break; - } - - /* - The following code is only reached if there exists a - reference token _and_ the current value is primitive. In - this case, we have an error situation, because primitive - values may only occur as single value; that is, with an - empty list of reference tokens. - */ - default: - { - throw std::domain_error("invalid value to unflatten"); - } - } - } - - return *result; - } - - /*! - @brief return a reference to the pointed to value - - @param[in] ptr a JSON value - - @return reference to the JSON value pointed to by the JSON pointer - - @complexity Linear in the length of the JSON pointer. - - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number - */ - reference get_unchecked(pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case value_t::array: - { - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - throw std::domain_error("array index must not begin with '0'"); - } - - if (reference_token == "-") - { - // explicityly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); - } - break; - } - - default: - { - throw std::out_of_range("unresolved reference token '" + reference_token + "'"); - } - } - } - - return *ptr; - } - - reference get_checked(pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" always fails the range check - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - throw std::domain_error("array index must not begin with '0'"); - } - - // note: at performs range check - ptr = &ptr->at(static_cast(std::stoi(reference_token))); - break; - } - - default: - { - throw std::out_of_range("unresolved reference token '" + reference_token + "'"); - } - } - } - - return *ptr; - } - - /*! - @brief return a const reference to the pointed to value - - @param[in] ptr a JSON value - - @return const reference to the JSON value pointed to by the JSON - pointer - */ - const_reference get_unchecked(const_pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" cannot be used for const access - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - throw std::domain_error("array index must not begin with '0'"); - } - - // use unchecked array access - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); - break; - } - - default: - { - throw std::out_of_range("unresolved reference token '" + reference_token + "'"); - } - } - } - - return *ptr; - } - - const_reference get_checked(const_pointer ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->m_type) - { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") - { - // "-" always fails the range check - throw std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range"); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and reference_token[0] == '0') - { - throw std::domain_error("array index must not begin with '0'"); - } - - // note: at performs range check - ptr = &ptr->at(static_cast(std::stoi(reference_token))); - break; - } - - default: - { - throw std::out_of_range("unresolved reference token '" + reference_token + "'"); - } - } - } - - return *ptr; - } - - /// split the string input to reference tokens - static std::vector split(std::string reference_string) - { - std::vector result; - - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { - return result; - } - - // check if nonempty reference string begins with slash - if (reference_string[0] != '/') - { - throw std::domain_error("JSON pointer must be empty or begin with '/'"); - } - - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - size_t slash = reference_string.find_first_of("/", 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == string::npos+1 = 0 - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = slash + 1, - // find next slash - slash = reference_string.find_first_of("/", start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); - - // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of("~"); - pos != std::string::npos; - pos = reference_token.find_first_of("~", pos + 1)) - { - assert(reference_token[pos] == '~'); - - // ~ must be followed by 0 or 1 - if (pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1')) - { - throw std::domain_error("escape error: '~' must be followed with '0' or '1'"); - } - } - - // finally, store the reference token - unescape(reference_token); - result.push_back(reference_token); - } - - return result; - } - - private: - /*! - @brief replace all occurrences of a substring by another string - - @param[in,out] s the string to manipulate - @param[in] f the substring to replace with @a t - @param[out] t the string to replace @a f - - @return The string @a s where all occurrences of @a f are replaced - with @a t. - - @pre The search string @a f must not be empty. - - @since version 2.0.0 - */ - static void replace_substring(std::string& s, - const std::string& f, - const std::string& t) - { - assert(not f.empty()); - - for ( - size_t pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t - pos = s.find(f, pos + t.size()) // find next occurrence of f - ); - } - - /// escape tilde and slash - static std::string escape(std::string s) - { - // escape "~"" to "~0" and "/" to "~1" - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } - - /// unescape tilde and slash - static void unescape(std::string& s) - { - // first transform any occurrence of the sequence '~1' to '/' - replace_substring(s, "~1", "/"); - // then transform any occurrence of the sequence '~0' to '~' - replace_substring(s, "~0", "~"); - } - - /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to - - @note Empty objects or arrays are flattened to `null`. - */ - static void flatten(const std::string& reference_string, - const basic_json& value, - basic_json& result) - { - switch (value.m_type) - { - case value_t::array: - { - if (value.m_value.array->empty()) - { - // flatten empty array as null - result[reference_string] = nullptr; - } - else - { - // iterate array and use index as reference string - for (size_t i = 0; i < value.m_value.array->size(); ++i) - { - flatten(reference_string + "/" + std::to_string(i), - value.m_value.array->operator[](i), result); - } - } - break; - } - - case value_t::object: - { - if (value.m_value.object->empty()) - { - // flatten empty object as null - result[reference_string] = nullptr; - } - else - { - // iterate object and use keys as reference string - for (const auto& element : *value.m_value.object) - { - flatten(reference_string + "/" + escape(element.first), - element.second, result); - } - } - break; - } - - default: - { - // add primitive value with its reference string - result[reference_string] = value; - break; - } - } - } - - /*! - @param[in] value flattened JSON - - @return unflattened JSON - */ - static basic_json unflatten(const basic_json& value) - { - if (not value.is_object()) - { - throw std::domain_error("only objects can be unflattened"); - } - - basic_json result; - - // iterate the JSON object values - for (const auto& element : *value.m_value.object) - { - if (not element.second.is_primitive()) - { - throw std::domain_error("values in object must be primitive"); - } - - // assign value to reference pointed to by JSON pointer; Note - // that if the JSON pointer is "" (i.e., points to the whole - // value), function get_and_create returns a reference to - // result itself. An assignment will then create a primitive - // value. - json_pointer(element.first).get_and_create(result) = element.second; - } - - return result; - } - - private: - /// the reference tokens - std::vector reference_tokens {}; - }; - - ////////////////////////// - // JSON Pointer support // - ////////////////////////// - - /// @name JSON Pointer functions - /// @{ - - /*! - @brief access specified element via JSON Pointer - - Uses a JSON pointer to retrieve a reference to the respective JSON value. - No bound checking is performed. Similar to @ref operator[](const typename - object_t::key_type&), `null` values are created in arrays and objects if - necessary. - - In particular: - - If the JSON pointer points to an object key that does not exist, it - is created an filled with a `null` value before a reference to it - is returned. - - If the JSON pointer points to an array index that does not exist, it - is created an filled with a `null` value before a reference to it - is returned. All indices between the current maximum and the given - index are also filled with `null`. - - The special value `-` is treated as a synonym for the index past the - end. - - @param[in] ptr a JSON pointer - - @return reference to the element pointed to by @a ptr - - @complexity Constant. - - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number - - @liveexample{The behavior is shown in the example.,operatorjson_pointer} - - @since version 2.0.0 - */ - reference operator[](const json_pointer& ptr) - { - return ptr.get_unchecked(this); - } - - /*! - @brief access specified element via JSON Pointer - - Uses a JSON pointer to retrieve a reference to the respective JSON value. - No bound checking is performed. The function does not change the JSON - value; no `null` values are created. In particular, the the special value - `-` yields an exception. - - @param[in] ptr JSON pointer to the desired element - - @return const reference to the element pointed to by @a ptr - - @complexity Constant. - - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number - - @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} - - @since version 2.0.0 - */ - const_reference operator[](const json_pointer& ptr) const - { - return ptr.get_unchecked(this); - } - - /*! - @brief access specified element via JSON Pointer - - Returns a reference to the element at with specified JSON pointer @a ptr, - with bounds checking. - - @param[in] ptr JSON pointer to the desired element - - @return reference to the element pointed to by @a ptr - - @complexity Constant. - - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number - - @liveexample{The behavior is shown in the example.,at_json_pointer} - - @since version 2.0.0 - */ - reference at(const json_pointer& ptr) - { - return ptr.get_checked(this); - } - - /*! - @brief access specified element via JSON Pointer - - Returns a const reference to the element at with specified JSON pointer @a - ptr, with bounds checking. - - @param[in] ptr JSON pointer to the desired element - - @return reference to the element pointed to by @a ptr - - @complexity Constant. - - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number - - @liveexample{The behavior is shown in the example.,at_json_pointer_const} - - @since version 2.0.0 - */ - const_reference at(const json_pointer& ptr) const - { - return ptr.get_checked(this); - } - - /*! - @brief return flattened JSON value - - The function creates a JSON object whose keys are JSON pointers (see [RFC - 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all - primitive. The original JSON value can be restored using the @ref - unflatten() function. - - @return an object that maps JSON pointers to primitve values - - @note Empty objects and arrays are flattened to `null` and will not be - reconstructed correctly by the @ref unflatten() function. - - @complexity Linear in the size the JSON value. - - @liveexample{The following code shows how a JSON object is flattened to an - object whose keys consist of JSON pointers.,flatten} - - @sa @ref unflatten() for the reverse function - - @since version 2.0.0 - */ - basic_json flatten() const - { - basic_json result(value_t::object); - json_pointer::flatten("", *this, result); - return result; - } - - /*! - @brief unflatten a previously flattened JSON value - - The function restores the arbitrary nesting of a JSON value that has been - flattened before using the @ref flatten() function. The JSON value must - meet certain constraints: - 1. The value must be an object. - 2. The keys must be JSON pointers (see - [RFC 6901](https://tools.ietf.org/html/rfc6901)) - 3. The mapped values must be primitive JSON types. - - @return the original JSON from a flattened version - - @note Empty objects and arrays are flattened by @ref flatten() to `null` - values and can not unflattened to their original type. Apart from - this example, for a JSON value `j`, the following is always true: - `j == j.flatten().unflatten()`. - - @complexity Linear in the size the JSON value. - - @liveexample{The following code shows how a flattened JSON object is - unflattened into the original nested JSON object.,unflatten} - - @sa @ref flatten() for the reverse function - - @since version 2.0.0 - */ - basic_json unflatten() const - { - return json_pointer::unflatten(*this); - } - - /// @} - - ////////////////////////// - // JSON Patch functions // - ////////////////////////// - - /// @name JSON Patch functions - /// @{ - - /*! - @brief applies a JSON patch - - [JSON Patch](http://jsonpatch.com) defines a JSON document structure for - expressing a sequence of operations to apply to a JSON) document. With - this funcion, a JSON Patch is applied to the current JSON value by - executing all operations from the patch. - - @param[in] json_patch JSON patch document - @return patched document - - @note The application of a patch is atomic: Either all operations succeed - and the patched document is returned or an exception is thrown. In - any case, the original value is not changed: the patch is applied - to a copy of the value. - - @throw std::out_of_range if a JSON pointer inside the patch could not - be resolved successfully in the current JSON value; example: `"key baz - not found"` - @throw invalid_argument if the JSON patch is malformed (e.g., mandatory - attributes are missing); example: `"operation add must have member path"` - - @complexity Linear in the size of the JSON value and the length of the - JSON patch. As usually only a fraction of the JSON value is affected by - the patch, the complexity can usually be neglected. - - @liveexample{The following code shows how a JSON patch is applied to a - value.,patch} - - @sa @ref diff -- create a JSON patch by comparing two JSON values - - @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) - @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) - - @since version 2.0.0 - */ - basic_json patch(const basic_json& json_patch) const - { - // make a working copy to apply the patch to - basic_json result = *this; - - // the valid JSON Patch operations - enum class patch_operations {add, remove, replace, move, copy, test, invalid}; - - const auto get_op = [](const std::string op) - { - if (op == "add") - { - return patch_operations::add; - } - if (op == "remove") - { - return patch_operations::remove; - } - if (op == "replace") - { - return patch_operations::replace; - } - if (op == "move") - { - return patch_operations::move; - } - if (op == "copy") - { - return patch_operations::copy; - } - if (op == "test") - { - return patch_operations::test; - } - - return patch_operations::invalid; - }; - - // wrapper for "add" operation; add value at ptr - const auto operation_add = [&result](json_pointer & ptr, basic_json val) - { - // adding to the root of the target document means replacing it - if (ptr.is_root()) - { - result = val; - } - else - { - // make sure the top element of the pointer exists - json_pointer top_pointer = ptr.top(); - if (top_pointer != ptr) - { - basic_json& x = result.at(top_pointer); - } - - // get reference to parent of JSON pointer ptr - const auto last_path = ptr.pop_back(); - basic_json& parent = result[ptr]; - - switch (parent.m_type) - { - case value_t::null: - case value_t::object: - { - // use operator[] to add value - parent[last_path] = val; - break; - } - - case value_t::array: - { - if (last_path == "-") - { - // special case: append to back - parent.push_back(val); - } - else - { - const auto idx = std::stoi(last_path); - if (static_cast(idx) > parent.size()) - { - // avoid undefined behavior - throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); - } - else - { - // default case: insert add offset - parent.insert(parent.begin() + static_cast(idx), val); - } - } - break; - } - - default: - { - // if there exists a parent it cannot be primitive - assert(false); // LCOV_EXCL_LINE - } - } - } - }; - - // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [&result](json_pointer & ptr) - { - // get reference to parent of JSON pointer ptr - const auto last_path = ptr.pop_back(); - basic_json& parent = result.at(ptr); - - // remove child - if (parent.is_object()) - { - // perform range check - auto it = parent.find(last_path); - if (it != parent.end()) - { - parent.erase(it); - } - else - { - throw std::out_of_range("key '" + last_path + "' not found"); - } - } - else if (parent.is_array()) - { - // note erase performs range check - parent.erase(static_cast(std::stoi(last_path))); - } - }; - - // type check - if (not json_patch.is_array()) - { - // a JSON patch must be an array of objects - throw std::invalid_argument("JSON patch must be an array of objects"); - } - - // iterate and apply th eoperations - for (const auto& val : json_patch) - { - // wrapper to get a value for an operation - const auto get_value = [&val](const std::string & op, - const std::string & member, - bool string_type) -> basic_json& - { - // find value - auto it = val.m_value.object->find(member); - - // context-sensitive error message - const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; - - // check if desired value is present - if (it == val.m_value.object->end()) - { - throw std::invalid_argument(error_msg + " must have member '" + member + "'"); - } - - // check if result is of type string - if (string_type and not it->second.is_string()) - { - throw std::invalid_argument(error_msg + " must have string member '" + member + "'"); - } - - // no error: return value - return it->second; - }; - - // type check - if (not val.is_object()) - { - throw std::invalid_argument("JSON patch must be an array of objects"); - } - - // collect mandatory members - const std::string op = get_value("op", "op", true); - const std::string path = get_value(op, "path", true); - json_pointer ptr(path); - - switch (get_op(op)) - { - case patch_operations::add: - { - operation_add(ptr, get_value("add", "value", false)); - break; - } - - case patch_operations::remove: - { - operation_remove(ptr); - break; - } - - case patch_operations::replace: - { - // the "path" location must exist - use at() - result.at(ptr) = get_value("replace", "value", false); - break; - } - - case patch_operations::move: - { - const std::string from_path = get_value("move", "from", true); - json_pointer from_ptr(from_path); - - // the "from" location must exist - use at() - basic_json v = result.at(from_ptr); - - // The move operation is functionally identical to a - // "remove" operation on the "from" location, followed - // immediately by an "add" operation at the target - // location with the value that was just removed. - operation_remove(from_ptr); - operation_add(ptr, v); - break; - } - - case patch_operations::copy: - { - const std::string from_path = get_value("copy", "from", true);; - const json_pointer from_ptr(from_path); - - // the "from" location must exist - use at() - result[ptr] = result.at(from_ptr); - break; - } - - case patch_operations::test: - { - bool success = false; - try - { - // check if "value" matches the one at "path" - // the "path" location must exist - use at() - success = (result.at(ptr) == get_value("test", "value", false)); - } - catch (std::out_of_range&) - { - // ignore out of range errors: success remains false - } - - // throw an exception if test fails - if (not success) - { - throw std::domain_error("unsuccessful: " + val.dump()); - } - - break; - } - - case patch_operations::invalid: - { - // op must be "add", "remove", "replace", "move", "copy", or - // "test" - throw std::invalid_argument("operation value '" + op + "' is invalid"); - } - } - } - - return result; - } - - /*! - @brief creates a diff as a JSON patch - - Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can - be changed into the value @a target by calling @ref patch function. - - @invariant For two JSON values @a source and @a target, the following code - yields always `true`: - @code {.cpp} - source.patch(diff(source, target)) == target; - @endcode - - @note Currently, only `remove`, `add`, and `replace` operations are - generated. - - @param[in] source JSON value to copare from - @param[in] target JSON value to copare against - @param[in] path helper value to create JSON pointers - - @return a JSON patch to convert the @a source to @a target - - @complexity Linear in the lengths of @a source and @a target. - - @liveexample{The following code shows how a JSON patch is created as a - diff for two JSON values.,diff} - - @sa @ref patch -- apply a JSON patch - - @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) - - @since version 2.0.0 - */ - static basic_json diff(const basic_json& source, - const basic_json& target, - std::string path = "") - { - // the patch - basic_json result(value_t::array); - - // if the values are the same, return empty patch - if (source == target) - { - return result; - } - - if (source.type() != target.type()) - { - // different types: replace value - result.push_back( - { - {"op", "replace"}, - {"path", path}, - {"value", target} - }); - } - else - { - switch (source.type()) - { - case value_t::array: - { - // first pass: traverse common elements - size_t i = 0; - while (i < source.size() and i < target.size()) - { - // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); - result.insert(result.end(), temp_diff.begin(), temp_diff.end()); - ++i; - } - - // i now reached the end of at least one array - // in a second pass, traverse the remaining elements - - // remove my remaining elements - while (i < source.size()) - { - result.push_back(object( - { - {"op", "remove"}, - {"path", path + "/" + std::to_string(i)} - })); - ++i; - } - - // add other remaining elements - while (i < target.size()) - { - result.push_back( - { - {"op", "add"}, - {"path", path + "/" + std::to_string(i)}, - {"value", target[i]} - }); - ++i; - } - - break; - } - - case value_t::object: - { - // first pass: traverse this object's elements - for (auto it = source.begin(); it != source.end(); ++it) - { - // escape the key name to be used in a JSON patch - const auto key = json_pointer::escape(it.key()); - - if (target.find(it.key()) != target.end()) - { - // recursive call to compare object values at key it - auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); - result.insert(result.end(), temp_diff.begin(), temp_diff.end()); - } - else - { - // found a key that is not in o -> remove it - result.push_back(object( - { - {"op", "remove"}, - {"path", path + "/" + key} - })); - } - } - - // second pass: traverse other object's elements - for (auto it = target.begin(); it != target.end(); ++it) - { - if (source.find(it.key()) == source.end()) - { - // found a key that is not in this -> add it - const auto key = json_pointer::escape(it.key()); - result.push_back( - { - {"op", "add"}, - {"path", path + "/" + key}, - {"value", it.value()} - }); - } - } - - break; - } - - default: - { - // both primitive type: replace value - result.push_back( - { - {"op", "replace"}, - {"path", path}, - {"value", target} - }); - break; - } - } - } - - return result; - } - - /// @} -}; - - -///////////// -// presets // -///////////// - -/*! -@brief default JSON class - -This type is the default specialization of the @ref basic_json class which -uses the standard template types. - -@since version 1.0.0 -*/ -using json = basic_json<>; -} - - -/////////////////////// -// nonmember support // -/////////////////////// - -// specialization of std::swap, and std::hash -namespace std -{ -/*! -@brief exchanges the values of two JSON objects - -@since version 1.0.0 -*/ -template <> -inline void swap(nlohmann::json& j1, - nlohmann::json& j2) noexcept( - is_nothrow_move_constructible::value and - is_nothrow_move_assignable::value - ) -{ - j1.swap(j2); -} - -/// hash value for JSON objects -template <> -struct hash -{ - /*! - @brief return a hash value for a JSON object - - @since version 1.0.0 - */ - std::size_t operator()(const nlohmann::json& j) const - { - // a naive hashing via the string representation - const auto& h = hash(); - return h(j.dump()); - } -}; -} - -/*! -@brief user-defined string literal for JSON values - -This operator implements a user-defined string literal for JSON objects. It -can be used by adding \p "_json" to a string literal and returns a JSON object -if no parse error occurred. - -@param[in] s a string representation of a JSON object -@return a JSON object - -@since version 1.0.0 -*/ -inline nlohmann::json operator "" _json(const char* s, std::size_t) -{ - return nlohmann::json::parse(reinterpret_cast(s)); -} - -/*! -@brief user-defined string literal for JSON pointer - -@since version 2.0.0 -*/ -inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t) -{ - return nlohmann::json::json_pointer(s); -} - -// restore GCC/clang diagnostic settings -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic pop -#endif - -#endif diff --git a/src/main.cpp b/src/main.cpp index f93bfba..23c4176 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,7 +17,7 @@ #include "tclap/CmdLine.h" #include "simpleini/SimpleIni.h" -#include "pushhandler.h" +#include "pushnotifier-sdk-cpp/PushNotifier.h" #define CONFIG_FILE "/etc/pusher.conf" @@ -56,14 +56,13 @@ int main(int argc, char **argv) TCLAP::CmdLine cmd("Push notifications to your phone easily.", ' ', "0.3"); // Values - TCLAP::ValueArg idArg("i","id","ID of the device.",false,0,"number"); + TCLAP::ValueArg idArg("i","id","ID of the device.",false,"0","string"); cmd.add(idArg); // Switches TCLAP::SwitchArg tokenSwitch("t", "token", "Request your token.", cmd, false); TCLAP::SwitchArg listSwitch("l", "list", "List all your devices.", cmd, false); TCLAP::SwitchArg pipeSwitch("p", "pipe", "Input via pipe.", cmd, false); - TCLAP::SwitchArg verifySwitch("v","verify","Checks if token is still valid.", cmd, false); // add unlabeled argument @@ -76,8 +75,7 @@ int main(int argc, char **argv) // Variables - string message; - int id; + string message, id; CSimpleIniA iniReader; iniReader.SetUnicode(); @@ -85,7 +83,8 @@ int main(int argc, char **argv) // Request token if(tokenSwitch.getValue()) { - string username, password, token; + string username, password; + PushNotifier::AppToken token; // Read username cout << "Username: "; @@ -103,13 +102,13 @@ int main(int argc, char **argv) cout << endl; // pusher instance - PushHandler buf(username); - token = buf.login(password); + PushNotifier buf; + token = buf.login(username, password); // Build config iniReader.SetValue("pusher", "username", username.c_str()); - iniReader.SetValue("pusher", "appToken", token.c_str()); + iniReader.SetValue("pusher", "appToken", token.token.c_str()); // Check if file is writable if(iniReader.SaveFile(CONFIG_FILE) < 0) @@ -135,7 +134,7 @@ int main(int argc, char **argv) // Check if reading of config is possible if(iniReader.LoadFile(CONFIG_FILE) < 0) { - throw PusherError("You need to login first."); + throw runtime_error("You need to login first."); } string username = iniReader.GetValue("pusher", "username", ""); @@ -143,37 +142,20 @@ int main(int argc, char **argv) if(username.empty() || appToken.empty()) { - throw PusherError("You need to login first."); + throw runtime_error("You need to login first."); } // Loading values - PushHandler pusherInstance(username, appToken); - - - - // Verify token - if(verifySwitch.getValue()) - { - if(pusherInstance.verifyToken()) - { - cout << "appToken is valid" << endl; - return 0; - } - else - { - cout << "appToken is invalid" << endl; - return 1; - } - } + PushNotifier pusherInstance(username, appToken, 0); // List devices if(listSwitch.getValue()) { - vector devices; + vector devices; devices = pusherInstance.getDevices(); unsigned int titleLength = 5; @@ -209,7 +191,7 @@ int main(int argc, char **argv) // Device id - if(idArg.getValue() != 0) + if(sizeof(idArg) != 0) { id = idArg.getValue(); } @@ -242,7 +224,7 @@ int main(int argc, char **argv) stringstream stringID; stringID << id; - pusherInstance.sendToDevice(stringID.str(), message); + pusherInstance.sendMessage(stringID.str(), message); } @@ -257,14 +239,6 @@ int main(int argc, char **argv) } - // errors thrown by pushhandler - catch(PusherError& e) - { - cout << "Error: " << e.what() << endl; - return 1; - } - - // other errors catch(exception& e) { diff --git a/src/pushhandler.cpp b/src/pushhandler.cpp deleted file mode 100644 index fe45e74..0000000 --- a/src/pushhandler.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include "pushhandler.h" -#include "json/json.hpp" -#include "curlhandler.h" - -#include -#include - - -using namespace std; -using json = nlohmann::json; - - -// constructor only username -PushHandler::PushHandler(string username) -{ - this->username = username; -} - - -// constructor with appToken -PushHandler::PushHandler(string username, string appToken) -{ - this->username = username; - this->appToken = appToken; -} - - -// login -string PushHandler::login(string password) -{ - // build request data - stringstream requestData; - requestData << "apiToken=" << API_TOKEN; - requestData << "&username=" << this->username; - requestData << "&password=" << password; - - // network request - string readBuffer; - readBuffer = CurlHandler::request(requestData.str(), URL_PN_LOGIN); - - // json parsing - stringstream jsonData; - jsonData << readBuffer; - - json j; - j << jsonData; - - if(j["status"].get() != "ok") - { - throw PusherError("wrong credentials"); - } - - this->appToken = j["appToken"].get(); - return this->appToken; -} - - -// get list of devices -vector PushHandler::getDevices() -{ - // build request data - stringstream requestData; - requestData << "apiToken=" << API_TOKEN; - requestData << "&appToken=" << this->appToken; - - // network request - string readBuffer; - readBuffer =CurlHandler::request(requestData.str(), URL_PN_GET_DEVICES); - - // json parsing - stringstream jsonData; - jsonData << readBuffer; - - json j; - j << jsonData; - - // handle the codes - switch(j["code"].get()) - { - case 1: throw PusherError("Invalid API Token"); - break; - case 2: throw PusherError("App Token missing"); - break; - case 3: throw PusherError("App Token invalid"); - } - - vector buffer; - - for(auto element : j["devices"]) - { - Device buf; - - buf.title = element["title"].get(); - buf.id = to_string(element["id"].get()); - buf.model = element["model"].get(); - - buffer.push_back(buf); - } - - return buffer; -} - - -// verify token -bool PushHandler::verifyToken() -{ - // build request data - stringstream requestData; - requestData << "apiToken=" << API_TOKEN; - requestData << "&username=" << this->username; - requestData << "&appToken=" << this->appToken; - - // network request - string readBuffer; - readBuffer = CurlHandler::request(requestData.str(), URL_PN_CHECK_TOKEN); - - // json parser - stringstream jsonData; - jsonData << readBuffer; - - json j; - j << jsonData; - - switch(j["code"].get()) - { - case 0: return true; - break; - case 1: throw PusherError("Invalid API-Token"); - break; - case 2: return false; - default: throw PusherError("Invalid server response"); - } -} - - -// send to device -void PushHandler::sendToDevice(string id, string message) -{ - // analyze content-type - // ...... - - - // actual sending - // build request data - stringstream requestData; - requestData << "apiToken=" << API_TOKEN; - requestData << "&appToken=" << this->appToken; - requestData << "&app=" << APP_PACKAGE; - requestData << "&deviceID=" << id; - requestData << "&type=" << "MESSAGE"; - requestData << "&content=" << CurlHandler::urlDecode(message); - - // network request - string readBuffer; - readBuffer = CurlHandler::request(requestData.str(), URL_PN_SEND_TO_DEVICE); - - // json parsing - stringstream jsonData; - jsonData << readBuffer; - - json j; - j << jsonData; - - switch(j["code"].get()) - { - case 0: //return 0; - break; - case 1: throw PusherError("Invalid API Token"); - break; - case 2: throw PusherError("App Token missing"); - break; - case 3: throw PusherError("App Token invalid"); - break; - case 4: throw PusherError("Package Name missing"); - break; - case 5: throw PusherError("Package Name invalid"); - break; - case 6: throw PusherError("Package Name is not linked with the provided API Token"); - break; - case 7: throw PusherError("Device ID missing"); - break; - case 8: throw PusherError("Device ID invalid"); - break; - case 9: throw PusherError("Type missing or invalid"); - break; - default: throw PusherError("Invalid server response"); - } -} diff --git a/src/pushhandler.h b/src/pushhandler.h deleted file mode 100644 index b59ecfb..0000000 --- a/src/pushhandler.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef H_PUSHHANDLER -#define H_PUSHHANDLER - -#include -#include - -// Change this line if you are using your own API-Token -const char API_TOKEN[] = { - 0x38, 0x45, 0x37, 0x44, 0x38, 0x42, 0x32, - 0x44, 0x44, 0x45, 0x37, 0x44, 0x44, 0x45, - 0x37, 0x44, 0x36, 0x43, 0x33, 0x56, 0x35, - 0x32, 0x56, 0x42, 0x35, 0x32, 0x56, 0x42, - 0x44, 0x34, 0x44, 0x44, 0x45, 0x54, 0x42, - 0x54, 0x54, 0x54, 0x4b, 0x46, 0x46, 0x42 }; - -#define APP_PACKAGE "com.hackherz.pusher" - -#define URL_PN_LOGIN "http://a.pushnotifier.de/1/login/" -#define URL_PN_CHECK_TOKEN "http://a.pushnotifier.de/1/checkToken/" -#define URL_PN_GET_DEVICES "http://a.pushnotifier.de/1/getDevices/" -#define URL_PN_SEND_TO_DEVICE "http://a.pushnotifier.de/1/sendToDevice/" - - - -class PushHandler -{ -public: - PushHandler(std::string username); - PushHandler(std::string username, std::string appToken); - - - typedef struct - { - std::string title; - std::string id; - std::string model; - } Device; - - - std::string login(std::string password); - std::vector getDevices(); - void sendToDevice(std::string deviceID, std::string message); - bool verifyToken(); - - -private: - std::string username; - std::string appToken; -}; - - -// class for exceptions -class PusherError -{ -public: - PusherError(std::string content) { this->content = content; } - std::string what() { return this->content; } -private: - std::string content; -}; - - -#endif - diff --git a/src/pushnotifier-sdk-cpp b/src/pushnotifier-sdk-cpp new file mode 160000 index 0000000..1503203 --- /dev/null +++ b/src/pushnotifier-sdk-cpp @@ -0,0 +1 @@ +Subproject commit 15032038ebc9df984f650181c176b1694aa3f33e diff --git a/src/simpleini b/src/simpleini new file mode 160000 index 0000000..2af65fc --- /dev/null +++ b/src/simpleini @@ -0,0 +1 @@ +Subproject commit 2af65fcc504f8242752755e836709762ef7ce062 diff --git a/src/simpleini/ConvertUTF.c b/src/simpleini/ConvertUTF.c deleted file mode 100644 index 9b3deeb..0000000 --- a/src/simpleini/ConvertUTF.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright 2001-2004 Unicode, Inc. - * - * Disclaimer - * - * This source code is provided as is by Unicode, Inc. No claims are - * made as to fitness for any particular purpose. No warranties of any - * kind are expressed or implied. The recipient agrees to determine - * applicability of information provided. If this file has been - * purchased on magnetic or optical media from Unicode, Inc., the - * sole remedy for any claim will be exchange of defective media - * within 90 days of receipt. - * - * Limitations on Rights to Redistribute This Code - * - * Unicode, Inc. hereby grants the right to freely use the information - * supplied in this file in the creation of products supporting the - * Unicode Standard, and to make copies of this file in any form - * for internal or external distribution as long as this notice - * remains attached. - */ - -/* --------------------------------------------------------------------- - - Conversions between UTF32, UTF-16, and UTF-8. Source code file. - Author: Mark E. Davis, 1994. - Rev History: Rick McGowan, fixes & updates May 2001. - Sept 2001: fixed const & error conditions per - mods suggested by S. Parent & A. Lillich. - June 2002: Tim Dodd added detection and handling of incomplete - source sequences, enhanced error detection, added casts - to eliminate compiler warnings. - July 2003: slight mods to back out aggressive FFFE detection. - Jan 2004: updated switches in from-UTF8 conversions. - Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. - - See the header file "ConvertUTF.h" for complete documentation. - ------------------------------------------------------------------------- */ - - -#include "ConvertUTF.h" -#ifdef CVTUTF_DEBUG -#include -#endif - -static const int halfShift = 10; /* used for shifting by 10 bits */ - -static const UTF32 halfBase = 0x0010000UL; -static const UTF32 halfMask = 0x3FFUL; - -#define UNI_SUR_HIGH_START (UTF32)0xD800 -#define UNI_SUR_HIGH_END (UTF32)0xDBFF -#define UNI_SUR_LOW_START (UTF32)0xDC00 -#define UNI_SUR_LOW_END (UTF32)0xDFFF -#define false 0 -#define true 1 - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF32toUTF16 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF32* source = *sourceStart; - UTF16* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch; - if (target >= targetEnd) { - result = targetExhausted; break; - } - ch = *source++; - if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ - /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { - if (flags == strictConversion) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - *target++ = (UTF16)ch; /* normal case */ - } - } else if (ch > UNI_MAX_LEGAL_UTF32) { - if (flags == strictConversion) { - result = sourceIllegal; - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - /* target is a character in range 0xFFFF - 0x10FFFF. */ - if (target + 1 >= targetEnd) { - --source; /* Back up source pointer! */ - result = targetExhausted; break; - } - ch -= halfBase; - *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); - *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); - } - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF16toUTF32 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF16* source = *sourceStart; - UTF32* target = *targetStart; - UTF32 ch, ch2; - while (source < sourceEnd) { - const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ - ch = *source++; - /* If we have a surrogate pair, convert to UTF32 first. */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { - /* If the 16 bits following the high surrogate are in the source buffer... */ - if (source < sourceEnd) { - ch2 = *source; - /* If it's a low surrogate, convert to UTF32. */ - if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { - ch = ((ch - UNI_SUR_HIGH_START) << halfShift) - + (ch2 - UNI_SUR_LOW_START) + halfBase; - ++source; - } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } else { /* We don't have the 16 bits following the high surrogate. */ - --source; /* return to the high surrogate */ - result = sourceExhausted; - break; - } - } else if (flags == strictConversion) { - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } - if (target >= targetEnd) { - source = oldSource; /* Back up source pointer! */ - result = targetExhausted; break; - } - *target++ = ch; - } - *sourceStart = source; - *targetStart = target; -#ifdef CVTUTF_DEBUG -if (result == sourceIllegal) { - fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); - fflush(stderr); -} -#endif - return result; -} - -/* --------------------------------------------------------------------- */ - -/* - * Index into the table below with the first byte of a UTF-8 sequence to - * get the number of trailing bytes that are supposed to follow it. - * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is - * left as-is for anyone who may want to do such conversion, which was - * allowed in earlier algorithms. - */ -static const char trailingBytesForUTF8[256] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 -}; - -/* - * Magic values subtracted from a buffer value during UTF8 conversion. - * This table contains as many values as there might be trailing bytes - * in a UTF-8 sequence. - */ -static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, - 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; - -/* - * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed - * into the first byte, depending on how many bytes follow. There are - * as many entries in this table as there are UTF-8 sequence types. - * (I.e., one byte sequence, two byte... etc.). Remember that sequencs - * for *legal* UTF-8 will be 4 or fewer bytes total. - */ -static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - -/* --------------------------------------------------------------------- */ - -/* The interface converts a whole buffer to avoid function-call overhead. - * Constants have been gathered. Loops & conditionals have been removed as - * much as possible for efficiency, in favor of drop-through switches. - * (See "Note A" at the bottom of the file for equivalent code.) - * If your compiler supports it, the "isLegalUTF8" call can be turned - * into an inline function. - */ - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF16toUTF8 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF16* source = *sourceStart; - UTF8* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch; - unsigned short bytesToWrite = 0; - const UTF32 byteMask = 0xBF; - const UTF32 byteMark = 0x80; - const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ - ch = *source++; - /* If we have a surrogate pair, convert to UTF32 first. */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { - /* If the 16 bits following the high surrogate are in the source buffer... */ - if (source < sourceEnd) { - UTF32 ch2 = *source; - /* If it's a low surrogate, convert to UTF32. */ - if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { - ch = ((ch - UNI_SUR_HIGH_START) << halfShift) - + (ch2 - UNI_SUR_LOW_START) + halfBase; - ++source; - } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } else { /* We don't have the 16 bits following the high surrogate. */ - --source; /* return to the high surrogate */ - result = sourceExhausted; - break; - } - } else if (flags == strictConversion) { - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } - /* Figure out how many bytes the result will require */ - if (ch < (UTF32)0x80) { bytesToWrite = 1; - } else if (ch < (UTF32)0x800) { bytesToWrite = 2; - } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; - } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; - } else { bytesToWrite = 3; - ch = UNI_REPLACEMENT_CHAR; - } - - target += bytesToWrite; - if (target > targetEnd) { - source = oldSource; /* Back up source pointer! */ - target -= bytesToWrite; result = targetExhausted; break; - } - switch (bytesToWrite) { /* note: everything falls through. */ - case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); - } - target += bytesToWrite; - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -/* - * Utility routine to tell whether a sequence of bytes is legal UTF-8. - * This must be called with the length pre-determined by the first byte. - * If not calling this from ConvertUTF8to*, then the length can be set by: - * length = trailingBytesForUTF8[*source]+1; - * and the sequence is illegal right away if there aren't that many bytes - * available. - * If presented with a length > 4, this returns false. The Unicode - * definition of UTF-8 goes up to 4-byte sequences. - */ - -static Boolean isLegalUTF8(const UTF8 *source, int length) { - UTF8 a; - const UTF8 *srcptr = source+length; - switch (length) { - default: return false; - /* Everything else falls through when "true"... */ - case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 2: if ((a = (*--srcptr)) > 0xBF) return false; - - switch (*source) { - /* no fall-through in this inner switch */ - case 0xE0: if (a < 0xA0) return false; break; - case 0xED: if (a > 0x9F) return false; break; - case 0xF0: if (a < 0x90) return false; break; - case 0xF4: if (a > 0x8F) return false; break; - default: if (a < 0x80) return false; - } - - case 1: if (*source >= 0x80 && *source < 0xC2) return false; - } - if (*source > 0xF4) return false; - return true; -} - -/* --------------------------------------------------------------------- */ - -/* - * Exported function to return whether a UTF-8 sequence is legal or not. - * This is not used here; it's just exported. - */ -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { - int length = trailingBytesForUTF8[*source]+1; - if (source+length > sourceEnd) { - return false; - } - return isLegalUTF8(source, length); -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF8toUTF16 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF8* source = *sourceStart; - UTF16* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch = 0; - unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; - if (source + extraBytesToRead >= sourceEnd) { - result = sourceExhausted; break; - } - /* Do this check whether lenient or strict */ - if (! isLegalUTF8(source, extraBytesToRead+1)) { - result = sourceIllegal; - break; - } - /* - * The cases all fall through. See "Note A" below. - */ - switch (extraBytesToRead) { - case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ - case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ - case 3: ch += *source++; ch <<= 6; - case 2: ch += *source++; ch <<= 6; - case 1: ch += *source++; ch <<= 6; - case 0: ch += *source++; - } - ch -= offsetsFromUTF8[extraBytesToRead]; - - if (target >= targetEnd) { - source -= (extraBytesToRead+1); /* Back up source pointer! */ - result = targetExhausted; break; - } - if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { - if (flags == strictConversion) { - source -= (extraBytesToRead+1); /* return to the illegal value itself */ - result = sourceIllegal; - break; - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - *target++ = (UTF16)ch; /* normal case */ - } - } else if (ch > UNI_MAX_UTF16) { - if (flags == strictConversion) { - result = sourceIllegal; - source -= (extraBytesToRead+1); /* return to the start */ - break; /* Bail out; shouldn't continue */ - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - /* target is a character in range 0xFFFF - 0x10FFFF. */ - if (target + 1 >= targetEnd) { - source -= (extraBytesToRead+1); /* Back up source pointer! */ - result = targetExhausted; break; - } - ch -= halfBase; - *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); - *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); - } - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF32toUTF8 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF32* source = *sourceStart; - UTF8* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch; - unsigned short bytesToWrite = 0; - const UTF32 byteMask = 0xBF; - const UTF32 byteMark = 0x80; - ch = *source++; - if (flags == strictConversion ) { - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } - /* - * Figure out how many bytes the result will require. Turn any - * illegally large UTF32 things (> Plane 17) into replacement chars. - */ - if (ch < (UTF32)0x80) { bytesToWrite = 1; - } else if (ch < (UTF32)0x800) { bytesToWrite = 2; - } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; - } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; - } else { bytesToWrite = 3; - ch = UNI_REPLACEMENT_CHAR; - result = sourceIllegal; - } - - target += bytesToWrite; - if (target > targetEnd) { - --source; /* Back up source pointer! */ - target -= bytesToWrite; result = targetExhausted; break; - } - switch (bytesToWrite) { /* note: everything falls through. */ - case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); - } - target += bytesToWrite; - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF8toUTF32 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF8* source = *sourceStart; - UTF32* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch = 0; - unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; - if (source + extraBytesToRead >= sourceEnd) { - result = sourceExhausted; break; - } - /* Do this check whether lenient or strict */ - if (! isLegalUTF8(source, extraBytesToRead+1)) { - result = sourceIllegal; - break; - } - /* - * The cases all fall through. See "Note A" below. - */ - switch (extraBytesToRead) { - case 5: ch += *source++; ch <<= 6; - case 4: ch += *source++; ch <<= 6; - case 3: ch += *source++; ch <<= 6; - case 2: ch += *source++; ch <<= 6; - case 1: ch += *source++; ch <<= 6; - case 0: ch += *source++; - } - ch -= offsetsFromUTF8[extraBytesToRead]; - - if (target >= targetEnd) { - source -= (extraBytesToRead+1); /* Back up the source pointer! */ - result = targetExhausted; break; - } - if (ch <= UNI_MAX_LEGAL_UTF32) { - /* - * UTF-16 surrogate values are illegal in UTF-32, and anything - * over Plane 17 (> 0x10FFFF) is illegal. - */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { - if (flags == strictConversion) { - source -= (extraBytesToRead+1); /* return to the illegal value itself */ - result = sourceIllegal; - break; - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - *target++ = ch; - } - } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ - result = sourceIllegal; - *target++ = UNI_REPLACEMENT_CHAR; - } - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- - - Note A. - The fall-through switches in UTF-8 reading code save a - temp variable, some decrements & conditionals. The switches - are equivalent to the following loop: - { - int tmpBytesToRead = extraBytesToRead+1; - do { - ch += *source++; - --tmpBytesToRead; - if (tmpBytesToRead) ch <<= 6; - } while (tmpBytesToRead > 0); - } - In UTF-8 writing code, the switches on "bytesToWrite" are - similarly unrolled loops. - - --------------------------------------------------------------------- */ diff --git a/src/simpleini/ConvertUTF.h b/src/simpleini/ConvertUTF.h deleted file mode 100644 index 14d7b70..0000000 --- a/src/simpleini/ConvertUTF.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2001-2004 Unicode, Inc. - * - * Disclaimer - * - * This source code is provided as is by Unicode, Inc. No claims are - * made as to fitness for any particular purpose. No warranties of any - * kind are expressed or implied. The recipient agrees to determine - * applicability of information provided. If this file has been - * purchased on magnetic or optical media from Unicode, Inc., the - * sole remedy for any claim will be exchange of defective media - * within 90 days of receipt. - * - * Limitations on Rights to Redistribute This Code - * - * Unicode, Inc. hereby grants the right to freely use the information - * supplied in this file in the creation of products supporting the - * Unicode Standard, and to make copies of this file in any form - * for internal or external distribution as long as this notice - * remains attached. - */ - -/* --------------------------------------------------------------------- - - Conversions between UTF32, UTF-16, and UTF-8. Header file. - - Several funtions are included here, forming a complete set of - conversions between the three formats. UTF-7 is not included - here, but is handled in a separate source file. - - Each of these routines takes pointers to input buffers and output - buffers. The input buffers are const. - - Each routine converts the text between *sourceStart and sourceEnd, - putting the result into the buffer between *targetStart and - targetEnd. Note: the end pointers are *after* the last item: e.g. - *(sourceEnd - 1) is the last item. - - The return result indicates whether the conversion was successful, - and if not, whether the problem was in the source or target buffers. - (Only the first encountered problem is indicated.) - - After the conversion, *sourceStart and *targetStart are both - updated to point to the end of last text successfully converted in - the respective buffers. - - Input parameters: - sourceStart - pointer to a pointer to the source buffer. - The contents of this are modified on return so that - it points at the next thing to be converted. - targetStart - similarly, pointer to pointer to the target buffer. - sourceEnd, targetEnd - respectively pointers to the ends of the - two buffers, for overflow checking only. - - These conversion functions take a ConversionFlags argument. When this - flag is set to strict, both irregular sequences and isolated surrogates - will cause an error. When the flag is set to lenient, both irregular - sequences and isolated surrogates are converted. - - Whether the flag is strict or lenient, all illegal sequences will cause - an error return. This includes sequences such as: , , - or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code - must check for illegal sequences. - - When the flag is set to lenient, characters over 0x10FFFF are converted - to the replacement character; otherwise (when the flag is set to strict) - they constitute an error. - - Output parameters: - The value "sourceIllegal" is returned from some routines if the input - sequence is malformed. When "sourceIllegal" is returned, the source - value will point to the illegal value that caused the problem. E.g., - in UTF-8 when a sequence is malformed, it points to the start of the - malformed sequence. - - Author: Mark E. Davis, 1994. - Rev History: Rick McGowan, fixes & updates May 2001. - Fixes & updates, Sept 2001. - ------------------------------------------------------------------------- */ - -/* --------------------------------------------------------------------- - The following 4 definitions are compiler-specific. - The C standard does not guarantee that wchar_t has at least - 16 bits, so wchar_t is no less portable than unsigned short! - All should be unsigned values to avoid sign extension during - bit mask & shift operations. ------------------------------------------------------------------------- */ - -typedef unsigned int UTF32; /* at least 32 bits */ -typedef unsigned short UTF16; /* at least 16 bits */ -typedef unsigned char UTF8; /* typically 8 bits */ -typedef unsigned char Boolean; /* 0 or 1 */ - -/* Some fundamental constants */ -#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD -#define UNI_MAX_BMP (UTF32)0x0000FFFF -#define UNI_MAX_UTF16 (UTF32)0x0010FFFF -#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF -#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF - -typedef enum { - conversionOK, /* conversion successful */ - sourceExhausted, /* partial character in source, but hit end */ - targetExhausted, /* insuff. room in target for conversion */ - sourceIllegal /* source sequence is illegal/malformed */ -} ConversionResult; - -typedef enum { - strictConversion = 0, - lenientConversion -} ConversionFlags; - -/* This is for C++ and does no harm in C */ -#ifdef __cplusplus -extern "C" { -#endif - -ConversionResult ConvertUTF8toUTF16 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF16toUTF8 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF8toUTF32 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF32toUTF8 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF16toUTF32 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF32toUTF16 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); - -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); - -#ifdef __cplusplus -} -#endif - -/* --------------------------------------------------------------------- */ diff --git a/src/simpleini/SimpleIni.h b/src/simpleini/SimpleIni.h deleted file mode 100644 index 2807a8f..0000000 --- a/src/simpleini/SimpleIni.h +++ /dev/null @@ -1,3435 +0,0 @@ -/** @mainpage - - -
Library SimpleIni -
File SimpleIni.h -
Author Brodie Thiesfield [code at jellycan dot com] -
Source https://github.com/brofield/simpleini -
Version 4.17 -
- - Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation. - - @section intro INTRODUCTION - - This component allows an INI-style configuration file to be used on both - Windows and Linux/Unix. It is fast, simple and source code using this - component will compile unchanged on either OS. - - - @section features FEATURES - - - MIT Licence allows free use in all software (including GPL and commercial) - - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix) - - loading and saving of INI-style configuration files - - configuration files can have any newline format on all platforms - - liberal acceptance of file format - - key/values with no section - - removal of whitespace around sections, keys and values - - support for multi-line values (values with embedded newline characters) - - optional support for multiple keys with the same name - - optional case-insensitive sections and keys (for ASCII characters only) - - saves files with sections and keys in the same order as they were loaded - - preserves comments on the file, section and keys where possible. - - supports both char or wchar_t programming interfaces - - supports both MBCS (system locale) and UTF-8 file encodings - - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file - - support for non-ASCII characters in section, keys, values and comments - - support for non-standard character types or file encodings - via user-written converter classes - - support for adding/modifying values programmatically - - compiles cleanly in the following compilers: - - Windows/VC6 (warning level 3) - - Windows/VC.NET 2003 (warning level 4) - - Windows/VC 2005 (warning level 4) - - Linux/gcc (-Wall) - - - @section usage USAGE SUMMARY - - -# Define the appropriate symbol for the converter you wish to use and - include the SimpleIni.h header file. If no specific converter is defined - then the default converter is used. The default conversion mode uses - SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other - platforms. If you are using ICU then SI_CONVERT_ICU is supported on all - platforms. - -# Declare an instance the appropriate class. Note that the following - definitions are just shortcuts for commonly used types. Other types - (PRUnichar, unsigned short, unsigned char) are also possible. - -
Interface Case-sensitive Load UTF-8 Load MBCS Typedef -
SI_CONVERT_GENERIC -
char No Yes Yes #1 CSimpleIniA -
char Yes Yes Yes CSimpleIniCaseA -
wchar_t No Yes Yes CSimpleIniW -
wchar_t Yes Yes Yes CSimpleIniCaseW -
SI_CONVERT_WIN32 -
char No No #2 Yes CSimpleIniA -
char Yes Yes Yes CSimpleIniCaseA -
wchar_t No Yes Yes CSimpleIniW -
wchar_t Yes Yes Yes CSimpleIniCaseW -
SI_CONVERT_ICU -
char No Yes Yes CSimpleIniA -
char Yes Yes Yes CSimpleIniCaseA -
UChar No Yes Yes CSimpleIniW -
UChar Yes Yes Yes CSimpleIniCaseW -
- #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.
- #2 Only affects Windows. On Windows this uses MBCS functions and - so may fold case incorrectly leading to uncertain results. - -# Call LoadData() or LoadFile() to load and parse the INI configuration file - -# Access and modify the data of the file using the following functions - -
GetAllSections Return all section names -
GetAllKeys Return all key names within a section -
GetAllValues Return all values within a section & key -
GetSection Return all key names and values in a section -
GetSectionSize Return the number of keys in a section -
GetValue Return a value for a section & key -
SetValue Add or update a value for a section & key -
Delete Remove a section, or a key from a section -
- -# Call Save() or SaveFile() to save the INI configuration data - - @section iostreams IO STREAMS - - SimpleIni supports reading from and writing to STL IO streams. Enable this - by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header - file. Ensure that if the streams are backed by a file (e.g. ifstream or - ofstream) then the flag ios_base::binary has been used when the file was - opened. - - @section multiline MULTI-LINE VALUES - - Values that span multiple lines are created using the following format. - -
-        key = <<
-
-    Note the following:
-    - The text used for ENDTAG can be anything and is used to find
-      where the multi-line text ends.
-    - The newline after ENDTAG in the start tag, and the newline
-      before ENDTAG in the end tag is not included in the data value.
-    - The ending tag must be on it's own line with no whitespace before
-      or after it.
-    - The multi-line value is modified at load so that each line in the value
-      is delimited by a single '\\n' character on all platforms. At save time
-      it will be converted into the newline format used by the current
-      platform.
-
-    @section comments COMMENTS
-
-    Comments are preserved in the file within the following restrictions:
-    - Every file may have a single "file comment". It must start with the
-      first character in the file, and will end with the first non-comment
-      line in the file.
-    - Every section may have a single "section comment". It will start
-      with the first comment line following the file comment, or the last
-      data entry. It ends at the beginning of the section.
-    - Every key may have a single "key comment". This comment will start
-      with the first comment line following the section start, or the file
-      comment if there is no section name.
-    - Comments are set at the time that the file, section or key is first
-      created. The only way to modify a comment on a section or a key is to
-      delete that entry and recreate it with the new comment. There is no
-      way to change the file comment.
-
-    @section save SAVE ORDER
-
-    The sections and keys are written out in the same order as they were
-    read in from the file. Sections and keys added to the data after the
-    file has been loaded will be added to the end of the file when it is
-    written. There is no way to specify the location of a section or key
-    other than in first-created, first-saved order.
-
-    @section notes NOTES
-
-    - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
-      Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
-    - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
-    - When using SI_CONVERT_ICU, ICU header files must be on the include
-      path and icuuc.lib must be linked in.
-    - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
-      you should use SI_CONVERT_GENERIC.
-    - The collation (sorting) order used for sections and keys returned from
-      iterators is NOT DEFINED. If collation order of the text is important
-      then it should be done yourself by either supplying a replacement
-      SI_STRLESS class, or by sorting the strings external to this library.
-    - Usage of the  header on Windows can be disabled by defining
-      SI_NO_MBCS. This is defined automatically on Windows CE platforms.
-
-    @section contrib CONTRIBUTIONS
-    
-    - 2010/05/03: Tobias Gehrig: added GetDoubleValue()
-
-    @section licence MIT LICENCE
-
-    The licence text below is the boilerplate "MIT Licence" used from:
-    http://www.opensource.org/licenses/mit-license.php
-
-    Copyright (c) 2006-2012, Brodie Thiesfield
-
-    Permission is hereby granted, free of charge, to any person obtaining a copy
-    of this software and associated documentation files (the "Software"), to deal
-    in the Software without restriction, including without limitation the rights
-    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-    copies of the Software, and to permit persons to whom the Software is furnished
-    to do so, subject to the following conditions:
-
-    The above copyright notice and this permission notice shall be included in
-    all copies or substantial portions of the Software.
-
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-    FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-    COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#ifndef INCLUDED_SimpleIni_h
-#define INCLUDED_SimpleIni_h
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1020)
-# pragma once
-#endif
-
-// Disable these warnings in MSVC:
-//  4127 "conditional expression is constant" as the conversion classes trigger
-//  it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
-//  be optimized away in a release build.
-//  4503 'insert' : decorated name length exceeded, name was truncated
-//  4702 "unreachable code" as the MS STL header causes it in release mode.
-//  Again, the code causing the warning will be cleaned up by the compiler.
-//  4786 "identifier truncated to 256 characters" as this is thrown hundreds
-//  of times VC6 as soon as STL is used.
-#ifdef _MSC_VER
-# pragma warning (push)
-# pragma warning (disable: 4127 4503 4702 4786)
-#endif
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-#ifdef SI_SUPPORT_IOSTREAMS
-# include 
-#endif // SI_SUPPORT_IOSTREAMS
-
-#ifdef _DEBUG
-# ifndef assert
-#  include 
-# endif
-# define SI_ASSERT(x)   assert(x)
-#else
-# define SI_ASSERT(x)
-#endif
-
-enum SI_Error {
-    SI_OK       =  0,   //!< No error
-    SI_UPDATED  =  1,   //!< An existing value was updated
-    SI_INSERTED =  2,   //!< A new value was inserted
-
-    // note: test for any error with (retval < 0)
-    SI_FAIL     = -1,   //!< Generic failure
-    SI_NOMEM    = -2,   //!< Out of memory error
-    SI_FILE     = -3    //!< File error (see errno for detail error)
-};
-
-#define SI_UTF8_SIGNATURE     "\xEF\xBB\xBF"
-
-#ifdef _WIN32
-# define SI_NEWLINE_A   "\r\n"
-# define SI_NEWLINE_W   L"\r\n"
-#else // !_WIN32
-# define SI_NEWLINE_A   "\n"
-# define SI_NEWLINE_W   L"\n"
-#endif // _WIN32
-
-#if defined(SI_CONVERT_ICU)
-# include 
-#endif
-
-#if defined(_WIN32)
-# define SI_HAS_WIDE_FILE
-# define SI_WCHAR_T     wchar_t
-#elif defined(SI_CONVERT_ICU)
-# define SI_HAS_WIDE_FILE
-# define SI_WCHAR_T     UChar
-#endif
-
-
-// ---------------------------------------------------------------------------
-//                              MAIN TEMPLATE CLASS
-// ---------------------------------------------------------------------------
-
-/** Simple INI file reader.
-
-    This can be instantiated with the choice of unicode or native characterset,
-    and case sensitive or insensitive comparisons of section and key names.
-    The supported combinations are pre-defined with the following typedefs:
-
-    
-        
Interface Case-sensitive Typedef -
char No CSimpleIniA -
char Yes CSimpleIniCaseA -
wchar_t No CSimpleIniW -
wchar_t Yes CSimpleIniCaseW -
- - Note that using other types for the SI_CHAR is supported. For instance, - unsigned char, unsigned short, etc. Note that where the alternative type - is a different size to char/wchar_t you may need to supply new helper - classes for SI_STRLESS and SI_CONVERTER. - */ -template -class CSimpleIniTempl -{ -public: - typedef SI_CHAR SI_CHAR_T; - - /** key entry */ - struct Entry { - const SI_CHAR * pItem; - const SI_CHAR * pComment; - int nOrder; - - Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0) - : pItem(a_pszItem) - , pComment(NULL) - , nOrder(a_nOrder) - { } - Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder) - : pItem(a_pszItem) - , pComment(a_pszComment) - , nOrder(a_nOrder) - { } - Entry(const Entry & rhs) { operator=(rhs); } - Entry & operator=(const Entry & rhs) { - pItem = rhs.pItem; - pComment = rhs.pComment; - nOrder = rhs.nOrder; - return *this; - } - -#if defined(_MSC_VER) && _MSC_VER <= 1200 - /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ - bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); } - bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); } -#endif - - /** Strict less ordering by name of key only */ - struct KeyOrder : std::binary_function { - bool operator()(const Entry & lhs, const Entry & rhs) const { - const static SI_STRLESS isLess = SI_STRLESS(); - return isLess(lhs.pItem, rhs.pItem); - } - }; - - /** Strict less ordering by order, and then name of key */ - struct LoadOrder : std::binary_function { - bool operator()(const Entry & lhs, const Entry & rhs) const { - if (lhs.nOrder != rhs.nOrder) { - return lhs.nOrder < rhs.nOrder; - } - return KeyOrder()(lhs.pItem, rhs.pItem); - } - }; - }; - - /** map keys to values */ - typedef std::multimap TKeyVal; - - /** map sections to key/value map */ - typedef std::map TSection; - - /** set of dependent string pointers. Note that these pointers are - dependent on memory owned by CSimpleIni. - */ - typedef std::list TNamesDepend; - - /** interface definition for the OutputWriter object to pass to Save() - in order to output the INI file data. - */ - class OutputWriter { - public: - OutputWriter() { } - virtual ~OutputWriter() { } - virtual void Write(const char * a_pBuf) = 0; - private: - OutputWriter(const OutputWriter &); // disable - OutputWriter & operator=(const OutputWriter &); // disable - }; - - /** OutputWriter class to write the INI data to a file */ - class FileWriter : public OutputWriter { - FILE * m_file; - public: - FileWriter(FILE * a_file) : m_file(a_file) { } - void Write(const char * a_pBuf) { - fputs(a_pBuf, m_file); - } - private: - FileWriter(const FileWriter &); // disable - FileWriter & operator=(const FileWriter &); // disable - }; - - /** OutputWriter class to write the INI data to a string */ - class StringWriter : public OutputWriter { - std::string & m_string; - public: - StringWriter(std::string & a_string) : m_string(a_string) { } - void Write(const char * a_pBuf) { - m_string.append(a_pBuf); - } - private: - StringWriter(const StringWriter &); // disable - StringWriter & operator=(const StringWriter &); // disable - }; - -#ifdef SI_SUPPORT_IOSTREAMS - /** OutputWriter class to write the INI data to an ostream */ - class StreamWriter : public OutputWriter { - std::ostream & m_ostream; - public: - StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { } - void Write(const char * a_pBuf) { - m_ostream << a_pBuf; - } - private: - StreamWriter(const StreamWriter &); // disable - StreamWriter & operator=(const StreamWriter &); // disable - }; -#endif // SI_SUPPORT_IOSTREAMS - - /** Characterset conversion utility class to convert strings to the - same format as is used for the storage. - */ - class Converter : private SI_CONVERTER { - public: - Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) { - m_scratch.resize(1024); - } - Converter(const Converter & rhs) { operator=(rhs); } - Converter & operator=(const Converter & rhs) { - m_scratch = rhs.m_scratch; - return *this; - } - bool ConvertToStore(const SI_CHAR * a_pszString) { - size_t uLen = SI_CONVERTER::SizeToStore(a_pszString); - if (uLen == (size_t)(-1)) { - return false; - } - while (uLen > m_scratch.size()) { - m_scratch.resize(m_scratch.size() * 2); - } - return SI_CONVERTER::ConvertToStore( - a_pszString, - const_cast(m_scratch.data()), - m_scratch.size()); - } - const char * Data() { return m_scratch.data(); } - private: - std::string m_scratch; - }; - -public: - /*-----------------------------------------------------------------------*/ - - /** Default constructor. - - @param a_bIsUtf8 See the method SetUnicode() for details. - @param a_bMultiKey See the method SetMultiKey() for details. - @param a_bMultiLine See the method SetMultiLine() for details. - */ - CSimpleIniTempl( - bool a_bIsUtf8 = false, - bool a_bMultiKey = false, - bool a_bMultiLine = false - ); - - /** Destructor */ - ~CSimpleIniTempl(); - - /** Deallocate all memory stored by this object */ - void Reset(); - - /** Has any data been loaded */ - bool IsEmpty() const { return m_data.empty(); } - - /*-----------------------------------------------------------------------*/ - /** @{ @name Settings */ - - /** Set the storage format of the INI data. This affects both the loading - and saving of the INI data using all of the Load/Save API functions. - This value cannot be changed after any INI data has been loaded. - - If the file is not set to Unicode (UTF-8), then the data encoding is - assumed to be the OS native encoding. This encoding is the system - locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP. - If the storage format is set to Unicode then the file will be loaded - as UTF-8 encoded data regardless of the native file encoding. If - SI_CHAR == char then all of the char* parameters take and return UTF-8 - encoded data regardless of the system locale. - - \param a_bIsUtf8 Assume UTF-8 encoding for the source? - */ - void SetUnicode(bool a_bIsUtf8 = true) { - if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8; - } - - /** Get the storage format of the INI data. */ - bool IsUnicode() const { return m_bStoreIsUtf8; } - - /** Should multiple identical keys be permitted in the file. If set to false - then the last value encountered will be used as the value of the key. - If set to true, then all values will be available to be queried. For - example, with the following input: - -
-        [section]
-        test=value1
-        test=value2
-        
- - Then with SetMultiKey(true), both of the values "value1" and "value2" - will be returned for the key test. If SetMultiKey(false) is used, then - the value for "test" will only be "value2". This value may be changed - at any time. - - \param a_bAllowMultiKey Allow multi-keys in the source? - */ - void SetMultiKey(bool a_bAllowMultiKey = true) { - m_bAllowMultiKey = a_bAllowMultiKey; - } - - /** Get the storage format of the INI data. */ - bool IsMultiKey() const { return m_bAllowMultiKey; } - - /** Should data values be permitted to span multiple lines in the file. If - set to false then the multi-line construct << - SI_CHAR FORMAT - char same format as when loaded (MBCS or UTF-8) - wchar_t UTF-8 - other UTF-8 - - - Note that comments from the original data is preserved as per the - documentation on comments. The order of the sections and values - from the original file will be preserved. - - Any data prepended or appended to the output device must use the the - same format (MBCS or UTF-8). You may use the GetConverter() method to - convert text to the correct format regardless of the output format - being used by SimpleIni. - - To add a BOM to UTF-8 data, write it out manually at the very beginning - like is done in SaveFile when a_bUseBOM is true. - - @param a_oOutput Output writer to write the data to. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in - UTF-8 format. If it is not UTF-8 then this value is - ignored. Do not set this to true if anything has - already been written to the OutputWriter. - - @return SI_Error See error definitions - */ - SI_Error Save( - OutputWriter & a_oOutput, - bool a_bAddSignature = false - ) const; - -#ifdef SI_SUPPORT_IOSTREAMS - /** Save the INI data to an ostream. See Save() for details. - - @param a_ostream String to have the INI data appended to. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in - UTF-8 format. If it is not UTF-8 then this value is - ignored. Do not set this to true if anything has - already been written to the stream. - - @return SI_Error See error definitions - */ - SI_Error Save( - std::ostream & a_ostream, - bool a_bAddSignature = false - ) const - { - StreamWriter writer(a_ostream); - return Save(writer, a_bAddSignature); - } -#endif // SI_SUPPORT_IOSTREAMS - - /** Append the INI data to a string. See Save() for details. - - @param a_sBuffer String to have the INI data appended to. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in - UTF-8 format. If it is not UTF-8 then this value is - ignored. Do not set this to true if anything has - already been written to the string. - - @return SI_Error See error definitions - */ - SI_Error Save( - std::string & a_sBuffer, - bool a_bAddSignature = false - ) const - { - StringWriter writer(a_sBuffer); - return Save(writer, a_bAddSignature); - } - - /*-----------------------------------------------------------------------*/ - /** @} - @{ @name Accessing INI Data */ - - /** Retrieve all section names. The list is returned as an STL vector of - names and can be iterated or searched as necessary. Note that the - sort order of the returned strings is NOT DEFINED. You can sort - the names into the load order if desired. Search this file for ".sort" - for an example. - - NOTE! This structure contains only pointers to strings. The actual - string data is stored in memory owned by CSimpleIni. Ensure that the - CSimpleIni object is not destroyed or Reset() while these pointers - are in use! - - @param a_names Vector that will receive all of the section - names. See note above! - */ - void GetAllSections( - TNamesDepend & a_names - ) const; - - /** Retrieve all unique key names in a section. The sort order of the - returned strings is NOT DEFINED. You can sort the names into the load - order if desired. Search this file for ".sort" for an example. Only - unique key names are returned. - - NOTE! This structure contains only pointers to strings. The actual - string data is stored in memory owned by CSimpleIni. Ensure that the - CSimpleIni object is not destroyed or Reset() while these strings - are in use! - - @param a_pSection Section to request data for - @param a_names List that will receive all of the key - names. See note above! - - @return true Section was found. - @return false Matching section was not found. - */ - bool GetAllKeys( - const SI_CHAR * a_pSection, - TNamesDepend & a_names - ) const; - - /** Retrieve all values for a specific key. This method can be used when - multiple keys are both enabled and disabled. Note that the sort order - of the returned strings is NOT DEFINED. You can sort the names into - the load order if desired. Search this file for ".sort" for an example. - - NOTE! The returned values are pointers to string data stored in memory - owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed - or Reset while you are using this pointer! - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_values List to return if the key is not found - - @return true Key was found. - @return false Matching section/key was not found. - */ - bool GetAllValues( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - TNamesDepend & a_values - ) const; - - /** Query the number of keys in a specific section. Note that if multiple - keys are enabled, then this value may be different to the number of - keys returned by GetAllKeys. - - @param a_pSection Section to request data for - - @return -1 Section does not exist in the file - @return >=0 Number of keys in the section - */ - int GetSectionSize( - const SI_CHAR * a_pSection - ) const; - - /** Retrieve all key and value pairs for a section. The data is returned - as a pointer to an STL map and can be iterated or searched as - desired. Note that multiple entries for the same key may exist when - multiple keys have been enabled. - - NOTE! This structure contains only pointers to strings. The actual - string data is stored in memory owned by CSimpleIni. Ensure that the - CSimpleIni object is not destroyed or Reset() while these strings - are in use! - - @param a_pSection Name of the section to return - @return boolean Was a section matching the supplied - name found. - */ - const TKeyVal * GetSection( - const SI_CHAR * a_pSection - ) const; - - /** Retrieve the value for a specific key. If multiple keys are enabled - (see SetMultiKey) then only the first value associated with that key - will be returned, see GetAllValues for getting all values with multikey. - - NOTE! The returned value is a pointer to string data stored in memory - owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed - or Reset while you are using this pointer! - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_pDefault Value to return if the key is not found - @param a_pHasMultiple Optionally receive notification of if there are - multiple entries for this key. - - @return a_pDefault Key was not found in the section - @return other Value of the key - */ - const SI_CHAR * GetValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pDefault = NULL, - bool * a_pHasMultiple = NULL - ) const; - - /** Retrieve a numeric value for a specific key. If multiple keys are enabled - (see SetMultiKey) then only the first value associated with that key - will be returned, see GetAllValues for getting all values with multikey. - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_nDefault Value to return if the key is not found - @param a_pHasMultiple Optionally receive notification of if there are - multiple entries for this key. - - @return a_nDefault Key was not found in the section - @return other Value of the key - */ - long GetLongValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - long a_nDefault = 0, - bool * a_pHasMultiple = NULL - ) const; - - /** Retrieve a numeric value for a specific key. If multiple keys are enabled - (see SetMultiKey) then only the first value associated with that key - will be returned, see GetAllValues for getting all values with multikey. - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_nDefault Value to return if the key is not found - @param a_pHasMultiple Optionally receive notification of if there are - multiple entries for this key. - - @return a_nDefault Key was not found in the section - @return other Value of the key - */ - double GetDoubleValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - double a_nDefault = 0, - bool * a_pHasMultiple = NULL - ) const; - - /** Retrieve a boolean value for a specific key. If multiple keys are enabled - (see SetMultiKey) then only the first value associated with that key - will be returned, see GetAllValues for getting all values with multikey. - - Strings starting with "t", "y", "on" or "1" are returned as logically true. - Strings starting with "f", "n", "of" or "0" are returned as logically false. - For all other values the default is returned. Character comparisons are - case-insensitive. - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_bDefault Value to return if the key is not found - @param a_pHasMultiple Optionally receive notification of if there are - multiple entries for this key. - - @return a_nDefault Key was not found in the section - @return other Value of the key - */ - bool GetBoolValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bDefault = false, - bool * a_pHasMultiple = NULL - ) const; - - /** Add or update a section or value. This will always insert - when multiple keys are enabled. - - @param a_pSection Section to add or update - @param a_pKey Key to add or update. Set to NULL to - create an empty section. - @param a_pValue Value to set. Set to NULL to create an - empty section. - @param a_pComment Comment to be associated with the section or the - key. If a_pKey is NULL then it will be associated - with the section, otherwise the key. Note that a - comment may be set ONLY when the section or key is - first created (i.e. when this function returns the - value SI_INSERTED). If you wish to create a section - with a comment then you need to create the section - separately to the key. The comment string must be - in full comment form already (have a comment - character starting every line). - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/SetValue and SetValue - with a_bForceReplace = true, is that the load - order and comment will be preserved this way. - - @return SI_Error See error definitions - @return SI_UPDATED Value was updated - @return SI_INSERTED Value was inserted - */ - SI_Error SetValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pValue, - const SI_CHAR * a_pComment = NULL, - bool a_bForceReplace = false - ) - { - return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true); - } - - /** Add or update a numeric value. This will always insert - when multiple keys are enabled. - - @param a_pSection Section to add or update - @param a_pKey Key to add or update. - @param a_nValue Value to set. - @param a_pComment Comment to be associated with the key. See the - notes on SetValue() for comments. - @param a_bUseHex By default the value will be written to the file - in decimal format. Set this to true to write it - as hexadecimal. - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/SetLongValue and - SetLongValue with a_bForceReplace = true, is that - the load order and comment will be preserved this - way. - - @return SI_Error See error definitions - @return SI_UPDATED Value was updated - @return SI_INSERTED Value was inserted - */ - SI_Error SetLongValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - long a_nValue, - const SI_CHAR * a_pComment = NULL, - bool a_bUseHex = false, - bool a_bForceReplace = false - ); - - /** Add or update a double value. This will always insert - when multiple keys are enabled. - - @param a_pSection Section to add or update - @param a_pKey Key to add or update. - @param a_nValue Value to set. - @param a_pComment Comment to be associated with the key. See the - notes on SetValue() for comments. - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/SetDoubleValue and - SetDoubleValue with a_bForceReplace = true, is that - the load order and comment will be preserved this - way. - - @return SI_Error See error definitions - @return SI_UPDATED Value was updated - @return SI_INSERTED Value was inserted - */ - SI_Error SetDoubleValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - double a_nValue, - const SI_CHAR * a_pComment = NULL, - bool a_bForceReplace = false - ); - - /** Add or update a boolean value. This will always insert - when multiple keys are enabled. - - @param a_pSection Section to add or update - @param a_pKey Key to add or update. - @param a_bValue Value to set. - @param a_pComment Comment to be associated with the key. See the - notes on SetValue() for comments. - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/SetBoolValue and - SetBoolValue with a_bForceReplace = true, is that - the load order and comment will be preserved this - way. - - @return SI_Error See error definitions - @return SI_UPDATED Value was updated - @return SI_INSERTED Value was inserted - */ - SI_Error SetBoolValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bValue, - const SI_CHAR * a_pComment = NULL, - bool a_bForceReplace = false - ); - - /** Delete an entire section, or a key from a section. Note that the - data returned by GetSection is invalid and must not be used after - anything has been deleted from that section using this method. - Note when multiple keys is enabled, this will delete all keys with - that name; to selectively delete individual key/values, use - DeleteValue. - - @param a_pSection Section to delete key from, or if - a_pKey is NULL, the section to remove. - @param a_pKey Key to remove from the section. Set to - NULL to remove the entire section. - @param a_bRemoveEmpty If the section is empty after this key has - been deleted, should the empty section be - removed? - - @return true Key or section was deleted. - @return false Key or section was not found. - */ - bool Delete( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bRemoveEmpty = false - ); - - /** Delete an entire section, or a key from a section. If value is - provided, only remove keys with the value. Note that the data - returned by GetSection is invalid and must not be used after - anything has been deleted from that section using this method. - Note when multiple keys is enabled, all keys with the value will - be deleted. - - @param a_pSection Section to delete key from, or if - a_pKey is NULL, the section to remove. - @param a_pKey Key to remove from the section. Set to - NULL to remove the entire section. - @param a_pValue Value of key to remove from the section. - Set to NULL to remove all keys. - @param a_bRemoveEmpty If the section is empty after this key has - been deleted, should the empty section be - removed? - - @return true Key/value or section was deleted. - @return false Key/value or section was not found. - */ - bool DeleteValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pValue, - bool a_bRemoveEmpty = false - ); - - /*-----------------------------------------------------------------------*/ - /** @} - @{ @name Converter */ - - /** Return a conversion object to convert text to the same encoding - as is used by the Save(), SaveFile() and SaveString() functions. - Use this to prepare the strings that you wish to append or prepend - to the output INI data. - */ - Converter GetConverter() const { - return Converter(m_bStoreIsUtf8); - } - - /*-----------------------------------------------------------------------*/ - /** @} */ - -private: - // copying is not permitted - CSimpleIniTempl(const CSimpleIniTempl &); // disabled - CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled - - /** Parse the data looking for a file comment and store it if found. - */ - SI_Error FindFileComment( - SI_CHAR *& a_pData, - bool a_bCopyStrings - ); - - /** Parse the data looking for the next valid entry. The memory pointed to - by a_pData is modified by inserting NULL characters. The pointer is - updated to the current location in the block of text. - */ - bool FindEntry( - SI_CHAR *& a_pData, - const SI_CHAR *& a_pSection, - const SI_CHAR *& a_pKey, - const SI_CHAR *& a_pVal, - const SI_CHAR *& a_pComment - ) const; - - /** Add the section/key/value to our data. - - @param a_pSection Section name. Sections will be created if they - don't already exist. - @param a_pKey Key name. May be NULL to create an empty section. - Existing entries will be updated. New entries will - be created. - @param a_pValue Value for the key. - @param a_pComment Comment to be associated with the section or the - key. If a_pKey is NULL then it will be associated - with the section, otherwise the key. This must be - a string in full comment form already (have a - comment character starting every line). - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/AddEntry and AddEntry - with a_bForceReplace = true, is that the load - order and comment will be preserved this way. - @param a_bCopyStrings Should copies of the strings be made or not. - If false then the pointers will be used as is. - */ - SI_Error AddEntry( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pValue, - const SI_CHAR * a_pComment, - bool a_bForceReplace, - bool a_bCopyStrings - ); - - /** Is the supplied character a whitespace character? */ - inline bool IsSpace(SI_CHAR ch) const { - return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); - } - - /** Does the supplied character start a comment line? */ - inline bool IsComment(SI_CHAR ch) const { - return (ch == ';' || ch == '#'); - } - - - /** Skip over a newline character (or characters) for either DOS or UNIX */ - inline void SkipNewLine(SI_CHAR *& a_pData) const { - a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1; - } - - /** Make a copy of the supplied string, replacing the original pointer */ - SI_Error CopyString(const SI_CHAR *& a_pString); - - /** Delete a string from the copied strings buffer if necessary */ - void DeleteString(const SI_CHAR * a_pString); - - /** Internal use of our string comparison function */ - bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const { - const static SI_STRLESS isLess = SI_STRLESS(); - return isLess(a_pLeft, a_pRight); - } - - bool IsMultiLineTag(const SI_CHAR * a_pData) const; - bool IsMultiLineData(const SI_CHAR * a_pData) const; - bool LoadMultiLineText( - SI_CHAR *& a_pData, - const SI_CHAR *& a_pVal, - const SI_CHAR * a_pTagName, - bool a_bAllowBlankLinesInComment = false - ) const; - bool IsNewLineChar(SI_CHAR a_c) const; - - bool OutputMultiLineText( - OutputWriter & a_oOutput, - Converter & a_oConverter, - const SI_CHAR * a_pText - ) const; - -private: - /** Copy of the INI file data in our character format. This will be - modified when parsed to have NULL characters added after all - interesting string entries. All of the string pointers to sections, - keys and values point into this block of memory. - */ - SI_CHAR * m_pData; - - /** Length of the data that we have stored. Used when deleting strings - to determine if the string is stored here or in the allocated string - buffer. - */ - size_t m_uDataLen; - - /** File comment for this data, if one exists. */ - const SI_CHAR * m_pFileComment; - - /** Parsed INI data. Section -> (Key -> Value). */ - TSection m_data; - - /** This vector stores allocated memory for copies of strings that have - been supplied after the file load. It will be empty unless SetValue() - has been called. - */ - TNamesDepend m_strings; - - /** Is the format of our datafile UTF-8 or MBCS? */ - bool m_bStoreIsUtf8; - - /** Are multiple values permitted for the same key? */ - bool m_bAllowMultiKey; - - /** Are data values permitted to span multiple lines? */ - bool m_bAllowMultiLine; - - /** Should spaces be written out surrounding the equals sign? */ - bool m_bSpaces; - - /** Next order value, used to ensure sections and keys are output in the - same order that they are loaded/added. - */ - int m_nOrder; -}; - -// --------------------------------------------------------------------------- -// IMPLEMENTATION -// --------------------------------------------------------------------------- - -template -CSimpleIniTempl::CSimpleIniTempl( - bool a_bIsUtf8, - bool a_bAllowMultiKey, - bool a_bAllowMultiLine - ) - : m_pData(0) - , m_uDataLen(0) - , m_pFileComment(NULL) - , m_bStoreIsUtf8(a_bIsUtf8) - , m_bAllowMultiKey(a_bAllowMultiKey) - , m_bAllowMultiLine(a_bAllowMultiLine) - , m_bSpaces(true) - , m_nOrder(0) -{ } - -template -CSimpleIniTempl::~CSimpleIniTempl() -{ - Reset(); -} - -template -void -CSimpleIniTempl::Reset() -{ - // remove all data - delete[] m_pData; - m_pData = NULL; - m_uDataLen = 0; - m_pFileComment = NULL; - if (!m_data.empty()) { - m_data.erase(m_data.begin(), m_data.end()); - } - - // remove all strings - if (!m_strings.empty()) { - typename TNamesDepend::iterator i = m_strings.begin(); - for (; i != m_strings.end(); ++i) { - delete[] const_cast(i->pItem); - } - m_strings.erase(m_strings.begin(), m_strings.end()); - } -} - -template -SI_Error -CSimpleIniTempl::LoadFile( - const char * a_pszFile - ) -{ - FILE * fp = NULL; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - fopen_s(&fp, a_pszFile, "rb"); -#else // !__STDC_WANT_SECURE_LIB__ - fp = fopen(a_pszFile, "rb"); -#endif // __STDC_WANT_SECURE_LIB__ - if (!fp) { - return SI_FILE; - } - SI_Error rc = LoadFile(fp); - fclose(fp); - return rc; -} - -#ifdef SI_HAS_WIDE_FILE -template -SI_Error -CSimpleIniTempl::LoadFile( - const SI_WCHAR_T * a_pwszFile - ) -{ -#ifdef _WIN32 - FILE * fp = NULL; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - _wfopen_s(&fp, a_pwszFile, L"rb"); -#else // !__STDC_WANT_SECURE_LIB__ - fp = _wfopen(a_pwszFile, L"rb"); -#endif // __STDC_WANT_SECURE_LIB__ - if (!fp) return SI_FILE; - SI_Error rc = LoadFile(fp); - fclose(fp); - return rc; -#else // !_WIN32 (therefore SI_CONVERT_ICU) - char szFile[256]; - u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); - return LoadFile(szFile); -#endif // _WIN32 -} -#endif // SI_HAS_WIDE_FILE - -template -SI_Error -CSimpleIniTempl::LoadFile( - FILE * a_fpFile - ) -{ - // load the raw file data - int retval = fseek(a_fpFile, 0, SEEK_END); - if (retval != 0) { - return SI_FILE; - } - long lSize = ftell(a_fpFile); - if (lSize < 0) { - return SI_FILE; - } - if (lSize == 0) { - return SI_OK; - } - - // allocate and ensure NULL terminated - char * pData = new char[lSize+1]; - if (!pData) { - return SI_NOMEM; - } - pData[lSize] = 0; - - // load data into buffer - fseek(a_fpFile, 0, SEEK_SET); - size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile); - if (uRead != (size_t) lSize) { - delete[] pData; - return SI_FILE; - } - - // convert the raw data to unicode - SI_Error rc = LoadData(pData, uRead); - delete[] pData; - return rc; -} - -template -SI_Error -CSimpleIniTempl::LoadData( - const char * a_pData, - size_t a_uDataLen - ) -{ - SI_CONVERTER converter(m_bStoreIsUtf8); - - if (a_uDataLen == 0) { - return SI_OK; - } - - // consume the UTF-8 BOM if it exists - if (m_bStoreIsUtf8 && a_uDataLen >= 3) { - if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) { - a_pData += 3; - a_uDataLen -= 3; - } - } - - // determine the length of the converted data - size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen); - if (uLen == (size_t)(-1)) { - return SI_FAIL; - } - - // allocate memory for the data, ensure that there is a NULL - // terminator wherever the converted data ends - SI_CHAR * pData = new SI_CHAR[uLen+1]; - if (!pData) { - return SI_NOMEM; - } - memset(pData, 0, sizeof(SI_CHAR)*(uLen+1)); - - // convert the data - if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) { - delete[] pData; - return SI_FAIL; - } - - // parse it - const static SI_CHAR empty = 0; - SI_CHAR * pWork = pData; - const SI_CHAR * pSection = ∅ - const SI_CHAR * pItem = NULL; - const SI_CHAR * pVal = NULL; - const SI_CHAR * pComment = NULL; - - // We copy the strings if we are loading data into this class when we - // already have stored some. - bool bCopyStrings = (m_pData != NULL); - - // find a file comment if it exists, this is a comment that starts at the - // beginning of the file and continues until the first blank line. - SI_Error rc = FindFileComment(pWork, bCopyStrings); - if (rc < 0) return rc; - - // add every entry in the file to the data table - while (FindEntry(pWork, pSection, pItem, pVal, pComment)) { - rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings); - if (rc < 0) return rc; - } - - // store these strings if we didn't copy them - if (bCopyStrings) { - delete[] pData; - } - else { - m_pData = pData; - m_uDataLen = uLen+1; - } - - return SI_OK; -} - -#ifdef SI_SUPPORT_IOSTREAMS -template -SI_Error -CSimpleIniTempl::LoadData( - std::istream & a_istream - ) -{ - std::string strData; - char szBuf[512]; - do { - a_istream.get(szBuf, sizeof(szBuf), '\0'); - strData.append(szBuf); - } - while (a_istream.good()); - return LoadData(strData); -} -#endif // SI_SUPPORT_IOSTREAMS - -template -SI_Error -CSimpleIniTempl::FindFileComment( - SI_CHAR *& a_pData, - bool a_bCopyStrings - ) -{ - // there can only be a single file comment - if (m_pFileComment) { - return SI_OK; - } - - // Load the file comment as multi-line text, this will modify all of - // the newline characters to be single \n chars - if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) { - return SI_OK; - } - - // copy the string if necessary - if (a_bCopyStrings) { - SI_Error rc = CopyString(m_pFileComment); - if (rc < 0) return rc; - } - - return SI_OK; -} - -template -bool -CSimpleIniTempl::FindEntry( - SI_CHAR *& a_pData, - const SI_CHAR *& a_pSection, - const SI_CHAR *& a_pKey, - const SI_CHAR *& a_pVal, - const SI_CHAR *& a_pComment - ) const -{ - a_pComment = NULL; - - SI_CHAR * pTrail = NULL; - while (*a_pData) { - // skip spaces and empty lines - while (*a_pData && IsSpace(*a_pData)) { - ++a_pData; - } - if (!*a_pData) { - break; - } - - // skip processing of comment lines but keep a pointer to - // the start of the comment. - if (IsComment(*a_pData)) { - LoadMultiLineText(a_pData, a_pComment, NULL, true); - continue; - } - - // process section names - if (*a_pData == '[') { - // skip leading spaces - ++a_pData; - while (*a_pData && IsSpace(*a_pData)) { - ++a_pData; - } - - // find the end of the section name (it may contain spaces) - // and convert it to lowercase as necessary - a_pSection = a_pData; - while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - - // if it's an invalid line, just skip it - if (*a_pData != ']') { - continue; - } - - // remove trailing spaces from the section - pTrail = a_pData - 1; - while (pTrail >= a_pSection && IsSpace(*pTrail)) { - --pTrail; - } - ++pTrail; - *pTrail = 0; - - // skip to the end of the line - ++a_pData; // safe as checked that it == ']' above - while (*a_pData && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - - a_pKey = NULL; - a_pVal = NULL; - return true; - } - - // find the end of the key name (it may contain spaces) - // and convert it to lowercase as necessary - a_pKey = a_pData; - while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - - // if it's an invalid line, just skip it - if (*a_pData != '=') { - continue; - } - - // empty keys are invalid - if (a_pKey == a_pData) { - while (*a_pData && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - continue; - } - - // remove trailing spaces from the key - pTrail = a_pData - 1; - while (pTrail >= a_pKey && IsSpace(*pTrail)) { - --pTrail; - } - ++pTrail; - *pTrail = 0; - - // skip leading whitespace on the value - ++a_pData; // safe as checked that it == '=' above - while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) { - ++a_pData; - } - - // find the end of the value which is the end of this line - a_pVal = a_pData; - while (*a_pData && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - - // remove trailing spaces from the value - pTrail = a_pData - 1; - if (*a_pData) { // prepare for the next round - SkipNewLine(a_pData); - } - while (pTrail >= a_pVal && IsSpace(*pTrail)) { - --pTrail; - } - ++pTrail; - *pTrail = 0; - - // check for multi-line entries - if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) { - // skip the "<<<" to get the tag that will end the multiline - const SI_CHAR * pTagName = a_pVal + 3; - return LoadMultiLineText(a_pData, a_pVal, pTagName); - } - - // return the standard entry - return true; - } - - return false; -} - -template -bool -CSimpleIniTempl::IsMultiLineTag( - const SI_CHAR * a_pVal - ) const -{ - // check for the "<<<" prefix for a multi-line entry - if (*a_pVal++ != '<') return false; - if (*a_pVal++ != '<') return false; - if (*a_pVal++ != '<') return false; - return true; -} - -template -bool -CSimpleIniTempl::IsMultiLineData( - const SI_CHAR * a_pData - ) const -{ - // data is multi-line if it has any of the following features: - // * whitespace prefix - // * embedded newlines - // * whitespace suffix - - // empty string - if (!*a_pData) { - return false; - } - - // check for prefix - if (IsSpace(*a_pData)) { - return true; - } - - // embedded newlines - while (*a_pData) { - if (IsNewLineChar(*a_pData)) { - return true; - } - ++a_pData; - } - - // check for suffix - if (IsSpace(*--a_pData)) { - return true; - } - - return false; -} - -template -bool -CSimpleIniTempl::IsNewLineChar( - SI_CHAR a_c - ) const -{ - return (a_c == '\n' || a_c == '\r'); -} - -template -bool -CSimpleIniTempl::LoadMultiLineText( - SI_CHAR *& a_pData, - const SI_CHAR *& a_pVal, - const SI_CHAR * a_pTagName, - bool a_bAllowBlankLinesInComment - ) const -{ - // we modify this data to strip all newlines down to a single '\n' - // character. This means that on Windows we need to strip out some - // characters which will make the data shorter. - // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become - // LINE1-LINE1\nLINE2-LINE2\0 - // The pDataLine entry is the pointer to the location in memory that - // the current line needs to start to run following the existing one. - // This may be the same as pCurrLine in which case no move is needed. - SI_CHAR * pDataLine = a_pData; - SI_CHAR * pCurrLine; - - // value starts at the current line - a_pVal = a_pData; - - // find the end tag. This tag must start in column 1 and be - // followed by a newline. No whitespace removal is done while - // searching for this tag. - SI_CHAR cEndOfLineChar = *a_pData; - for(;;) { - // if we are loading comments then we need a comment character as - // the first character on every line - if (!a_pTagName && !IsComment(*a_pData)) { - // if we aren't allowing blank lines then we're done - if (!a_bAllowBlankLinesInComment) { - break; - } - - // if we are allowing blank lines then we only include them - // in this comment if another comment follows, so read ahead - // to find out. - SI_CHAR * pCurr = a_pData; - int nNewLines = 0; - while (IsSpace(*pCurr)) { - if (IsNewLineChar(*pCurr)) { - ++nNewLines; - SkipNewLine(pCurr); - } - else { - ++pCurr; - } - } - - // we have a comment, add the blank lines to the output - // and continue processing from here - if (IsComment(*pCurr)) { - for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n'; - a_pData = pCurr; - continue; - } - - // the comment ends here - break; - } - - // find the end of this line - pCurrLine = a_pData; - while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData; - - // move this line down to the location that it should be if necessary - if (pDataLine < pCurrLine) { - size_t nLen = (size_t) (a_pData - pCurrLine); - memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR)); - pDataLine[nLen] = '\0'; - } - - // end the line with a NULL - cEndOfLineChar = *a_pData; - *a_pData = 0; - - // if are looking for a tag then do the check now. This is done before - // checking for end of the data, so that if we have the tag at the end - // of the data then the tag is removed correctly. - if (a_pTagName && - (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine))) - { - break; - } - - // if we are at the end of the data then we just automatically end - // this entry and return the current data. - if (!cEndOfLineChar) { - return true; - } - - // otherwise we need to process this newline to ensure that it consists - // of just a single \n character. - pDataLine += (a_pData - pCurrLine); - *a_pData = cEndOfLineChar; - SkipNewLine(a_pData); - *pDataLine++ = '\n'; - } - - // if we didn't find a comment at all then return false - if (a_pVal == a_pData) { - a_pVal = NULL; - return false; - } - - // the data (which ends at the end of the last line) needs to be - // null-terminated BEFORE before the newline character(s). If the - // user wants a new line in the multi-line data then they need to - // add an empty line before the tag. - *--pDataLine = '\0'; - - // if looking for a tag and if we aren't at the end of the data, - // then move a_pData to the start of the next line. - if (a_pTagName && cEndOfLineChar) { - SI_ASSERT(IsNewLineChar(cEndOfLineChar)); - *a_pData = cEndOfLineChar; - SkipNewLine(a_pData); - } - - return true; -} - -template -SI_Error -CSimpleIniTempl::CopyString( - const SI_CHAR *& a_pString - ) -{ - size_t uLen = 0; - if (sizeof(SI_CHAR) == sizeof(char)) { - uLen = strlen((const char *)a_pString); - } - else if (sizeof(SI_CHAR) == sizeof(wchar_t)) { - uLen = wcslen((const wchar_t *)a_pString); - } - else { - for ( ; a_pString[uLen]; ++uLen) /*loop*/ ; - } - ++uLen; // NULL character - SI_CHAR * pCopy = new SI_CHAR[uLen]; - if (!pCopy) { - return SI_NOMEM; - } - memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen); - m_strings.push_back(pCopy); - a_pString = pCopy; - return SI_OK; -} - -template -SI_Error -CSimpleIniTempl::AddEntry( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pValue, - const SI_CHAR * a_pComment, - bool a_bForceReplace, - bool a_bCopyStrings - ) -{ - SI_Error rc; - bool bInserted = false; - - SI_ASSERT(!a_pComment || IsComment(*a_pComment)); - - // if we are copying strings then make a copy of the comment now - // because we will need it when we add the entry. - if (a_bCopyStrings && a_pComment) { - rc = CopyString(a_pComment); - if (rc < 0) return rc; - } - - // create the section entry if necessary - typename TSection::iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - // if the section doesn't exist then we need a copy as the - // string needs to last beyond the end of this function - if (a_bCopyStrings) { - rc = CopyString(a_pSection); - if (rc < 0) return rc; - } - - // only set the comment if this is a section only entry - Entry oSection(a_pSection, ++m_nOrder); - if (a_pComment && (!a_pKey || !a_pValue)) { - oSection.pComment = a_pComment; - } - - typename TSection::value_type oEntry(oSection, TKeyVal()); - typedef typename TSection::iterator SectionIterator; - std::pair i = m_data.insert(oEntry); - iSection = i.first; - bInserted = true; - } - if (!a_pKey || !a_pValue) { - // section only entries are specified with pItem and pVal as NULL - return bInserted ? SI_INSERTED : SI_UPDATED; - } - - // check for existence of the key - TKeyVal & keyval = iSection->second; - typename TKeyVal::iterator iKey = keyval.find(a_pKey); - - // remove all existing entries but save the load order and - // comment of the first entry - int nLoadOrder = ++m_nOrder; - if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) { - const SI_CHAR * pComment = NULL; - while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) { - if (iKey->first.nOrder < nLoadOrder) { - nLoadOrder = iKey->first.nOrder; - pComment = iKey->first.pComment; - } - ++iKey; - } - if (pComment) { - DeleteString(a_pComment); - a_pComment = pComment; - CopyString(a_pComment); - } - Delete(a_pSection, a_pKey); - iKey = keyval.end(); - } - - // make string copies if necessary - bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace; - if (a_bCopyStrings) { - if (bForceCreateNewKey || iKey == keyval.end()) { - // if the key doesn't exist then we need a copy as the - // string needs to last beyond the end of this function - // because we will be inserting the key next - rc = CopyString(a_pKey); - if (rc < 0) return rc; - } - - // we always need a copy of the value - rc = CopyString(a_pValue); - if (rc < 0) return rc; - } - - // create the key entry - if (iKey == keyval.end() || bForceCreateNewKey) { - Entry oKey(a_pKey, nLoadOrder); - if (a_pComment) { - oKey.pComment = a_pComment; - } - typename TKeyVal::value_type oEntry(oKey, static_cast(NULL)); - iKey = keyval.insert(oEntry); - bInserted = true; - } - iKey->second = a_pValue; - return bInserted ? SI_INSERTED : SI_UPDATED; -} - -template -const SI_CHAR * -CSimpleIniTempl::GetValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pDefault, - bool * a_pHasMultiple - ) const -{ - if (a_pHasMultiple) { - *a_pHasMultiple = false; - } - if (!a_pSection || !a_pKey) { - return a_pDefault; - } - typename TSection::const_iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return a_pDefault; - } - typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); - if (iKeyVal == iSection->second.end()) { - return a_pDefault; - } - - // check for multiple entries with the same key - if (m_bAllowMultiKey && a_pHasMultiple) { - typename TKeyVal::const_iterator iTemp = iKeyVal; - if (++iTemp != iSection->second.end()) { - if (!IsLess(a_pKey, iTemp->first.pItem)) { - *a_pHasMultiple = true; - } - } - } - - return iKeyVal->second; -} - -template -long -CSimpleIniTempl::GetLongValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - long a_nDefault, - bool * a_pHasMultiple - ) const -{ - // return the default if we don't have a value - const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); - if (!pszValue || !*pszValue) return a_nDefault; - - // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII - char szValue[64] = { 0 }; - SI_CONVERTER c(m_bStoreIsUtf8); - if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { - return a_nDefault; - } - - // handle the value as hex if prefaced with "0x" - long nValue = a_nDefault; - char * pszSuffix = szValue; - if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) { - if (!szValue[2]) return a_nDefault; - nValue = strtol(&szValue[2], &pszSuffix, 16); - } - else { - nValue = strtol(szValue, &pszSuffix, 10); - } - - // any invalid strings will return the default value - if (*pszSuffix) { - return a_nDefault; - } - - return nValue; -} - -template -SI_Error -CSimpleIniTempl::SetLongValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - long a_nValue, - const SI_CHAR * a_pComment, - bool a_bUseHex, - bool a_bForceReplace - ) -{ - // use SetValue to create sections - if (!a_pSection || !a_pKey) return SI_FAIL; - - // convert to an ASCII string - char szInput[64]; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); -#else // !__STDC_WANT_SECURE_LIB__ - sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); -#endif // __STDC_WANT_SECURE_LIB__ - - // convert to output text - SI_CHAR szOutput[64]; - SI_CONVERTER c(m_bStoreIsUtf8); - c.ConvertFromStore(szInput, strlen(szInput) + 1, - szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); - - // actually add it - return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); -} - -template -double -CSimpleIniTempl::GetDoubleValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - double a_nDefault, - bool * a_pHasMultiple - ) const -{ - // return the default if we don't have a value - const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); - if (!pszValue || !*pszValue) return a_nDefault; - - // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII - char szValue[64] = { 0 }; - SI_CONVERTER c(m_bStoreIsUtf8); - if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { - return a_nDefault; - } - - char * pszSuffix = NULL; - double nValue = strtod(szValue, &pszSuffix); - - // any invalid strings will return the default value - if (!pszSuffix || *pszSuffix) { - return a_nDefault; - } - - return nValue; -} - -template -SI_Error -CSimpleIniTempl::SetDoubleValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - double a_nValue, - const SI_CHAR * a_pComment, - bool a_bForceReplace - ) -{ - // use SetValue to create sections - if (!a_pSection || !a_pKey) return SI_FAIL; - - // convert to an ASCII string - char szInput[64]; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - sprintf_s(szInput, "%f", a_nValue); -#else // !__STDC_WANT_SECURE_LIB__ - sprintf(szInput, "%f", a_nValue); -#endif // __STDC_WANT_SECURE_LIB__ - - // convert to output text - SI_CHAR szOutput[64]; - SI_CONVERTER c(m_bStoreIsUtf8); - c.ConvertFromStore(szInput, strlen(szInput) + 1, - szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); - - // actually add it - return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); -} - -template -bool -CSimpleIniTempl::GetBoolValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bDefault, - bool * a_pHasMultiple - ) const -{ - // return the default if we don't have a value - const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); - if (!pszValue || !*pszValue) return a_bDefault; - - // we only look at the minimum number of characters - switch (pszValue[0]) { - case 't': case 'T': // true - case 'y': case 'Y': // yes - case '1': // 1 (one) - return true; - - case 'f': case 'F': // false - case 'n': case 'N': // no - case '0': // 0 (zero) - return false; - - case 'o': case 'O': - if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on - if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off - break; - } - - // no recognized value, return the default - return a_bDefault; -} - -template -SI_Error -CSimpleIniTempl::SetBoolValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bValue, - const SI_CHAR * a_pComment, - bool a_bForceReplace - ) -{ - // use SetValue to create sections - if (!a_pSection || !a_pKey) return SI_FAIL; - - // convert to an ASCII string - const char * pszInput = a_bValue ? "true" : "false"; - - // convert to output text - SI_CHAR szOutput[64]; - SI_CONVERTER c(m_bStoreIsUtf8); - c.ConvertFromStore(pszInput, strlen(pszInput) + 1, - szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); - - // actually add it - return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); -} - -template -bool -CSimpleIniTempl::GetAllValues( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - TNamesDepend & a_values - ) const -{ - a_values.clear(); - - if (!a_pSection || !a_pKey) { - return false; - } - typename TSection::const_iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return false; - } - typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); - if (iKeyVal == iSection->second.end()) { - return false; - } - - // insert all values for this key - a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); - if (m_bAllowMultiKey) { - ++iKeyVal; - while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) { - a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); - ++iKeyVal; - } - } - - return true; -} - -template -int -CSimpleIniTempl::GetSectionSize( - const SI_CHAR * a_pSection - ) const -{ - if (!a_pSection) { - return -1; - } - - typename TSection::const_iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return -1; - } - const TKeyVal & section = iSection->second; - - // if multi-key isn't permitted then the section size is - // the number of keys that we have. - if (!m_bAllowMultiKey || section.empty()) { - return (int) section.size(); - } - - // otherwise we need to count them - int nCount = 0; - const SI_CHAR * pLastKey = NULL; - typename TKeyVal::const_iterator iKeyVal = section.begin(); - for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) { - if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { - ++nCount; - pLastKey = iKeyVal->first.pItem; - } - } - return nCount; -} - -template -const typename CSimpleIniTempl::TKeyVal * -CSimpleIniTempl::GetSection( - const SI_CHAR * a_pSection - ) const -{ - if (a_pSection) { - typename TSection::const_iterator i = m_data.find(a_pSection); - if (i != m_data.end()) { - return &(i->second); - } - } - return 0; -} - -template -void -CSimpleIniTempl::GetAllSections( - TNamesDepend & a_names - ) const -{ - a_names.clear(); - typename TSection::const_iterator i = m_data.begin(); - for (int n = 0; i != m_data.end(); ++i, ++n ) { - a_names.push_back(i->first); - } -} - -template -bool -CSimpleIniTempl::GetAllKeys( - const SI_CHAR * a_pSection, - TNamesDepend & a_names - ) const -{ - a_names.clear(); - - if (!a_pSection) { - return false; - } - - typename TSection::const_iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return false; - } - - const TKeyVal & section = iSection->second; - const SI_CHAR * pLastKey = NULL; - typename TKeyVal::const_iterator iKeyVal = section.begin(); - for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) { - if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { - a_names.push_back(iKeyVal->first); - pLastKey = iKeyVal->first.pItem; - } - } - - return true; -} - -template -SI_Error -CSimpleIniTempl::SaveFile( - const char * a_pszFile, - bool a_bAddSignature - ) const -{ - FILE * fp = NULL; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - fopen_s(&fp, a_pszFile, "wb"); -#else // !__STDC_WANT_SECURE_LIB__ - fp = fopen(a_pszFile, "wb"); -#endif // __STDC_WANT_SECURE_LIB__ - if (!fp) return SI_FILE; - SI_Error rc = SaveFile(fp, a_bAddSignature); - fclose(fp); - return rc; -} - -#ifdef SI_HAS_WIDE_FILE -template -SI_Error -CSimpleIniTempl::SaveFile( - const SI_WCHAR_T * a_pwszFile, - bool a_bAddSignature - ) const -{ -#ifdef _WIN32 - FILE * fp = NULL; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - _wfopen_s(&fp, a_pwszFile, L"wb"); -#else // !__STDC_WANT_SECURE_LIB__ - fp = _wfopen(a_pwszFile, L"wb"); -#endif // __STDC_WANT_SECURE_LIB__ - if (!fp) return SI_FILE; - SI_Error rc = SaveFile(fp, a_bAddSignature); - fclose(fp); - return rc; -#else // !_WIN32 (therefore SI_CONVERT_ICU) - char szFile[256]; - u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); - return SaveFile(szFile, a_bAddSignature); -#endif // _WIN32 -} -#endif // SI_HAS_WIDE_FILE - -template -SI_Error -CSimpleIniTempl::SaveFile( - FILE * a_pFile, - bool a_bAddSignature - ) const -{ - FileWriter writer(a_pFile); - return Save(writer, a_bAddSignature); -} - -template -SI_Error -CSimpleIniTempl::Save( - OutputWriter & a_oOutput, - bool a_bAddSignature - ) const -{ - Converter convert(m_bStoreIsUtf8); - - // add the UTF-8 signature if it is desired - if (m_bStoreIsUtf8 && a_bAddSignature) { - a_oOutput.Write(SI_UTF8_SIGNATURE); - } - - // get all of the sections sorted in load order - TNamesDepend oSections; - GetAllSections(oSections); -#if defined(_MSC_VER) && _MSC_VER <= 1200 - oSections.sort(); -#elif defined(__BORLANDC__) - oSections.sort(Entry::LoadOrder()); -#else - oSections.sort(typename Entry::LoadOrder()); -#endif - - // write the file comment if we have one - bool bNeedNewLine = false; - if (m_pFileComment) { - if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) { - return SI_FAIL; - } - bNeedNewLine = true; - } - - // iterate through our sections and output the data - typename TNamesDepend::const_iterator iSection = oSections.begin(); - for ( ; iSection != oSections.end(); ++iSection ) { - // write out the comment if there is one - if (iSection->pComment) { - if (bNeedNewLine) { - a_oOutput.Write(SI_NEWLINE_A); - a_oOutput.Write(SI_NEWLINE_A); - } - if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) { - return SI_FAIL; - } - bNeedNewLine = false; - } - - if (bNeedNewLine) { - a_oOutput.Write(SI_NEWLINE_A); - a_oOutput.Write(SI_NEWLINE_A); - bNeedNewLine = false; - } - - // write the section (unless there is no section name) - if (*iSection->pItem) { - if (!convert.ConvertToStore(iSection->pItem)) { - return SI_FAIL; - } - a_oOutput.Write("["); - a_oOutput.Write(convert.Data()); - a_oOutput.Write("]"); - a_oOutput.Write(SI_NEWLINE_A); - } - - // get all of the keys sorted in load order - TNamesDepend oKeys; - GetAllKeys(iSection->pItem, oKeys); -#if defined(_MSC_VER) && _MSC_VER <= 1200 - oKeys.sort(); -#elif defined(__BORLANDC__) - oKeys.sort(Entry::LoadOrder()); -#else - oKeys.sort(typename Entry::LoadOrder()); -#endif - - // write all keys and values - typename TNamesDepend::const_iterator iKey = oKeys.begin(); - for ( ; iKey != oKeys.end(); ++iKey) { - // get all values for this key - TNamesDepend oValues; - GetAllValues(iSection->pItem, iKey->pItem, oValues); - - typename TNamesDepend::const_iterator iValue = oValues.begin(); - for ( ; iValue != oValues.end(); ++iValue) { - // write out the comment if there is one - if (iValue->pComment) { - a_oOutput.Write(SI_NEWLINE_A); - if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) { - return SI_FAIL; - } - } - - // write the key - if (!convert.ConvertToStore(iKey->pItem)) { - return SI_FAIL; - } - a_oOutput.Write(convert.Data()); - - // write the value - if (!convert.ConvertToStore(iValue->pItem)) { - return SI_FAIL; - } - a_oOutput.Write(m_bSpaces ? " = " : "="); - if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) { - // multi-line data needs to be processed specially to ensure - // that we use the correct newline format for the current system - a_oOutput.Write("<<pItem)) { - return SI_FAIL; - } - a_oOutput.Write("END_OF_TEXT"); - } - else { - a_oOutput.Write(convert.Data()); - } - a_oOutput.Write(SI_NEWLINE_A); - } - } - - bNeedNewLine = true; - } - - return SI_OK; -} - -template -bool -CSimpleIniTempl::OutputMultiLineText( - OutputWriter & a_oOutput, - Converter & a_oConverter, - const SI_CHAR * a_pText - ) const -{ - const SI_CHAR * pEndOfLine; - SI_CHAR cEndOfLineChar = *a_pText; - while (cEndOfLineChar) { - // find the end of this line - pEndOfLine = a_pText; - for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ; - cEndOfLineChar = *pEndOfLine; - - // temporarily null terminate, convert and output the line - *const_cast(pEndOfLine) = 0; - if (!a_oConverter.ConvertToStore(a_pText)) { - return false; - } - *const_cast(pEndOfLine) = cEndOfLineChar; - a_pText += (pEndOfLine - a_pText) + 1; - a_oOutput.Write(a_oConverter.Data()); - a_oOutput.Write(SI_NEWLINE_A); - } - return true; -} - -template -bool -CSimpleIniTempl::Delete( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bRemoveEmpty - ) -{ - return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty); -} - -template -bool -CSimpleIniTempl::DeleteValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pValue, - bool a_bRemoveEmpty - ) -{ - if (!a_pSection) { - return false; - } - - typename TSection::iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return false; - } - - // remove a single key if we have a keyname - if (a_pKey) { - typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey); - if (iKeyVal == iSection->second.end()) { - return false; - } - - const static SI_STRLESS isLess = SI_STRLESS(); - - // remove any copied strings and then the key - typename TKeyVal::iterator iDelete; - bool bDeleted = false; - do { - iDelete = iKeyVal++; - - if(a_pValue == NULL || - (isLess(a_pValue, iDelete->second) == false && - isLess(iDelete->second, a_pValue) == false)) { - DeleteString(iDelete->first.pItem); - DeleteString(iDelete->second); - iSection->second.erase(iDelete); - bDeleted = true; - } - } - while (iKeyVal != iSection->second.end() - && !IsLess(a_pKey, iKeyVal->first.pItem)); - - if(!bDeleted) { - return false; - } - - // done now if the section is not empty or we are not pruning away - // the empty sections. Otherwise let it fall through into the section - // deletion code - if (!a_bRemoveEmpty || !iSection->second.empty()) { - return true; - } - } - else { - // delete all copied strings from this section. The actual - // entries will be removed when the section is removed. - typename TKeyVal::iterator iKeyVal = iSection->second.begin(); - for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) { - DeleteString(iKeyVal->first.pItem); - DeleteString(iKeyVal->second); - } - } - - // delete the section itself - DeleteString(iSection->first.pItem); - m_data.erase(iSection); - - return true; -} - -template -void -CSimpleIniTempl::DeleteString( - const SI_CHAR * a_pString - ) -{ - // strings may exist either inside the data block, or they will be - // individually allocated and stored in m_strings. We only physically - // delete those stored in m_strings. - if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) { - typename TNamesDepend::iterator i = m_strings.begin(); - for (;i != m_strings.end(); ++i) { - if (a_pString == i->pItem) { - delete[] const_cast(i->pItem); - m_strings.erase(i); - break; - } - } - } -} - -// --------------------------------------------------------------------------- -// CONVERSION FUNCTIONS -// --------------------------------------------------------------------------- - -// Defines the conversion classes for different libraries. Before including -// SimpleIni.h, set the converter that you wish you use by defining one of the -// following symbols. -// -// SI_CONVERT_GENERIC Use the Unicode reference conversion library in -// the accompanying files ConvertUTF.h/c -// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires -// ICU headers on include path and icuuc.lib -// SI_CONVERT_WIN32 Use the Win32 API functions for conversion. - -#if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU) -# ifdef _WIN32 -# define SI_CONVERT_WIN32 -# else -# define SI_CONVERT_GENERIC -# endif -#endif - -/** - * Generic case-sensitive less than comparison. This class returns numerically - * ordered ASCII case-sensitive text for all possible sizes and types of - * SI_CHAR. - */ -template -struct SI_GenericCase { - bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { - long cmp; - for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { - cmp = (long) *pLeft - (long) *pRight; - if (cmp != 0) { - return cmp < 0; - } - } - return *pRight != 0; - } -}; - -/** - * Generic ASCII case-insensitive less than comparison. This class returns - * numerically ordered ASCII case-insensitive text for all possible sizes - * and types of SI_CHAR. It is not safe for MBCS text comparison where - * ASCII A-Z characters are used in the encoding of multi-byte characters. - */ -template -struct SI_GenericNoCase { - inline SI_CHAR locase(SI_CHAR ch) const { - return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a'); - } - bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { - long cmp; - for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { - cmp = (long) locase(*pLeft) - (long) locase(*pRight); - if (cmp != 0) { - return cmp < 0; - } - } - return *pRight != 0; - } -}; - -/** - * Null conversion class for MBCS/UTF-8 to char (or equivalent). - */ -template -class SI_ConvertA { - bool m_bStoreIsUtf8; -protected: - SI_ConvertA() { } -public: - SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } - - /* copy and assignment */ - SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); } - SI_ConvertA & operator=(const SI_ConvertA & rhs) { - m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; - return *this; - } - - /** Calculate the number of SI_CHAR required for converting the input - * from the storage format. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @return Number of SI_CHAR required by the string when - * converted. If there are embedded NULL bytes in the - * input data, only the string up and not including - * the NULL byte will be converted. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeFromStore( - const char * a_pInputData, - size_t a_uInputDataLen) - { - (void)a_pInputData; - SI_ASSERT(a_uInputDataLen != (size_t) -1); - - // ASCII/MBCS/UTF-8 needs no conversion - return a_uInputDataLen; - } - - /** Convert the input string from the storage format to SI_CHAR. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @param a_pOutputData Pointer to the output buffer to received the - * converted data. - * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. - * @return true if all of the input data was successfully - * converted. - */ - bool ConvertFromStore( - const char * a_pInputData, - size_t a_uInputDataLen, - SI_CHAR * a_pOutputData, - size_t a_uOutputDataSize) - { - // ASCII/MBCS/UTF-8 needs no conversion - if (a_uInputDataLen > a_uOutputDataSize) { - return false; - } - memcpy(a_pOutputData, a_pInputData, a_uInputDataLen); - return true; - } - - /** Calculate the number of char required by the storage format of this - * data. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated string to calculate the number of - * bytes required to be converted to storage format. - * @return Number of bytes required by the string when - * converted to storage format. This size always - * includes space for the terminating NULL character. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeToStore( - const SI_CHAR * a_pInputData) - { - // ASCII/MBCS/UTF-8 needs no conversion - return strlen((const char *)a_pInputData) + 1; - } - - /** Convert the input string to the storage format of this data. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated source string to convert. All of - * the data will be converted including the - * terminating NULL character. - * @param a_pOutputData Pointer to the buffer to receive the converted - * string. - * @param a_uOutputDataSize Size of the output buffer in char. - * @return true if all of the input data, including the - * terminating NULL character was successfully - * converted. - */ - bool ConvertToStore( - const SI_CHAR * a_pInputData, - char * a_pOutputData, - size_t a_uOutputDataSize) - { - // calc input string length (SI_CHAR type and size independent) - size_t uInputLen = strlen((const char *)a_pInputData) + 1; - if (uInputLen > a_uOutputDataSize) { - return false; - } - - // ascii/UTF-8 needs no conversion - memcpy(a_pOutputData, a_pInputData, uInputLen); - return true; - } -}; - - -// --------------------------------------------------------------------------- -// SI_CONVERT_GENERIC -// --------------------------------------------------------------------------- -#ifdef SI_CONVERT_GENERIC - -#define SI_Case SI_GenericCase -#define SI_NoCase SI_GenericNoCase - -#include -#include "ConvertUTF.h" - -/** - * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference - * library functions. This can be used on all platforms. - */ -template -class SI_ConvertW { - bool m_bStoreIsUtf8; -protected: - SI_ConvertW() { } -public: - SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } - - /* copy and assignment */ - SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } - SI_ConvertW & operator=(const SI_ConvertW & rhs) { - m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; - return *this; - } - - /** Calculate the number of SI_CHAR required for converting the input - * from the storage format. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @return Number of SI_CHAR required by the string when - * converted. If there are embedded NULL bytes in the - * input data, only the string up and not including - * the NULL byte will be converted. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeFromStore( - const char * a_pInputData, - size_t a_uInputDataLen) - { - SI_ASSERT(a_uInputDataLen != (size_t) -1); - - if (m_bStoreIsUtf8) { - // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t - // so we just return the same number of characters required as for - // the source text. - return a_uInputDataLen; - } - -#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux)) - // fall back processing for platforms that don't support a NULL dest to mbstowcs - // worst case scenario is 1:1, this will be a sufficient buffer size - (void)a_pInputData; - return a_uInputDataLen; -#else - // get the actual required buffer size - return mbstowcs(NULL, a_pInputData, a_uInputDataLen); -#endif - } - - /** Convert the input string from the storage format to SI_CHAR. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @param a_pOutputData Pointer to the output buffer to received the - * converted data. - * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. - * @return true if all of the input data was successfully - * converted. - */ - bool ConvertFromStore( - const char * a_pInputData, - size_t a_uInputDataLen, - SI_CHAR * a_pOutputData, - size_t a_uOutputDataSize) - { - if (m_bStoreIsUtf8) { - // This uses the Unicode reference implementation to do the - // conversion from UTF-8 to wchar_t. The required files are - // ConvertUTF.h and ConvertUTF.c which should be included in - // the distribution but are publically available from unicode.org - // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ - ConversionResult retval; - const UTF8 * pUtf8 = (const UTF8 *) a_pInputData; - if (sizeof(wchar_t) == sizeof(UTF32)) { - UTF32 * pUtf32 = (UTF32 *) a_pOutputData; - retval = ConvertUTF8toUTF32( - &pUtf8, pUtf8 + a_uInputDataLen, - &pUtf32, pUtf32 + a_uOutputDataSize, - lenientConversion); - } - else if (sizeof(wchar_t) == sizeof(UTF16)) { - UTF16 * pUtf16 = (UTF16 *) a_pOutputData; - retval = ConvertUTF8toUTF16( - &pUtf8, pUtf8 + a_uInputDataLen, - &pUtf16, pUtf16 + a_uOutputDataSize, - lenientConversion); - } - return retval == conversionOK; - } - - // convert to wchar_t - size_t retval = mbstowcs(a_pOutputData, - a_pInputData, a_uOutputDataSize); - return retval != (size_t)(-1); - } - - /** Calculate the number of char required by the storage format of this - * data. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated string to calculate the number of - * bytes required to be converted to storage format. - * @return Number of bytes required by the string when - * converted to storage format. This size always - * includes space for the terminating NULL character. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeToStore( - const SI_CHAR * a_pInputData) - { - if (m_bStoreIsUtf8) { - // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char - size_t uLen = 0; - while (a_pInputData[uLen]) { - ++uLen; - } - return (6 * uLen) + 1; - } - else { - size_t uLen = wcstombs(NULL, a_pInputData, 0); - if (uLen == (size_t)(-1)) { - return uLen; - } - return uLen + 1; // include NULL terminator - } - } - - /** Convert the input string to the storage format of this data. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated source string to convert. All of - * the data will be converted including the - * terminating NULL character. - * @param a_pOutputData Pointer to the buffer to receive the converted - * string. - * @param a_uOutputDataSize Size of the output buffer in char. - * @return true if all of the input data, including the - * terminating NULL character was successfully - * converted. - */ - bool ConvertToStore( - const SI_CHAR * a_pInputData, - char * a_pOutputData, - size_t a_uOutputDataSize - ) - { - if (m_bStoreIsUtf8) { - // calc input string length (SI_CHAR type and size independent) - size_t uInputLen = 0; - while (a_pInputData[uInputLen]) { - ++uInputLen; - } - ++uInputLen; // include the NULL char - - // This uses the Unicode reference implementation to do the - // conversion from wchar_t to UTF-8. The required files are - // ConvertUTF.h and ConvertUTF.c which should be included in - // the distribution but are publically available from unicode.org - // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ - ConversionResult retval; - UTF8 * pUtf8 = (UTF8 *) a_pOutputData; - if (sizeof(wchar_t) == sizeof(UTF32)) { - const UTF32 * pUtf32 = (const UTF32 *) a_pInputData; - retval = ConvertUTF32toUTF8( - &pUtf32, pUtf32 + uInputLen, - &pUtf8, pUtf8 + a_uOutputDataSize, - lenientConversion); - } - else if (sizeof(wchar_t) == sizeof(UTF16)) { - const UTF16 * pUtf16 = (const UTF16 *) a_pInputData; - retval = ConvertUTF16toUTF8( - &pUtf16, pUtf16 + uInputLen, - &pUtf8, pUtf8 + a_uOutputDataSize, - lenientConversion); - } - return retval == conversionOK; - } - else { - size_t retval = wcstombs(a_pOutputData, - a_pInputData, a_uOutputDataSize); - return retval != (size_t) -1; - } - } -}; - -#endif // SI_CONVERT_GENERIC - - -// --------------------------------------------------------------------------- -// SI_CONVERT_ICU -// --------------------------------------------------------------------------- -#ifdef SI_CONVERT_ICU - -#define SI_Case SI_GenericCase -#define SI_NoCase SI_GenericNoCase - -#include - -/** - * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms. - */ -template -class SI_ConvertW { - const char * m_pEncoding; - UConverter * m_pConverter; -protected: - SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { } -public: - SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) { - m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL; - } - - /* copy and assignment */ - SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } - SI_ConvertW & operator=(const SI_ConvertW & rhs) { - m_pEncoding = rhs.m_pEncoding; - m_pConverter = NULL; - return *this; - } - ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); } - - /** Calculate the number of UChar required for converting the input - * from the storage format. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to UChar. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @return Number of UChar required by the string when - * converted. If there are embedded NULL bytes in the - * input data, only the string up and not including - * the NULL byte will be converted. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeFromStore( - const char * a_pInputData, - size_t a_uInputDataLen) - { - SI_ASSERT(a_uInputDataLen != (size_t) -1); - - UErrorCode nError; - - if (!m_pConverter) { - nError = U_ZERO_ERROR; - m_pConverter = ucnv_open(m_pEncoding, &nError); - if (U_FAILURE(nError)) { - return (size_t) -1; - } - } - - nError = U_ZERO_ERROR; - int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0, - a_pInputData, (int32_t) a_uInputDataLen, &nError); - if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { - return (size_t) -1; - } - - return (size_t) nLen; - } - - /** Convert the input string from the storage format to UChar. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to UChar. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @param a_pOutputData Pointer to the output buffer to received the - * converted data. - * @param a_uOutputDataSize Size of the output buffer in UChar. - * @return true if all of the input data was successfully - * converted. - */ - bool ConvertFromStore( - const char * a_pInputData, - size_t a_uInputDataLen, - UChar * a_pOutputData, - size_t a_uOutputDataSize) - { - UErrorCode nError; - - if (!m_pConverter) { - nError = U_ZERO_ERROR; - m_pConverter = ucnv_open(m_pEncoding, &nError); - if (U_FAILURE(nError)) { - return false; - } - } - - nError = U_ZERO_ERROR; - ucnv_toUChars(m_pConverter, - a_pOutputData, (int32_t) a_uOutputDataSize, - a_pInputData, (int32_t) a_uInputDataLen, &nError); - if (U_FAILURE(nError)) { - return false; - } - - return true; - } - - /** Calculate the number of char required by the storage format of this - * data. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated string to calculate the number of - * bytes required to be converted to storage format. - * @return Number of bytes required by the string when - * converted to storage format. This size always - * includes space for the terminating NULL character. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeToStore( - const UChar * a_pInputData) - { - UErrorCode nError; - - if (!m_pConverter) { - nError = U_ZERO_ERROR; - m_pConverter = ucnv_open(m_pEncoding, &nError); - if (U_FAILURE(nError)) { - return (size_t) -1; - } - } - - nError = U_ZERO_ERROR; - int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0, - a_pInputData, -1, &nError); - if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { - return (size_t) -1; - } - - return (size_t) nLen + 1; - } - - /** Convert the input string to the storage format of this data. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated source string to convert. All of - * the data will be converted including the - * terminating NULL character. - * @param a_pOutputData Pointer to the buffer to receive the converted - * string. - * @param a_pOutputDataSize Size of the output buffer in char. - * @return true if all of the input data, including the - * terminating NULL character was successfully - * converted. - */ - bool ConvertToStore( - const UChar * a_pInputData, - char * a_pOutputData, - size_t a_uOutputDataSize) - { - UErrorCode nError; - - if (!m_pConverter) { - nError = U_ZERO_ERROR; - m_pConverter = ucnv_open(m_pEncoding, &nError); - if (U_FAILURE(nError)) { - return false; - } - } - - nError = U_ZERO_ERROR; - ucnv_fromUChars(m_pConverter, - a_pOutputData, (int32_t) a_uOutputDataSize, - a_pInputData, -1, &nError); - if (U_FAILURE(nError)) { - return false; - } - - return true; - } -}; - -#endif // SI_CONVERT_ICU - - -// --------------------------------------------------------------------------- -// SI_CONVERT_WIN32 -// --------------------------------------------------------------------------- -#ifdef SI_CONVERT_WIN32 - -#define SI_Case SI_GenericCase - -// Windows CE doesn't have errno or MBCS libraries -#ifdef _WIN32_WCE -# ifndef SI_NO_MBCS -# define SI_NO_MBCS -# endif -#endif - -#include -#ifdef SI_NO_MBCS -# define SI_NoCase SI_GenericNoCase -#else // !SI_NO_MBCS -/** - * Case-insensitive comparison class using Win32 MBCS functions. This class - * returns a case-insensitive semi-collation order for MBCS text. It may not - * be safe for UTF-8 text returned in char format as we don't know what - * characters will be folded by the function! Therefore, if you are using - * SI_CHAR == char and SetUnicode(true), then you need to use the generic - * SI_NoCase class instead. - */ -#include -template -struct SI_NoCase { - bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { - if (sizeof(SI_CHAR) == sizeof(char)) { - return _mbsicmp((const unsigned char *)pLeft, - (const unsigned char *)pRight) < 0; - } - if (sizeof(SI_CHAR) == sizeof(wchar_t)) { - return _wcsicmp((const wchar_t *)pLeft, - (const wchar_t *)pRight) < 0; - } - return SI_GenericNoCase()(pLeft, pRight); - } -}; -#endif // SI_NO_MBCS - -/** - * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses - * only the Win32 functions and doesn't require the external Unicode UTF-8 - * conversion library. It will not work on Windows 95 without using Microsoft - * Layer for Unicode in your application. - */ -template -class SI_ConvertW { - UINT m_uCodePage; -protected: - SI_ConvertW() { } -public: - SI_ConvertW(bool a_bStoreIsUtf8) { - m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP; - } - - /* copy and assignment */ - SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } - SI_ConvertW & operator=(const SI_ConvertW & rhs) { - m_uCodePage = rhs.m_uCodePage; - return *this; - } - - /** Calculate the number of SI_CHAR required for converting the input - * from the storage format. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @return Number of SI_CHAR required by the string when - * converted. If there are embedded NULL bytes in the - * input data, only the string up and not including - * the NULL byte will be converted. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeFromStore( - const char * a_pInputData, - size_t a_uInputDataLen) - { - SI_ASSERT(a_uInputDataLen != (size_t) -1); - - int retval = MultiByteToWideChar( - m_uCodePage, 0, - a_pInputData, (int) a_uInputDataLen, - 0, 0); - return (size_t)(retval > 0 ? retval : -1); - } - - /** Convert the input string from the storage format to SI_CHAR. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @param a_pOutputData Pointer to the output buffer to received the - * converted data. - * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. - * @return true if all of the input data was successfully - * converted. - */ - bool ConvertFromStore( - const char * a_pInputData, - size_t a_uInputDataLen, - SI_CHAR * a_pOutputData, - size_t a_uOutputDataSize) - { - int nSize = MultiByteToWideChar( - m_uCodePage, 0, - a_pInputData, (int) a_uInputDataLen, - (wchar_t *) a_pOutputData, (int) a_uOutputDataSize); - return (nSize > 0); - } - - /** Calculate the number of char required by the storage format of this - * data. The storage format is always UTF-8. - * - * @param a_pInputData NULL terminated string to calculate the number of - * bytes required to be converted to storage format. - * @return Number of bytes required by the string when - * converted to storage format. This size always - * includes space for the terminating NULL character. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeToStore( - const SI_CHAR * a_pInputData) - { - int retval = WideCharToMultiByte( - m_uCodePage, 0, - (const wchar_t *) a_pInputData, -1, - 0, 0, 0, 0); - return (size_t) (retval > 0 ? retval : -1); - } - - /** Convert the input string to the storage format of this data. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated source string to convert. All of - * the data will be converted including the - * terminating NULL character. - * @param a_pOutputData Pointer to the buffer to receive the converted - * string. - * @param a_pOutputDataSize Size of the output buffer in char. - * @return true if all of the input data, including the - * terminating NULL character was successfully - * converted. - */ - bool ConvertToStore( - const SI_CHAR * a_pInputData, - char * a_pOutputData, - size_t a_uOutputDataSize) - { - int retval = WideCharToMultiByte( - m_uCodePage, 0, - (const wchar_t *) a_pInputData, -1, - a_pOutputData, (int) a_uOutputDataSize, 0, 0); - return retval > 0; - } -}; - -#endif // SI_CONVERT_WIN32 - - -// --------------------------------------------------------------------------- -// TYPE DEFINITIONS -// --------------------------------------------------------------------------- - -typedef CSimpleIniTempl,SI_ConvertA > CSimpleIniA; -typedef CSimpleIniTempl,SI_ConvertA > CSimpleIniCaseA; - -#if defined(SI_CONVERT_ICU) -typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniW; -typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniCaseW; -#else -typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniW; -typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniCaseW; -#endif - -#ifdef _UNICODE -# define CSimpleIni CSimpleIniW -# define CSimpleIniCase CSimpleIniCaseW -# define SI_NEWLINE SI_NEWLINE_W -#else // !_UNICODE -# define CSimpleIni CSimpleIniA -# define CSimpleIniCase CSimpleIniCaseA -# define SI_NEWLINE SI_NEWLINE_A -#endif // _UNICODE - -#ifdef _MSC_VER -# pragma warning (pop) -#endif - -#endif // INCLUDED_SimpleIni_h -