From 844bd543867eacd46343d3c963fb4b53a50534f6 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Mon, 26 Jan 2015 17:59:13 +0100 Subject: [PATCH 01/23] Got rid of CMake --- CMakeLists.txt | 32 ------ Makefile | 284 ------------------------------------------------- makefile | 67 ++++++++++++ 3 files changed, 67 insertions(+), 316 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 Makefile create mode 100644 makefile diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 5edede6..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -project(pusher CXX) - -# C++11 -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - -add_executable(pusher src/main.cpp - src/pushhandler.cpp - src/json/json.cc - src/simpleini/ConvertUTF.c) - -# Libraries -target_link_libraries(pusher curl) - -# Install -INSTALL_TARGETS("/bin" pusher) - -# Debian package -set(CPACK_GENERATOR "DEB") -set(CPACK_DEBIAN_PACKAGE_NAME "pusher") - -set(CPACK_PACKAGE_VERSION "0.3") -set(CPACK_DEBIAN_PACKAGE_VERSION "0.3") -execute_process(COMMAND dpkg --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE) - -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libcurl3 (>= 7.37.1), libstdc++6 (>= 4.9.1), libgcc1 (1:4.9.1)") - -set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Daniel Stein ") -set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "push it to the limits.") -set(CPACK_DEBIAN_PACKAGE_SECTION "util") - -include(CPack) diff --git a/Makefile b/Makefile deleted file mode 100644 index 091a89e..0000000 --- a/Makefile +++ /dev/null @@ -1,284 +0,0 @@ -# CMAKE generated file: DO NOT EDIT! -# Generated by "Unix Makefiles" Generator, CMake Version 2.8 - -# Default target executed when no arguments are given to make. -default_target: all -.PHONY : default_target - -#============================================================================= -# Special targets provided by cmake. - -# Disable implicit rules so canonical targets will work. -.SUFFIXES: - -# Remove some rules from gmake that .SUFFIXES does not remove. -SUFFIXES = - -.SUFFIXES: .hpux_make_needs_suffix_list - -# Suppress display of executed commands. -$(VERBOSE).SILENT: - -# A target that is always out of date. -cmake_force: -.PHONY : cmake_force - -#============================================================================= -# Set environment variables for the build. - -# The shell in which to execute make rules. -SHELL = /bin/sh - -# The CMake executable. -CMAKE_COMMAND = /usr/bin/cmake - -# The command to remove a file. -RM = /usr/bin/cmake -E remove -f - -# Escaping for special characters. -EQUALS = = - -# The top-level source directory on which CMake was run. -CMAKE_SOURCE_DIR = /home/hackherz/Dokumente/GitHub/pusher - -# The top-level build directory on which CMake was run. -CMAKE_BINARY_DIR = /home/hackherz/Dokumente/GitHub/pusher - -#============================================================================= -# Targets provided globally by CMake. - -# Special rule for the target edit_cache -edit_cache: - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running interactive CMake command-line interface..." - /usr/bin/cmake -i . -.PHONY : edit_cache - -# Special rule for the target edit_cache -edit_cache/fast: edit_cache -.PHONY : edit_cache/fast - -# Special rule for the target install -install: preinstall - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." - /usr/bin/cmake -P cmake_install.cmake -.PHONY : install - -# Special rule for the target install -install/fast: preinstall/fast - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." - /usr/bin/cmake -P cmake_install.cmake -.PHONY : install/fast - -# Special rule for the target install/local -install/local: preinstall - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." - /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake -.PHONY : install/local - -# Special rule for the target install/local -install/local/fast: install/local -.PHONY : install/local/fast - -# Special rule for the target install/strip -install/strip: preinstall - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." - /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake -.PHONY : install/strip - -# Special rule for the target install/strip -install/strip/fast: install/strip -.PHONY : install/strip/fast - -# Special rule for the target list_install_components -list_install_components: - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\"" -.PHONY : list_install_components - -# Special rule for the target list_install_components -list_install_components/fast: list_install_components -.PHONY : list_install_components/fast - -# Special rule for the target package -package: preinstall - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Run CPack packaging tool..." - /usr/bin/cpack --config ./CPackConfig.cmake -.PHONY : package - -# Special rule for the target package -package/fast: package -.PHONY : package/fast - -# Special rule for the target package_source -package_source: - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Run CPack packaging tool for source..." - /usr/bin/cpack --config ./CPackSourceConfig.cmake /home/hackherz/Dokumente/GitHub/pusher/CPackSourceConfig.cmake -.PHONY : package_source - -# Special rule for the target package_source -package_source/fast: package_source -.PHONY : package_source/fast - -# Special rule for the target rebuild_cache -rebuild_cache: - @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." - /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) -.PHONY : rebuild_cache - -# Special rule for the target rebuild_cache -rebuild_cache/fast: rebuild_cache -.PHONY : rebuild_cache/fast - -# The main all target -all: cmake_check_build_system - $(CMAKE_COMMAND) -E cmake_progress_start /home/hackherz/Dokumente/GitHub/pusher/CMakeFiles /home/hackherz/Dokumente/GitHub/pusher/CMakeFiles/progress.marks - $(MAKE) -f CMakeFiles/Makefile2 all - $(CMAKE_COMMAND) -E cmake_progress_start /home/hackherz/Dokumente/GitHub/pusher/CMakeFiles 0 -.PHONY : all - -# The main clean target -clean: - $(MAKE) -f CMakeFiles/Makefile2 clean -.PHONY : clean - -# The main clean target -clean/fast: clean -.PHONY : clean/fast - -# Prepare targets for installation. -preinstall: all - $(MAKE) -f CMakeFiles/Makefile2 preinstall -.PHONY : preinstall - -# Prepare targets for installation. -preinstall/fast: - $(MAKE) -f CMakeFiles/Makefile2 preinstall -.PHONY : preinstall/fast - -# clear depends -depend: - $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 -.PHONY : depend - -#============================================================================= -# Target rules for targets named pusher - -# Build rule for target. -pusher: cmake_check_build_system - $(MAKE) -f CMakeFiles/Makefile2 pusher -.PHONY : pusher - -# fast build rule for target. -pusher/fast: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/build -.PHONY : pusher/fast - -src/json/json.o: src/json/json.cc.o -.PHONY : src/json/json.o - -# target to build an object file -src/json/json.cc.o: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/json/json.cc.o -.PHONY : src/json/json.cc.o - -src/json/json.i: src/json/json.cc.i -.PHONY : src/json/json.i - -# target to preprocess a source file -src/json/json.cc.i: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/json/json.cc.i -.PHONY : src/json/json.cc.i - -src/json/json.s: src/json/json.cc.s -.PHONY : src/json/json.s - -# target to generate assembly for a file -src/json/json.cc.s: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/json/json.cc.s -.PHONY : src/json/json.cc.s - -src/main.o: src/main.cpp.o -.PHONY : src/main.o - -# target to build an object file -src/main.cpp.o: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/main.cpp.o -.PHONY : src/main.cpp.o - -src/main.i: src/main.cpp.i -.PHONY : src/main.i - -# target to preprocess a source file -src/main.cpp.i: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/main.cpp.i -.PHONY : src/main.cpp.i - -src/main.s: src/main.cpp.s -.PHONY : src/main.s - -# target to generate assembly for a file -src/main.cpp.s: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/main.cpp.s -.PHONY : src/main.cpp.s - -src/pushhandler.o: src/pushhandler.cpp.o -.PHONY : src/pushhandler.o - -# target to build an object file -src/pushhandler.cpp.o: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/pushhandler.cpp.o -.PHONY : src/pushhandler.cpp.o - -src/pushhandler.i: src/pushhandler.cpp.i -.PHONY : src/pushhandler.i - -# target to preprocess a source file -src/pushhandler.cpp.i: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/pushhandler.cpp.i -.PHONY : src/pushhandler.cpp.i - -src/pushhandler.s: src/pushhandler.cpp.s -.PHONY : src/pushhandler.s - -# target to generate assembly for a file -src/pushhandler.cpp.s: - $(MAKE) -f CMakeFiles/pusher.dir/build.make CMakeFiles/pusher.dir/src/pushhandler.cpp.s -.PHONY : src/pushhandler.cpp.s - -# Help Target -help: - @echo "The following are some of the valid targets for this Makefile:" - @echo "... all (the default if no target is provided)" - @echo "... clean" - @echo "... depend" - @echo "... edit_cache" - @echo "... install" - @echo "... install/local" - @echo "... install/strip" - @echo "... list_install_components" - @echo "... package" - @echo "... package_source" - @echo "... pusher" - @echo "... rebuild_cache" - @echo "... src/json/json.o" - @echo "... src/json/json.i" - @echo "... src/json/json.s" - @echo "... src/main.o" - @echo "... src/main.i" - @echo "... src/main.s" - @echo "... src/pushhandler.o" - @echo "... src/pushhandler.i" - @echo "... src/pushhandler.s" -.PHONY : help - - - -#============================================================================= -# Special targets to cleanup operation of make. - -# Special rule to run CMake to check the build system integrity. -# No rule that depends on this can have commands that come from listfiles -# because they might be regenerated. -cmake_check_build_system: - $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 -.PHONY : cmake_check_build_system - diff --git a/makefile b/makefile new file mode 100644 index 0000000..a8b246b --- /dev/null +++ b/makefile @@ -0,0 +1,67 @@ +#============================================================================= +# Set variables for the build + +TARGET = pusher +INSTALL_DIR = /usr/local/bin +CXX = g++ +CPPFLAGS = -std=c++11 +BUILDCOMMAND = $(CXX) $(CPPFLAGS) +LIBS = -lcurl + + +#============================================================================= +# Build +all: pusher + +pusher: json simpleini pushhandler main + $(BUILDCOMMAND) src/json/json.o src/simpleini/ConvertUTF.o src/pushhandler.o src/main.o $(LIBS) -o $(TARGET) + + +# json library +.PHONY: json +json: src/json/json.o + +src/json/json.o: src/json/json.cc + $(BUILDCOMMAND) -c src/json/json.cc -o src/json/json.o + + +# simpleini +.PHONY: simpleini +simpleini: src/simpleini/ConvertUTF.o + +src/simpleini/ConvertUTF.o: src/simpleini/ConvertUTF.c + $(BUILDCOMMAND) -c src/simpleini/ConvertUTF.c -o src/simpleini/ConvertUTF.o + + +# pushhandler +.PHONY: pushhandler +pushhandler: src/pushhandler.o + +src/pushhandler.o: src/pushhandler.cpp + $(BUILDCOMMAND) -c src/pushhandler.cpp -o src/pushhandler.o + + +# main +.PHONY: main +main: src/main.o + +src/main.o: src/main.cpp + $(BUILDCOMMAND) -c src/main.cpp -o src/main.o + +#============================================================================= +# Other targets +.PHONY: clean +clean: + rm -f src/*.o + rm -f src/json/*.o + rm -f src/simpleini/*.o + rm -f $(TARGET) + + +install: pusher + cp $(TARGET) $(INSTALL_DIR)/ + + +.PHONY: remove +remove: + rm -f $(INSTALL_DIR)/$(TARGET) From 66a3db82fa4acb6d3ace7b5148c819b1be435dba Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Mon, 26 Jan 2015 20:12:00 +0100 Subject: [PATCH 02/23] Cleaned .gitignore --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index e5afc2a..e34ab2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,4 @@ pusher *~ *.o -*.txt *.deb -*cmake* -*cpack* -CMake* -_CPack* From 1b7439f60e1ec07537e9b0a23559e8c80d44cd23 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Mon, 26 Jan 2015 20:19:24 +0100 Subject: [PATCH 03/23] Added missing dependency to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0011e1a..63637e6 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Send push-notifications to your phone from the command line. Uses [PushNotifier] You can download debian packages from [my website](http://hackherz.com/pusher#download). ### Option 2: From source +Make sure you have **libcurl4-openssl-dev** installed. ```bash $ git clone https://github.com/HackHerz/pusher.git $ cd pusher/ From 9ca0833a48f40c929cbef891aa91745af3b685ce Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Tue, 27 Jan 2015 15:56:24 +0100 Subject: [PATCH 04/23] Examples section added --- README.md | 2 ++ examples/README.md | 8 ++++++++ examples/ssh-notification.md | 16 ++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 examples/README.md create mode 100644 examples/ssh-notification.md diff --git a/README.md b/README.md index 63637e6..b070a23 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ $ make install ## Usage +If you want some real life use cases make sure to check out our [example section](https://github.com/hackherz/pusher/examples/). + ### Basic usage ```bash $ pusher -i 1 "Hello World" # Send Hello World to device-id 1 diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..46875a2 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,8 @@ +# Examples + +This is a collection of examples how you can user pusher. Feel free to contact me or add it yourself if you have a snippet to share. + + +## Server + +- [Notification on SSH login](https://github.com/hackherz/pusher/examples/ssh-notification.md) diff --git a/examples/ssh-notification.md b/examples/ssh-notification.md new file mode 100644 index 0000000..7aced63 --- /dev/null +++ b/examples/ssh-notification.md @@ -0,0 +1,16 @@ +# Notification on SSH login + +Maybe you want to receive a notification every time someone logs into your server. Just post this snippet at the end of your **/etc/profile**. + + +```bash +# SSH-login notification +if [ -n "${SSH_CLIENT}" ] +then + TEXT="$(date "+%Y-%m-%d %H:%M:%S") ssh login to ${USER}@$(hostname -f)" + TEXT+=" from $(echo "${SSH_CLIENT}" | awk '{print $1}')" + pusher -i 297 "${TEXT}" +fi +``` + +Do not forget to change the id (297 in this example) to the one of your own device. From b50012b3847d62c1e6e981b5d27a3e2545732062 Mon Sep 17 00:00:00 2001 From: James Rose Date: Thu, 29 Jan 2015 20:53:33 +0000 Subject: [PATCH 05/23] Fixed link to examples --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b070a23..e6e61dc 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ $ make install ## Usage -If you want some real life use cases make sure to check out our [example section](https://github.com/hackherz/pusher/examples/). +If you want some real life use cases make sure to check out our [example section](https://github.com/HackHerz/pusher/tree/master/examples). ### Basic usage ```bash From e8355f15563a5725df8359a44a754a9645d88670 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Fri, 30 Jan 2015 12:46:59 +0100 Subject: [PATCH 06/23] Link in examples fixed --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 46875a2..9ae1197 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,4 +5,4 @@ This is a collection of examples how you can user pusher. Feel free to contact m ## Server -- [Notification on SSH login](https://github.com/hackherz/pusher/examples/ssh-notification.md) +- [Notification on SSH login](https://github.com/HackHerz/pusher/blob/master/examples/ssh-notification.md) From cbd9fcfdc9afd51ef15e0b35c867b7821dae4b37 Mon Sep 17 00:00:00 2001 From: Niels Date: Sat, 21 Feb 2015 11:57:37 +0100 Subject: [PATCH 07/23] new version of JSON for Modern C++ The new version of JSON for Modern C++ is a header-only library with better UTF-8 support. See https://github.com/nlohmann/json#updates-since-last-version for more information. --- makefile | 13 +- src/json/json.cc | 2538 ---------------------------- src/json/json.h | 570 ------- src/json/json.hpp | 3844 +++++++++++++++++++++++++++++++++++++++++++ src/pushhandler.cpp | 2 +- 5 files changed, 3847 insertions(+), 3120 deletions(-) delete mode 100644 src/json/json.cc delete mode 100644 src/json/json.h create mode 100644 src/json/json.hpp diff --git a/makefile b/makefile index a8b246b..30dfb3f 100644 --- a/makefile +++ b/makefile @@ -13,16 +13,8 @@ LIBS = -lcurl # Build all: pusher -pusher: json simpleini pushhandler main - $(BUILDCOMMAND) src/json/json.o src/simpleini/ConvertUTF.o src/pushhandler.o src/main.o $(LIBS) -o $(TARGET) - - -# json library -.PHONY: json -json: src/json/json.o - -src/json/json.o: src/json/json.cc - $(BUILDCOMMAND) -c src/json/json.cc -o src/json/json.o +pusher: simpleini pushhandler main + $(BUILDCOMMAND) src/simpleini/ConvertUTF.o src/pushhandler.o src/main.o $(LIBS) -o $(TARGET) # simpleini @@ -53,7 +45,6 @@ src/main.o: src/main.cpp .PHONY: clean clean: rm -f src/*.o - rm -f src/json/*.o rm -f src/simpleini/*.o rm -f $(TARGET) diff --git a/src/json/json.cc b/src/json/json.cc deleted file mode 100644 index bec3fff..0000000 --- a/src/json/json.cc +++ /dev/null @@ -1,2538 +0,0 @@ -/*! -@file -@copyright The code is licensed under the MIT License - , - Copyright (c) 2013-2015 Niels Lohmann. - -@author Niels Lohmann - -@see https://github.com/nlohmann/json -*/ - -#include "json.h" - -#include // std::isdigit, std::isspace -#include // std::size_t -#include // std::runtime_error -#include // std::swap, std::move - -namespace nlohmann -{ - -/////////////////////////////////// -// CONSTRUCTORS OF UNION "value" // -/////////////////////////////////// - -json::value::value(array_t* _array): array(_array) {} -json::value::value(object_t* object_): object(object_) {} -json::value::value(string_t* _string): string(_string) {} -json::value::value(boolean_t _boolean) : boolean(_boolean) {} -json::value::value(number_t _number) : number(_number) {} -json::value::value(number_float_t _number_float) : number_float(_number_float) {} - - -///////////////////////////////// -// CONSTRUCTORS AND DESTRUCTOR // -///////////////////////////////// - -/*! -Construct an empty JSON given the type. - -@param t the type from the @ref json::type enumeration. - -@post Memory for array, object, and string are allocated. -*/ -json::json(const value_t t) - : type_(t) -{ - switch (type_) - { - case (value_t::array): - { - value_.array = new array_t(); - break; - } - case (value_t::object): - { - value_.object = new object_t(); - break; - } - case (value_t::string): - { - value_.string = new string_t(); - break; - } - case (value_t::boolean): - { - value_.boolean = boolean_t(); - break; - } - case (value_t::number): - { - value_.number = number_t(); - break; - } - case (value_t::number_float): - { - value_.number_float = number_float_t(); - break; - } - default: - { - break; - } - } -} - -/*! -Construct a null JSON object. -*/ -json::json(std::nullptr_t) noexcept : json() -{} - -/*! -Construct a string JSON object. - -@param s a string to initialize the JSON object with -*/ -json::json(const std::string& s) - : type_(value_t::string), value_(new string_t(s)) -{} - -json::json(std::string&& s) - : type_(value_t::string), value_(new string_t(std::move(s))) -{} - -json::json(const char* s) - : type_(value_t::string), value_(new string_t(s)) -{} - -json::json(const bool b) noexcept - : type_(value_t::boolean), value_(b) -{} - -json::json(const array_t& a) - : type_(value_t::array), value_(new array_t(a)) -{} - -json::json(array_t&& a) - : type_(value_t::array), value_(new array_t(std::move(a))) -{} - -json::json(const object_t& o) - : type_(value_t::object), value_(new object_t(o)) -{} - -json::json(object_t&& o) - : type_(value_t::object), value_(new object_t(std::move(o))) -{} - -/*! -This function is a bit tricky as it uses an initializer list of JSON objects -for both arrays and objects. This is not supported by C++, so we use the -following trick. Both initializer lists for objects and arrays will transform -to a list of JSON objects. The only difference is that in case of an object, -the list will contain JSON array objects with two elements - one for the key -and one for the value. As a result, it is sufficient to check if each element -of the initializer list is an array (1) with two elements (2) whose first -element is of type string (3). If this is the case, we treat the whole -initializer list as list of pairs to construct an object. If not, we pass it -as is to create an array. - -@bug With the described approach, we would fail to recognize an array whose - first element is again an arrays as array. -*/ -json::json(list_init_t a) -{ - // check if each element is an array with two elements whose first element - // is a string - for (const auto& element : a) - { - if (element.type_ != value_t::array or - element.size() != 2 or - element[0].type_ != value_t::string) - { - - // the initializer list describes an array - type_ = value_t::array; - value_ = new array_t(a); - return; - } - } - - // the initializer list is a list of pairs - type_ = value_t::object; - value_ = new object_t(); - for (const json& element : a) - { - const std::string k = element[0]; - value_.object->emplace(std::make_pair(std::move(k), - std::move(element[1]))); - } -} - -/*! -A copy constructor for the JSON class. - -@param o the JSON object to copy -*/ -json::json(const json& o) - : type_(o.type_) -{ - switch (type_) - { - case (value_t::array): - { - value_.array = new array_t(*o.value_.array); - break; - } - case (value_t::object): - { - value_.object = new object_t(*o.value_.object); - break; - } - case (value_t::string): - { - value_.string = new string_t(*o.value_.string); - break; - } - case (value_t::boolean): - { - value_.boolean = o.value_.boolean; - break; - } - case (value_t::number): - { - value_.number = o.value_.number; - break; - } - case (value_t::number_float): - { - value_.number_float = o.value_.number_float; - break; - } - default: - { - break; - } - } -} - -/*! -A move constructor for the JSON class. - -@param o the JSON object to move - -@post The JSON object \p o is invalidated. -*/ -json::json(json&& o) noexcept - : type_(std::move(o.type_)), value_(std::move(o.value_)) -{ - // invalidate payload - o.type_ = value_t::null; - o.value_ = {}; -} - -/*! -A copy assignment operator for the JSON class, following the copy-and-swap -idiom. - -@param o A JSON object to assign to this object. -*/ -json& json::operator=(json o) noexcept -{ - std::swap(type_, o.type_); - std::swap(value_, o.value_); - return *this; -} - -json::~json() noexcept -{ - switch (type_) - { - case (value_t::array): - { - delete value_.array; - break; - } - case (value_t::object): - { - delete value_.object; - break; - } - case (value_t::string): - { - delete value_.string; - break; - } - default: - { - // nothing to do for non-pointer types - break; - } - } -} - -/*! -@param s a string representation of a JSON object -@return a JSON object -*/ -json json::parse(const std::string& s) -{ - return parser(s).parse(); -} - -/*! -@param s a string representation of a JSON object -@return a JSON object -*/ -json json::parse(const char* s) -{ - return parser(s).parse(); -} - - -std::string json::type_name() const noexcept -{ - switch (type_) - { - case (value_t::array): - { - return "array"; - } - case (value_t::object): - { - return "object"; - } - case (value_t::null): - { - return "null"; - } - case (value_t::string): - { - return "string"; - } - case (value_t::boolean): - { - return "boolean"; - } - default: - { - return "number"; - } - } -} - - -/////////////////////////////// -// OPERATORS AND CONVERSIONS // -/////////////////////////////// - -/*! -@exception std::logic_error if the function is called for JSON objects whose - type is not string -*/ -template<> -std::string json::get() const -{ - switch (type_) - { - case (value_t::string): - return *value_.string; - default: - throw std::logic_error("cannot cast " + type_name() + " to JSON string"); - } -} - -/*! -@exception std::logic_error if the function is called for JSON objects whose - type is not number (int or float) -*/ -template<> -int json::get() const -{ - switch (type_) - { - case (value_t::number): - return value_.number; - case (value_t::number_float): - return static_cast(value_.number_float); - default: - throw std::logic_error("cannot cast " + type_name() + " to JSON number"); - } -} - -/*! -@exception std::logic_error if the function is called for JSON objects whose - type is not number (int or float) -*/ -template<> -int64_t json::get() const -{ - switch (type_) - { - case (value_t::number): - return value_.number; - case (value_t::number_float): - return static_cast(value_.number_float); - default: - throw std::logic_error("cannot cast " + type_name() + " to JSON number"); - } -} - -/*! -@exception std::logic_error if the function is called for JSON objects whose - type is not number (int or float) -*/ -template<> -double json::get() const -{ - switch (type_) - { - case (value_t::number): - return static_cast(value_.number); - case (value_t::number_float): - return value_.number_float; - default: - throw std::logic_error("cannot cast " + type_name() + " to JSON number"); - } -} - -/*! -@exception std::logic_error if the function is called for JSON objects whose - type is not boolean -*/ -template<> -bool json::get() const -{ - switch (type_) - { - case (value_t::boolean): - return value_.boolean; - default: - throw std::logic_error("cannot cast " + type_name() + " to JSON Boolean"); - } -} - -/*! -@exception std::logic_error if the function is called for JSON objects whose - type is an object -*/ -template<> -json::array_t json::get() const -{ - if (type_ == value_t::array) - { - return *value_.array; - } - if (type_ == value_t::object) - { - throw std::logic_error("cannot cast " + type_name() + " to JSON array"); - } - - array_t result; - result.push_back(*this); - return result; -} - -/*! -@exception std::logic_error if the function is called for JSON objects whose - type is not object -*/ -template<> -json::object_t json::get() const -{ - if (type_ == value_t::object) - { - return *value_.object; - } - else - { - throw std::logic_error("cannot cast " + type_name() + " to JSON object"); - } -} - -json::operator std::string() const -{ - return get(); -} - -json::operator int() const -{ - return get(); -} - -json::operator int64_t() const -{ - return get(); -} - -json::operator double() const -{ - return get(); -} - -json::operator bool() const -{ - return get(); -} - -json::operator array_t() const -{ - return get(); -} - -json::operator object_t() const -{ - return get(); -} - -/*! -Internal implementation of the serialization function. - -\param prettyPrint whether the output shall be pretty-printed -\param indentStep the indent level -\param currentIndent the current indent level (only used internally) -*/ -std::string json::dump(const bool prettyPrint, const unsigned int indentStep, - unsigned int currentIndent) const noexcept -{ - // helper function to return whitespace as indentation - const auto indent = [prettyPrint, ¤tIndent]() - { - return prettyPrint ? std::string(currentIndent, ' ') : std::string(); - }; - - switch (type_) - { - case (value_t::string): - { - return std::string("\"") + escapeString(*value_.string) + "\""; - } - - case (value_t::boolean): - { - return value_.boolean ? "true" : "false"; - } - - case (value_t::number): - { - return std::to_string(value_.number); - } - - case (value_t::number_float): - { - return std::to_string(value_.number_float); - } - - case (value_t::array): - { - if (value_.array->empty()) - { - return "[]"; - } - - std::string result = "["; - - // increase indentation - if (prettyPrint) - { - currentIndent += indentStep; - result += "\n"; - } - - for (array_t::const_iterator i = value_.array->begin(); i != value_.array->end(); ++i) - { - if (i != value_.array->begin()) - { - result += prettyPrint ? ",\n" : ","; - } - result += indent() + i->dump(prettyPrint, indentStep, currentIndent); - } - - // decrease indentation - if (prettyPrint) - { - currentIndent -= indentStep; - result += "\n"; - } - - return result + indent() + "]"; - } - - case (value_t::object): - { - if (value_.object->empty()) - { - return "{}"; - } - - std::string result = "{"; - - // increase indentation - if (prettyPrint) - { - currentIndent += indentStep; - result += "\n"; - } - - for (object_t::const_iterator i = value_.object->begin(); i != value_.object->end(); ++i) - { - if (i != value_.object->begin()) - { - result += prettyPrint ? ",\n" : ","; - } - result += indent() + "\"" + i->first + "\":" + (prettyPrint ? " " : "") + i->second.dump( - prettyPrint, indentStep, - currentIndent); - } - - // decrease indentation - if (prettyPrint) - { - currentIndent -= indentStep; - result += "\n"; - } - - return result + indent() + "}"; - } - - // actually only value_t::null - but making the compiler happy - default: - { - return "null"; - } - } -} - -/*! -Internal function to replace all occurrences of a character in a given string -with another string. - -\param str the string that contains tokens to replace -\param c the character that needs to be replaced -\param replacement the string that is the replacement for the character -*/ -void json::replaceChar(std::string& str, char c, const std::string& replacement) -const -{ - size_t start_pos = 0; - while ((start_pos = str.find(c, start_pos)) != std::string::npos) - { - str.replace(start_pos, 1, replacement); - start_pos += replacement.length(); - } -} - -/*! -Escapes all special characters in the given string according to ECMA-404. -Necessary as some characters such as quotes, backslashes and so on -can't be used as is when dumping a string value. - -\param str the string that should be escaped. - -\return a copy of the given string with all special characters escaped. -*/ -std::string json::escapeString(const std::string& str) const -{ - std::string result(str); - // we first need to escape the backslashes as all other methods will insert - // legitimate backslashes into the result. - replaceChar(result, '\\', "\\\\"); - // replace all other characters - replaceChar(result, '"', "\\\""); - replaceChar(result, '\n', "\\n"); - replaceChar(result, '\r', "\\r"); - replaceChar(result, '\f', "\\f"); - replaceChar(result, '\b', "\\b"); - replaceChar(result, '\t', "\\t"); - return result; -} - -/*! -Serialization function for JSON objects. The function tries to mimick Python's -\p json.dumps() function, and currently supports its \p indent parameter. - -\param 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 - -\see https://docs.python.org/2/library/json.html#json.dump -*/ -std::string json::dump(int indent) const noexcept -{ - if (indent >= 0) - { - return dump(true, static_cast(indent)); - } - else - { - return dump(false, 0); - } -} - - -/////////////////////////////////////////// -// ADDING ELEMENTS TO OBJECTS AND ARRAYS // -/////////////////////////////////////////// - -json& json::operator+=(const json& o) -{ - push_back(o); - return *this; -} - -/*! -@todo comment me -*/ -json& json::operator+=(const object_t::value_type& p) -{ - return operator[](p.first) = p.second; -} - -/*! -@todo comment me -*/ -json& json::operator+=(list_init_t a) -{ - push_back(a); - return *this; -} - -/*! -This function implements the actual "adding to array" function and is called -by all other push_back or operator+= functions. If the function is called for -an array, the passed element is added to the array. - -@param o The element to add to the array. - -@pre The JSON object is an array or null. -@post The JSON object is an array whose last element is the passed element o. -@exception std::runtime_error The function was called for a JSON type that - does not support addition to an array (e.g., int or string). - -@note Null objects are silently transformed into an array before the addition. -*/ -void json::push_back(const json& o) -{ - // push_back only works for null objects or arrays - if (not(type_ == value_t::null or type_ == value_t::array)) - { - throw std::runtime_error("cannot add element to " + type_name()); - } - - // transform null object into an array - if (type_ == value_t::null) - { - type_ = value_t::array; - value_.array = new array_t; - } - - // add element to array - value_.array->push_back(o); -} - -/*! -This function implements the actual "adding to array" function and is called -by all other push_back or operator+= functions. If the function is called for -an array, the passed element is added to the array using move semantics. - -@param o The element to add to the array. - -@pre The JSON object is an array or null. -@post The JSON object is an array whose last element is the passed element o. -@post The element o is destroyed. -@exception std::runtime_error The function was called for a JSON type that - does not support addition to an array (e.g., int or string). - -@note Null objects are silently transformed into an array before the addition. -@note This function applies move semantics for the given element. -*/ -void json::push_back(json&& o) -{ - // push_back only works for null objects or arrays - if (not(type_ == value_t::null or type_ == value_t::array)) - { - throw std::runtime_error("cannot add element to " + type_name()); - } - - // transform null object into an array - if (type_ == value_t::null) - { - type_ = value_t::array; - value_.array = new array_t; - } - - // add element to array (move semantics) - value_.array->emplace_back(std::move(o)); - // invalidate object - o.type_ = value_t::null; -} - -/*! -@todo comment me -*/ -void json::push_back(const object_t::value_type& p) -{ - operator[](p.first) = p.second; -} - -/*! -@todo comment me -*/ -void json::push_back(list_init_t a) -{ - bool is_array = false; - - // check if each element is an array with two elements whose first element - // is a string - for (const auto& element : a) - { - if (element.type_ != value_t::array or - element.size() != 2 or - element[0].type_ != value_t::string) - { - // the initializer list describes an array - is_array = true; - break; - } - } - - if (is_array) - { - for (const json& element : a) - { - push_back(element); - } - } - else - { - for (const json& element : a) - { - const object_t::value_type tmp {element[0].get(), element[1]}; - push_back(tmp); - } - } -} - -/*! -This operator realizes read/write access to array elements given an integer -index. Bounds will not be checked. - -@note The "index" variable should be of type size_t as it is compared against - size() and used in the at() function. However, the compiler will have - problems in case integer literals are used. In this case, an implicit - conversion to both size_t and JSON is possible. Therefore, we use int as - type and convert it to size_t where necessary. - -@param index the index of the element to return from the array -@return reference to element for the given index - -@pre Object is an array. -@exception std::domain_error if object is not an array -*/ -json::reference json::operator[](const int index) -{ - // this [] operator only works for arrays - if (type_ != value_t::array) - { - throw std::domain_error("cannot add entry with index " + - std::to_string(index) + " to " + type_name()); - } - - // return reference to element from array at given index - return (*value_.array)[static_cast(index)]; -} - -/*! -This operator realizes read-only access to array elements given an integer -index. Bounds will not be checked. - -@note The "index" variable should be of type size_t as it is compared against - size() and used in the at() function. However, the compiler will have - problems in case integer literals are used. In this case, an implicit - conversion to both size_t and JSON is possible. Therefore, we use int as - type and convert it to size_t where necessary. - -@param index the index of the element to return from the array -@return read-only reference to element for the given index - -@pre Object is an array. -@exception std::domain_error if object is not an array -*/ -json::const_reference json::operator[](const int index) const -{ - // this [] operator only works for arrays - if (type_ != value_t::array) - { - throw std::domain_error("cannot get entry with index " + - std::to_string(index) + " from " + type_name()); - } - - // return element from array at given index - return (*value_.array)[static_cast(index)]; -} - -/*! -This function realizes read/write access to array elements given an integer -index. Bounds will be checked. - -@note The "index" variable should be of type size_t as it is compared against - size() and used in the at() function. However, the compiler will have - problems in case integer literals are used. In this case, an implicit - conversion to both size_t and JSON is possible. Therefore, we use int as - type and convert it to size_t where necessary. - -@param index the index of the element to return from the array -@return reference to element for the given index - -@pre Object is an array. -@exception std::domain_error if object is not an array -@exception std::out_of_range if index is out of range (via std::vector::at) -*/ -json::reference json::at(const int index) -{ - // this function only works for arrays - if (type_ != value_t::array) - { - throw std::domain_error("cannot add entry with index " + - std::to_string(index) + " to " + type_name()); - } - - // return reference to element from array at given index - return value_.array->at(static_cast(index)); -} - -/*! -This operator realizes read-only access to array elements given an integer -index. Bounds will be checked. - -@note The "index" variable should be of type size_t as it is compared against - size() and used in the at() function. However, the compiler will have - problems in case integer literals are used. In this case, an implicit - conversion to both size_t and JSON is possible. Therefore, we use int as - type and convert it to size_t where necessary. - -@param index the index of the element to return from the array -@return read-only reference to element for the given index - -@pre Object is an array. -@exception std::domain_error if object is not an array -@exception std::out_of_range if index is out of range (via std::vector::at) -*/ -json::const_reference json::at(const int index) const -{ - // this function only works for arrays - if (type_ != value_t::array) - { - throw std::domain_error("cannot get entry with index " + - std::to_string(index) + " from " + type_name()); - } - - // return element from array at given index - return value_.array->at(static_cast(index)); -} - -/*! -@copydoc json::operator[](const char* key) -*/ -json::reference json::operator[](const std::string& key) -{ - return operator[](key.c_str()); -} - -/*! -This operator realizes read/write access to object elements given a string -key. - -@param key the key index of the element to return from the object -@return reference to a JSON object for the given key (null if key does not - exist) - -@pre Object is an object or a null object. -@post null objects are silently converted to objects. - -@exception std::domain_error if object is not an object (or null) -*/ -json::reference json::operator[](const char* key) -{ - // implicitly convert null to object - if (type_ == value_t::null) - { - type_ = value_t::object; - value_.object = new object_t; - } - - // this [] operator only works for objects - if (type_ != value_t::object) - { - throw std::domain_error("cannot add entry with key " + - std::string(key) + " to " + type_name()); - } - - // if the key does not exist, create it - if (value_.object->find(key) == value_.object->end()) - { - (*value_.object)[key] = json(); - } - - // return reference to element from array at given index - return (*value_.object)[key]; -} - -/*! -@copydoc json::operator[](const char* key) -*/ -json::const_reference json::operator[](const std::string& key) const -{ - return operator[](key.c_str()); -} - -/*! -This operator realizes read-only access to object elements given a string -key. - -@param key the key index of the element to return from the object -@return read-only reference to element for the given key - -@pre Object is an object. -@exception std::domain_error if object is not an object -@exception std::out_of_range if key is not found in object -*/ -json::const_reference json::operator[](const char* key) const -{ - // this [] operator only works for objects - if (type_ != value_t::object) - { - throw std::domain_error("cannot get entry with key " + - std::string(key) + " from " + type_name()); - } - - // search for the key - const auto it = value_.object->find(key); - - // make sure the key exists in the object - if (it == value_.object->end()) - { - throw std::out_of_range("key " + std::string(key) + " not found"); - } - - // return element from array at given key - return it->second; -} - -/*! -@copydoc json::at(const char* key) -*/ -json::reference json::at(const std::string& key) -{ - return at(key.c_str()); -} - -/*! -This function realizes read/write access to object elements given a string -key. - -@param key the key index of the element to return from the object -@return reference to a JSON object for the given key (exception if key does not - exist) - -@pre Object is an object. - -@exception std::domain_error if object is not an object -@exception std::out_of_range if key was not found (via std::map::at) -*/ -json::reference json::at(const char* key) -{ - // this function operator only works for objects - if (type_ != value_t::object) - { - throw std::domain_error("cannot add entry with key " + - std::string(key) + " to " + type_name()); - } - - // return reference to element from array at given index - return value_.object->at(key); -} - -/*! -@copydoc json::at(const char *key) const -*/ -json::const_reference json::at(const std::string& key) const -{ - return at(key.c_str()); -} - -/*! -This operator realizes read-only access to object elements given a string -key. - -@param key the key index of the element to return from the object -@return read-only reference to element for the given key - -@pre Object is an object. -@exception std::domain_error if object is not an object -@exception std::out_of_range if key is not found (via std::map::at) -*/ -json::const_reference json::at(const char* key) const -{ - // this [] operator only works for objects - if (type_ != value_t::object) - { - throw std::domain_error("cannot get entry with key " + - std::string(key) + " from " + type_name()); - } - - // return element from array at given key - return value_.object->at(key); -} - -/*! -Returns the size of the JSON object. - -@return the size of the JSON object; the size is the number of elements in - compounds (array and object), 1 for value types (true, false, number, - string), and 0 for null. - -@invariant The size is reported as 0 if and only if empty() would return true. -*/ -json::size_type json::size() const noexcept -{ - switch (type_) - { - case (value_t::array): - { - return value_.array->size(); - } - case (value_t::object): - { - return value_.object->size(); - } - case (value_t::null): - { - return 0; - } - default: - { - return 1; - } - } -} - -/*! -Returns the maximal size of the JSON object. - -@return the maximal size of the JSON object; the maximal size is the maximal - number of elements in compounds (array and object), 1 for value types - (true, false, number, string), and 0 for null. -*/ -json::size_type json::max_size() const noexcept -{ - switch (type_) - { - case (value_t::array): - { - return value_.array->max_size(); - } - case (value_t::object): - { - return value_.object->max_size(); - } - case (value_t::null): - { - return 0; - } - default: - { - return 1; - } - } -} - -/*! -Returns whether a JSON object is empty. - -@return true for null objects and empty compounds (array and object); false - for value types (true, false, number, string) and filled compounds - (array and object). - -@invariant Empty would report true if and only if size() would return 0. -*/ -bool json::empty() const noexcept -{ - switch (type_) - { - case (value_t::array): - { - return value_.array->empty(); - } - case (value_t::object): - { - return value_.object->empty(); - } - case (value_t::null): - { - return true; - } - default: - { - return false; - } - } -} - -/*! -Removes all elements from compounds and resets values to default. - -@invariant Clear will set any value type to its default value which is empty - for compounds, false for booleans, 0 for integer numbers, and 0.0 - for floating numbers. -*/ -void json::clear() noexcept -{ - switch (type_) - { - case (value_t::array): - { - value_.array->clear(); - break; - } - case (value_t::object): - { - value_.object->clear(); - break; - } - case (value_t::string): - { - value_.string->clear(); - break; - } - case (value_t::boolean): - { - value_.boolean = {}; - break; - } - case (value_t::number): - { - value_.number = {}; - break; - } - case (value_t::number_float): - { - value_.number_float = {}; - break; - } - default: - { - break; - } - } -} - -void json::swap(json& o) noexcept -{ - std::swap(type_, o.type_); - std::swap(value_, o.value_); -} - -json::value_t json::type() const noexcept -{ - return type_; -} - -json::iterator json::find(const std::string& key) -{ - return find(key.c_str()); -} - -json::const_iterator json::find(const std::string& key) const -{ - return find(key.c_str()); -} - -json::iterator json::find(const char* key) -{ - auto result = end(); - - if (type_ == value_t::object) - { - delete result.oi_; - result.oi_ = new object_t::iterator(value_.object->find(key)); - result.invalid = (*(result.oi_) == value_.object->end()); - } - - return result; -} - -json::const_iterator json::find(const char* key) const -{ - auto result = cend(); - - if (type_ == value_t::object) - { - delete result.oi_; - result.oi_ = new object_t::const_iterator(value_.object->find(key)); - result.invalid = (*(result.oi_) == value_.object->cend()); - } - - return result; -} - -bool json::operator==(const json& o) const noexcept -{ - switch (type_) - { - case (value_t::array): - { - if (o.type_ == value_t::array) - { - return *value_.array == *o.value_.array; - } - break; - } - case (value_t::object): - { - if (o.type_ == value_t::object) - { - return *value_.object == *o.value_.object; - } - break; - } - case (value_t::null): - { - if (o.type_ == value_t::null) - { - return true; - } - break; - } - case (value_t::string): - { - if (o.type_ == value_t::string) - { - return *value_.string == *o.value_.string; - } - break; - } - case (value_t::boolean): - { - if (o.type_ == value_t::boolean) - { - return value_.boolean == o.value_.boolean; - } - break; - } - case (value_t::number): - { - if (o.type_ == value_t::number) - { - return value_.number == o.value_.number; - } - if (o.type_ == value_t::number_float) - { - return value_.number == static_cast(o.value_.number_float); - } - break; - } - case (value_t::number_float): - { - if (o.type_ == value_t::number) - { - return value_.number_float == static_cast(o.value_.number); - } - if (o.type_ == value_t::number_float) - { - return value_.number_float == o.value_.number_float; - } - break; - } - } - - return false; -} - -bool json::operator!=(const json& o) const noexcept -{ - return not operator==(o); -} - - -json::iterator json::begin() noexcept -{ - return json::iterator(this, true); -} - -json::iterator json::end() noexcept -{ - return json::iterator(this, false); -} - -json::const_iterator json::begin() const noexcept -{ - return json::const_iterator(this, true); -} - -json::const_iterator json::end() const noexcept -{ - return json::const_iterator(this, false); -} - -json::const_iterator json::cbegin() const noexcept -{ - return json::const_iterator(this, true); -} - -json::const_iterator json::cend() const noexcept -{ - return json::const_iterator(this, false); -} - -json::reverse_iterator json::rbegin() noexcept -{ - return reverse_iterator(end()); -} - -json::reverse_iterator json::rend() noexcept -{ - return reverse_iterator(begin()); -} - -json::const_reverse_iterator json::crbegin() const noexcept -{ - return const_reverse_iterator(cend()); -} - -json::const_reverse_iterator json::crend() const noexcept -{ - return const_reverse_iterator(cbegin()); -} - - -json::iterator::iterator(json* j, bool begin) - : object_(j), invalid(not begin or j == nullptr) -{ - if (object_ != nullptr) - { - if (object_->type_ == json::value_t::array) - { - if (begin) - { - vi_ = new array_t::iterator(object_->value_.array->begin()); - invalid = (*vi_ == object_->value_.array->end()); - } - else - { - vi_ = new array_t::iterator(object_->value_.array->end()); - } - } - else if (object_->type_ == json::value_t::object) - { - if (begin) - { - oi_ = new object_t::iterator(object_->value_.object->begin()); - invalid = (*oi_ == object_->value_.object->end()); - } - else - { - oi_ = new object_t::iterator(object_->value_.object->end()); - } - } - } -} - -json::iterator::iterator(const json::iterator& o) - : object_(o.object_), invalid(o.invalid) -{ - if (o.vi_ != nullptr) - { - vi_ = new array_t::iterator(*(o.vi_)); - } - - if (o.oi_ != nullptr) - { - oi_ = new object_t::iterator(*(o.oi_)); - } -} - -json::iterator::~iterator() -{ - delete vi_; - delete oi_; -} - -json::iterator& json::iterator::operator=(json::iterator o) -{ - std::swap(object_, o.object_); - std::swap(vi_, o.vi_); - std::swap(oi_, o.oi_); - std::swap(invalid, o.invalid); - return *this; -} - -bool json::iterator::operator==(const json::iterator& o) const -{ - if (object_ != nullptr and o.object_ != nullptr) - { - if (object_->type_ == json::value_t::array and o.object_->type_ == json::value_t::array) - { - return (*vi_ == *(o.vi_)); - } - if (object_->type_ == json::value_t::object and o.object_->type_ == json::value_t::object) - { - return (*oi_ == *(o.oi_)); - } - - if (invalid == o.invalid and object_ == o.object_) - { - return true; - } - } - return false; -} - -bool json::iterator::operator!=(const json::iterator& o) const -{ - return not operator==(o); -} - -json::iterator& json::iterator::operator++() -{ - if (object_ != nullptr) - { - switch (object_->type_) - { - case (json::value_t::array): - { - std::advance(*vi_, 1); - invalid = (*vi_ == object_->value_.array->end()); - break; - } - case (json::value_t::object): - { - std::advance(*oi_, 1); - invalid = (*oi_ == object_->value_.object->end()); - break; - } - default: - { - invalid = true; - break; - } - } - } - - return *this; -} - -json::iterator& json::iterator::operator--() -{ - if (object_ != nullptr) - { - switch (object_->type_) - { - case (json::value_t::array): - { - invalid = (*vi_ == object_->value_.array->begin()); - std::advance(*vi_, -1); - break; - } - case (json::value_t::object): - { - invalid = (*oi_ == object_->value_.object->begin()); - std::advance(*oi_, -1); - break; - } - default: - { - invalid = true; - break; - } - } - } - - return *this; -} - -json& json::iterator::operator*() const -{ - if (object_ == nullptr or invalid) - { - throw std::out_of_range("cannot get value"); - } - - switch (object_->type_) - { - case (json::value_t::array): - { - return **vi_; - } - case (json::value_t::object): - { - return (*oi_)->second; - } - default: - { - return *object_; - } - } -} - -json* json::iterator::operator->() const -{ - if (object_ == nullptr or invalid) - { - throw std::out_of_range("cannot get value"); - } - - switch (object_->type_) - { - case (json::value_t::array): - { - return &(**vi_); - } - case (json::value_t::object): - { - return &((*oi_)->second); - } - default: - { - return object_; - } - } -} - -std::string json::iterator::key() const -{ - if (object_ == nullptr or invalid or object_->type_ != json::value_t::object) - { - throw std::out_of_range("cannot get value"); - } - - return (*oi_)->first; -} - -json& json::iterator::value() const -{ - if (object_ == nullptr or invalid) - { - throw std::out_of_range("cannot get value"); - } - - switch (object_->type_) - { - case (json::value_t::array): - { - return **vi_; - } - case (json::value_t::object): - { - return (*oi_)->second; - } - default: - { - return *object_; - } - } -} - - -json::const_iterator::const_iterator(const json* j, bool begin) - : object_(j), invalid(not begin or j == nullptr) -{ - if (object_ != nullptr) - { - if (object_->type_ == json::value_t::array) - { - if (begin) - { - vi_ = new array_t::const_iterator(object_->value_.array->cbegin()); - invalid = (*vi_ == object_->value_.array->cend()); - } - else - { - vi_ = new array_t::const_iterator(object_->value_.array->cend()); - } - } - else if (object_->type_ == json::value_t::object) - { - if (begin) - { - oi_ = new object_t::const_iterator(object_->value_.object->cbegin()); - invalid = (*oi_ == object_->value_.object->cend()); - } - else - { - oi_ = new object_t::const_iterator(object_->value_.object->cend()); - } - } - } -} - -json::const_iterator::const_iterator(const json::const_iterator& o) - : object_(o.object_), invalid(o.invalid) -{ - if (o.vi_ != nullptr) - { - vi_ = new array_t::const_iterator(*(o.vi_)); - } - if (o.oi_ != nullptr) - { - oi_ = new object_t::const_iterator(*(o.oi_)); - } -} - -json::const_iterator::const_iterator(const json::iterator& o) - : object_(o.object_), invalid(o.invalid) -{ - if (o.vi_ != nullptr) - { - vi_ = new array_t::const_iterator(*(o.vi_)); - } - if (o.oi_ != nullptr) - { - oi_ = new object_t::const_iterator(*(o.oi_)); - } -} - -json::const_iterator::~const_iterator() -{ - delete vi_; - delete oi_; -} - -json::const_iterator& json::const_iterator::operator=(json::const_iterator o) -{ - std::swap(object_, o.object_); - std::swap(vi_, o.vi_); - std::swap(oi_, o.oi_); - std::swap(invalid, o.invalid); - return *this; -} - -bool json::const_iterator::operator==(const json::const_iterator& o) const -{ - if (object_ != nullptr and o.object_ != nullptr) - { - if (object_->type_ == json::value_t::array and o.object_->type_ == json::value_t::array) - { - return (*vi_ == *(o.vi_)); - } - if (object_->type_ == json::value_t::object and o.object_->type_ == json::value_t::object) - { - return (*oi_ == *(o.oi_)); - } - if (invalid == o.invalid and object_ == o.object_) - { - return true; - } - } - - return false; -} - -bool json::const_iterator::operator!=(const json::const_iterator& o) const -{ - return not operator==(o); -} - -json::const_iterator& json::const_iterator::operator++() -{ - if (object_ != nullptr) - { - switch (object_->type_) - { - case (json::value_t::array): - { - std::advance(*vi_, 1); - invalid = (*vi_ == object_->value_.array->end()); - break; - } - case (json::value_t::object): - { - std::advance(*oi_, 1); - invalid = (*oi_ == object_->value_.object->end()); - break; - } - default: - { - invalid = true; - break; - } - } - } - - return *this; -} - -json::const_iterator& json::const_iterator::operator--() -{ - if (object_ != nullptr) - { - switch (object_->type_) - { - case (json::value_t::array): - { - invalid = (*vi_ == object_->value_.array->begin()); - std::advance(*vi_, -1); - break; - } - case (json::value_t::object): - { - invalid = (*oi_ == object_->value_.object->begin()); - std::advance(*oi_, -1); - break; - } - default: - { - invalid = true; - break; - } - } - } - - return *this; -} - -const json& json::const_iterator::operator*() const -{ - if (object_ == nullptr or invalid) - { - throw std::out_of_range("cannot get value"); - } - - switch (object_->type_) - { - case (json::value_t::array): - { - return **vi_; - } - case (json::value_t::object): - { - return (*oi_)->second; - } - default: - { - return *object_; - } - } -} - -const json* json::const_iterator::operator->() const -{ - if (object_ == nullptr or invalid) - { - throw std::out_of_range("cannot get value"); - } - - switch (object_->type_) - { - case (json::value_t::array): - { - return &(**vi_); - } - case (json::value_t::object): - { - return &((*oi_)->second); - } - default: - { - return object_; - } - } -} - -std::string json::const_iterator::key() const -{ - if (object_ == nullptr or invalid or object_->type_ != json::value_t::object) - { - throw std::out_of_range("cannot get value"); - } - - return (*oi_)->first; -} - -const json& json::const_iterator::value() const -{ - if (object_ == nullptr or invalid) - { - throw std::out_of_range("cannot get value"); - } - - switch (object_->type_) - { - case (json::value_t::array): - { - return **vi_; - } - case (json::value_t::object): - { - return (*oi_)->second; - } - default: - { - return *object_; - } - } -} - - -/*! -Initialize the JSON parser given a string \p s. - -@note After initialization, the function @ref parse has to be called manually. - -@param s string to parse - -@post \p s is copied to the buffer @ref buffer_ and the first character is - read. Whitespace is skipped. -*/ -json::parser::parser(const char* s) - : buffer_(s) -{ - // read first character - next(); -} - -/*! -@copydoc json::parser::parser(const char* s) -*/ -json::parser::parser(const std::string& s) - : buffer_(s) -{ - // read first character - next(); -} - -/*! -Initialize the JSON parser given an input stream \p _is. - -@note After initialization, the function @ref parse has to be called manually. - -\param _is input stream to parse - -@post \p _is is copied to the buffer @ref buffer_ and the firsr character is - read. Whitespace is skipped. - -*/ -json::parser::parser(std::istream& _is) -{ - while (_is) - { - std::string input_line; - std::getline(_is, input_line); - buffer_ += input_line; - } - - // read first character - next(); -} - -json json::parser::parse() -{ - switch (current_) - { - case ('{'): - { - // explicitly set result to object to cope with {} - json result(value_t::object); - - next(); - - // process nonempty object - if (current_ != '}') - { - do - { - // key - auto key = parseString(); - - // colon - expect(':'); - - // value - result[std::move(key)] = parse(); - key.clear(); - } - while (current_ == ',' and next()); - } - - // closing brace - expect('}'); - - return result; - } - - case ('['): - { - // explicitly set result to array to cope with [] - json result(value_t::array); - - next(); - - // process nonempty array - if (current_ != ']') - { - do - { - result.push_back(parse()); - } - while (current_ == ',' and next()); - } - - // closing bracket - expect(']'); - - return result; - } - - case ('\"'): - { - return json(parseString()); - } - - case ('t'): - { - parseTrue(); - return json(true); - } - - case ('f'): - { - parseFalse(); - return json(false); - } - - case ('n'): - { - parseNull(); - return json(); - } - - case ('-'): - case ('0'): - case ('1'): - case ('2'): - case ('3'): - case ('4'): - case ('5'): - case ('6'): - case ('7'): - case ('8'): - case ('9'): - { - // remember position of number's first character - const auto _firstpos_ = pos_ - 1; - - while (next() and (std::isdigit(current_) or current_ == '.' - or current_ == 'e' or current_ == 'E' - or current_ == '+' or current_ == '-')); - - try - { - const auto float_val = std::stold(buffer_.substr(_firstpos_, pos_ - _firstpos_)); - const auto int_val = static_cast(float_val); - - // check if conversion loses precision - if (float_val == int_val) - { - // we would not lose precision -> int - return json(int_val); - } - else - { - // we would lose precision -> float - return json(static_cast(float_val)); - } - } - catch (...) - { - error("error translating " + - buffer_.substr(_firstpos_, pos_ - _firstpos_) + " to number"); - } - } - - default: - { - error("unexpected character"); - } - } -} - -/*! -This function reads the next character from the buffer while ignoring all -trailing whitespace. If another character could be read, the function returns -true. If the end of the buffer is reached, false is returned. - -@return whether another non-whitespace character could be read - -@post current_ holds the next character -*/ -bool json::parser::next() -{ - if (pos_ == buffer_.size()) - { - return false; - } - - current_ = buffer_[pos_++]; - - // skip trailing whitespace - while (std::isspace(current_)) - { - if (pos_ == buffer_.size()) - { - return false; - } - - current_ = buffer_[pos_++]; - } - - return true; -} - -/*! -This function encapsulates the error reporting functions of the parser class. -It throws a \p std::invalid_argument exception with a description where the -error occurred (given as the number of characters read), what went wrong (using -the error message \p msg), and the last read token. - -@param msg an error message -@return This function does not return. - -@exception std::invalid_argument whenever the function is called -*/ -void json::parser::error(const std::string& msg) const -{ - throw std::invalid_argument("parse error at position " + - std::to_string(pos_) + ": " + msg + - ", last read: '" + current_ + "'"); -} - -/*! -Parses a string after opening quotes (\p ") where read. - -@return the parsed string - -@pre An opening quote \p " was read in the main parse function @ref parse. - pos_ is the position after the opening quote. - -@post The character after the closing quote \p " is the current character @ref - current_. Whitespace is skipped. - -@todo Unicode escapes such as \uxxxx are missing - see - https://github.com/nlohmann/json/issues/12 -*/ -std::string json::parser::parseString() -{ - // true if and only if the amount of backslashes before the current - // character is even - bool evenAmountOfBackslashes = true; - - // the result of the parse process - std::string result; - - // iterate with pos_ over the whole input until we found the end and return - // or we exit via error() - for (; pos_ < buffer_.size(); pos_++) - { - char currentChar = buffer_[pos_]; - - if (not evenAmountOfBackslashes) - { - // uneven amount of backslashes means the user wants to escape - // something so we know there is a case such as '\X' or '\\\X' but - // we don't know yet what X is. - // at this point in the code, the currentChar has the value of X. - - // slash, backslash and quote are copied as is - if (currentChar == '/' or currentChar == '\\' or currentChar == '"') - { - result += currentChar; - } - else - { - // all other characters are replaced by their respective special - // character - switch (currentChar) - { - 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 'u': - { - // \uXXXX[\uXXXX] is used for escaping unicode, which - // has it's own subroutine. - result += parseUnicodeEscape(); - // the parsing process has brought us one step behind - // the unicode escape sequence: - // \uXXXX - // ^ - // we need to go one character back or the parser would - // skip the character we are currently pointing at as - // the for-loop will decrement pos_ after this iteration - pos_--; - break; - } - default: - { - error("expected one of \\, /, b, f, n, r, t, u behind backslash."); - } - } - } - } - else - { - if (currentChar == '"') - { - // currentChar is a quote, so we found the end of the string - - // set pos_ behind the trailing quote - pos_++; - // find next char to parse - next(); - - // bring the result of the parsing process back to the caller - return result; - } - else if (currentChar != '\\') - { - // all non-backslash characters are added to the end of the - // result string. The only backslashes we want in the result - // are the ones that are escaped (which happens above). - result += currentChar; - } - } - - // remember if we have an even amount of backslashes before the current - // character - if (currentChar == '\\') - { - // jump between even/uneven for each backslash we encounter - evenAmountOfBackslashes = not evenAmountOfBackslashes; - } - else - { - // zero backslashes are also an even number, so as soon as we - // encounter a non-backslash the chain of backslashes breaks and - // we start again from zero - evenAmountOfBackslashes = true; - } - } - - // we iterated over the whole string without finding a unescaped quote - // so the given string is malformed - error("expected '\"'"); -} - - - -/*! -Turns a code point into it's UTF-8 representation. -You should only pass numbers < 0x10ffff into this function -(everything else is a invalid code point). - -@return the UTF-8 representation of the given code point -*/ -std::string json::parser::codePointToUTF8(unsigned int codePoint) const -{ - // this method contains a lot of bit manipulations to - // build the bytes for UTF-8. - - // the '(... >> S) & 0xHH'-patterns are used to retrieve - // certain bits from the code points. - - // all static casts in this method have boundary checks - - // we initialize all strings with their final length - // (e.g. 1 to 4 bytes) to save the reallocations. - - if (codePoint <= 0x7f) - { - // it's just a ASCII compatible codePoint, - // so we just interpret the point as a character - // and return ASCII - - return std::string(1, static_cast(codePoint)); - } - // if true, we need two bytes to encode this as UTF-8 - else if (codePoint <= 0x7ff) - { - // the 0xC0 enables the two most significant two bits - // to make this a two-byte UTF-8 character. - std::string result(2, static_cast(0xC0 | ((codePoint >> 6) & 0x1F))); - result[1] = static_cast(0x80 | (codePoint & 0x3F)); - return result; - } - // if true, now we need three bytes to encode this as UTF-8 - else if (codePoint <= 0xffff) - { - // the 0xE0 enables the three most significant two bits - // to make this a three-byte UTF-8 character. - std::string result(3, static_cast(0xE0 | ((codePoint >> 12) & 0x0F))); - result[1] = static_cast(0x80 | ((codePoint >> 6) & 0x3F)); - result[2] = static_cast(0x80 | (codePoint & 0x3F)); - return result; - } - // if true, we need maximal four bytes to encode this as UTF-8 - else if (codePoint <= 0x10ffff) - { - // the 0xE0 enables the four most significant two bits - // to make this a three-byte UTF-8 character. - std::string result(4, static_cast(0xF0 | ((codePoint >> 18) & 0x07))); - result[1] = static_cast(0x80 | ((codePoint >> 12) & 0x3F)); - result[2] = static_cast(0x80 | ((codePoint >> 6) & 0x3F)); - result[3] = static_cast(0x80 | (codePoint & 0x3F)); - return result; - } - else - { - // Can't be tested without direct access to this private method. - std::string errorMessage = "Invalid codePoint: "; - errorMessage += codePoint; - error(errorMessage); - } -} - -/*! -Parses 4 hexadecimal characters as a number. - -@return the value of the number the hexadecimal characters represent. - -@pre pos_ is pointing to the first of the 4 hexadecimal characters. - -@post pos_ is pointing to the character after the 4 hexadecimal characters. -*/ -unsigned int json::parser::parse4HexCodePoint() -{ - const auto startPos = pos_; - - // check if the remaining buffer is long enough to even hold 4 characters - if (pos_ + 3 >= buffer_.size()) - { - error("Got end of input while parsing unicode escape sequence \\uXXXX"); - } - - // make a string that can hold the pair - std::string hexCode(4, ' '); - - for (; pos_ < startPos + 4; pos_++) - { - // no boundary check here as we already checked above - char currentChar = buffer_[pos_]; - - // check if we have a hexadecimal character - if ((currentChar >= '0' and currentChar <= '9') - or (currentChar >= 'a' and currentChar <= 'f') - or (currentChar >= 'A' and currentChar <= 'F')) - { - // all is well, we have valid hexadecimal chars - // so we copy that char into our string - hexCode[pos_ - startPos] = currentChar; - } - else - { - error("Found non-hexadecimal character in unicode escape sequence!"); - } - } - // the cast is safe as 4 hex characters can't present more than 16 bits - // the input to stoul was checked to contain only hexadecimal characters - // (see above) - return static_cast(std::stoul(hexCode, nullptr, 16)); -} - -/*! -Parses the unicode escape codes as defined in the ECMA-404. -The escape sequence has two forms: -1. \uXXXX -2. \uXXXX\uYYYY -where X and Y are a hexadecimal character (a-zA-Z0-9). - -Form 1 just contains the unicode code point in the hexadecimal number XXXX. -Form 2 is encoding a UTF-16 surrogate pair. The high surrogate is XXXX, the low -surrogate is YYYY. - -@return the UTF-8 character this unicode escape sequence escaped. - -@pre pos_ is pointing at at the 'u' behind the first backslash. - -@post pos_ is pointing at the character behind the last X (or Y in form 2). -*/ -std::string json::parser::parseUnicodeEscape() -{ - // jump to the first hex value - pos_++; - // parse the hex first hex values - unsigned int firstCodePoint = parse4HexCodePoint(); - - if (firstCodePoint >= 0xD800 and firstCodePoint <= 0xDBFF) - { - // we found invalid code points, which means we either have a malformed - // input or we found a high surrogate. - // we can only find out by seeing if the next character also wants to - // encode a unicode character (so, we have the \uXXXX\uXXXX case here). - - // jump behind the next \u - pos_ += 2; - // try to parse the next hex values. - // the method does boundary checking for us, so no need to do that here - unsigned secondCodePoint = parse4HexCodePoint(); - // ok, we have a low surrogate, check if it is a valid one - if (secondCodePoint >= 0xDC00 and secondCodePoint <= 0xDFFF) - { - // calculate the code point from the pair according to the spec - unsigned int finalCodePoint = - // high surrogate occupies the most significant 22 bits - (firstCodePoint << 10) - // low surrogate occupies the least significant 15 bits - + secondCodePoint - // there is still the 0xD800, 0xDC00 and 0x10000 noise in - // the result - // so we have to substract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00; - - // we transform the calculated point into UTF-8 - return codePointToUTF8(finalCodePoint); - } - else - { - error("missing low surrogate"); - } - - } - // We have Form 1, so we just interpret the XXXX as a code point - return codePointToUTF8(firstCodePoint); -} - - -/*! -This function is called in case a \p "t" is read in the main parse function -@ref parse. In the standard, the \p "true" token is the only candidate, so the -next three characters are expected to be \p "rue". In case of a mismatch, an -error is raised via @ref error. - -@pre A \p "t" was read in the main parse function @ref parse. -@post The character after the \p "true" is the current character. Whitespace is - skipped. -*/ -void json::parser::parseTrue() -{ - if (buffer_.substr(pos_, 3) != "rue") - { - error("expected true"); - } - - pos_ += 3; - - // read next character - next(); -} - -/*! -This function is called in case an \p "f" is read in the main parse function -@ref parse. In the standard, the \p "false" token is the only candidate, so the -next four characters are expected to be \p "alse". In case of a mismatch, an -error is raised via @ref error. - -@pre An \p "f" was read in the main parse function. -@post The character after the \p "false" is the current character. Whitespace - is skipped. -*/ -void json::parser::parseFalse() -{ - if (buffer_.substr(pos_, 4) != "alse") - { - error("expected false"); - } - - pos_ += 4; - - // read next character - next(); -} - -/*! -This function is called in case an \p "n" is read in the main parse function -@ref parse. In the standard, the \p "null" token is the only candidate, so the -next three characters are expected to be \p "ull". In case of a mismatch, an -error is raised via @ref error. - -@pre An \p "n" was read in the main parse function. -@post The character after the \p "null" is the current character. Whitespace is - skipped. -*/ -void json::parser::parseNull() -{ - if (buffer_.substr(pos_, 3) != "ull") - { - error("expected null"); - } - - pos_ += 3; - - // read next character - next(); -} - -/*! -This function wraps functionality to check whether the current character @ref -current_ matches a given character \p c. In case of a match, the next character -of the buffer @ref buffer_ is read. In case of a mismatch, an error is raised -via @ref error. - -@param c character that is expected - -@post The next chatacter is read. Whitespace is skipped. -*/ -void json::parser::expect(const char c) -{ - if (current_ != c) - { - std::string msg = "expected '"; - msg.append(1, c); - msg += "'"; - error(msg); - } - else - { - next(); - } -} - -} - -/*! -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 s a string representation of a JSON object -@return a JSON object -*/ -nlohmann::json operator "" _json(const char* s, std::size_t) -{ - return nlohmann::json::parse(s); -} diff --git a/src/json/json.h b/src/json/json.h deleted file mode 100644 index f6acc76..0000000 --- a/src/json/json.h +++ /dev/null @@ -1,570 +0,0 @@ -/*! -@file -@copyright The code is licensed under the MIT License - , - Copyright (c) 2013-2015 Niels Lohmann. - -@author Niels Lohmann - -@see https://github.com/nlohmann/json -*/ - -#pragma once - -#include // std::initializer_list -#include // std::istream, std::ostream -#include // std::map -#include // std::string -#include // std::vector -#include // std::iterator -#include // std::numeric_limits -#include // std::hash - -namespace nlohmann -{ - -/*! -@brief JSON for Modern C++ - -The size of a JSON object is 16 bytes: 8 bytes for the value union whose -largest item is a pointer type and another 8 byte for an element of the -type union. The latter only needs 1 byte - the remaining 7 bytes are wasted -due to alignment. - -@see http://stackoverflow.com/questions/7758580/writing-your-own-stl-container/7759622#7759622 - -@bug Numbers are currently handled too generously. There are several formats - that are forbidden by the standard, but are accepted by the parser. - -@todo Implement json::insert(), json::emplace(), json::emplace_back, json::erase -*/ -class json -{ - public: - // forward declaration to friend this class - class iterator; - class const_iterator; - - public: - // container types - /// the type of elements in a JSON class - using value_type = json; - /// the type of element references - using reference = json&; - /// the type of const element references - using const_reference = const json&; - /// the type of pointers to elements - using pointer = json*; - /// the type of const pointers to elements - using const_pointer = const json*; - /// 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; - /// an iterator for a JSON container - using iterator = json::iterator; - /// a const iterator for a JSON container - using const_iterator = json::const_iterator; - /// a reverse iterator for a JSON container - using reverse_iterator = std::reverse_iterator; - /// a const reverse iterator for a JSON container - using const_reverse_iterator = std::reverse_iterator; - - /// a type for an object - using object_t = std::map; - /// a type for an array - using array_t = std::vector; - /// a type for a string - using string_t = std::string; - /// a type for a Boolean - using boolean_t = bool; - /// a type for an integer number - using number_t = int64_t; - /// a type for a floating point number - using number_float_t = double; - /// a type for list initialization - using list_init_t = std::initializer_list; - - /// a JSON value - union value - { - /// array as pointer to array_t - array_t* array; - /// object as pointer to object_t - object_t* object; - /// string as pointer to string_t - string_t* string; - /// Boolean - boolean_t boolean; - /// number (integer) - number_t number; - /// number (float) - number_float_t number_float; - - /// default constructor - value() = default; - /// constructor for arrays - value(array_t*); - /// constructor for objects - value(object_t*); - /// constructor for strings - value(string_t*); - /// constructor for Booleans - value(boolean_t); - /// constructor for numbers (integer) - value(number_t); - /// constructor for numbers (float) - value(number_float_t); - }; - - /// possible types of a JSON object - enum class value_t : uint8_t - { - /// ordered collection of values - array = 0, - /// unordered set of name/value pairs - object, - /// null value - null, - /// string value - string, - /// Boolean value - boolean, - /// number value (integer) - number, - /// number value (float) - number_float - }; - - public: - /// create an object according to given type - json(const value_t); - /// create a null object - json() = default; - /// create a null object - json(std::nullptr_t) noexcept; - /// create a string object from a C++ string - json(const std::string&); - /// create a string object from a C++ string (move) - json(std::string&&); - /// create a string object from a C string - json(const char*); - /// create a Boolean object - json(const bool) noexcept; - /// create an array - json(const array_t&); - /// create an array (move) - json(array_t&&); - /// create an object - json(const object_t&); - /// create an object (move) - json(object_t&&); - /// create from an initializer list (to an array or object) - json(list_init_t); - - /*! - @brief create a number object (integer) - @param n an integer number to wrap in a JSON object - */ - template::is_integer, T>::type - = 0> - json(const T n) noexcept - : type_(value_t::number), - value_(static_cast(n)) - {} - - /*! - @brief create a number object (float) - @param n a floating point number to wrap in a JSON object - */ - template::value>::type - > - json(const T n) noexcept - : type_(value_t::number_float), - value_(static_cast(n)) - {} - - /*! - @brief create an array object - @param v any type of container whose elements can be use to construct - JSON objects (e.g., std::vector, std::set, std::array) - @note For some reason, we need to explicitly forbid JSON iterator types. - */ - template ::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> - json(const V& v) : json(array_t(v.begin(), v.end())) - {} - - /*! - @brief create a JSON object - @param v any type of associative container whose elements can be use to - construct JSON objects (e.g., std::map) - */ - template ::value and - std::is_constructible::value, int>::type - = 0> - json(const V& v) : json(object_t(v.begin(), v.end())) - {} - - /// copy constructor - json(const json&); - /// move constructor - json(json&&) noexcept; - - /// copy assignment - json& operator=(json) noexcept; - - /// destructor - ~json() noexcept; - - /// create from string representation - static json parse(const std::string&); - /// create from string representation - static json parse(const char*); - - private: - /// return the type as string - std::string type_name() const noexcept; - - /// dump the object (with pretty printer) - std::string dump(const bool, const unsigned int, unsigned int = 0) const noexcept; - /// replaced a character in a string with another string - void replaceChar(std::string& str, char c, const std::string& replacement) const; - /// escapes special characters to safely dump the string - std::string escapeString(const std::string&) const; - - public: - /// explicit value conversion - template - T get() const; - - /// implicit conversion to string representation - operator std::string() const; - /// implicit conversion to integer (only for numbers) - operator int() const; - /// implicit conversion to integer (only for numbers) - operator int64_t() const; - /// implicit conversion to double (only for numbers) - operator double() const; - /// implicit conversion to Boolean (only for Booleans) - operator bool() const; - /// implicit conversion to JSON vector (not for objects) - operator array_t() const; - /// implicit conversion to JSON map (only for objects) - operator object_t() const; - - /// serialize to stream - friend std::ostream& operator<<(std::ostream& o, const json& j) - { - o << j.dump(); - return o; - } - /// serialize to stream - friend std::ostream& operator>>(const json& j, std::ostream& o) - { - o << j.dump(); - return o; - } - - /// deserialize from stream - friend std::istream& operator>>(std::istream& i, json& j) - { - j = parser(i).parse(); - return i; - } - /// deserialize from stream - friend std::istream& operator<<(json& j, std::istream& i) - { - j = parser(i).parse(); - return i; - } - - /// explicit serialization - std::string dump(int = -1) const noexcept; - - /// add constructible objects to an array - template::value>::type = 0> - json & operator+=(const T& o) - { - push_back(json(o)); - return *this; - } - - /// add an object/array to an array - json& operator+=(const json&); - - /// add a pair to an object - json& operator+=(const object_t::value_type&); - /// add a list of elements to array or list of pairs to object - json& operator+=(list_init_t); - - /// add constructible objects to an array - template::value>::type = 0> - void push_back(const T& o) - { - push_back(json(o)); - } - - /// add an object/array to an array - void push_back(const json&); - /// add an object/array to an array (move) - void push_back(json&&); - - /// add a pair to an object - void push_back(const object_t::value_type&); - /// add a list of elements to array or list of pairs to object - void push_back(list_init_t); - - /// operator to set an element in an array - reference operator[](const int); - /// operator to get an element in an array - const_reference operator[](const int) const; - /// operator to get an element in an array - reference at(const int); - /// operator to get an element in an array - const_reference at(const int) const; - - /// operator to set an element in an object - reference operator[](const std::string&); - /// operator to set an element in an object - reference operator[](const char*); - /// operator to get an element in an object - const_reference operator[](const std::string&) const; - /// operator to get an element in an object - const_reference operator[](const char*) const; - /// operator to set an element in an object - reference at(const std::string&); - /// operator to set an element in an object - reference at(const char*); - /// operator to get an element in an object - const_reference at(const std::string&) const; - /// operator to get an element in an object - const_reference at(const char*) const; - - /// return the number of stored values - size_type size() const noexcept; - /// return the maximal number of values that can be stored - size_type max_size() const noexcept; - /// checks whether object is empty - bool empty() const noexcept; - /// removes all elements from compounds and resets values to default - void clear() noexcept; - - /// swaps content with other object - void swap(json&) noexcept; - - /// return the type of the object - value_t type() const noexcept; - - /// find an element in an object (returns end() iterator otherwise) - iterator find(const std::string&); - /// find an element in an object (returns end() iterator otherwise) - const_iterator find(const std::string&) const; - /// find an element in an object (returns end() iterator otherwise) - iterator find(const char*); - /// find an element in an object (returns end() iterator otherwise) - const_iterator find(const char*) const; - - /// lexicographically compares the values - bool operator==(const json&) const noexcept; - /// lexicographically compares the values - bool operator!=(const json&) const noexcept; - - /// returns an iterator to the beginning (array/object) - iterator begin() noexcept; - /// returns an iterator to the end (array/object) - iterator end() noexcept; - /// returns an iterator to the beginning (array/object) - const_iterator begin() const noexcept; - /// returns an iterator to the end (array/object) - const_iterator end() const noexcept; - /// returns an iterator to the beginning (array/object) - const_iterator cbegin() const noexcept; - /// returns an iterator to the end (array/object) - const_iterator cend() const noexcept; - /// returns a reverse iterator to the beginning - reverse_iterator rbegin() noexcept; - /// returns a reverse iterator to the end - reverse_iterator rend() noexcept; - /// returns a reverse iterator to the beginning - const_reverse_iterator crbegin() const noexcept; - /// returns a reverse iterator to the end - const_reverse_iterator crend() const noexcept; - - private: - /// the type of this object - value_t type_ = value_t::null; - - /// the payload - value value_ {}; - - public: - /// an iterator - class iterator : public std::iterator - { - friend class json; - friend class json::const_iterator; - - public: - iterator() = default; - iterator(json*, bool); - iterator(const iterator&); - ~iterator(); - - iterator& operator=(iterator); - bool operator==(const iterator&) const; - bool operator!=(const iterator&) const; - iterator& operator++(); - iterator& operator--(); - json& operator*() const; - json* operator->() const; - - /// getter for the key (in case of objects) - std::string key() const; - /// getter for the value - json& value() const; - - private: - /// a JSON value - json* object_ = nullptr; - /// an iterator for JSON arrays - array_t::iterator* vi_ = nullptr; - /// an iterator for JSON objects - object_t::iterator* oi_ = nullptr; - /// whether iterator points to a valid object - bool invalid = true; - }; - - /// a const iterator - class const_iterator : public std::iterator - { - friend class json; - - public: - const_iterator() = default; - const_iterator(const json*, bool); - const_iterator(const const_iterator&); - const_iterator(const json::iterator&); - ~const_iterator(); - - const_iterator& operator=(const_iterator); - bool operator==(const const_iterator&) const; - bool operator!=(const const_iterator&) const; - const_iterator& operator++(); - const_iterator& operator--(); - const json& operator*() const; - const json* operator->() const; - - /// getter for the key (in case of objects) - std::string key() const; - /// getter for the value - const json& value() const; - - private: - /// a JSON value - const json* object_ = nullptr; - /// an iterator for JSON arrays - array_t::const_iterator* vi_ = nullptr; - /// an iterator for JSON objects - object_t::const_iterator* oi_ = nullptr; - /// whether iterator reached past the end - bool invalid = true; - }; - - private: - /// a helper class to parse a JSON object - class parser - { - public: - /// a parser reading from a C string - parser(const char*); - /// a parser reading from a C++ string - parser(const std::string&); - /// a parser reading from an input stream - parser(std::istream&); - /// destructor of the parser - ~parser() = default; - - // no copy constructor - parser(const parser&) = delete; - // no copy assignment - parser& operator=(parser) = delete; - - /// parse and return a JSON object - json parse(); - - private: - /// read the next character, stripping whitespace - bool next(); - /// raise an exception with an error message - [[noreturn]] inline void error(const std::string&) const; - /// parse a quoted string - inline std::string parseString(); - /// transforms a unicode codepoint to it's UTF-8 presentation - std::string codePointToUTF8(unsigned int codePoint) const; - /// parses 4 hex characters that represent a unicode code point - inline unsigned int parse4HexCodePoint(); - /// parses \uXXXX[\uXXXX] unicode escape characters - inline std::string parseUnicodeEscape(); - /// parse a Boolean "true" - inline void parseTrue(); - /// parse a Boolean "false" - inline void parseFalse(); - /// parse a null object - inline void parseNull(); - /// a helper function to expect a certain character - inline void expect(const char); - - private: - /// a buffer of the input - std::string buffer_ {}; - /// the current character - char current_ {}; - /// the position inside the input buffer - std::size_t pos_ = 0; - }; -}; - -} - -/// user-defined literal operator to create JSON objects from strings -nlohmann::json operator "" _json(const char*, std::size_t); - -// specialization of std::swap, and std::hash -namespace std -{ -template <> -/// swaps the values of two JSON objects -inline void swap(nlohmann::json& j1, - nlohmann::json& j2) noexcept( - is_nothrow_move_constructible::value and - is_nothrow_move_assignable::value - ) -{ - j1.swap(j2); -} - -template <> -/// hash value for JSON objects -struct hash -{ - size_t operator()(const nlohmann::json& j) const - { - // a naive hashing via the string representation - return hash()(j.dump()); - } -}; - -} diff --git a/src/json/json.hpp b/src/json/json.hpp new file mode 100644 index 0000000..c1ab876 --- /dev/null +++ b/src/json/json.hpp @@ -0,0 +1,3844 @@ +/*! +@file +@copyright The code is licensed under the MIT License + , + Copyright (c) 2013-2015 Niels Lohmann. +@author Niels Lohmann +@see https://github.com/nlohmann/json +*/ + +#ifndef _NLOHMANN_JSON +#define _NLOHMANN_JSON + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! +@see https://github.com/nlohmann +*/ +namespace nlohmann +{ + +/*! +@brief JSON + +@tparam ObjectType type for JSON objects + (@c std::map by default) +@tparam ArrayType type for JSON arrays + (@c std::vector by default) +@tparam StringType type for JSON strings and object keys + (@c std::string by default) +@tparam BooleanType type for JSON booleans + (@c bool by default) +@tparam NumberIntegerType type for JSON integer numbers + (@c int64_t by default) +@tparam NumberFloatType type for JSON floating-point numbers + (@c double by default) +@tparam Allocator type of the allocator to use + (@c std::allocator by default) + +@note ObjectType trick from http://stackoverflow.com/a/9860911 + +@see RFC 7159 +@see ECMA 404 +*/ +template < + template class ObjectType = std::map, + template class ArrayType = std::vector, + class StringType = std::string, + class BooleanType = bool, + class NumberIntegerType = int64_t, + class NumberFloatType = double, + template class Allocator = std::allocator + > +class basic_json +{ + public: + ///////////////////// + // container types // + ///////////////////// + + // forward declarations + class iterator; + class const_iterator; + + /// the type of elements in a basic_json container + using value_type = basic_json; + /// the type of an element reference + using reference = basic_json&; + /// the type of an element const reference + using const_reference = const basic_json&; + /// the type of an element pointer + using pointer = basic_json*; + /// the type of an element const pointer + using const_pointer = const basic_json*; + /// 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; + /// an iterator for a basic_json container + using iterator = basic_json::iterator; + /// a const iterator for a basic_json container + using const_iterator = basic_json::const_iterator; + /// a reverse iterator for a basic_json container + using reverse_iterator = std::reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = std::reverse_iterator; + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// a type for an object + using object_t = ObjectType; + /// a type for an array + using array_t = ArrayType; + /// a type for a string + using string_t = StringType; + /// a type for a boolean + using boolean_t = BooleanType; + /// a type for a number (integer) + using number_integer_t = NumberIntegerType; + /// a type for a number (floating-point) + using number_float_t = NumberFloatType; + /// a type for list initialization + using list_init_t = std::initializer_list; + + + //////////////////////// + // JSON value storage // + //////////////////////// + + /// a JSON value + 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; + /// bolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) : number_integer(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) : number_float(v) {} + }; + + + ///////////////////////////////// + // JSON value type enumeration // + ///////////////////////////////// + + /// JSON value type enumeration + 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_float ///< number value (floating-point) + }; + + + ////////////////// + // constructors // + ////////////////// + + /// create an empty value with a given type + inline basic_json(const value_t value) + : m_type(value) + { + switch (m_type) + { + case (value_t::null): + { + break; + } + + case (value_t::object): + { + Allocator alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object); + break; + } + + case (value_t::array): + { + Allocator alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array); + break; + } + + case (value_t::string): + { + Allocator alloc; + m_value.string = alloc.allocate(1); + alloc.construct(m_value.string, ""); + break; + } + + case (value_t::boolean): + { + m_value.boolean = boolean_t(false); + break; + } + + case (value_t::number_integer): + { + m_value.number_integer = number_integer_t(0); + break; + } + + case (value_t::number_float): + { + m_value.number_float = number_float_t(0.0); + break; + } + } + } + + /// create a null object (implicitly) + inline basic_json() noexcept + : m_type(value_t::null) + {} + + /// create a null object (explicitly) + inline basic_json(std::nullptr_t) noexcept + : m_type(value_t::null) + {} + + /// create an object (explicit) + inline basic_json(const object_t& value) + : m_type(value_t::object) + { + Allocator alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object, value); + } + + /// create an object (implicit) + template ::value and + std::is_constructible::value, int>::type + = 0> + inline basic_json(const V& value) + : m_type(value_t::object) + { + Allocator alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object, value.begin(), value.end()); + } + + /// create an array (explicit) + inline basic_json(const array_t& value) + : m_type(value_t::array) + { + Allocator alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array, value); + } + + /// create an array (implicit) + 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> + inline basic_json(const V& value) + : m_type(value_t::array) + { + Allocator alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array, value.begin(), value.end()); + } + + /// create a string (explicit) + inline basic_json(const string_t& value) + : m_type(value_t::string) + { + Allocator alloc; + m_value.string = alloc.allocate(1); + alloc.construct(m_value.string, value); + } + + /// create a string (explicit) + inline basic_json(const typename string_t::value_type* value) + : m_type(value_t::string) + { + Allocator alloc; + m_value.string = alloc.allocate(1); + alloc.construct(m_value.string, value); + } + + /// create a string (implicit) + template ::value, int>::type + = 0> + inline basic_json(const V& value) + : basic_json(string_t(value)) + {} + + /// create a boolean (explicit) + inline basic_json(boolean_t value) + : m_type(value_t::boolean), m_value(value) + {} + + /// create an integer number (explicit) + inline basic_json(const number_integer_t& value) + : m_type(value_t::number_integer), m_value(value) + {} + + /// create an integer number (implicit) + template::value and + std::numeric_limits::is_integer, T>::type + = 0> + inline basic_json(const T value) noexcept + : m_type(value_t::number_integer), m_value(number_integer_t(value)) + {} + + /// create a floating-point number (explicit) + inline basic_json(const number_float_t& value) + : m_type(value_t::number_float), m_value(value) + {} + + /// create a floating-point number (implicit) + template::value and + std::is_floating_point::value>::type + > + inline basic_json(const T value) noexcept + : m_type(value_t::number_float), m_value(number_float_t(value)) + {} + + /// create a container (array or object) from an initializer list + inline basic_json(list_init_t l, bool type_deduction = true, value_t manual_type = value_t::array) + { + // the initializer list could describe an object + bool is_object = true; + + // check if each element is an array with two elements whose first element + // is a string + for (const auto& element : l) + { + if ((element.m_final and element.m_type == value_t::array) + or (element.m_type != value_t::array or element.size() != 2 + or element[0].m_type != value_t::string)) + { + // we found an element that makes it impossible to use the + // initializer list as object + is_object = false; + break; + } + } + + // adjust type if type deduction is not wanted + if (not type_deduction) + { + // mark this object's type as final + m_final = true; + + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_object = false; + } + + // if object is wanted but impossible, throw an exception + if (manual_type == value_t::object and not is_object) + { + throw std::logic_error("cannot create JSON object from initializer list"); + } + } + + if (is_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + Allocator alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object); + + for (auto& element : l) + { + m_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1])); + } + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + Allocator alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array, std::move(l)); + } + } + + /// explicitly create an array from an initializer list + inline static basic_json array(list_init_t l = list_init_t()) + { + return basic_json(l, false, value_t::array); + } + + /// explicitly create an object from an initializer list + inline static basic_json object(list_init_t l = list_init_t()) + { + return basic_json(l, false, value_t::object); + } + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + /// copy constructor + inline basic_json(const basic_json& other) + : m_type(other.m_type) + { + switch (m_type) + { + case (value_t::null): + { + break; + } + case (value_t::object): + { + Allocator alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object, *other.m_value.object); + break; + } + case (value_t::array): + { + Allocator alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array, *other.m_value.array); + break; + } + case (value_t::string): + { + Allocator alloc; + m_value.string = alloc.allocate(1); + alloc.construct(m_value.string, *other.m_value.string); + break; + } + case (value_t::boolean): + { + m_value.boolean = other.m_value.boolean; + break; + } + case (value_t::number_integer): + { + m_value.number_integer = other.m_value.number_integer; + break; + } + case (value_t::number_float): + { + m_value.number_float = other.m_value.number_float; + break; + } + } + } + + /// move constructor + inline 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 = {}; + } + + /// copy assignment + inline 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 + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + return *this; + } + + /// destructor + inline ~basic_json() noexcept + { + switch (m_type) + { + case (value_t::object): + { + Allocator alloc; + alloc.destroy(m_value.object); + alloc.deallocate(m_value.object, 1); + m_value.object = nullptr; + break; + } + + case (value_t::array): + { + Allocator alloc; + alloc.destroy(m_value.array); + alloc.deallocate(m_value.array, 1); + m_value.array = nullptr; + break; + } + + case (value_t::string): + { + Allocator alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + break; + } + + default: + { + // all other types need no specific destructor + break; + } + } + } + + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /*! + @brief serialization + + Serialization function for JSON objects. The function tries to mimick Python's + @p json.dumps() function, and currently supports its @p indent parameter. + + @param indent sif 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 + + @see https://docs.python.org/2/library/json.html#json.dump + */ + inline string_t dump(const int indent = -1) const noexcept + { + if (indent >= 0) + { + return dump(true, static_cast(indent)); + } + else + { + return dump(false, 0); + } + } + + /// return the type of the object (explicit) + inline value_t type() const noexcept + { + return m_type; + } + + /// return the type of the object (implicit) + operator value_t() const noexcept + { + return m_type; + } + + + ////////////////////// + // value conversion // + ////////////////////// + + /// get an object (explicit) + template ::value and + std::is_constructible::value, int>::type + = 0> + inline T get() const + { + switch (m_type) + { + case (value_t::object): + return T(m_value.object->begin(), m_value.object->end()); + default: + throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + } + } + + /// get an array (explicit) + template ::value and + std::is_constructible::value, int>::type + = 0> + inline T get() const + { + switch (m_type) + { + case (value_t::array): + return T(m_value.array->begin(), m_value.array->end()); + default: + throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + } + } + + /// get a string (explicit) + template ::value, int>::type + = 0> + inline T get() const + { + switch (m_type) + { + case (value_t::string): + return *m_value.string; + default: + throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + } + } + + /// get a boolean (explicit) + template ::value, int>::type + = 0> + inline T get() const + { + switch (m_type) + { + case (value_t::boolean): + return m_value.boolean; + default: + throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + } + } + + /// get a number (explicit) + template::value and + std::is_arithmetic::value, int>::type + = 0> + inline T get() const + { + switch (m_type) + { + case (value_t::number_integer): + return static_cast(m_value.number_integer); + case (value_t::number_float): + return static_cast(m_value.number_float); + default: + throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + } + } + + /// get a value (implicit) + template + inline operator T() const + { + return get(); + } + + + //////////////////// + // element access // + //////////////////// + + /// access specified element with bounds checking + inline reference at(size_type pos) + { + // at only works for arrays + if (m_type != value_t::array) + { + throw std::runtime_error("cannot use at with " + type_name()); + } + + return m_value.array->at(pos); + } + + /// access specified element with bounds checking + inline const_reference at(size_type pos) const + { + // at only works for arrays + if (m_type != value_t::array) + { + throw std::runtime_error("cannot use at with " + type_name()); + } + + return m_value.array->at(pos); + } + + /// access specified element + inline reference operator[](size_type pos) + { + // at only works for arrays + if (m_type != value_t::array) + { + throw std::runtime_error("cannot use [] with " + type_name()); + } + + return m_value.array->operator[](pos); + } + + /// access specified element + inline const_reference operator[](size_type pos) const + { + // at only works for arrays + if (m_type != value_t::array) + { + throw std::runtime_error("cannot use [] with " + type_name()); + } + + return m_value.array->operator[](pos); + } + + /// access specified element with bounds checking + inline reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (m_type != value_t::object) + { + throw std::runtime_error("cannot use at with " + type_name()); + } + + return m_value.object->at(key); + } + + /// access specified element with bounds checking + inline const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (m_type != value_t::object) + { + throw std::runtime_error("cannot use at with " + type_name()); + } + + return m_value.object->at(key); + } + + /// access specified element + inline reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null to object + if (m_type == value_t::null) + { + m_type = value_t::object; + Allocator alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object); + } + + // at only works for objects + if (m_type != value_t::object) + { + throw std::runtime_error("cannot use [] with " + type_name()); + } + + return m_value.object->operator[](key); + } + + /// access specified element + inline const_reference operator[](const typename object_t::key_type& key) const + { + // at only works for objects + if (m_type != value_t::object) + { + throw std::runtime_error("cannot use [] with " + type_name()); + } + + return m_value.object->operator[](key); + } + + /// access specified element (needed for clang) + template + inline reference operator[](const T (&key)[n]) + { + // implicitly convert null to object + if (m_type == value_t::null) + { + m_type = value_t::object; + Allocator alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object); + } + + // at only works for objects + if (m_type != value_t::object) + { + throw std::runtime_error("cannot use [] with " + type_name()); + } + + return m_value.object->operator[](key); + } + + /// access specified element (needed for clang) + template + inline const_reference operator[](const T (&key)[n]) const + { + // at only works for objects + if (m_type != value_t::object) + { + throw std::runtime_error("cannot use [] with " + type_name()); + } + + return m_value.object->operator[](key); + } + + + /// find an element in an object + inline iterator find(typename object_t::key_type key) + { + auto result = end(); + + if (m_type == value_t::object) + { + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + /// find an element in an object + inline const_iterator find(typename object_t::key_type key) const + { + auto result = cend(); + + if (m_type == value_t::object) + { + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + + /////////////// + // iterators // + /////////////// + + /// returns an iterator to the beginning of the container + inline iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /// returns a const iterator to the beginning of the container + inline const_iterator begin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /// returns a const iterator to the beginning of the container + inline const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /// returns an iterator to the end of the container + inline iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /// returns a const iterator to the end of the container + inline const_iterator end() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /// returns a const iterator to the end of the container + inline const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /// returns a reverse iterator to the beginning + inline reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /// returns a reverse iterator to the beginning + inline const_reverse_iterator rbegin() const noexcept + { + return const_reverse_iterator(end()); + } + + /// returns a reverse iterator to the end + inline reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /// returns a reverse iterator to the end + inline const_reverse_iterator rend() const noexcept + { + return const_reverse_iterator(begin()); + } + + /// returns a reverse iterator to the beginning + inline const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /// returns a reverse iterator to the end + inline const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + + ////////////// + // capacity // + ////////////// + + /// checks whether the container is empty + inline bool empty() const noexcept + { + switch (m_type) + { + case (value_t::null): + { + return true; + } + + case (value_t::array): + { + return m_value.array->empty(); + } + + case (value_t::object): + { + return m_value.object->empty(); + } + + default: + { + // all other types are nonempty + return false; + } + } + } + + /// returns the number of elements + inline size_type size() const noexcept + { + switch (m_type) + { + case (value_t::null): + { + return 0; + } + + case (value_t::array): + { + return m_value.array->size(); + } + + case (value_t::object): + { + return m_value.object->size(); + } + + default: + { + // all other types have size 1 + return 1; + } + } + } + + /// returns the maximum possible number of elements + inline size_type max_size() const noexcept + { + switch (m_type) + { + case (value_t::null): + { + return 0; + } + + case (value_t::array): + { + return m_value.array->max_size(); + } + + case (value_t::object): + { + return m_value.object->max_size(); + } + + default: + { + // all other types have max_size 1 + return 1; + } + } + } + + + /////////////// + // modifiers // + /////////////// + + /// clears the contents + inline void clear() noexcept + { + switch (m_type) + { + case (value_t::null): + { + break; + } + + case (value_t::number_integer): + { + m_value.number_integer = 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): + { + m_value.string->clear(); + break; + } + + case (value_t::array): + { + m_value.array->clear(); + break; + } + + case (value_t::object): + { + m_value.object->clear(); + break; + } + } + } + + /// add an object to an array + inline void push_back(basic_json&& value) + { + // push_back only works for null objects or arrays + if (not(m_type == value_t::null or m_type == value_t::array)) + { + throw std::runtime_error("cannot add element to " + type_name()); + } + + // transform null object into an array + if (m_type == value_t::null) + { + m_type = value_t::array; + Allocator alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array); + } + + // add element to array (move semantics) + m_value.array->push_back(std::move(value)); + // invalidate object + value.m_type = value_t::null; + } + + /// add an object to an array + inline reference operator+=(basic_json&& value) + { + push_back(std::move(value)); + return *this; + } + + /// add an object to an array + inline void push_back(const basic_json& value) + { + // push_back only works for null objects or arrays + if (not(m_type == value_t::null or m_type == value_t::array)) + { + throw std::runtime_error("cannot add element to " + type_name()); + } + + // transform null object into an array + if (m_type == value_t::null) + { + m_type = value_t::array; + Allocator alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array); + } + + // add element to array + m_value.array->push_back(value); + } + + /// add an object to an array + inline reference operator+=(const basic_json& value) + { + push_back(value); + return *this; + } + + /// add an object to an object + inline void push_back(const typename object_t::value_type& value) + { + // push_back only works for null objects or objects + if (not(m_type == value_t::null or m_type == value_t::object)) + { + throw std::runtime_error("cannot add element to " + type_name()); + } + + // transform null object into an object + if (m_type == value_t::null) + { + m_type = value_t::object; + Allocator alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object); + } + + // add element to array + m_value.object->insert(value); + } + + /// add an object to an object + inline reference operator+=(const typename object_t::value_type& value) + { + push_back(value); + return operator[](value.first); + } + + /// swaps the contents + inline 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); + } + + /// swaps the contents + inline void swap(array_t& other) + { + // swap only works for arrays + if (m_type != value_t::array) + { + throw std::runtime_error("cannot use swap with " + type_name()); + } + + // swap arrays + std::swap(*(m_value.array), other); + } + + /// swaps the contents + inline void swap(object_t& other) + { + // swap only works for objects + if (m_type != value_t::object) + { + throw std::runtime_error("cannot use swap with " + type_name()); + } + + // swap arrays + std::swap(*(m_value.object), other); + } + + /// swaps the contents + inline void swap(string_t& other) + { + // swap only works for strings + if (m_type != value_t::string) + { + throw std::runtime_error("cannot use swap with " + type_name()); + } + + // swap arrays + std::swap(*(m_value.string), other); + } + + + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// comparison: equal + friend bool operator==(const_reference lhs, const_reference rhs) + { + switch (lhs.type()) + { + case (value_t::array): + { + if (rhs.type() == value_t::array) + { + return *lhs.m_value.array == *rhs.m_value.array; + } + break; + } + case (value_t::object): + { + if (rhs.type() == value_t::object) + { + return *lhs.m_value.object == *rhs.m_value.object; + } + break; + } + case (value_t::null): + { + if (rhs.type() == value_t::null) + { + return true; + } + break; + } + case (value_t::string): + { + if (rhs.type() == value_t::string) + { + return *lhs.m_value.string == *rhs.m_value.string; + } + break; + } + case (value_t::boolean): + { + if (rhs.type() == value_t::boolean) + { + return lhs.m_value.boolean == rhs.m_value.boolean; + } + break; + } + case (value_t::number_integer): + { + if (rhs.type() == value_t::number_integer) + { + return lhs.m_value.number_integer == rhs.m_value.number_integer; + } + if (rhs.type() == value_t::number_float) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_float); + } + break; + } + case (value_t::number_float): + { + if (rhs.type() == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + if (rhs.type() == value_t::number_float) + { + return lhs.m_value.number_float == rhs.m_value.number_float; + } + break; + } + } + + return false; + } + + /// comparison: not equal + friend bool operator!=(const_reference lhs, const_reference rhs) + { + return not (lhs == rhs); + } + + /// comparison: less than + friend bool operator<(const_reference lhs, const_reference rhs) + { + switch (lhs.type()) + { + case (value_t::array): + { + if (rhs.type() == value_t::array) + { + return *lhs.m_value.array < *rhs.m_value.array; + } + break; + } + case (value_t::object): + { + if (rhs.type() == value_t::object) + { + return *lhs.m_value.object < *rhs.m_value.object; + } + break; + } + case (value_t::null): + { + if (rhs.type() == value_t::null) + { + return false; + } + break; + } + case (value_t::string): + { + if (rhs.type() == value_t::string) + { + return *lhs.m_value.string < *rhs.m_value.string; + } + break; + } + case (value_t::boolean): + { + if (rhs.type() == value_t::boolean) + { + return lhs.m_value.boolean < rhs.m_value.boolean; + } + break; + } + case (value_t::number_integer): + { + if (rhs.type() == value_t::number_integer) + { + return lhs.m_value.number_integer < rhs.m_value.number_integer; + } + if (rhs.type() == value_t::number_float) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_float); + } + break; + } + case (value_t::number_float): + { + if (rhs.type() == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + if (rhs.type() == value_t::number_float) + { + return lhs.m_value.number_float < rhs.m_value.number_float; + } + break; + } + } + + return false; + } + + /// comparison: less than or equal + friend bool operator<=(const_reference lhs, const_reference rhs) + { + return not (rhs < lhs); + } + + /// comparison: greater than + friend bool operator>(const_reference lhs, const_reference rhs) + { + return not (lhs <= rhs); + } + + /// comparison: greater than or equal + friend bool operator>=(const_reference lhs, const_reference rhs) + { + return not (lhs < rhs); + } + + + /////////////////// + // serialization // + /////////////////// + + /// serialize to stream + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const int indentation = (o.width() == 0) ? -1 : o.width(); + + o << j.dump(indentation); + return o; + } + + /// serialize to stream + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + // read width member and use it as indentation parameter if nonzero + const int indentation = (o.width() == 0) ? -1 : o.width(); + + o << j.dump(indentation); + return o; + } + + + ///////////////////// + // deserialization // + ///////////////////// + + /// deserialize from string + static basic_json parse(const string_t& s) + { + return parser(s).parse(); + } + + /// deserialize from stream + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + j = parser(i).parse(); + return i; + } + + /// deserialize from stream + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + j = parser(i).parse(); + return i; + } + + + private: + /////////////////////////// + // convenience functions // + /////////////////////////// + + /// return the type as string + inline 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"; + } + + default: + { + return "number"; + } + } + } + + /*! + @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 s the string to escape + @return escaped string + */ + static string_t escape_string(const string_t& s) noexcept + { + // create a result string of at least the size than s + string_t result; + result.reserve(s.size()); + + for (const auto c : s) + { + switch (c) + { + // quotation mark (0x22) + case '"': + { + result += "\\\""; + break; + } + // reverse solidus (0x5c) + case '\\': + { + result += "\\\\"; + break; + } + // backspace (0x08) + case '\b': + { + result += "\\b"; + break; + } + // formfeed (0x0c) + case '\f': + { + result += "\\f"; + break; + } + // newline (0x0a) + case '\n': + { + result += "\\n"; + break; + } + // carriage return (0x0d) + case '\r': + { + result += "\\r"; + break; + } + // horizontal tab (0x09) + case '\t': + { + result += "\\t"; + break; + } + + default: + { + if (c >= 0 and c <= 0x1f) + { + // control characters (everything between 0x00 and 0x1f) + // -> create four-digit hex representation + std::stringstream ss; + ss << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(c); + result += ss.str(); + } + else + { + // all other characters are added as-is + result.append(1, c); + } + break; + } + } + } + + return result; + } + + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serializaion 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() + - numbers are converted to a string before output using std::to_string() + + @param prettyPrint whether the output shall be pretty-printed + @param indentStep the indent level + @param currentIndent the current indent level (only used internally) + */ + inline string_t dump(const bool prettyPrint, const unsigned int indentStep, + const unsigned int currentIndent = 0) const noexcept + { + // variable to hold indentation for recursive calls + auto new_indent = currentIndent; + + // helper function to return whitespace as indentation + const auto indent = [prettyPrint, &new_indent]() + { + return prettyPrint ? string_t(new_indent, ' ') : string_t(); + }; + + switch (m_type) + { + case (value_t::object): + { + if (m_value.object->empty()) + { + return "{}"; + } + + string_t result = "{"; + + // increase indentation + if (prettyPrint) + { + new_indent += indentStep; + result += "\n"; + } + + for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) + { + if (i != m_value.object->cbegin()) + { + result += prettyPrint ? ",\n" : ","; + } + result += indent() + "\"" + escape_string(i->first) + "\":" + (prettyPrint ? " " : "") + + i->second.dump(prettyPrint, indentStep, new_indent); + } + + // decrease indentation + if (prettyPrint) + { + new_indent -= indentStep; + result += "\n"; + } + + return result + indent() + "}"; + } + + case (value_t::array): + { + if (m_value.array->empty()) + { + return "[]"; + } + + string_t result = "["; + + // increase indentation + if (prettyPrint) + { + new_indent += indentStep; + result += "\n"; + } + + for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) + { + if (i != m_value.array->cbegin()) + { + result += prettyPrint ? ",\n" : ","; + } + result += indent() + i->dump(prettyPrint, indentStep, new_indent); + } + + // decrease indentation + if (prettyPrint) + { + new_indent -= indentStep; + result += "\n"; + } + + return result + indent() + "]"; + } + + case (value_t::string): + { + return string_t("\"") + escape_string(*m_value.string) + "\""; + } + + case (value_t::boolean): + { + return m_value.boolean ? "true" : "false"; + } + + case (value_t::number_integer): + { + return std::to_string(m_value.number_integer); + } + + case (value_t::number_float): + { + return std::to_string(m_value.number_float); + } + + default: + { + return "null"; + } + } + } + + + private: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// whether the type of JSON object may change later + bool m_final = false; + + /// the value of the current element + json_value m_value = {}; + + + private: + /////////////// + // iterators // + /////////////// + + /// values of a generic iterator type of non-container JSON values + enum class generic_iterator_value + { + /// the iterator was not initialized + uninitialized, + /// the iterator points to the only value + begin, + /// the iterator points past the only value + end, + /// the iterator points to an invalid value + invalid + }; + + /// an iterator value + template + union internal_iterator + { + /// iterator for JSON objects + object_iterator_t object_iterator; + /// iterator for JSON arrays + array_iterator_t array_iterator; + /// generic iteraotr for all other value types + generic_iterator_value generic_iterator; + + /// default constructor + internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {} + /// constructor for object iterators + internal_iterator(object_iterator_t v) : object_iterator(v) {} + /// constructor for array iterators + internal_iterator(array_iterator_t v) : array_iterator(v) {} + /// constructor for generic iterators + internal_iterator(generic_iterator_value v) : generic_iterator(v) {} + }; + + public: + /// a bidirectional iterator for the basic_json class + class iterator : public std::iterator + { + public: + /// the type of the values when the iterator is dereferenced + using value_type = basic_json::value_type; + /// a type to represent differences between iterators + using difference_type = basic_json::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = basic_json::pointer; + /// defines a reference to the type iterated over (value_type) + using reference = basic_json::reference; + /// the category of the iterator + using iterator_category = std::bidirectional_iterator_tag; + + /// default constructor + inline iterator() = default; + + /// constructor for a given JSON instance + inline iterator(pointer object) : m_object(object) + { + 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.generic_iterator = generic_iterator_value::uninitialized; + break; + } + } + } + + /// copy assignment + inline iterator& operator=(const iterator& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + /// set the iterator to the first value + inline void set_begin() noexcept + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case (basic_json::value_t::array): + { + 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.generic_iterator = generic_iterator_value::end; + break; + } + + default: + { + m_it.generic_iterator = generic_iterator_value::begin; + break; + } + } + } + + /// set the iterator past the last value + inline void set_end() noexcept + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case (basic_json::value_t::array): + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.generic_iterator = generic_iterator_value::end; + break; + } + } + } + + /// return a reference to the value pointed to by the iterator + inline reference operator*() const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + return m_it.object_iterator->second; + } + + case (basic_json::value_t::array): + { + return *m_it.array_iterator; + } + + case (basic_json::value_t::null): + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.generic_iterator == generic_iterator_value::begin) + { + return *m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + + /// dereference the iterator + inline pointer operator->() const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + return &(m_it.object_iterator->second); + } + + case (basic_json::value_t::array): + { + return &*m_it.array_iterator; + } + + default: + { + if (m_it.generic_iterator == generic_iterator_value::begin) + { + return m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + + /// post-increment (it++) + inline iterator operator++(int) + { + iterator result = *this; + + 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: + { + if (m_it.generic_iterator == generic_iterator_value::begin) + { + m_it.generic_iterator = generic_iterator_value::end; + } + else + { + m_it.generic_iterator = generic_iterator_value::invalid; + } + break; + } + } + + return result; + } + + /// pre-increment (++it) + inline iterator& operator++() + { + 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: + { + if (m_it.generic_iterator == generic_iterator_value::begin) + { + m_it.generic_iterator = generic_iterator_value::end; + } + else + { + m_it.generic_iterator = generic_iterator_value::invalid; + } + break; + } + } + + return *this; + } + + /// post-decrement (it--) + inline iterator operator--(int) + { + iterator result = *this; + + 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; + } + + case (basic_json::value_t::null): + { + m_it.generic_iterator = generic_iterator_value::invalid; + break; + } + + default: + { + if (m_it.generic_iterator == generic_iterator_value::end) + { + m_it.generic_iterator = generic_iterator_value::begin; + } + else + { + m_it.generic_iterator = generic_iterator_value::invalid; + } + break; + } + } + + return result; + } + + /// pre-decrement (--it) + inline iterator& operator--() + { + 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; + } + + case (basic_json::value_t::null): + { + m_it.generic_iterator = generic_iterator_value::invalid; + break; + } + + default: + { + if (m_it.generic_iterator == generic_iterator_value::end) + { + m_it.generic_iterator = generic_iterator_value::begin; + } + else + { + m_it.generic_iterator = generic_iterator_value::invalid; + } + break; + } + } + + return *this; + } + + /// comparison: equal + inline bool operator==(const iterator& other) const + { + if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) + { + return false; + } + + 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.generic_iterator == other.m_it.generic_iterator); + } + } + } + + /// comparison: not equal + inline bool operator!=(const iterator& other) const + { + return not operator==(other); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator m_it; + }; + + /// a const bidirectional iterator for the basic_json class + class const_iterator : public std::iterator + { + public: + /// the type of the values when the iterator is dereferenced + using value_type = basic_json::value_type; + /// a type to represent differences between iterators + using difference_type = basic_json::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = basic_json::const_pointer; + /// defines a reference to the type iterated over (value_type) + using reference = basic_json::const_reference; + /// the category of the iterator + using iterator_category = std::bidirectional_iterator_tag; + + /// default constructor + inline const_iterator() = default; + + /// constructor for a given JSON instance + inline const_iterator(pointer object) : m_object(object) + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + m_it.object_iterator = typename object_t::const_iterator(); + break; + } + case (basic_json::value_t::array): + { + m_it.array_iterator = typename array_t::const_iterator(); + break; + } + default: + { + m_it.generic_iterator = generic_iterator_value::uninitialized; + break; + } + } + } + + /// copy constructor given a nonconst iterator + inline const_iterator(const iterator& other) : m_object(other.m_object) + { + 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.generic_iterator = other.m_it.generic_iterator; + break; + } + } + } + + /// copy assignment + inline const_iterator& operator=(const const_iterator& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + /// set the iterator to the first value + inline void set_begin() noexcept + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + m_it.object_iterator = m_object->m_value.object->cbegin(); + break; + } + + case (basic_json::value_t::array): + { + m_it.array_iterator = m_object->m_value.array->cbegin(); + break; + } + + case (basic_json::value_t::null): + { + // set to end so begin()==end() is true: null is empty + m_it.generic_iterator = generic_iterator_value::end; + break; + } + + default: + { + m_it.generic_iterator = generic_iterator_value::begin; + break; + } + } + } + + /// set the iterator past the last value + inline void set_end() noexcept + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + m_it.object_iterator = m_object->m_value.object->cend(); + break; + } + + case (basic_json::value_t::array): + { + m_it.array_iterator = m_object->m_value.array->cend(); + break; + } + + default: + { + m_it.generic_iterator = generic_iterator_value::end; + break; + } + } + } + + /// return a reference to the value pointed to by the iterator + inline reference operator*() const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + return m_it.object_iterator->second; + } + + case (basic_json::value_t::array): + { + return *m_it.array_iterator; + } + + case (basic_json::value_t::null): + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.generic_iterator == generic_iterator_value::begin) + { + return *m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + + /// dereference the iterator + inline pointer operator->() const + { + switch (m_object->m_type) + { + case (basic_json::value_t::object): + { + return &(m_it.object_iterator->second); + } + + case (basic_json::value_t::array): + { + return &*m_it.array_iterator; + } + + default: + { + if (m_it.generic_iterator == generic_iterator_value::begin) + { + return m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + + /// post-increment (it++) + inline const_iterator operator++(int) + { + const_iterator result = *this; + + 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: + { + if (m_it.generic_iterator == generic_iterator_value::begin) + { + m_it.generic_iterator = generic_iterator_value::end; + } + else + { + m_it.generic_iterator = generic_iterator_value::invalid; + } + break; + } + } + + return result; + } + + /// pre-increment (++it) + inline const_iterator& operator++() + { + 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: + { + if (m_it.generic_iterator == generic_iterator_value::begin) + { + m_it.generic_iterator = generic_iterator_value::end; + } + else + { + m_it.generic_iterator = generic_iterator_value::invalid; + } + break; + } + } + + return *this; + } + + /// post-decrement (it--) + inline const_iterator operator--(int) + { + const_iterator result = *this; + + 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; + } + + case (basic_json::value_t::null): + { + m_it.generic_iterator = generic_iterator_value::invalid; + break; + } + + default: + { + if (m_it.generic_iterator == generic_iterator_value::end) + { + m_it.generic_iterator = generic_iterator_value::begin; + } + else + { + m_it.generic_iterator = generic_iterator_value::invalid; + } + break; + } + } + + return result; + } + + /// pre-decrement (--it) + inline const_iterator& operator--() + { + 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; + } + + case (basic_json::value_t::null): + { + m_it.generic_iterator = generic_iterator_value::invalid; + break; + } + + default: + { + if (m_it.generic_iterator == generic_iterator_value::end) + { + m_it.generic_iterator = generic_iterator_value::begin; + } + else + { + m_it.generic_iterator = generic_iterator_value::invalid; + } + break; + } + } + + return *this; + } + + /// comparison: equal + inline bool operator==(const const_iterator& other) const + { + if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) + { + return false; + } + + 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.generic_iterator == other.m_it.generic_iterator); + } + } + } + + /// comparison: not equal + inline bool operator!=(const const_iterator& other) const + { + return not operator==(other); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator m_it; + }; + + + 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 that processes + a buffer and recognizes tokens according to RFC 7159 and ECMA-404. + */ + 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 + inline lexer(const string_t& s) noexcept + : m_content(reinterpret_cast(s.c_str())) + { + m_start = m_cursor = m_content; + m_limit = m_content + s.size(); + } + + /// default constructor + inline lexer() = default; + + /*! + @brief create a string from a Unicode code point + + @param codepoint the code point (must be in [0x0, 0x10ffff] + @return string representation of the code point + @exception std::out_of_range if code point is >0x10ffff + @exception std::invalid_argument if the low surrogate is invalid + + @see + */ + inline static string_t to_unicode(const size_t codepoint1, const size_t codepoint2 = 0) + { + string_t result; + + // calculate the codepoint from the given code points + size_t codepoint = codepoint1; + if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) + { + 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 substract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00; + } + else + { + throw std::invalid_argument("missing or wrong low surrogate"); + } + } + + if (codepoint <= 0x7f) + { + // 1-byte characters: 0xxxxxxx (ASCI) + 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 + inline static std::string token_type_name(token_type t) noexcept + { + 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::end_of_input): + return ""; + default: + return ""; + } + } + + /*! + This function implements a scanner for JSON. It is specified using + regular expressions that try to follow RFC 7159 and ECMA-404 as close + as possible. These regular expressions are then translated into a + deterministic finite automaton (DFA) by the tool re2c + . 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 + */ + inline token_type scan() noexcept + { + // pointer for backtracking information + const lexer_char_t* m_marker = nullptr; + + // remember the begin of the token + m_start = m_cursor; + + + { + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = + { + 0, 64, 64, 64, 64, 64, 64, 64, + 64, 96, 96, 64, 64, 96, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 96, 64, 0, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 0, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }; + + yych = *m_cursor; + if (yych <= '9') + { + if (yych <= ' ') + { + if (yych <= '\n') + { + if (yych <= 0x00) + { + goto basic_json_parser_27; + } + if (yych <= 0x08) + { + goto basic_json_parser_29; + } + if (yych >= '\n') + { + goto basic_json_parser_4; + } + } + else + { + if (yych == '\r') + { + goto basic_json_parser_2; + } + if (yych <= 0x1F) + { + goto basic_json_parser_29; + } + } + } + else + { + if (yych <= ',') + { + if (yych == '"') + { + goto basic_json_parser_26; + } + if (yych <= '+') + { + goto basic_json_parser_29; + } + goto basic_json_parser_14; + } + else + { + if (yych <= '-') + { + goto basic_json_parser_22; + } + if (yych <= '/') + { + goto basic_json_parser_29; + } + if (yych <= '0') + { + goto basic_json_parser_23; + } + goto basic_json_parser_25; + } + } + } + else + { + if (yych <= 'm') + { + if (yych <= '\\') + { + if (yych <= ':') + { + goto basic_json_parser_16; + } + if (yych == '[') + { + goto basic_json_parser_6; + } + goto basic_json_parser_29; + } + else + { + if (yych <= ']') + { + goto basic_json_parser_8; + } + if (yych == 'f') + { + goto basic_json_parser_21; + } + goto basic_json_parser_29; + } + } + else + { + if (yych <= 'z') + { + if (yych <= 'n') + { + goto basic_json_parser_18; + } + if (yych == 't') + { + goto basic_json_parser_20; + } + goto basic_json_parser_29; + } + else + { + if (yych <= '{') + { + goto basic_json_parser_10; + } + if (yych == '}') + { + goto basic_json_parser_12; + } + goto basic_json_parser_29; + } + } + } +basic_json_parser_2: + ++m_cursor; + yych = *m_cursor; + goto basic_json_parser_5; +basic_json_parser_3: + { + return scan(); + } +basic_json_parser_4: + ++m_cursor; + yych = *m_cursor; +basic_json_parser_5: + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_4; + } + goto basic_json_parser_3; +basic_json_parser_6: + ++m_cursor; + { + return token_type::begin_array; + } +basic_json_parser_8: + ++m_cursor; + { + return token_type::end_array; + } +basic_json_parser_10: + ++m_cursor; + { + return token_type::begin_object; + } +basic_json_parser_12: + ++m_cursor; + { + return token_type::end_object; + } +basic_json_parser_14: + ++m_cursor; + { + return token_type::value_separator; + } +basic_json_parser_16: + ++m_cursor; + { + return token_type::name_separator; + } +basic_json_parser_18: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'u') + { + goto basic_json_parser_59; + } +basic_json_parser_19: + { + return token_type::parse_error; + } +basic_json_parser_20: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'r') + { + goto basic_json_parser_55; + } + goto basic_json_parser_19; +basic_json_parser_21: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') + { + goto basic_json_parser_50; + } + goto basic_json_parser_19; +basic_json_parser_22: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_19; + } + if (yych <= '0') + { + goto basic_json_parser_49; + } + if (yych <= '9') + { + goto basic_json_parser_40; + } + goto basic_json_parser_19; +basic_json_parser_23: + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_42; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_43; + } + if (yych == 'e') + { + goto basic_json_parser_43; + } + } +basic_json_parser_24: + { + return token_type::value_number; + } +basic_json_parser_25: + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + goto basic_json_parser_41; +basic_json_parser_26: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych <= 0x00) + { + goto basic_json_parser_19; + } + goto basic_json_parser_31; +basic_json_parser_27: + ++m_cursor; + { + return token_type::end_of_input; + } +basic_json_parser_29: + yych = *++m_cursor; + goto basic_json_parser_19; +basic_json_parser_30: + ++m_cursor; + yych = *m_cursor; +basic_json_parser_31: + if (yybm[0 + yych] & 64) + { + goto basic_json_parser_30; + } + if (yych <= 0x00) + { + goto basic_json_parser_32; + } + if (yych <= '"') + { + goto basic_json_parser_34; + } + goto basic_json_parser_33; +basic_json_parser_32: + m_cursor = m_marker; + if (yyaccept == 0) + { + goto basic_json_parser_19; + } + else + { + goto basic_json_parser_24; + } +basic_json_parser_33: + ++m_cursor; + yych = *m_cursor; + if (yych <= 'e') + { + if (yych <= '/') + { + if (yych == '"') + { + goto basic_json_parser_30; + } + if (yych <= '.') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych <= '\\') + { + if (yych <= '[') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych == 'b') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + } + } + else + { + if (yych <= 'q') + { + if (yych <= 'f') + { + goto basic_json_parser_30; + } + if (yych == 'n') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 's') + { + if (yych <= 'r') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 't') + { + goto basic_json_parser_30; + } + if (yych <= 'u') + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; + } + } + } +basic_json_parser_34: + ++m_cursor; + { + return token_type::value_string; + } +basic_json_parser_36: + ++m_cursor; + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych >= ':') + { + goto basic_json_parser_32; + } + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_37; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych >= 'g') + { + goto basic_json_parser_32; + } + } +basic_json_parser_37: + ++m_cursor; + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych >= ':') + { + goto basic_json_parser_32; + } + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_38; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych >= 'g') + { + goto basic_json_parser_32; + } + } +basic_json_parser_38: + ++m_cursor; + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych >= ':') + { + goto basic_json_parser_32; + } + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_39; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych >= 'g') + { + goto basic_json_parser_32; + } + } +basic_json_parser_39: + ++m_cursor; + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_30; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } +basic_json_parser_40: + yyaccept = 1; + m_marker = ++m_cursor; + yych = *m_cursor; +basic_json_parser_41: + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_40; + } + if (yych <= 'D') + { + if (yych != '.') + { + goto basic_json_parser_24; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_43; + } + if (yych == 'e') + { + goto basic_json_parser_43; + } + goto basic_json_parser_24; + } +basic_json_parser_42: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_47; + } + goto basic_json_parser_32; +basic_json_parser_43: + yych = *++m_cursor; + if (yych <= ',') + { + if (yych != '+') + { + goto basic_json_parser_32; + } + } + else + { + if (yych <= '-') + { + goto basic_json_parser_44; + } + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_45; + } + goto basic_json_parser_32; + } +basic_json_parser_44: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych >= ':') + { + goto basic_json_parser_32; + } +basic_json_parser_45: + ++m_cursor; + yych = *m_cursor; + if (yych <= '/') + { + goto basic_json_parser_24; + } + if (yych <= '9') + { + goto basic_json_parser_45; + } + goto basic_json_parser_24; +basic_json_parser_47: + yyaccept = 1; + m_marker = ++m_cursor; + yych = *m_cursor; + if (yych <= 'D') + { + if (yych <= '/') + { + goto basic_json_parser_24; + } + if (yych <= '9') + { + goto basic_json_parser_47; + } + goto basic_json_parser_24; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_43; + } + if (yych == 'e') + { + goto basic_json_parser_43; + } + goto basic_json_parser_24; + } +basic_json_parser_49: + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_42; + } + goto basic_json_parser_24; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_43; + } + if (yych == 'e') + { + goto basic_json_parser_43; + } + goto basic_json_parser_24; + } +basic_json_parser_50: + yych = *++m_cursor; + if (yych != 'l') + { + goto basic_json_parser_32; + } + yych = *++m_cursor; + if (yych != 's') + { + goto basic_json_parser_32; + } + yych = *++m_cursor; + if (yych != 'e') + { + goto basic_json_parser_32; + } + ++m_cursor; + { + return token_type::literal_false; + } +basic_json_parser_55: + yych = *++m_cursor; + if (yych != 'u') + { + goto basic_json_parser_32; + } + yych = *++m_cursor; + if (yych != 'e') + { + goto basic_json_parser_32; + } + ++m_cursor; + { + return token_type::literal_true; + } +basic_json_parser_59: + yych = *++m_cursor; + if (yych != 'l') + { + goto basic_json_parser_32; + } + yych = *++m_cursor; + if (yych != 'l') + { + goto basic_json_parser_32; + } + ++m_cursor; + { + return token_type::literal_null; + } + } + + } + + /// return string representation of last read token + inline string_t get_token() const noexcept + { + 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., @c "\\n" is replaced by @c "\n"), some are copied + as is (e.g., @c "\\\\"). Furthermore, Unicode escapes of the shape + @c "\\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 + @exception std::out_of_range if to_unicode fails + */ + inline 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; + } + + // characters that are not "un"escsaped + 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); + + 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 11 characters (xxxx\uyyyy) + i += 11; + } + 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 return number value for number tokens + + This function translates the last token into a floating point number. + The pointer m_begin points to the beginning of the parsed number. We + pass this pointer to std::strtod which sets endptr to the first + character past the converted number. If this pointer is not the same as + m_cursor, then either more or less characters have been used during the + comparison. This can happen for inputs like "01" which will be treated + like number 0 followed by number 1. + + @return the result of the number conversion or NAN if the conversion + read past the current token. The latter case needs to be treated by the + caller function. + + @exception std::range_error if passed value is out of range + */ + inline number_float_t get_number() const + { + // conversion + typename string_t::value_type* endptr; + const auto float_val = std::strtod(reinterpret_cast(m_start), + &endptr); + + // return float_val if the whole number was translated and NAN + // otherwise + return (reinterpret_cast(endptr) == m_cursor) ? float_val : NAN; + } + + private: + /// the buffer + const lexer_char_t* m_content = nullptr; + /// pointer to he beginning of the current symbol + const lexer_char_t* m_start = 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; + }; + + class parser + { + public: + /// constructor for strings + inline parser(const string_t& s) : m_buffer(s), m_lexer(m_buffer) + { + // read first token + get_token(); + } + + /// a parser reading from an input stream + inline parser(std::istream& _is) + { + while (_is) + { + string_t input_line; + std::getline(_is, input_line); + m_buffer += input_line; + } + + // initializer lexer + m_lexer = lexer(m_buffer); + + // read first token + get_token(); + } + + /// public parser interface + inline basic_json parse() + { + basic_json result = parse_internal(); + + expect(lexer::token_type::end_of_input); + + return result; + } + + private: + /// the actual parser + inline basic_json parse_internal() + { + switch (last_token) + { + case (lexer::token_type::begin_object): + { + // explicitly set result to object to cope with {} + basic_json result(value_t::object); + + // read next token + get_token(); + + // closing } -> we are done + if (last_token == lexer::token_type::end_object) + { + get_token(); + return result; + } + + // 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(); + + // parse separator (:) + get_token(); + expect(lexer::token_type::name_separator); + + // parse value + get_token(); + result[key] = parse_internal(); + } + while (last_token == lexer::token_type::value_separator); + + // closing } + expect(lexer::token_type::end_object); + get_token(); + + return result; + } + + case (lexer::token_type::begin_array): + { + // explicitly set result to object to cope with [] + basic_json result(value_t::array); + + // read next token + get_token(); + + // closing ] -> we are done + if (last_token == lexer::token_type::end_array) + { + get_token(); + return result; + } + + // otherwise: parse values + do + { + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) + { + get_token(); + } + + // parse value + result.push_back(parse_internal()); + } + while (last_token == lexer::token_type::value_separator); + + // closing ] + expect(lexer::token_type::end_array); + get_token(); + + return result; + } + + case (lexer::token_type::literal_null): + { + get_token(); + return basic_json(nullptr); + } + + case (lexer::token_type::value_string): + { + const auto s = m_lexer.get_string(); + get_token(); + return basic_json(s); + } + + case (lexer::token_type::literal_true): + { + get_token(); + return basic_json(true); + } + + case (lexer::token_type::literal_false): + { + get_token(); + return basic_json(false); + } + + case (lexer::token_type::value_number): + { + auto float_val = m_lexer.get_number(); + + // NAN is returned if token could not be translated + // completely + if (std::isnan(float_val)) + { + throw std::invalid_argument(std::string("parse error - ") + + m_lexer.get_token() + " is not a number"); + } + + get_token(); + + // check if conversion loses precision + const auto int_val = static_cast(float_val); + if (float_val == int_val) + { + // we basic_json not lose precision -> return int + return basic_json(int_val); + } + else + { + // we would lose precision -> returnfloat + return basic_json(float_val); + } + } + + default: + { + std::string error_msg = "parse error - unexpected \'"; + error_msg += m_lexer.get_token(); + error_msg += "\' ("; + error_msg += lexer::token_type_name(last_token) + ")"; + throw std::invalid_argument(error_msg); + } + } + } + + /// get next token from lexer + inline typename lexer::token_type get_token() + { + last_token = m_lexer.scan(); + return last_token; + } + + inline void expect(typename lexer::token_type t) const + { + if (t != last_token) + { + std::string error_msg = "parse error - unexpected \'"; + error_msg += m_lexer.get_token(); + error_msg += "\' (" + lexer::token_type_name(last_token); + error_msg += "); expected " + lexer::token_type_name(t); + throw std::invalid_argument(error_msg); + } + } + + private: + /// the buffer + string_t m_buffer; + /// the type of the last read token + typename lexer::token_type last_token = lexer::token_type::uninitialized; + /// the lexer + lexer m_lexer; + }; +}; + + +///////////// +// presets // +///////////// + +/// default JSON class +using json = basic_json<>; + +} + + +///////////////////////// +// nonmember functions // +///////////////////////// + +// specialization of std::swap, and std::hash +namespace std +{ +/// swaps the values of two JSON objects +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 +{ + /// return a hash value for a JSON object + inline size_t operator()(const nlohmann::json& j) const + { + // a naive hashing via the string representation + const auto& h = hash(); + return h(j.dump()); + } +}; +} + +/*! +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 s a string representation of a JSON object +@return a JSON object +*/ +inline nlohmann::json operator "" _json(const char* s, std::size_t) +{ + return nlohmann::json::parse(reinterpret_cast + (const_cast(s))); +} + +#endif diff --git a/src/pushhandler.cpp b/src/pushhandler.cpp index 9785d38..1097dbd 100644 --- a/src/pushhandler.cpp +++ b/src/pushhandler.cpp @@ -1,5 +1,5 @@ #include "pushhandler.h" -#include "json/json.h" +#include "json/json.hpp" #include From 7d011c09bdf803deae61df0e9922d308a8a5d2c3 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Thu, 17 Mar 2016 15:57:34 +0100 Subject: [PATCH 08/23] Removed installation option from README --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index e6e61dc..0beff15 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,6 @@ Send push-notifications to your phone from the command line. Uses [PushNotifier] ## Installation -### Option 1: Debian package -You can download debian packages from [my website](http://hackherz.com/pusher#download). - -### Option 2: From source Make sure you have **libcurl4-openssl-dev** installed. ```bash $ git clone https://github.com/HackHerz/pusher.git From 34bd85a206a83b513cd0285953a13f75eda27a7e Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Tue, 21 Jun 2016 10:52:05 +0200 Subject: [PATCH 09/23] Using pkg-config now --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index 30dfb3f..98e94cf 100644 --- a/makefile +++ b/makefile @@ -6,7 +6,7 @@ INSTALL_DIR = /usr/local/bin CXX = g++ CPPFLAGS = -std=c++11 BUILDCOMMAND = $(CXX) $(CPPFLAGS) -LIBS = -lcurl +LIBS = `pkg-config libcurl --cflags --libs` #============================================================================= From bf4d174431905501a20633e2a8bde50311272676 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Tue, 21 Jun 2016 11:03:55 +0200 Subject: [PATCH 10/23] New version of the json library --- src/json/json.hpp | 10299 ++++++++++++++++++++++++++++++++++-------- src/main.cpp | 8 +- src/pushhandler.cpp | 2 +- 3 files changed, 8324 insertions(+), 1985 deletions(-) diff --git a/src/json/json.hpp b/src/json/json.hpp index c1ab876..9d6687d 100644 --- a/src/json/json.hpp +++ b/src/json/json.hpp @@ -1,17 +1,43 @@ -/*! -@file -@copyright The code is licensed under the MIT License - , - Copyright (c) 2013-2015 Niels Lohmann. -@author Niels Lohmann -@see https://github.com/nlohmann/json +/* + __ _____ _____ _____ + __| | __| | | | 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 -#define _NLOHMANN_JSON +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP #include +#include +#include +#include +#include #include +#include +#include +#include #include #include #include @@ -21,109 +47,762 @@ #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 JSON +@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; +}; -@tparam ObjectType type for JSON objects - (@c std::map by default) -@tparam ArrayType type for JSON arrays - (@c std::vector by default) -@tparam StringType type for JSON strings and object keys - (@c std::string by default) -@tparam BooleanType type for JSON booleans - (@c bool by default) -@tparam NumberIntegerType type for JSON integer numbers - (@c int64_t by default) -@tparam NumberFloatType type for JSON floating-point numbers - (@c double by default) -@tparam Allocator type of the allocator to use - (@c std::allocator by default) +/*! +@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 -@see ECMA 404 +@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 = int64_t, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, - template class Allocator = std::allocator + 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 // ///////////////////// - // forward declarations - class iterator; - class const_iterator; + /// @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 = basic_json&; + using reference = value_type&; /// the type of an element const reference - using const_reference = const basic_json&; - /// the type of an element pointer - using pointer = basic_json*; - /// the type of an element const pointer - using const_pointer = const basic_json*; + 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 - using iterator = basic_json::iterator; + class iterator; /// a const iterator for a basic_json container - using const_iterator = basic_json::const_iterator; + class const_iterator; /// a reverse iterator for a basic_json container - using reverse_iterator = std::reverse_iterator; + using reverse_iterator = json_reverse_iterator; /// a const reverse iterator for a basic_json container - using const_reverse_iterator = std::reverse_iterator; + 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 // /////////////////////////// - /// a type for an object - using object_t = ObjectType; - /// a type for an array - using array_t = ArrayType; - /// a type for a string - using string_t = StringType; - /// a type for a boolean - using boolean_t = BooleanType; - /// a type for a number (integer) - using number_integer_t = NumberIntegerType; - /// a type for a number (floating-point) - using number_float_t = NumberFloatType; - /// a type for list initialization - using list_init_t = std::initializer_list; + /// @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 // //////////////////////// - /// a JSON value + /*! + @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) @@ -132,240 +811,829 @@ class basic_json array_t* array; /// string (stored with pointer to save storage) string_t* string; - /// bolean + /// 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) : boolean(v) {} + json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) - json_value(number_integer_t v) : number_integer(v) {} + 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) : number_float(v) {} + 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); + } }; - ///////////////////////////////// - // JSON value type enumeration // - ///////////////////////////////// + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// - /// JSON value type enumeration - enum class value_t : uint8_t + /*! + @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 { - 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_float ///< number value (floating-point) + /// 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 // ////////////////// - /// create an empty value with a given type - inline basic_json(const value_t value) - : m_type(value) + /// @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) { - switch (m_type) + 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)) { - case (value_t::null): - { - break; - } - - case (value_t::object): - { - Allocator alloc; - m_value.object = alloc.allocate(1); - alloc.construct(m_value.object); - break; - } - - case (value_t::array): - { - Allocator alloc; - m_value.array = alloc.allocate(1); - alloc.construct(m_value.array); - break; - } - - case (value_t::string): - { - Allocator alloc; - m_value.string = alloc.allocate(1); - alloc.construct(m_value.string, ""); - break; - } - - case (value_t::boolean): - { - m_value.boolean = boolean_t(false); - break; - } - - case (value_t::number_integer): - { - m_value.number_integer = number_integer_t(0); - break; - } - - case (value_t::number_float): - { - m_value.number_float = number_float_t(0.0); - break; - } + m_type = value_t::null; + m_value = json_value(); } } - /// create a null object (implicitly) - inline basic_json() noexcept - : m_type(value_t::null) - {} + /*! + @brief create an floating-point number (implicit) - /// create a null object (explicitly) - inline basic_json(std::nullptr_t) noexcept - : m_type(value_t::null) - {} + 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. - /// create an object (explicit) - inline basic_json(const object_t& value) - : m_type(value_t::object) - { - Allocator alloc; - m_value.object = alloc.allocate(1); - alloc.construct(m_value.object, value); - } + @tparam CompatibleNumberFloatType A floating-point type which is + compatible to @ref number_float_t. Examples may include the types `float` + or `double`. - /// create an object (implicit) - template ::value and - std::is_constructible::value, int>::type - = 0> - inline basic_json(const V& value) - : m_type(value_t::object) - { - Allocator alloc; - m_value.object = alloc.allocate(1); - alloc.construct(m_value.object, value.begin(), value.end()); - } + @param[in] val a floating-point to create a JSON number from - /// create an array (explicit) - inline basic_json(const array_t& value) - : m_type(value_t::array) - { - Allocator alloc; - m_value.array = alloc.allocate(1); - alloc.construct(m_value.array, value); - } + @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. - /// create an array (implicit) - 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> - inline basic_json(const V& value) - : m_type(value_t::array) - { - Allocator alloc; - m_value.array = alloc.allocate(1); - alloc.construct(m_value.array, value.begin(), value.end()); - } + @complexity Constant. - /// create a string (explicit) - inline basic_json(const string_t& value) - : m_type(value_t::string) - { - Allocator alloc; - m_value.string = alloc.allocate(1); - alloc.construct(m_value.string, value); - } + @liveexample{The example below shows the construction of several + floating-point number values from compatible + types.,basic_json__CompatibleNumberFloatType} - /// create a string (explicit) - inline basic_json(const typename string_t::value_type* value) - : m_type(value_t::string) - { - Allocator alloc; - m_value.string = alloc.allocate(1); - alloc.construct(m_value.string, value); - } + @sa @ref basic_json(const number_float_t) -- create a number value + (floating-point) - /// create a string (implicit) - template ::value, int>::type - = 0> - inline basic_json(const V& value) - : basic_json(string_t(value)) - {} - - /// create a boolean (explicit) - inline basic_json(boolean_t value) - : m_type(value_t::boolean), m_value(value) - {} - - /// create an integer number (explicit) - inline basic_json(const number_integer_t& value) - : m_type(value_t::number_integer), m_value(value) - {} - - /// create an integer number (implicit) - template::value and - std::numeric_limits::is_integer, T>::type - = 0> - inline basic_json(const T value) noexcept - : m_type(value_t::number_integer), m_value(number_integer_t(value)) - {} - - /// create a floating-point number (explicit) - inline basic_json(const number_float_t& value) - : m_type(value_t::number_float), m_value(value) - {} - - /// create a floating-point number (implicit) - template::value and - std::is_floating_point::value>::type + std::is_constructible::value and + std::is_floating_point::value>::type > - inline basic_json(const T value) noexcept - : m_type(value_t::number_float), m_value(number_float_t(value)) + basic_json(const CompatibleNumberFloatType val) noexcept + : basic_json(number_float_t(val)) {} - /// create a container (array or object) from an initializer list - inline basic_json(list_init_t l, bool type_deduction = true, value_t manual_type = value_t::array) + /*! + @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_object = true; + 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 : l) + // check if each element is an array with two elements whose first + // element is a string + for (const auto& element : init) { - if ((element.m_final and element.m_type == value_t::array) - or (element.m_type != value_t::array or element.size() != 2 - or element[0].m_type != value_t::string)) + 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_object = false; + is_an_object = false; break; } } @@ -373,112 +1641,398 @@ class basic_json // adjust type if type deduction is not wanted if (not type_deduction) { - // mark this object's type as final - m_final = true; - // if array is wanted, do not create an object though possible if (manual_type == value_t::array) { - is_object = false; + is_an_object = false; } // if object is wanted but impossible, throw an exception - if (manual_type == value_t::object and not is_object) + if (manual_type == value_t::object and not is_an_object) { - throw std::logic_error("cannot create JSON object from initializer list"); + throw std::domain_error("cannot create object from initializer list"); } } - if (is_object) + if (is_an_object) { // the initializer list is a list of pairs -> create object m_type = value_t::object; - Allocator alloc; - m_value.object = alloc.allocate(1); - alloc.construct(m_value.object); + m_value = value_t::object; - for (auto& element : l) + assert(m_value.object != nullptr); + + for (auto& element : init) { - m_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1])); + 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; - Allocator alloc; - m_value.array = alloc.allocate(1); - alloc.construct(m_value.array, std::move(l)); + m_value.array = create(init); } } - /// explicitly create an array from an initializer list - inline static basic_json array(list_init_t l = list_init_t()) + /*! + @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(l, false, value_t::array); + return basic_json(init, false, value_t::array); } - /// explicitly create an object from an initializer list - inline static basic_json object(list_init_t l = list_init_t()) + /*! + @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(l, false, value_t::object); + 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 // /////////////////////////////////////// - /// copy constructor - inline basic_json(const basic_json& other) + /*! + @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::null): + case value_t::object: { + assert(other.m_value.object != nullptr); + m_value = *other.m_value.object; break; } - case (value_t::object): + + case value_t::array: { - Allocator alloc; - m_value.object = alloc.allocate(1); - alloc.construct(m_value.object, *other.m_value.object); + assert(other.m_value.array != nullptr); + m_value = *other.m_value.array; break; } - case (value_t::array): + + case value_t::string: { - Allocator alloc; - m_value.array = alloc.allocate(1); - alloc.construct(m_value.array, *other.m_value.array); + assert(other.m_value.string != nullptr); + m_value = *other.m_value.string; break; } - case (value_t::string): + + case value_t::boolean: { - Allocator alloc; - m_value.string = alloc.allocate(1); - alloc.construct(m_value.string, *other.m_value.string); + m_value = other.m_value.boolean; break; } - case (value_t::boolean): + + case value_t::number_integer: { - m_value.boolean = other.m_value.boolean; + m_value = other.m_value.number_integer; break; } - case (value_t::number_integer): + + case value_t::number_unsigned: { - m_value.number_integer = other.m_value.number_integer; + m_value = other.m_value.number_unsigned; break; } - case (value_t::number_float): + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + default: { - m_value.number_float = other.m_value.number_float; break; } } } - /// move constructor - inline basic_json(basic_json&& other) noexcept + /*! + @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)) { @@ -487,48 +2041,82 @@ class basic_json other.m_value = {}; } - /// copy assignment - inline reference& operator=(basic_json other) noexcept ( + /*! + @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 ) { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); return *this; } - /// destructor - inline ~basic_json() noexcept + /*! + @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): + case value_t::object: { - Allocator alloc; + AllocatorType alloc; alloc.destroy(m_value.object); alloc.deallocate(m_value.object, 1); - m_value.object = nullptr; break; } - case (value_t::array): + case value_t::array: { - Allocator alloc; + AllocatorType alloc; alloc.destroy(m_value.array); alloc.deallocate(m_value.array, 1); - m_value.array = nullptr; break; } - case (value_t::string): + case value_t::string: { - Allocator alloc; + AllocatorType alloc; alloc.destroy(m_value.string); alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; break; } @@ -540,429 +2128,2402 @@ class basic_json } } + /// @} public: /////////////////////// // object inspection // /////////////////////// + /// @name object inspection + /// @{ + /*! @brief serialization - Serialization function for JSON objects. The function tries to mimick Python's - @p json.dumps() function, and currently supports its @p indent parameter. + Serialization function for JSON values. The function tries to mimic + Python's @p json.dumps() function, and currently supports its @p indent + parameter. - @param indent sif 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 + @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 */ - inline string_t dump(const int indent = -1) const noexcept + string_t dump(const int indent = -1) const { + std::stringstream ss; + if (indent >= 0) { - return dump(true, static_cast(indent)); + dump(ss, true, static_cast(indent)); } else { - return dump(false, 0); + dump(ss, false, 0); } + + return ss.str(); } - /// return the type of the object (explicit) - inline value_t type() const noexcept + /*! + @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; } - /// return the type of the object (implicit) - operator value_t() const noexcept + /*! + @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; } + /// @} - ////////////////////// - // value conversion // - ////////////////////// + private: + ////////////////// + // value access // + ////////////////// /// get an object (explicit) template ::value and - std::is_constructible::value, int>::type - = 0> - inline T get() const + std::is_convertible::value and + std::is_convertible::value + , int>::type = 0> + T get_impl(T*) const { - switch (m_type) + if (is_object()) { - case (value_t::object): - return T(m_value.object->begin(), m_value.object->end()); - default: - throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + 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 - std::is_constructible::value, int>::type - = 0> - inline T get() const + std::is_convertible::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 { - switch (m_type) + if (is_array()) { - case (value_t::array): - return T(m_value.array->begin(), m_value.array->end()); - default: - throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + 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> - inline T get() const + std::is_convertible::value + , int>::type = 0> + T get_impl(T*) const { - switch (m_type) + if (is_string()) { - case (value_t::string): - return *m_value.string; - default: - throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + assert(m_value.string != nullptr); + return *m_value.string; } - } - - /// get a boolean (explicit) - template ::value, int>::type - = 0> - inline T get() const - { - switch (m_type) + else { - case (value_t::boolean): - return m_value.boolean; - default: - throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + throw std::domain_error("type must be string, but is " + type_name()); } } /// get a number (explicit) template::value and - std::is_arithmetic::value, int>::type - = 0> - inline T get() const + std::is_arithmetic::value + , int>::type = 0> + T get_impl(T*) const { switch (m_type) { - case (value_t::number_integer): + case value_t::number_integer: + { return static_cast(m_value.number_integer); - case (value_t::number_float): + } + + 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::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); + { + throw std::domain_error("type must be number, but is " + type_name()); + } } } - /// get a value (implicit) - template - inline operator T() const + /// get a boolean (explicit) + constexpr boolean_t get_impl(boolean_t*) const { - return get(); + 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 // //////////////////// - /// access specified element with bounds checking - inline reference at(size_type pos) + /// @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 (m_type != value_t::array) + if (is_array()) { - throw std::runtime_error("cannot use at with " + type_name()); + 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()); } - - return m_value.array->at(pos); } - /// access specified element with bounds checking - inline const_reference at(size_type pos) const + /*! + @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 (m_type != value_t::array) + if (is_array()) { - throw std::runtime_error("cannot use at with " + type_name()); + 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()); } - - return m_value.array->at(pos); } - /// access specified element - inline reference operator[](size_type pos) - { - // at only works for arrays - if (m_type != value_t::array) - { - throw std::runtime_error("cannot use [] with " + type_name()); - } + /*! + @brief access specified object element with bounds checking - return m_value.array->operator[](pos); - } + Returns a reference to the element at with specified key @a key, with + bounds checking. - /// access specified element - inline const_reference operator[](size_type pos) const - { - // at only works for arrays - if (m_type != value_t::array) - { - throw std::runtime_error("cannot use [] with " + type_name()); - } + @param[in] key key of the element to access - return m_value.array->operator[](pos); - } + @return reference to the element at key @a key - /// access specified element with bounds checking - inline reference at(const typename object_t::key_type& 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 (m_type != value_t::object) + if (is_object()) { - throw std::runtime_error("cannot use at with " + type_name()); + 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()); } - - return m_value.object->at(key); } - /// access specified element with bounds checking - inline const_reference at(const typename object_t::key_type& key) const + /*! + @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 (m_type != value_t::object) + if (is_object()) { - throw std::runtime_error("cannot use at with " + type_name()); + 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()); } - - return m_value.object->at(key); } - /// access specified element - inline reference operator[](const typename object_t::key_type& key) + /*! + @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 to object - if (m_type == value_t::null) + // 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; - Allocator alloc; - m_value.object = alloc.allocate(1); - alloc.construct(m_value.object); + m_value.object = create(); } - // at only works for objects - if (m_type != value_t::object) + // operator[] only works for objects + if (is_object()) { - throw std::runtime_error("cannot use [] with " + type_name()); + assert(m_value.object != nullptr); + return m_value.object->operator[](key); + } + else + { + throw std::domain_error("cannot use operator[] with " + type_name()); } - - return m_value.object->operator[](key); } - /// access specified element - inline const_reference operator[](const typename object_t::key_type& key) const + /*! + @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 { - // at only works for objects - if (m_type != value_t::object) + // const operator[] only works for objects + if (is_object()) { - throw std::runtime_error("cannot use [] with " + type_name()); + 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()); } - - return m_value.object->operator[](key); } - /// access specified element (needed for clang) - template - inline reference operator[](const T (&key)[n]) + /*! + @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 (m_type == value_t::null) + if (is_null()) { m_type = value_t::object; - Allocator alloc; - m_value.object = alloc.allocate(1); - alloc.construct(m_value.object); + m_value = value_t::object; } // at only works for objects - if (m_type != value_t::object) + if (is_object()) { - throw std::runtime_error("cannot use [] with " + type_name()); + assert(m_value.object != nullptr); + return m_value.object->operator[](key); + } + else + { + throw std::domain_error("cannot use operator[] with " + type_name()); } - - return m_value.object->operator[](key); } - /// access specified element (needed for clang) - template - inline const_reference operator[](const T (&key)[n]) const + /*! + @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 (m_type != value_t::object) + if (is_object()) { - throw std::runtime_error("cannot use [] with " + type_name()); + 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()); } - - return m_value.object->operator[](key); } + /*! + @brief access specified object element with default value - /// find an element in an object - inline iterator find(typename object_t::key_type key) + 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 (m_type == value_t::object) + if (is_object()) { + assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } return result; } - /// find an element in an object - inline const_iterator find(typename object_t::key_type key) const + /*! + @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 (m_type == value_t::object) + 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 // /////////////// - /// returns an iterator to the beginning of the container - inline iterator begin() noexcept + /// @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; } - /// returns a const iterator to the beginning of the container - inline const_iterator begin() const noexcept + /*! + @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; } - /// returns a const iterator to the beginning of the container - inline 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 the end of the container - inline iterator end() noexcept + 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; } - /// returns a const iterator to the end of the container - inline const_iterator end() const noexcept + /*! + @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; } - /// returns a const iterator to the end of the container - inline const_iterator cend() const noexcept - { - const_iterator result(this); - result.set_end(); - return result; - } + /*! + @brief returns an iterator to the reverse-beginning - /// returns a reverse iterator to the beginning - inline reverse_iterator rbegin() noexcept + 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()); } - /// returns a reverse iterator to the beginning - inline const_reverse_iterator rbegin() const noexcept + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept { - return const_reverse_iterator(end()); + return crbegin(); } - /// returns a reverse iterator to the end - inline reverse_iterator rend() noexcept + /*! + @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()); } - /// returns a reverse iterator to the end - inline const_reverse_iterator rend() const noexcept + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept { - return const_reverse_iterator(begin()); + return crend(); } - /// returns a reverse iterator to the beginning - inline const_reverse_iterator crbegin() const noexcept + /*! + @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()); } - /// returns a reverse iterator to the end - inline const_reverse_iterator crend() const noexcept + /*! + @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 // ////////////// - /// checks whether the container is empty - inline bool empty() const noexcept + /// @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): + case value_t::null: { + // null values are empty return true; } - case (value_t::array): + case value_t::array: { + assert(m_value.array != nullptr); return m_value.array->empty(); } - case (value_t::object): + case value_t::object: { + assert(m_value.object != nullptr); return m_value.object->empty(); } @@ -974,23 +4535,59 @@ class basic_json } } - /// returns the number of elements - inline size_type size() const noexcept + /*! + @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): + case value_t::null: { + // null values are empty return 0; } - case (value_t::array): + case value_t::array: { + assert(m_value.array != nullptr); return m_value.array->size(); } - case (value_t::object): + case value_t::object: { + assert(m_value.object != nullptr); return m_value.object->size(); } @@ -1002,178 +4599,570 @@ class basic_json } } - /// returns the maximum possible number of elements - inline size_type max_size() const noexcept + /*! + @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::null): - { - return 0; - } - - case (value_t::array): + case value_t::array: { + assert(m_value.array != nullptr); return m_value.array->max_size(); } - case (value_t::object): + case value_t::object: { + assert(m_value.object != nullptr); return m_value.object->max_size(); } default: { - // all other types have max_size 1 - return 1; + // all other types have max_size() == size() + return size(); } } } + /// @} + /////////////// // modifiers // /////////////// - /// clears the contents - inline void clear() noexcept + /// @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::null): - { - break; - } - - case (value_t::number_integer): + case value_t::number_integer: { m_value.number_integer = 0; break; } - case (value_t::number_float): + 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): + case value_t::boolean: { m_value.boolean = false; break; } - case (value_t::string): + case value_t::string: { + assert(m_value.string != nullptr); m_value.string->clear(); break; } - case (value_t::array): + case value_t::array: { + assert(m_value.array != nullptr); m_value.array->clear(); break; } - case (value_t::object): + case value_t::object: { + assert(m_value.object != nullptr); m_value.object->clear(); break; } + + default: + { + break; + } } } - /// add an object to an array - inline void push_back(basic_json&& value) + /*! + @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(m_type == value_t::null or m_type == value_t::array)) + if (not(is_null() or is_array())) { - throw std::runtime_error("cannot add element to " + type_name()); + throw std::domain_error("cannot use push_back() with " + type_name()); } // transform null object into an array - if (m_type == value_t::null) + if (is_null()) { m_type = value_t::array; - Allocator alloc; - m_value.array = alloc.allocate(1); - alloc.construct(m_value.array); + m_value = value_t::array; } // add element to array (move semantics) - m_value.array->push_back(std::move(value)); + assert(m_value.array != nullptr); + m_value.array->push_back(std::move(val)); // invalidate object - value.m_type = value_t::null; + val.m_type = value_t::null; } - /// add an object to an array - inline reference operator+=(basic_json&& value) + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) { - push_back(std::move(value)); + push_back(std::move(val)); return *this; } - /// add an object to an array - inline void push_back(const basic_json& value) + /*! + @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(m_type == value_t::null or m_type == value_t::array)) + if (not(is_null() or is_array())) { - throw std::runtime_error("cannot add element to " + type_name()); + throw std::domain_error("cannot use push_back() with " + type_name()); } // transform null object into an array - if (m_type == value_t::null) + if (is_null()) { m_type = value_t::array; - Allocator alloc; - m_value.array = alloc.allocate(1); - alloc.construct(m_value.array); + m_value = value_t::array; } // add element to array - m_value.array->push_back(value); + assert(m_value.array != nullptr); + m_value.array->push_back(val); } - /// add an object to an array - inline reference operator+=(const basic_json& value) + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) { - push_back(value); + push_back(val); return *this; } - /// add an object to an object - inline void push_back(const typename object_t::value_type& value) + /*! + @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(m_type == value_t::null or m_type == value_t::object)) + if (not(is_null() or is_object())) { - throw std::runtime_error("cannot add element to " + type_name()); + throw std::domain_error("cannot use push_back() with " + type_name()); } // transform null object into an object - if (m_type == value_t::null) + if (is_null()) { m_type = value_t::object; - Allocator alloc; - m_value.object = alloc.allocate(1); - alloc.construct(m_value.object); + m_value = value_t::object; } // add element to array - m_value.object->insert(value); + assert(m_value.object != nullptr); + m_value.object->insert(val); } - /// add an object to an object - inline reference operator+=(const typename object_t::value_type& value) + /*! + @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(value); - return operator[](value.first); + push_back(val); + return *this; } - /// swaps the contents - inline void swap(reference other) noexcept ( + /*! + @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 @@ -1184,271 +5173,686 @@ class basic_json std::swap(m_value, other.m_value); } - /// swaps the contents - inline void swap(array_t& other) + /*! + @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 (m_type != value_t::array) + if (is_array()) { - throw std::runtime_error("cannot use swap with " + type_name()); + assert(m_value.array != nullptr); + std::swap(*(m_value.array), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); } - - // swap arrays - std::swap(*(m_value.array), other); } - /// swaps the contents - inline void swap(object_t& other) + /*! + @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 (m_type != value_t::object) + if (is_object()) { - throw std::runtime_error("cannot use swap with " + type_name()); + assert(m_value.object != nullptr); + std::swap(*(m_value.object), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); } - - // swap arrays - std::swap(*(m_value.object), other); } - /// swaps the contents - inline void swap(string_t& other) + /*! + @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 (m_type != value_t::string) + if (is_string()) { - throw std::runtime_error("cannot use swap with " + type_name()); + assert(m_value.string != nullptr); + std::swap(*(m_value.string), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); } - - // swap arrays - std::swap(*(m_value.string), other); } + /// @} + ////////////////////////////////////////// // lexicographical comparison operators // ////////////////////////////////////////// - /// comparison: equal - friend bool operator==(const_reference lhs, const_reference rhs) + /// @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 { - switch (lhs.type()) + 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) { - case (value_t::array): + 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) { - if (rhs.type() == value_t::array) + 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; } - break; - } - case (value_t::object): - { - if (rhs.type() == value_t::object) + 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; } - break; - } - case (value_t::null): - { - if (rhs.type() == value_t::null) + case value_t::null: { return true; } - break; - } - case (value_t::string): - { - if (rhs.type() == value_t::string) + 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; } - break; - } - case (value_t::boolean): - { - if (rhs.type() == value_t::boolean) + case value_t::boolean: { return lhs.m_value.boolean == rhs.m_value.boolean; } - break; - } - case (value_t::number_integer): - { - if (rhs.type() == value_t::number_integer) + case value_t::number_integer: { return lhs.m_value.number_integer == rhs.m_value.number_integer; } - if (rhs.type() == value_t::number_float) + case value_t::number_unsigned: { - return lhs.m_value.number_integer == static_cast(rhs.m_value.number_float); + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; } - break; - } - case (value_t::number_float): - { - if (rhs.type() == value_t::number_integer) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); - } - if (rhs.type() == value_t::number_float) + case value_t::number_float: { return lhs.m_value.number_float == rhs.m_value.number_float; } - break; + 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; } - /// comparison: not equal - friend bool operator!=(const_reference lhs, const_reference rhs) + /*! + @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); } - /// comparison: less than - friend bool operator<(const_reference lhs, const_reference 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 { - switch (lhs.type()) + 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) { - case (value_t::array): + switch (lhs_type) { - if (rhs.type() == value_t::array) + 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; } - break; - } - case (value_t::object): - { - if (rhs.type() == value_t::object) + 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; } - break; - } - case (value_t::null): - { - if (rhs.type() == value_t::null) + case value_t::null: { return false; } - break; - } - case (value_t::string): - { - if (rhs.type() == value_t::string) + 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; } - break; - } - case (value_t::boolean): - { - if (rhs.type() == value_t::boolean) + case value_t::boolean: { return lhs.m_value.boolean < rhs.m_value.boolean; } - break; - } - case (value_t::number_integer): - { - if (rhs.type() == value_t::number_integer) + case value_t::number_integer: { return lhs.m_value.number_integer < rhs.m_value.number_integer; } - if (rhs.type() == value_t::number_float) + case value_t::number_unsigned: { - return lhs.m_value.number_integer < static_cast(rhs.m_value.number_float); + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; } - break; - } - case (value_t::number_float): - { - if (rhs.type() == value_t::number_integer) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); - } - if (rhs.type() == value_t::number_float) + case value_t::number_float: { return lhs.m_value.number_float < rhs.m_value.number_float; } - break; + 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; + } - return false; + // 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); } - /// comparison: less than or equal - friend bool operator<=(const_reference lhs, const_reference rhs) + /*! + @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); } - /// comparison: greater than - friend bool operator>(const_reference lhs, const_reference rhs) + /*! + @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); } - /// comparison: greater than or equal - friend bool operator>=(const_reference lhs, const_reference 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 // /////////////////// - /// serialize to stream + /// @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 int indentation = (o.width() == 0) ? -1 : o.width(); + const bool pretty_print = (o.width() > 0); + const auto indentation = (pretty_print ? o.width() : 0); - o << j.dump(indentation); + // 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; } - /// serialize to stream + /*! + @brief serialize to stream + @copydoc operator<<(std::ostream&, const basic_json&) + */ friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { - // read width member and use it as indentation parameter if nonzero - const int indentation = (o.width() == 0) ? -1 : o.width(); - - o << j.dump(indentation); - return o; + return o << j; } + /// @} + ///////////////////// // deserialization // ///////////////////// - /// deserialize from string - static basic_json parse(const string_t& s) + /// @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).parse(); + return parser(s, cb).parse(); } - /// deserialize from stream + /*! + @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; } - /// deserialize from stream - friend std::istream& operator<<(basic_json& j, std::istream& i) - { - j = parser(i).parse(); - return i; - } + /// @} private: @@ -1457,120 +5861,62 @@ class basic_json /////////////////////////// /// return the type as string - inline string_t type_name() const noexcept + string_t type_name() const noexcept { switch (m_type) { - case (value_t::null): - { + case value_t::null: return "null"; - } - - case (value_t::object): - { + case value_t::object: return "object"; - } - - case (value_t::array): - { + case value_t::array: return "array"; - } - - case (value_t::string): - { + case value_t::string: return "string"; - } - - case (value_t::boolean): - { + case value_t::boolean: return "boolean"; - } - + case value_t::discarded: + return "discarded"; default: - { return "number"; - } } } /*! - @brief escape a string + @brief calculates the extra space to escape a JSON 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 number of characters required to escape string @a s - @param s the string to escape - @return escaped string + @complexity Linear in the length of string @a s. */ - static string_t escape_string(const string_t& s) noexcept + static std::size_t extra_space(const string_t& s) noexcept { - // create a result string of at least the size than s - string_t result; - result.reserve(s.size()); + std::size_t result = 0; - for (const auto c : s) + for (const auto& c : s) { switch (c) { - // quotation mark (0x22) case '"': - { - result += "\\\""; - break; - } - // reverse solidus (0x5c) case '\\': - { - result += "\\\\"; - break; - } - // backspace (0x08) case '\b': - { - result += "\\b"; - break; - } - // formfeed (0x0c) case '\f': - { - result += "\\f"; - break; - } - // newline (0x0a) case '\n': - { - result += "\\n"; - break; - } - // carriage return (0x0d) case '\r': - { - result += "\\r"; - break; - } - // horizontal tab (0x09) case '\t': { - result += "\\t"; + // from c (1 byte) to \x (2 bytes) + result += 1; break; } default: { - if (c >= 0 and c <= 0x1f) + if (c >= 0x00 and c <= 0x1f) { - // control characters (everything between 0x00 and 0x1f) - // -> create four-digit hex representation - std::stringstream ss; - ss << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(c); - result += ss.str(); - } - else - { - // all other characters are added as-is - result.append(1, c); + // from c (1 byte) to \uxxxx (6 bytes) + result += 5; } break; } @@ -1580,145 +5926,361 @@ class basic_json 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 serializaion internally. The indentation level is propagated as - additional parameter. In case of arrays and objects, the function is called - recursively. Note that + 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() - - numbers are converted to a string before output using std::to_string() + - 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 prettyPrint whether the output shall be pretty-printed - @param indentStep the indent level - @param currentIndent the current indent level (only used internally) + @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) */ - inline string_t dump(const bool prettyPrint, const unsigned int indentStep, - const unsigned int currentIndent = 0) const noexcept + 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 - auto new_indent = currentIndent; - - // helper function to return whitespace as indentation - const auto indent = [prettyPrint, &new_indent]() - { - return prettyPrint ? string_t(new_indent, ' ') : string_t(); - }; + unsigned int new_indent = current_indent; switch (m_type) { - case (value_t::object): + case value_t::object: { + assert(m_value.object != nullptr); + if (m_value.object->empty()) { - return "{}"; + o << "{}"; + return; } - string_t result = "{"; + o << "{"; // increase indentation - if (prettyPrint) + if (pretty_print) { - new_indent += indentStep; - result += "\n"; + 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()) { - result += prettyPrint ? ",\n" : ","; + o << (pretty_print ? ",\n" : ","); } - result += indent() + "\"" + escape_string(i->first) + "\":" + (prettyPrint ? " " : "") - + i->second.dump(prettyPrint, indentStep, new_indent); + o << string_t(new_indent, ' ') << "\"" + << escape_string(i->first) << "\":" + << (pretty_print ? " " : ""); + i->second.dump(o, pretty_print, indent_step, new_indent); } // decrease indentation - if (prettyPrint) + if (pretty_print) { - new_indent -= indentStep; - result += "\n"; + new_indent -= indent_step; + o << "\n"; } - return result + indent() + "}"; + o << string_t(new_indent, ' ') + "}"; + return; } - case (value_t::array): + case value_t::array: { + assert(m_value.array != nullptr); + if (m_value.array->empty()) { - return "[]"; + o << "[]"; + return; } - string_t result = "["; + o << "["; // increase indentation - if (prettyPrint) + if (pretty_print) { - new_indent += indentStep; - result += "\n"; + 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()) { - result += prettyPrint ? ",\n" : ","; + o << (pretty_print ? ",\n" : ","); } - result += indent() + i->dump(prettyPrint, indentStep, new_indent); + o << string_t(new_indent, ' '); + i->dump(o, pretty_print, indent_step, new_indent); } // decrease indentation - if (prettyPrint) + if (pretty_print) { - new_indent -= indentStep; - result += "\n"; + new_indent -= indent_step; + o << "\n"; } - return result + indent() + "]"; + o << string_t(new_indent, ' ') << "]"; + return; } - case (value_t::string): + case value_t::string: { - return string_t("\"") + escape_string(*m_value.string) + "\""; + assert(m_value.string != nullptr); + o << string_t("\"") << escape_string(*m_value.string) << "\""; + return; } - case (value_t::boolean): + case value_t::boolean: { - return m_value.boolean ? "true" : "false"; + o << (m_value.boolean ? "true" : "false"); + return; } - case (value_t::number_integer): + case value_t::number_integer: { - return std::to_string(m_value.number_integer); + o << m_value.number_integer; + return; } - case (value_t::number_float): + case value_t::number_unsigned: { - return std::to_string(m_value.number_float); + o << m_value.number_unsigned; + return; } - default: + case value_t::number_float: { - return "null"; + // 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 - value_t m_type = value_t::null; - - /// whether the type of JSON object may change later - bool m_final = false; + type_data_t m_type = value_t::null; /// the value of the current element json_value m_value = {}; @@ -1729,455 +6291,257 @@ class basic_json // iterators // /////////////// - /// values of a generic iterator type of non-container JSON values - enum class generic_iterator_value + /*! + @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 { - /// the iterator was not initialized - uninitialized, - /// the iterator points to the only value - begin, - /// the iterator points past the only value - end, - /// the iterator points to an invalid value - invalid + 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(); }; - /// an iterator value - template - union internal_iterator + /*! + @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 - object_iterator_t object_iterator; + typename object_t::iterator object_iterator; /// iterator for JSON arrays - array_iterator_t array_iterator; - /// generic iteraotr for all other value types - generic_iterator_value generic_iterator; + typename array_t::iterator array_iterator; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator; - /// default constructor - internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {} - /// constructor for object iterators - internal_iterator(object_iterator_t v) : object_iterator(v) {} - /// constructor for array iterators - internal_iterator(array_iterator_t v) : array_iterator(v) {} - /// constructor for generic iterators - internal_iterator(generic_iterator_value v) : generic_iterator(v) {} + /// 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: - /// a bidirectional iterator for the basic_json class - class iterator : public std::iterator + /*! + @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 = basic_json::value_type; + using value_type = typename basic_json::value_type; /// a type to represent differences between iterators - using difference_type = basic_json::difference_type; + using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) - using pointer = basic_json::pointer; + using pointer = typename basic_json::const_pointer; /// defines a reference to the type iterated over (value_type) - using reference = basic_json::reference; + using reference = typename basic_json::const_reference; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// default constructor - inline iterator() = default; + const_iterator() = default; /// constructor for a given JSON instance - inline iterator(pointer object) : m_object(object) + explicit const_iterator(pointer object) noexcept + : m_object(object) { + assert(m_object != nullptr); + switch (m_object->m_type) { - case (basic_json::value_t::object): + case basic_json::value_t::object: { m_it.object_iterator = typename object_t::iterator(); break; } - case (basic_json::value_t::array): + + case basic_json::value_t::array: { m_it.array_iterator = typename array_t::iterator(); break; } - default: - { - m_it.generic_iterator = generic_iterator_value::uninitialized; - break; - } - } - } - - /// copy assignment - inline iterator& operator=(const iterator& other) noexcept - { - m_object = other.m_object; - m_it = other.m_it; - return *this; - } - - /// set the iterator to the first value - inline void set_begin() noexcept - { - switch (m_object->m_type) - { - case (basic_json::value_t::object): - { - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } - - case (basic_json::value_t::array): - { - 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.generic_iterator = generic_iterator_value::end; - break; - } default: { - m_it.generic_iterator = generic_iterator_value::begin; - break; - } - } - } - - /// set the iterator past the last value - inline void set_end() noexcept - { - switch (m_object->m_type) - { - case (basic_json::value_t::object): - { - m_it.object_iterator = m_object->m_value.object->end(); - break; - } - - case (basic_json::value_t::array): - { - m_it.array_iterator = m_object->m_value.array->end(); - break; - } - - default: - { - m_it.generic_iterator = generic_iterator_value::end; - break; - } - } - } - - /// return a reference to the value pointed to by the iterator - inline reference operator*() const - { - switch (m_object->m_type) - { - case (basic_json::value_t::object): - { - return m_it.object_iterator->second; - } - - case (basic_json::value_t::array): - { - return *m_it.array_iterator; - } - - case (basic_json::value_t::null): - { - throw std::out_of_range("cannot get value"); - } - - default: - { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - return *m_object; - } - else - { - throw std::out_of_range("cannot get value"); - } - } - } - } - - /// dereference the iterator - inline pointer operator->() const - { - switch (m_object->m_type) - { - case (basic_json::value_t::object): - { - return &(m_it.object_iterator->second); - } - - case (basic_json::value_t::array): - { - return &*m_it.array_iterator; - } - - default: - { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - return m_object; - } - else - { - throw std::out_of_range("cannot get value"); - } - } - } - } - - /// post-increment (it++) - inline iterator operator++(int) - { - iterator result = *this; - - 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: - { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } - break; - } - } - - return result; - } - - /// pre-increment (++it) - inline iterator& operator++() - { - 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: - { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } - break; - } - } - - return *this; - } - - /// post-decrement (it--) - inline iterator operator--(int) - { - iterator result = *this; - - 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; - } - - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - - default: - { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } - break; - } - } - - return result; - } - - /// pre-decrement (--it) - inline iterator& operator--() - { - 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; - } - - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - - default: - { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } - break; - } - } - - return *this; - } - - /// comparison: equal - inline bool operator==(const iterator& other) const - { - if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) - { - return false; - } - - 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.generic_iterator == other.m_it.generic_iterator); - } - } - } - - /// comparison: not equal - inline bool operator!=(const iterator& other) const - { - return not operator==(other); - } - - private: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - internal_iterator m_it; - }; - - /// a const bidirectional iterator for the basic_json class - class const_iterator : public std::iterator - { - public: - /// the type of the values when the iterator is dereferenced - using value_type = basic_json::value_type; - /// a type to represent differences between iterators - using difference_type = basic_json::difference_type; - /// defines a pointer to the type iterated over (value_type) - using pointer = basic_json::const_pointer; - /// defines a reference to the type iterated over (value_type) - using reference = basic_json::const_reference; - /// the category of the iterator - using iterator_category = std::bidirectional_iterator_tag; - - /// default constructor - inline const_iterator() = default; - - /// constructor for a given JSON instance - inline const_iterator(pointer object) : m_object(object) - { - switch (m_object->m_type) - { - case (basic_json::value_t::object): - { - m_it.object_iterator = typename object_t::const_iterator(); - break; - } - case (basic_json::value_t::array): - { - m_it.array_iterator = typename array_t::const_iterator(); - break; - } - default: - { - m_it.generic_iterator = generic_iterator_value::uninitialized; + m_it.primitive_iterator = primitive_iterator_t(); break; } } } /// copy constructor given a nonconst iterator - inline const_iterator(const iterator& other) : m_object(other.m_object) + 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): + case basic_json::value_t::object: { m_it.object_iterator = other.m_it.object_iterator; break; } - case (basic_json::value_t::array): + case basic_json::value_t::array: { m_it.array_iterator = other.m_it.array_iterator; break; @@ -2185,100 +6549,126 @@ class basic_json default: { - m_it.generic_iterator = other.m_it.generic_iterator; + 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 - inline const_iterator& operator=(const const_iterator& other) noexcept + 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 + ) { - m_object = other.m_object; - m_it = other.m_it; + std::swap(m_object, other.m_object); + std::swap(m_it, other.m_it); return *this; } + private: /// set the iterator to the first value - inline void set_begin() noexcept + void set_begin() noexcept { + assert(m_object != nullptr); + switch (m_object->m_type) { - case (basic_json::value_t::object): + case basic_json::value_t::object: { - m_it.object_iterator = m_object->m_value.object->cbegin(); + assert(m_object->m_value.object != nullptr); + m_it.object_iterator = m_object->m_value.object->begin(); break; } - case (basic_json::value_t::array): + case basic_json::value_t::array: { - m_it.array_iterator = m_object->m_value.array->cbegin(); + assert(m_object->m_value.array != nullptr); + m_it.array_iterator = m_object->m_value.array->begin(); break; } - case (basic_json::value_t::null): + case basic_json::value_t::null: { // set to end so begin()==end() is true: null is empty - m_it.generic_iterator = generic_iterator_value::end; + m_it.primitive_iterator.set_end(); break; } default: { - m_it.generic_iterator = generic_iterator_value::begin; + m_it.primitive_iterator.set_begin(); break; } } } /// set the iterator past the last value - inline void set_end() noexcept + void set_end() noexcept { + assert(m_object != nullptr); + switch (m_object->m_type) { - case (basic_json::value_t::object): + case basic_json::value_t::object: { - m_it.object_iterator = m_object->m_value.object->cend(); + assert(m_object->m_value.object != nullptr); + m_it.object_iterator = m_object->m_value.object->end(); break; } - case (basic_json::value_t::array): + case basic_json::value_t::array: { - m_it.array_iterator = m_object->m_value.array->cend(); + assert(m_object->m_value.array != nullptr); + m_it.array_iterator = m_object->m_value.array->end(); break; } default: { - m_it.generic_iterator = generic_iterator_value::end; + m_it.primitive_iterator.set_end(); break; } } } + public: /// return a reference to the value pointed to by the iterator - inline reference operator*() const + reference operator*() const { + assert(m_object != nullptr); + switch (m_object->m_type) { - case (basic_json::value_t::object): + 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): + 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): + case basic_json::value_t::null: { throw std::out_of_range("cannot get value"); } default: { - if (m_it.generic_iterator == generic_iterator_value::begin) + if (m_it.primitive_iterator.is_begin()) { return *m_object; } @@ -2291,23 +6681,29 @@ class basic_json } /// dereference the iterator - inline pointer operator->() const + pointer operator->() const { + assert(m_object != nullptr); + switch (m_object->m_type) { - case (basic_json::value_t::object): + 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): + 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.generic_iterator == generic_iterator_value::begin) + if (m_it.primitive_iterator.is_begin()) { return m_object; } @@ -2320,53 +6716,27 @@ class basic_json } /// post-increment (it++) - inline const_iterator operator++(int) + const_iterator operator++(int) { - const_iterator result = *this; - - 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: - { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } - break; - } - } - + auto result = *this; + ++(*this); return result; } /// pre-increment (++it) - inline const_iterator& operator++() + const_iterator& operator++() { + assert(m_object != nullptr); + switch (m_object->m_type) { - case (basic_json::value_t::object): + case basic_json::value_t::object: { ++m_it.object_iterator; break; } - case (basic_json::value_t::array): + case basic_json::value_t::array: { ++m_it.array_iterator; break; @@ -2374,14 +6744,7 @@ class basic_json default: { - if (m_it.generic_iterator == generic_iterator_value::begin) - { - m_it.generic_iterator = generic_iterator_value::end; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + ++m_it.primitive_iterator; break; } } @@ -2390,80 +6753,35 @@ class basic_json } /// post-decrement (it--) - inline const_iterator operator--(int) + const_iterator operator--(int) { - const_iterator result = *this; - - 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; - } - - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - - default: - { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } - break; - } - } - + auto result = *this; + --(*this); return result; } /// pre-decrement (--it) - inline const_iterator& operator--() + const_iterator& operator--() { + assert(m_object != nullptr); + switch (m_object->m_type) { - case (basic_json::value_t::object): + case basic_json::value_t::object: { --m_it.object_iterator; break; } - case (basic_json::value_t::array): + case basic_json::value_t::array: { --m_it.array_iterator; break; } - case (basic_json::value_t::null): - { - m_it.generic_iterator = generic_iterator_value::invalid; - break; - } - default: { - if (m_it.generic_iterator == generic_iterator_value::end) - { - m_it.generic_iterator = generic_iterator_value::begin; - } - else - { - m_it.generic_iterator = generic_iterator_value::invalid; - } + --m_it.primitive_iterator; break; } } @@ -2472,43 +6790,472 @@ class basic_json } /// comparison: equal - inline bool operator==(const const_iterator& other) const + bool operator==(const const_iterator& other) const { - if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) { - return false; + 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): + case basic_json::value_t::object: { return (m_it.object_iterator == other.m_it.object_iterator); } - case (basic_json::value_t::array): + case basic_json::value_t::array: { return (m_it.array_iterator == other.m_it.array_iterator); } default: { - return (m_it.generic_iterator == other.m_it.generic_iterator); + return (m_it.primitive_iterator == other.m_it.primitive_iterator); } } } /// comparison: not equal - inline bool operator!=(const const_iterator& other) const + 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 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 * (); + } }; @@ -2521,8 +7268,8 @@ class basic_json @brief lexical analysis This class organizes the lexical analysis during JSON deserialization. The - core of it is a scanner generated by re2c that processes - a buffer and recognizes tokens according to RFC 7159 and ECMA-404. + 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 { @@ -2530,54 +7277,79 @@ class basic_json /// 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 + 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 - inline lexer(const string_t& s) noexcept - : m_content(reinterpret_cast(s.c_str())) + 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 - inline lexer() = default; + 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 codepoint the code point (must be in [0x0, 0x10ffff] + @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 - @exception std::out_of_range if code point is >0x10ffff - @exception std::invalid_argument if the low surrogate is invalid + + @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 */ - inline static string_t to_unicode(const size_t codepoint1, const size_t codepoint2 = 0) + static string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) { - string_t result; - // calculate the codepoint from the given code points - size_t codepoint = codepoint1; + 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 = @@ -2586,7 +7358,7 @@ class basic_json // 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 substract with: + // in the result so we have to subtract with: // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - 0x35FDC00; } @@ -2596,9 +7368,11 @@ class basic_json } } - if (codepoint <= 0x7f) + string_t result; + + if (codepoint < 0x80) { - // 1-byte characters: 0xxxxxxx (ASCI) + // 1-byte characters: 0xxxxxxx (ASCII) result.append(1, static_cast(codepoint)); } else if (codepoint <= 0x7ff) @@ -2630,59 +7404,65 @@ class basic_json return result; } - /// return name of values of type token_type - inline static std::string token_type_name(token_type t) noexcept + /// 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): + case token_type::uninitialized: return ""; - case (token_type::literal_true): + case token_type::literal_true: return "true literal"; - case (token_type::literal_false): + case token_type::literal_false: return "false literal"; - case (token_type::literal_null): + case token_type::literal_null: return "null literal"; - case (token_type::value_string): + case token_type::value_string: return "string literal"; - case (token_type::value_number): + 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::end_of_input): - return ""; - default: + 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 and ECMA-404 as close - as possible. These regular expressions are then translated into a - deterministic finite automaton (DFA) by the tool re2c - . As a result, the translated code for this function - consists of a large block of code with goto jumps. + 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 */ - inline token_type scan() noexcept + token_type scan() noexcept { // pointer for backtracking information - const lexer_char_t* m_marker = nullptr; + m_marker = nullptr; // remember the begin of the token m_start = m_cursor; + assert(m_start != nullptr); { @@ -2690,324 +7470,367 @@ class basic_json unsigned int yyaccept = 0; static const unsigned char yybm[] = { - 0, 64, 64, 64, 64, 64, 64, 64, - 64, 96, 96, 64, 64, 96, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 96, 64, 0, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, + 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, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 0, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, + 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, }; - - yych = *m_cursor; - if (yych <= '9') + if ((m_limit - m_cursor) < 5) { - if (yych <= ' ') + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + if (yych <= '\\') + { + if (yych <= '-') { - if (yych <= '\n') + if (yych <= '"') { if (yych <= 0x00) { - goto basic_json_parser_27; + goto basic_json_parser_2; } - if (yych <= 0x08) - { - goto basic_json_parser_29; - } - if (yych >= '\n') + if (yych <= '!') { goto basic_json_parser_4; } + goto basic_json_parser_9; } else { - if (yych == '\r') + if (yych <= '+') { - goto basic_json_parser_2; + goto basic_json_parser_4; } - if (yych <= 0x1F) + if (yych <= ',') { - goto basic_json_parser_29; + goto basic_json_parser_10; } + goto basic_json_parser_12; } } else { - if (yych <= ',') + if (yych <= '9') { - if (yych == '"') - { - goto basic_json_parser_26; - } - if (yych <= '+') - { - goto basic_json_parser_29; - } - goto basic_json_parser_14; - } - else - { - if (yych <= '-') - { - goto basic_json_parser_22; - } if (yych <= '/') { - goto basic_json_parser_29; + goto basic_json_parser_4; } if (yych <= '0') { - goto basic_json_parser_23; + goto basic_json_parser_13; } - goto basic_json_parser_25; + 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 <= 'm') + if (yych <= 't') { - if (yych <= '\\') - { - if (yych <= ':') - { - goto basic_json_parser_16; - } - if (yych == '[') - { - goto basic_json_parser_6; - } - goto basic_json_parser_29; - } - else + if (yych <= 'f') { if (yych <= ']') - { - goto basic_json_parser_8; - } - if (yych == 'f') { goto basic_json_parser_21; } - goto basic_json_parser_29; + 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 <= 'z') + if (yych <= '|') { - if (yych <= 'n') + if (yych == '{') { - goto basic_json_parser_18; + goto basic_json_parser_26; } - if (yych == 't') - { - goto basic_json_parser_20; - } - goto basic_json_parser_29; + goto basic_json_parser_4; } else { - if (yych <= '{') + if (yych <= '}') { - goto basic_json_parser_10; + goto basic_json_parser_28; } - if (yych == '}') + if (yych == 0xEF) { - goto basic_json_parser_12; + goto basic_json_parser_30; } - goto basic_json_parser_29; + goto basic_json_parser_4; } } } basic_json_parser_2: ++m_cursor; - yych = *m_cursor; - goto basic_json_parser_5; -basic_json_parser_3: { - return scan(); + return token_type::end_of_input; } basic_json_parser_4: ++m_cursor; - yych = *m_cursor; basic_json_parser_5: - if (yybm[0 + yych] & 32) { - goto basic_json_parser_4; + return token_type::parse_error; } - goto basic_json_parser_3; basic_json_parser_6: ++m_cursor; + if (m_limit <= m_cursor) { - return token_type::begin_array; + yyfill(); // LCOV_EXCL_LINE; } -basic_json_parser_8: - ++m_cursor; + yych = *m_cursor; + if (yybm[0 + yych] & 32) { - return token_type::end_array; + 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::begin_object; - } -basic_json_parser_12: - ++m_cursor; - { - return token_type::end_object; - } -basic_json_parser_14: ++m_cursor; { return token_type::value_separator; } -basic_json_parser_16: - ++m_cursor; - { - return token_type::name_separator; - } -basic_json_parser_18: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'u') - { - goto basic_json_parser_59; - } -basic_json_parser_19: - { - return token_type::parse_error; - } -basic_json_parser_20: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'r') - { - goto basic_json_parser_55; - } - goto basic_json_parser_19; -basic_json_parser_21: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') - { - goto basic_json_parser_50; - } - goto basic_json_parser_19; -basic_json_parser_22: +basic_json_parser_12: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_19; + goto basic_json_parser_5; } if (yych <= '0') { - goto basic_json_parser_49; + goto basic_json_parser_13; } if (yych <= '9') { - goto basic_json_parser_40; + goto basic_json_parser_15; } - goto basic_json_parser_19; -basic_json_parser_23: + 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_42; + goto basic_json_parser_37; } } else { if (yych <= 'E') { - goto basic_json_parser_43; + goto basic_json_parser_38; } if (yych == 'e') { - goto basic_json_parser_43; + goto basic_json_parser_38; } } -basic_json_parser_24: +basic_json_parser_14: { return token_type::value_number; } -basic_json_parser_25: +basic_json_parser_15: yyaccept = 1; - yych = *(m_marker = ++m_cursor); - goto basic_json_parser_41; -basic_json_parser_26: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x00) + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) { - goto basic_json_parser_19; + yyfill(); // LCOV_EXCL_LINE; } - goto basic_json_parser_31; -basic_json_parser_27: - ++m_cursor; - { - return token_type::end_of_input; - } -basic_json_parser_29: - yych = *++m_cursor; - goto basic_json_parser_19; -basic_json_parser_30: - ++m_cursor; yych = *m_cursor; -basic_json_parser_31: if (yybm[0 + yych] & 64) { - goto basic_json_parser_30; + goto basic_json_parser_15; } - if (yych <= 0x00) + if (yych <= 'D') { - goto basic_json_parser_32; + 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_33; -basic_json_parser_32: + goto basic_json_parser_36; +basic_json_parser_33: m_cursor = m_marker; if (yyaccept == 0) { - goto basic_json_parser_19; + goto basic_json_parser_5; } else { - goto basic_json_parser_24; + goto basic_json_parser_14; } -basic_json_parser_33: +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') { @@ -3015,13 +7838,13 @@ basic_json_parser_33: { if (yych == '"') { - goto basic_json_parser_30; + goto basic_json_parser_31; } if (yych <= '.') { - goto basic_json_parser_32; + goto basic_json_parser_33; } - goto basic_json_parser_30; + goto basic_json_parser_31; } else { @@ -3029,17 +7852,17 @@ basic_json_parser_33: { if (yych <= '[') { - goto basic_json_parser_32; + goto basic_json_parser_33; } - goto basic_json_parser_30; + goto basic_json_parser_31; } else { if (yych == 'b') { - goto basic_json_parser_30; + goto basic_json_parser_31; } - goto basic_json_parser_32; + goto basic_json_parser_33; } } } @@ -3049,13 +7872,13 @@ basic_json_parser_33: { if (yych <= 'f') { - goto basic_json_parser_30; + goto basic_json_parser_31; } if (yych == 'n') { - goto basic_json_parser_30; + goto basic_json_parser_31; } - goto basic_json_parser_32; + goto basic_json_parser_33; } else { @@ -3063,341 +7886,369 @@ basic_json_parser_33: { if (yych <= 'r') { - goto basic_json_parser_30; + goto basic_json_parser_31; } - goto basic_json_parser_32; + goto basic_json_parser_33; } else { if (yych <= 't') { - goto basic_json_parser_30; + goto basic_json_parser_31; } if (yych <= 'u') { - goto basic_json_parser_36; + goto basic_json_parser_43; } - goto basic_json_parser_32; + goto basic_json_parser_33; } } } -basic_json_parser_34: - ++m_cursor; - { - return token_type::value_string; - } -basic_json_parser_36: - ++m_cursor; - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych >= ':') - { - goto basic_json_parser_32; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_37; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych >= 'g') - { - goto basic_json_parser_32; - } - } basic_json_parser_37: - ++m_cursor; - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych >= ':') - { - goto basic_json_parser_32; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_38; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych >= 'g') - { - goto basic_json_parser_32; - } - } -basic_json_parser_38: - ++m_cursor; - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych >= ':') - { - goto basic_json_parser_32; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_39; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych >= 'g') - { - goto basic_json_parser_32; - } - } -basic_json_parser_39: - ++m_cursor; - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_30; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } -basic_json_parser_40: - yyaccept = 1; - m_marker = ++m_cursor; - yych = *m_cursor; -basic_json_parser_41: - if (yybm[0 + yych] & 128) - { - goto basic_json_parser_40; - } - if (yych <= 'D') - { - if (yych != '.') - { - goto basic_json_parser_24; - } - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_43; - } - if (yych == 'e') - { - goto basic_json_parser_43; - } - goto basic_json_parser_24; - } -basic_json_parser_42: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_32; + goto basic_json_parser_33; } if (yych <= '9') { - goto basic_json_parser_47; + goto basic_json_parser_44; } - goto basic_json_parser_32; -basic_json_parser_43: + goto basic_json_parser_33; +basic_json_parser_38: yych = *++m_cursor; if (yych <= ',') { - if (yych != '+') + if (yych == '+') { - goto basic_json_parser_32; + goto basic_json_parser_46; } + goto basic_json_parser_33; } else { if (yych <= '-') { - goto basic_json_parser_44; + goto basic_json_parser_46; } if (yych <= '/') { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_45; - } - goto basic_json_parser_32; - } -basic_json_parser_44: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych >= ':') - { - goto basic_json_parser_32; - } -basic_json_parser_45: - ++m_cursor; - yych = *m_cursor; - if (yych <= '/') - { - goto basic_json_parser_24; - } - if (yych <= '9') - { - goto basic_json_parser_45; - } - goto basic_json_parser_24; -basic_json_parser_47: - yyaccept = 1; - m_marker = ++m_cursor; - yych = *m_cursor; - if (yych <= 'D') - { - if (yych <= '/') - { - goto basic_json_parser_24; + goto basic_json_parser_33; } if (yych <= '9') { goto basic_json_parser_47; } - goto basic_json_parser_24; + 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 <= 'E') + if (yych <= 'F') { - goto basic_json_parser_43; + goto basic_json_parser_54; } - if (yych == 'e') + if (yych <= '`') { - goto basic_json_parser_43; + goto basic_json_parser_33; } - goto basic_json_parser_24; + if (yych <= 'f') + { + goto basic_json_parser_54; + } + goto basic_json_parser_33; } -basic_json_parser_49: +basic_json_parser_44: yyaccept = 1; - yych = *(m_marker = ++m_cursor); + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; if (yych <= 'D') { - if (yych == '.') + if (yych <= '/') { - goto basic_json_parser_42; + goto basic_json_parser_14; } - goto basic_json_parser_24; + if (yych <= '9') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; } else { if (yych <= 'E') { - goto basic_json_parser_43; + goto basic_json_parser_38; } if (yych == 'e') { - goto basic_json_parser_43; + goto basic_json_parser_38; } - goto basic_json_parser_24; + 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') + if (yych == 'l') { - goto basic_json_parser_32; + goto basic_json_parser_56; } + goto basic_json_parser_33; +basic_json_parser_51: yych = *++m_cursor; - if (yych != 's') + if (yych == 'e') { - goto basic_json_parser_32; - } - yych = *++m_cursor; - if (yych != 'e') - { - goto basic_json_parser_32; + goto basic_json_parser_58; } + goto basic_json_parser_33; +basic_json_parser_52: ++m_cursor; { - return token_type::literal_false; + 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 != 'u') + if (yych == 'e') { - goto basic_json_parser_32; + goto basic_json_parser_61; } - yych = *++m_cursor; - if (yych != 'e') + goto basic_json_parser_33; +basic_json_parser_56: + ++m_cursor; { - goto basic_json_parser_32; + return token_type::literal_null; } +basic_json_parser_58: ++m_cursor; { return token_type::literal_true; } -basic_json_parser_59: - yych = *++m_cursor; - if (yych != 'l') +basic_json_parser_60: + ++m_cursor; + if (m_limit <= m_cursor) { - goto basic_json_parser_32; + yyfill(); // LCOV_EXCL_LINE; } - yych = *++m_cursor; - if (yych != 'l') + yych = *m_cursor; + if (yych <= '@') { - goto basic_json_parser_32; + 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_null; + 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; } } } - /// return string representation of last read token - inline string_t get_token() const noexcept + /// 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)); } @@ -3414,16 +8265,17 @@ basic_json_parser_59: 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., @c "\\n" is replaced by @c "\n"), some are copied - as is (e.g., @c "\\\\"). Furthermore, Unicode escapes of the shape - @c "\\uxxxx" need special care. In this case, to_unicode takes care + 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 - @exception std::out_of_range if to_unicode fails + @return string value of current token without opening and closing + quotes + @throw std::out_of_range if to_unicode fails */ - inline string_t get_string() const + string_t get_string() const { string_t result; result.reserve(static_cast(m_cursor - m_start - 2)); @@ -3465,21 +8317,19 @@ basic_json_parser_59: result += "\r"; break; } - - // characters that are not "un"escsaped case '\\': { - result += "\\\\"; + result += "\\"; break; } case '/': { - result += "\\/"; + result += "/"; break; } case '"': { - result += "\\\""; + result += "\""; break; } @@ -3490,6 +8340,7 @@ basic_json_parser_59: 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 @@ -3502,8 +8353,8 @@ basic_json_parser_59: auto codepoint2 = std::strtoul(std::string(reinterpret_cast (i + 7), 4).c_str(), nullptr, 16); result += to_unicode(codepoint, codepoint2); - // skip the next 11 characters (xxxx\uyyyy) - i += 11; + // skip the next 10 characters (xxxx\uyyyy) + i += 10; } else { @@ -3527,93 +8378,277 @@ basic_json_parser_59: 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 a floating point number. - The pointer m_begin points to the beginning of the parsed number. We - pass this pointer to std::strtod which sets endptr to the first - character past the converted number. If this pointer is not the same as - m_cursor, then either more or less characters have been used during the - comparison. This can happen for inputs like "01" which will be treated - like number 0 followed by number 1. + 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. - @return the result of the number conversion or NAN if the conversion - read past the current token. The latter case needs to be treated by the - caller function. + 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. - @exception std::range_error if passed value is out of range + 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. */ - inline number_float_t get_number() const + void get_number(basic_json& result) const { - // conversion - typename string_t::value_type* endptr; - const auto float_val = std::strtod(reinterpret_cast(m_start), - &endptr); + assert(m_start != nullptr); - // return float_val if the whole number was translated and NAN - // otherwise - return (reinterpret_cast(endptr) == m_cursor) ? float_val : NAN; + 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 he beginning of the current symbol + /// 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 - inline parser(const string_t& s) : m_buffer(s), m_lexer(m_buffer) + 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 - inline parser(std::istream& _is) + parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept + : callback(cb), m_lexer(&_is) { - while (_is) - { - string_t input_line; - std::getline(_is, input_line); - m_buffer += input_line; - } - - // initializer lexer - m_lexer = lexer(m_buffer); - // read first token get_token(); } /// public parser interface - inline basic_json parse() + basic_json parse() { - basic_json result = parse_internal(); + basic_json result = parse_internal(true); expect(lexer::token_type::end_of_input); - return result; + // 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 - inline basic_json parse_internal() + basic_json parse_internal(bool keep) { + auto result = basic_json(value_t::discarded); + switch (last_token) { - case (lexer::token_type::begin_object): + case lexer::token_type::begin_object: { - // explicitly set result to object to cope with {} - basic_json result(value_t::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(); @@ -3622,9 +8657,16 @@ basic_json_parser_59: 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 { @@ -3638,27 +8680,53 @@ basic_json_parser_59: 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 value + // parse and add value get_token(); - result[key] = parse_internal(); + 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): + case lexer::token_type::begin_array: { - // explicitly set result to object to cope with [] - basic_json result(value_t::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(); @@ -3667,9 +8735,16 @@ basic_json_parser_59: 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 { @@ -3680,108 +8755,1340 @@ basic_json_parser_59: } // parse value - result.push_back(parse_internal()); + 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): + case lexer::token_type::literal_null: { get_token(); - return basic_json(nullptr); + result.m_type = value_t::null; + break; } - case (lexer::token_type::value_string): + case lexer::token_type::value_string: { const auto s = m_lexer.get_string(); get_token(); - return basic_json(s); + result = basic_json(s); + break; } - case (lexer::token_type::literal_true): + case lexer::token_type::literal_true: { get_token(); - return basic_json(true); + result.m_type = value_t::boolean; + result.m_value = true; + break; } - case (lexer::token_type::literal_false): + case lexer::token_type::literal_false: { get_token(); - return basic_json(false); + result.m_type = value_t::boolean; + result.m_value = false; + break; } - case (lexer::token_type::value_number): + case lexer::token_type::value_number: { - auto float_val = m_lexer.get_number(); - - // NAN is returned if token could not be translated - // completely - if (std::isnan(float_val)) - { - throw std::invalid_argument(std::string("parse error - ") + - m_lexer.get_token() + " is not a number"); - } - + m_lexer.get_number(result); get_token(); - - // check if conversion loses precision - const auto int_val = static_cast(float_val); - if (float_val == int_val) - { - // we basic_json not lose precision -> return int - return basic_json(int_val); - } - else - { - // we would lose precision -> returnfloat - return basic_json(float_val); - } + break; } default: { - std::string error_msg = "parse error - unexpected \'"; - error_msg += m_lexer.get_token(); - error_msg += "\' ("; - error_msg += lexer::token_type_name(last_token) + ")"; - throw std::invalid_argument(error_msg); + // 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 - inline typename lexer::token_type get_token() + typename lexer::token_type get_token() noexcept { last_token = m_lexer.scan(); return last_token; } - inline void expect(typename lexer::token_type t) const + void expect(typename lexer::token_type t) const { if (t != last_token) { - std::string error_msg = "parse error - unexpected \'"; - error_msg += m_lexer.get_token(); - error_msg += "\' (" + lexer::token_type_name(last_token); - error_msg += "); expected " + lexer::token_type_name(t); + 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: - /// the buffer - string_t m_buffer; + /// 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; + } + + /// @} }; @@ -3789,20 +10096,30 @@ basic_json_parser_59: // presets // ///////////// -/// default JSON class -using json = basic_json<>; +/*! +@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 functions // -///////////////////////// +/////////////////////// +// nonmember support // +/////////////////////// // specialization of std::swap, and std::hash namespace std { -/// swaps the values of two JSON objects +/*! +@brief exchanges the values of two JSON objects + +@since version 1.0.0 +*/ template <> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( @@ -3817,8 +10134,12 @@ inline void swap(nlohmann::json& j1, template <> struct hash { - /// return a hash value for a JSON object - inline size_t operator()(const nlohmann::json& j) const + /*! + @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(); @@ -3828,17 +10149,35 @@ struct hash } /*! -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. +@brief user-defined string literal for JSON values -@param s a string representation of a JSON object +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 - (const_cast(s))); + 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 f662761..2d84d00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -192,7 +192,7 @@ int main(int argc, char **argv) if(devices[i].id.length() > idLength) { idLength = devices[i].id.length(); } } - cout + cout << "ID\033[" << (idLength - 2 + 2) << "C" << "Title\033[" << (titleLength - 5 + 2) << "C" << "Model" << endl; @@ -202,9 +202,9 @@ int main(int argc, char **argv) for(unsigned int i = 0; i < devices.size(); i++) { - cout + cout << devices[i].id << "\033[" << (idLength - devices[i].id.length() + 2) << "C" - << devices[i].title << "\033[" << (titleLength - devices[i].title.length() + 2) << "C" + << devices[i].title << "\033[" << (titleLength - devices[i].title.length() + 2) << "C" << devices[i].model << endl; } @@ -260,7 +260,7 @@ int main(int argc, char **argv) cerr << "error: " << e.error() << " for arg " << e.argId() << endl; } - + // errors thrown by pushhandler catch(PusherError& e) { diff --git a/src/pushhandler.cpp b/src/pushhandler.cpp index 1097dbd..c609902 100644 --- a/src/pushhandler.cpp +++ b/src/pushhandler.cpp @@ -159,7 +159,7 @@ vector PushHandler::getDevices() Device buf; buf.title = element["title"].get(); - buf.id = element["id"].get(); + buf.id = to_string(element["id"].get()); buf.model = element["model"].get(); buffer.push_back(buf); From aece4a5de2fec1e3fd693ecd180b2b286908e116 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Tue, 21 Jun 2016 14:12:03 +0200 Subject: [PATCH 11/23] Putting curlhandling in its own class. --- makefile | 12 +++++-- src/main.cpp | 2 +- src/pushhandler.cpp | 80 ++++----------------------------------------- src/pushhandler.h | 3 +- 4 files changed, 18 insertions(+), 79 deletions(-) diff --git a/makefile b/makefile index 98e94cf..e81d13c 100644 --- a/makefile +++ b/makefile @@ -13,8 +13,8 @@ LIBS = `pkg-config libcurl --cflags --libs` # Build all: pusher -pusher: simpleini pushhandler main - $(BUILDCOMMAND) src/simpleini/ConvertUTF.o src/pushhandler.o src/main.o $(LIBS) -o $(TARGET) +$(TARGET): simpleini curlhandler pushhandler main + $(BUILDCOMMAND) src/simpleini/ConvertUTF.o src/curlhandler.o src/pushhandler.o src/main.o $(LIBS) -o $(TARGET) # simpleini @@ -25,6 +25,14 @@ 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 + +src/curlhandler.o: src/curlhandler.cpp + $(BUILDCOMMAND) -c src/curlhandler.cpp -o src/curlhandler.o + + # pushhandler .PHONY: pushhandler pushhandler: src/pushhandler.o diff --git a/src/main.cpp b/src/main.cpp index 2d84d00..0ba81fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /* * pusher - * (c) 2014-2015 Daniel Stein + * (c) 2014-2016 Daniel Stein * https://github.com/HackHerz/pusher * * TODO diff --git a/src/pushhandler.cpp b/src/pushhandler.cpp index c609902..fe45e74 100644 --- a/src/pushhandler.cpp +++ b/src/pushhandler.cpp @@ -1,83 +1,15 @@ #include "pushhandler.h" #include "json/json.hpp" - +#include "curlhandler.h" #include #include -#include using namespace std; using json = nlohmann::json; -// urlDecode matches -string matches[][2] = { - {"$", "%24"}, - {"&", "%26"}, - {"+", "%2B"}, - {",", "%2C"}, - {"/", "%2F"}, - {":", "%3A"}, - {";", "%3B"}, - {"=", "%3D"}, - {"?", "%3F"}, - {"@", "%40"} -}; - -// needed for urlencoding -string 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; -} - - -// 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; -} - - -// curl wrapper -string curlHandler(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; -} - - // constructor only username PushHandler::PushHandler(string username) { @@ -104,7 +36,7 @@ string PushHandler::login(string password) // network request string readBuffer; - readBuffer = curlHandler(requestData.str(), URL_PN_LOGIN); + readBuffer = CurlHandler::request(requestData.str(), URL_PN_LOGIN); // json parsing stringstream jsonData; @@ -133,7 +65,7 @@ vector PushHandler::getDevices() // network request string readBuffer; - readBuffer = curlHandler(requestData.str(), URL_PN_GET_DEVICES); + readBuffer =CurlHandler::request(requestData.str(), URL_PN_GET_DEVICES); // json parsing stringstream jsonData; @@ -180,7 +112,7 @@ bool PushHandler::verifyToken() // network request string readBuffer; - readBuffer = curlHandler(requestData.str(), URL_PN_CHECK_TOKEN); + readBuffer = CurlHandler::request(requestData.str(), URL_PN_CHECK_TOKEN); // json parser stringstream jsonData; @@ -216,11 +148,11 @@ void PushHandler::sendToDevice(string id, string message) requestData << "&app=" << APP_PACKAGE; requestData << "&deviceID=" << id; requestData << "&type=" << "MESSAGE"; - requestData << "&content=" << urlDecode(message); + requestData << "&content=" << CurlHandler::urlDecode(message); // network request string readBuffer; - readBuffer = curlHandler(requestData.str(), URL_PN_SEND_TO_DEVICE); + readBuffer = CurlHandler::request(requestData.str(), URL_PN_SEND_TO_DEVICE); // json parsing stringstream jsonData; diff --git a/src/pushhandler.h b/src/pushhandler.h index 32058ac..b59ecfb 100644 --- a/src/pushhandler.h +++ b/src/pushhandler.h @@ -6,7 +6,7 @@ // Change this line if you are using your own API-Token const char API_TOKEN[] = { - 0x38, 0x45, 0x37, 0x44, 0x38, 0x42, 0x32, + 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, @@ -14,7 +14,6 @@ const char API_TOKEN[] = { 0x54, 0x54, 0x54, 0x4b, 0x46, 0x46, 0x42 }; #define APP_PACKAGE "com.hackherz.pusher" -#define USER_AGENT "pusher/0.2" #define URL_PN_LOGIN "http://a.pushnotifier.de/1/login/" #define URL_PN_CHECK_TOKEN "http://a.pushnotifier.de/1/checkToken/" From 65c1816b2fe85bcc7e12d2e0d99eb91f76f9deff Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Tue, 21 Jun 2016 14:13:43 +0200 Subject: [PATCH 12/23] Forgot to include curlhandler. --- src/curlhandler.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/curlhandler.h | 17 +++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/curlhandler.cpp create mode 100644 src/curlhandler.h diff --git a/src/curlhandler.cpp b/src/curlhandler.cpp new file mode 100644 index 0000000..8bb5fbc --- /dev/null +++ b/src/curlhandler.cpp @@ -0,0 +1,71 @@ +#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 new file mode 100644 index 0000000..34e95da --- /dev/null +++ b/src/curlhandler.h @@ -0,0 +1,17 @@ +#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 From a0da807c233379dc20586a43213a1cc2274b7a21 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Tue, 21 Jun 2016 14:29:01 +0200 Subject: [PATCH 13/23] Writing config file with simpleini --- src/main.cpp | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0ba81fe..40256d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -79,6 +78,9 @@ int main(int argc, char **argv) // Variables string message; int id; + CSimpleIniA iniReader; + iniReader.SetUnicode(); + // Request token if(tokenSwitch.getValue()) @@ -105,44 +107,32 @@ int main(int argc, char **argv) token = buf.login(password); - // Write config - ofstream dat_aus; - dat_aus.open(CONFIG_FILE, ios_base::out); + // Build config + iniReader.SetValue("pusher", "username", username.c_str()); + iniReader.SetValue("pusher", "appToken", token.c_str()); // Check if file is writable - if(!dat_aus.is_open()) + if(iniReader.SaveFile(CONFIG_FILE) < 0) { cout << "Try running pusher as root or save the following in " << CONFIG_FILE << "\n" << endl; // Data - cout << "[pusher]" << endl; - cout << "username=" << username << endl; - cout << "appToken=" << token << endl; + string strData; + iniReader.Save(strData); + cout << strData << endl; return 1; } - else - { - cout << "Success!" << endl; - } - // Data - dat_aus << "[pusher]\n"; - dat_aus << "username=" << username << "\n"; - dat_aus << "appToken=" << token; - - dat_aus.close(); + cout << "Success!" << endl; return 0; } - // Load conf and check if token is specified - CSimpleIniA iniReader; - iniReader.SetUnicode(); - // Check if reading is possible + // Check if reading of config is possible if(iniReader.LoadFile(CONFIG_FILE) < 0) { cout << "Error" << endl; @@ -258,6 +248,7 @@ int main(int argc, char **argv) catch (TCLAP::ArgException &e) { cerr << "error: " << e.error() << " for arg " << e.argId() << endl; + return 1; } @@ -265,6 +256,7 @@ int main(int argc, char **argv) catch(PusherError& e) { cout << "Error: " << e.what() << endl; + return 1; } @@ -272,6 +264,7 @@ int main(int argc, char **argv) catch(exception& e) { cout << "Some kind of error occured: " << e.what() << endl; + return 1; } return 0; From b4dcd02aa6e2ee5ba1aea82fbe9676139208149a Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Sat, 25 Jun 2016 14:29:07 +0200 Subject: [PATCH 14/23] Login instructions. --- src/main.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 40256d3..f93bfba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -135,12 +135,17 @@ int main(int argc, char **argv) // Check if reading of config is possible if(iniReader.LoadFile(CONFIG_FILE) < 0) { - cout << "Error" << endl; - return 1; + throw PusherError("You need to login first."); + } + + string username = iniReader.GetValue("pusher", "username", ""); + string appToken = iniReader.GetValue("pusher", "appToken", ""); + + if(username.empty() || appToken.empty()) + { + throw PusherError("You need to login first."); } - string username = iniReader.GetValue("pusher", "username", NULL); - string appToken = iniReader.GetValue("pusher", "appToken", NULL); // Loading values From 37b49bd8a40213f7b50f1b7120e603f254cf0bce Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 09:58:03 +0100 Subject: [PATCH 15/23] Banner added to the repository --- README.md | 2 +- pusher.png | Bin 0 -> 275707 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 pusher.png diff --git a/README.md b/README.md index 0beff15..c1403b4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Logo](http://hackherz.com/wp-content/uploads/2014/09/Pusher-UNIX.png) +![Logo](httpsgithub.com/pusher/blob/master/pusher.png) # pusher Send push-notifications to your phone from the command line. Uses [PushNotifier](http://pushnotifier.de/) to receive notifications. diff --git a/pusher.png b/pusher.png new file mode 100644 index 0000000000000000000000000000000000000000..d373170ccbc8db1ebc17fc93400fdffaddb860cb GIT binary patch literal 275707 zcmV(}K+wO5P)O3rpFllC|twQ(Gb84BqT697cduvjLtFR22c>@ z8c;D5Ktab~W*UFp{c-o@u_Je8Rc2OpSM9GVmjAr|`p;SsnHkyL-|xTW=b!(_fBskh z`~UeL{_TJKkAMF2@4x@{Prv>7`#=8q`=9^jw?BXT-TwF6?|3ph&_&SFkpvi2Hu4^Y`(AAmGe{^S@sEZ&pby@1;q z9``O@l4lOok;^fz+r|BWEat%7&!Ax=Hd7u;&H70_BwmNV*;^9R?*FzP!Ws~Tlnx;4 zD|_lWPf)n=?sqwOa+VR^1nuWbEM0%eXFN&Yj>>hJyq zO8&=1&L)_MlbNUP!U zjKU8S`d;xUbvvHjNFjlbbf0SN^+>M|4aYd(_<;=6G1!*QUO0%qh{2vpAv z9HXrsX%~0d0PdL{1P`856!v5aeo)O7`4c-f;Nh$YAM1lG?mg{hN^Uaa?=sVKEhV$< z$k5@z8(`+Bulo9=*OwES2Ou!GjFkP_{P#M+p0L|;)qD@{J#*d?CZB!d@DG3f_W#%| z`hOexe>M4E{?&i?U;hvP^84@qLDY`=&;I*={!jn>|Ni~I7}o8OEcY8PSLVPeXuwnK5R`HaIQI0tNh#Tm~97y&vlFS@Zt0sVXrbQ z=FGGEZ-x_BVGS7hMZ54Dmjs}2JBf_>1>Irh1Q$tk#X_X%_D34eg}~Z_)S;X@;_${A zEb=)+7{~RpU(k&7g7;Ab5F~Af^z>tmGiuu5?(CH==c{`Rd(;=CBjQ-UBxXG+P_-sd2A@C* z1ona0u>tN4Fwd>f;%p(GD?H9iiDqoO;g3CNUx7qPtkYAaD|rdP{nrX{B#HGN`IBdH z7wao$MEKE@ksLW7wVU4U_62}AZ>|-Pu_kGi?$!-Pzg7^Qd=9x{7lZL+I&&|cqQicc zhDImAvG*GBu&n8O32_kvC@V7w>3#FHz9l%QXy*r?cHo$Ejr`W#jlba!0GKGj!P^+R z&+maN<|Hppkt89!6j!=1;18o5m(P)VcsCs^4 zEQq_V5qU+Q*+m#R$&A0tOwYBHOu8dOhX-$fnIq57^!joleNIkdc3h^jUz`74CpaqD zxZGfK$}_O0N~>>qP50Qb7r{u3zQ_Ch-~H?Vqdsr>hG1B1oNna~NdXJ=JU^40SDhT6oA5;38z*M~$jGfSWI^ z$&#SrG?K+r2W?_pCYJoM^6(^X=kKVUNTg;v%!c-WFyexvO7c*=N#UVpOLKdVs8l%k z2H(I-vJFrQv)P~OU91+@_A@89J$Up>^4vs90M~l(M$O#=vv0mLf(}lraqZ?4uyL&? z6NFxl29b#3lRiPn&7{yeD`{pvPU{R2cSd<&KyY*b(pt%Xnwc-wgU&+uA-NwqgJ*~y z@4*U!ja3FYakLyeD0}$6wSVfa@sqk%a+kq#ZYw+!b;>p|f)Y$j?d2J8qXo<3EcN znLHf1n5|8)3On(kJ>uH7>f|603J?3lR_c7<^Rr}N>BZbm=L}zX58ROv1(uT}+O(77 z185BW#~CtvhP6F-i5OL8Lh|hV=oz1L?)h&!JdufQ0gryoA3Pl$@!S)2uR;JJ<&_>1mM19cK1_63rC9_bs-OExEA^4$eqt^1y|s4u|(rxY{aE6?sx8 zAGA1$df5Lc1AOA3iSY5YB>C`HnCxz}3&zA=3NCxHfQQrqN=z&eLa(bu#GfO-$5~I# zw@FBX{nC2?j=lH9m%z4`t7qn>vd_xRxa|jcVo&)D%l^#ht=I)Wh55Je5A5zYyW-}v z;D{%+K>Bc&YNG>P@$24ey9e9FvlY#=d#^UMzR-%tK2=YsXP&U>%4zC-q~5?penUNM zx_M}eJp22aYo3U0yBAZ0ZMR13kL#Bj+OXQ)?Vse8MS?hXbeiQJuE=)HDOl8KtGPYI zE@#ceL8u^XlAnD1K0WciXM>J>o!>amkVl3GiC0jn>Oi^}`tIMGg>EFd)SWqib`Axd)`TWx`N)w`bqiwuhky8Y|N6iEul|ca^p9=Z4Us=Q zACy;R*6>W8_1wwV44-Ua8neJpF5x@UQ8isawe9Hmwcf>SEVyl-T)*T;6VT+C8Z!DL z{9nvQE3~%DzdJZ(M^NAQ(u@v({;Su3AyBOd>+^@~nmZsNz6NH!f0eb7TYpGnxXPW(Pv zn`6ekU51U7Y@b)b+iU)gh}ouS(4nMl^tbwn`?cBx7#>4K|23SLfabydCd-{1=+|DE zva{LG%>VEI^gsXGKlJZB2LW!=O!PMmZZf$2@iz_T@{U!aq{yibE2UA^QzTy*RXRK zMBdDtUpPotRisdLQD_pvSNv3P)V1do`xP5O{s@H6HFBB4Z=!vo;+9W;ha9ylSK4iH z#WB`F+O;Uw{+lb3g2NXB3OheW>ZXL23!Q;6j|Aj6qej_Cm3YEqwG1xkJOx47laI0z z7BwA40n}UQuX-SgFSO{|6A_?~X&FKCB(LW(T3B?XIYv9c5f~^IVFjx>1TKA-(JcLHk zEjH|t$=QDY?VtZJ+&?iy=ay{tI{t%lA(`ObPmcQb4cS8@4 z?AXAt-r4cruiqYd&-qvO%riCRzerD%ta7JJ`Py#mY`t`B6DJuv*m@|)iSYuG^VjF; z1eo7b;D-Q|ovnXKR&wEHwNsY=+deO4{dK0UGi6@#>>b$pHTq(Kj?KM03e_U&DBLdo>@Ot2w7BM>Ho-+LXf^(b$Pj)_?lLBK{Geq6M(Vtv4OIyp)rNv5)^wu0OH?H9B+oh zIj#;M&J_O>QFm{V9`|mXc~nDiTI)g!xXnuSl(oQ_Fd zzbKMDeqofciaY6pD{bTe(sP!j`-=XJI|1FlDzhA!1iKrL3sofkQdNS_0-89&`bB>x z-1SgL!bb&>pDOOs`*227;VOR$l|R8w%)B&JePooRT_N)sFI0Vi%MhC_A>2U|bSXPi z90r3~2DfXEBE&bvk0iMb55gzBK@+W1HKkQ$*qFzd1%`TeT4RNlao@>Vu zXWXXyT3l zCQ##dJI|s<#o04Xr&X*3t(n*le)F1Rea?C+E>UNGV^0GW)bTrLEH~*_w&!XFst*&{ zZ&zF&3=RqWk=|;ACD7@uVh2YIZWzwU;RvMn9~hs3iOxO>aPNa$1LCw~!j3+3CcByP zoB?)=E_e1B8O)q1pC$3O1|>U6f(O-b7;?Jx+{+NAPV1Wq-BKPO>Y@UifiKoDA0$>I zWe&TW?9!$H?Xz^`F>vo^M+y^li4eQ^6cD^&PuT;8ES(SG=mvA7@yexn+COpkkALL< zRTZ#Ug@j=8zg0e2zo14#kDC2@Ch1-ulad(~G*{>W|%jEwhKbp@Y+h1%_YC(rY zf05M!@@{eKmjgy1gd&@=^k1K^Gy%4BZsp!FFx1U1Yn`(EGj__8W6E&$x*6qpIih2K zW{J7Ia$dmMB(3_(XZvLbi%;v6CuYmS@tHEV4_iq#oBOb2e-mP6_Ob1dW$z63oqgMh z!_M(k+>|%Zx9enNRpy`d2ZJaK0EMd*7wjDF7}^!HnVr; z!F@L8gbmD$dn^m7C?YRxzyJ1c>^IQ$qq%>9^M7~Fkp0FY!vvFExnM@tDwx^*4M55= z`)f@P3ZA<+7>*U38)%f}KIce&;RuL|h6b<^a;7!ocz*&0uK~9SJsEacl{f|FcCn|y z+p}~dqkAHay}@w-fGOyP3Xw?H%t|lvkGRaIh4(@t@MtIs1Hv3l8(wtNA~tvMcwHSJ zuVhdZeF~1^VmK-%#s1Cn<*Y;jStC)MW$(@A=dBb+-svp-l4sUyGPr(=)j6jW`6rud z%(spLX36#M`Xw_FWa_#j0KM~LpV>XtV?(~#-u(ma{9z$qmK#;N;+s}|9A`cKJ*#9t zNMzGIWYk~KA@?Z^J9Sad0UyyO#wQN|ti`|juQ?Lu>m?i8RVe!95mo$#z*oZ|DvKe8 zGZJod`h4u-xUQV=$rrJD^Vwdc&W|P597)BX7YZ(Bw1-@}RYEW^>65BG+P_&=4%rpv z7`yo0kiAB?W^}u0VW`qV2xFxd(fEXFC&vM{hi*`egyyW7WsY3NTp$k5J`K#bzCKYy zhHZcGXWdik?3;umd?qy_*Y*GvAe7k>$|YmXv;d#dNM>~SzH!U)`zL>1t4;ger( zt;od5?mnBS%euseTG%H%p3WfXjvoDo{qPz%b%HZTEonb;%9;Jd+3qJ$z*B#~ePl`IJLq5d&{@53;bm~nG&@M$&b=tn$Wq#2FWjoOzei0lu~YQrb9 z%O1`1!#QOUTV(Zn$%a$K1L60q?0JBa&(hIxu9lFoJ0-eS@_Q$7ET#ojXWpd3f7=W;u^cI<| zRe%|o{tgR2VbFAfh-0@%@2i158nVNQVovu{N9g^;HM{-cjJOgeCpf5~Fwet!Dne%| zBKyDDk@XZ|3N(z}YY{dwlo^~{Hq?L68r{pVWclWY!of)|<`?A2@k3=5OcOt}kXI$& z)!yma0=D5*#BT!KWqlLLFYs5Lz`Pw^lJV_(AN^^65C|xF>rV-OGQgu5&6$1tIPNN- zin;Y4_xyR(=UoNZ6*J{W96yt7&SJ{nI0Gnt+yVH;!k>kyF`5&0>(AnUCjZ@R?(C0e zbBEscJ6%WcbDuqr`?$BNaz0bW_BUJaHtN!50`(J{|J1evF9E*{HGyBVs!<$*=E7&n z*kYZYmt=f=nUl`*2Kf&+nU$a9Im`YwOZNPU+jT{uXHEEBHGloYUq7iUWq-)imXP47 zw`ds#4R1#3>suNs4nlyc)jql8Lzed);XAwz^ajFN`GcYAf^qwKjMHuwe&+9aQbDXX z6$e@{@h+Ni6A%a#zuEoW-JM-=hGhaHEF#lTZ%L^G1H9tNkME^(26(?c0>=s-y;9_? zm@&1nw>1+2bB(ZmW69PR45D>b@ZaZ;K=SXpE@wQ6Vj-!`I{Tu2`0n%Li4{D3(dn)m z{xjM%O348Dz`5Bj+>`X_{%tp|rZXpg8BLC}#pxo$3=+sboF``sTjfupJ>Tv>D3Yn3 zP4r)JRN4hcAo~$oUeD|@Hh)0Kp8OC~$RX@5;NSK4gH8VI5skKs9Hh-Qh(~>ay z=g#oJ$x@riyl_%2_Ub7C*cl6W;_BdwANI~4(_u>00D%;g@4ophO6O;~ z(llEZ9|Bi7G^iB(_FbeWMpXk513j!B2foGbsWfSEn_zyy*# z&7D`Av!-6>BP(VjQq2?BTJ>2b7rOsx&m7jJ=d?>!AG#^te-z|}F~6G2q@z}B85d^D zK@#)VY%*ibCSzT{j?{N6BpEzdYUZdpMFJNwDHFxJ#$unxS5 za{8w2K6~DQ_cAG(c+-i4?TEu@YlK=|#Ja^4bGR=Z^rg_S2Z`GEP~y!%oJ^9n{^3_N z#Y#q>vV8jkPWCBp+?Usx($_9jBi4s%(Zl+>>yAZdy=QrjxR3oAJb%;qs4X3|d4F^8 z-qP|db~>i~Gn|>(BfT@+>H8Y;tc+@BW&4PLVZHN<1I*<6C4cJ#G);Dk$!`4(+=MD* z%CDuEB|MVFgZ;If?}}4$HAZtkw3v78?C(OhcLsg;hkfthOZxV0&bR4e+f{7+ZH!+U zO>1{WGTe>B&W+Cxa{Uv}9pg`EXLt+^{U;p7t6FAGtO}ZPkjTO$AIb6&HE|IgSN%KC zj_UJkx}p6ibBS`;AKpA1IQ*i}!5=uU%uHRvOgm)<^-H*VGZ2{0mwJX?>nTSHR?PTB zmbkVVjM<0i{?zLY#j1<@96$9iap>s3-+^}aL-GKt!%H;9{`lLL5ZxZG^G%3w_Q(DH z9*FosKHm?yJK%M7sFMs6IZh1#?BX(}P5bC^cJuPU|!sgcu+4RaCOhIhX znJ8eb*PbN2795ktWS_5WSrGY2&u?9@Zyb8mLsyxk*lp)?c8v(>K7abN|0+KTx}GwS z^jMiMzX?xtVd`yO(ZuGts$dPm{&c(9LCpeW1jGLm2Y<;WakGA%pA6nBp@iX%zMHEBc| zxx0cxKfxnukP-!qmAcRQcdo)!Z7Gy)%m(5aMK8YY;Ow9n(jW85xtJ4OI4BPOVJ8nb z@6!l;#VDf7$DH^hsMRQ-db-aYq)uZuz0;7VZhBe6bNIn|uzAu6k}vhZh&@WSpvYa} zp*z?vI3ck$;&XqC4E6*rmHx?6VF|z9`{9UN0<@wfH~G9}k}8&*Kh@5dWKMW+bq2!I zPJ%>lo^ac~bxDrg`h6WiyypX5*LAgM4DPp!dr4|Vp%YJZwUN8nC+;2@T;%F|%(-@M zGak9!AE3?^Rk(gB>|!v&i1Z4rH(zOF^oj{yM>^Oz<4+bxep~Gx3%;)IQUQj%p<>U; zrt$(nU8R>HGq67yS~&D$CFx;g6Z$y0+pzYh(q*&s@2f*zz!?t{V9m_VE%y3%o_zqu z#b@k~YEWNV1rthR!>$m?`aJ4iKKnz z7%O^Md)u~u>);=)a1sONYTpQuzt)%l^I>wbV*A1~n&m!{)dF_fxzlIM8*>?f0Fw~; z0{qDhKW;923(?sSRfdf!rR(q1C8L}8&@cPzKQrshCWe{m^^Cue4^ zV@sy2I6nBLPZ|FYTUCrcG!xpd>!S(mS$*5gVA&b4!-@_Lef#fp;(o2~G=V*6XxSL_ zyOp2p_(5&!cX2ES?hIXphWaYCR%=?!A)JV{UhAwk~a@>yQEs>5hBJD-9l|PM@&z8*lXID}5yr!ppr(-?7p+Yv(!nPVIUU+HaC8RY1+4xx4E? zX%{3ZFqn2dVVw*i^UIFyj{{e9_Nd5AIS=q*jcc=WD%w&~1!m0g>ymKpT%1K7Q^5rh z%+w$|`LVH@k1$NE(Ac{3g>EzVSO`Wy4GS9>$kC0PCP-qC_HzH(?z!DO_D6fEeR8jZ zqd$rId-YQXs|Rr_SAVohFVDuhX>WlTd|Kz6t@L}KhYyONQ zoWcO++U3oBHf!po6X(%+2*U82ocx@=gWf3XF4|v>UP7g9V z*9UIdLv15Sw-9TJpE}e1s|>nVD^Nz|QV$d6M^}|5fCW!KZs3Ho-Jx407&0$BS3Hb~ zSZdW>!yroPLBIth)@>C! z5u-P^=Aq+EuogduG7aP@|FWNcw64b`yWrtDiQNx^D}nH70rp&nLK(NKQ<*oS`rc#b zs}O)SpNQdm8^r@TlVRO;g>L@{cV=gOgo8uO;La?6F>N>b7c2isxi59cR^DUoJkKMa zl=U}rGbiTq7jIAP%zGP|_NE{CRV`1jWPK)SSbklZ05d}Wyp_!C&a=!DCz*J~O&OkP zn==0AtD?yIZ4A$I{2dT;MpIUA?9?~RZ;v19XCs>_r~EAL!~MIV4^Q|vNWi&y?dHF( zr9v3ld_fq1TO)})72%e-Ux&aoFZ}a z=H&8rEYtJr!_J>A9E6sm@ba7DIyI}mkb!}xj-~@wNKe>IkMc*#@XYIoa&hQJ@0t}5 zrRH0#5st;*z8aWl^oE)xNYX@P$1q2_>VKCYS)D&*-nA80tmfJ}?1w#|Rfh|eA_W3` zwTqj3hr>-3X5V|4da_wv5dj1CEW-H1Qg$|-wKv~Ff~#Up2zO^;BgogD$N&TW#wTk6 zI(=1k0xA%H2Cjat`2*+qDTY-UBpapQdnPkqewERDi4}O2z zYdPDbFU#u=<4Rg)S6PZKgt8|ycrT)|WgkPY1f136CGS2Pm8TOp+r}-oVS>2IW)mR9 zpMmgE2WS0+k2@^q1up4q>5MA$&`cqZ#}&B&#V4>G8vmMNb-B9>s#~AmI6H@PMk3Fp zAQyHed4C$8yQysCsd%HPn}-@B2=cQJv&LD6n;_s7%r01Vvtc&4S7&S3(=mECruv*_ zo0G%I4QaxmN;RkOAin{ajm;z}Bg&L!6WJ+()V`aciUh3$&A}mis(Vw`kBhb$5BK&P1`F*z0fS*Yd-Rt&< z0PPxhaAx%A!Y7)f)|C6vA@S8c)sH`WWY@DttHn55QVJM$YHV&G_#tgQ5XXI|9R6{X zNwAnm;4ldqX6kyB-w@C#K%b#Ion3+l^5ipXL75ka0#5?Yy|>B}$C-)5otsR|_{5Pv z`yse$m@QZV_UC*~N_S&KI>at~hU@Gfz;%t(hR(u^Exss~{c`hgW{lE9Gf=Wb&B^8L zE$K%AVTpeSdYIoNl1)KVhNQYN!<6y=@bya;_1#lpv*`}~EyTpWCFU1a6Zo4;@;+h`>gC9j=V#L% z&FvFrFt=*TpOE@}Qv5no*Euqiy`kvWi1)CNb#r0A%Z`2H&^O=v>&mpNc>gRP&d9)M zWhcC|KZ}EhAG(-T=>UGM{Wb1x*uQ3UPT>3Qy&e6UsmBk{7U?f0{P2%36a5W@o6PAr z8D+Q92ecarBQC)negG_Iy+}#>`35BGG~e}pi%h&Z@*;&h_(^J3#KSNC)6w~pKmFJI zL3BoUe(D$Qk2L`&BCc4;D#w(iN$&u> zQ9uzyepds_eCVr@DXsoTiUV5t4r}&5Y@8oV(4YCUk95|(NPC{lF0NyQajtfstin1^ z#J$vi#5O_2VtOfcmT~#zQjsKJafNE9)3~*#5g&>CN=^g%8AhLNmkc?@2+TCKH}d!M zi!e&uv*RA=nVreWc3NT88lL8li2dK_;Ea%`7&4H$;K6@+_V_)4S8G;Afm!f1PsETX zwQOH!Dg&zk>5v9mSL(s%2!kHa9vS1|YYtPW$?fEUp`ng*LQiCX#Hx;>!A5*$o^f7i zp9!heN{73-9GCvn?Re#v@Zekt=H*3!J|BbDJi-}d=)RaA-9&>B-AszRN1$wMq zk}9z5X%SC~sM{kwXhecfQ}2D!&MY>cBR8?6@-$`~_K~UWhtYz;ryX~gb3caBMPf#7 zpNQFe2MLNCt}!R#;3+7E0Ms659QV-PX2Kuab?+tL)BF_18q9=selnhQ;wQSFeTjp} zRohRm%epMOv;UrH01{s3pz=sCi2I?im9u-?Gvopvp>-7%6g7Vt{gWP^J#{{F*BP{7 zJ&iTy5lxspEbTbXzL}|=a*2qQxMAHU%dJ>9S6z?U#KMfS@~odeAn*8)hP`rRO+bO9 zc`xNIArO2$i$dm}$iKjl9%d`RYcQkR&x#3Q?f%2U{GG%?*`Ht`YOI-q66#E27|<<> zQ{#hMA?n{l&FFsF^6yC*QRi=Yv$_=APr;6XoH5WwC#I*xj`MVvTEC)r$Ti(*iA`YXt{ZtNzw!I%FhZ%11y&{+z;( zp9K{>+U~h0;O0)gB+uBl;C5Ue%wK2eI-6zAGY8%L2_$Nshz79EGze83U+nwZbQsG_f1-FMG`);1CLy&>Da_0983f5Qa! zu-_2+w}j5a^?Wwmsft2oEHI&K1qW8E{^}DyKpTR$KD^LyfL&a-wSMcwkNBCX-dK<& zc#AL9UMfBbvXpIZJ#geHJ9#jmS8MQhYL}|9(++>V%}ZY9!CUjf<--ykHmC1Im~$Q) zq)b-ZE{{O$rRN)WtH)iVU-v*ca&C$$LPF-}w?&2Jw*qBIVf@CKw?q9_D3TLG9#1jq z%%r%hb;ngrCh&$4hZ9@2AWRyZrlv?eB-nd`ojSmC=9?%wDf`35RA(}J{zGxcHGdRJ z8O9-97h3D=!OFUA$WN59;op2(--~wJRB5({a*n$T_*XGQ!?qh|8yRSIIjvn z9FvNQePv$UYn3v?^WNPQ>Hy{g82WWHlPj? z`Av=z3BZv_wz>4gp(mXAFLS5E;o&Ix%qmn)&)hyY!}@%HyRJSm>8o-xdndJsp7^A} z>gV2}k?bhf26pI_;YPohrCG~@DGi+~2nv^cu4{j!*KeX*cNl(G3<-(rTrgSzUPI$3 z31qCfA37)b0p}LGIKqq|T(6s&{p=|?ZrwY(|DB76#l~^Q#)vJ{a#Bn>hznlre{|6} zGVV2S_3X(mYX(U`J51pjZ{)YCs!oJr?n2DvR==D$`Xb7MOmRCgxO;vBa8f znxeRY!NJ~}FPI~4JYqab&)tX)98APe4_WO0#%kqkWMt44^u)qE{acp)B|fv*FHz9L zBB+ur0JL@N8;_2kxNXO;_3Fby^hE!XuM=QiSI=L{qBhv_*^+GNQ+{Nd4fDYA2N;0_ zP2kTeYZm&uhmQXFb76OO1zW$Xdd6=V{cT;%%#vYY72A~G#{Tz3GdInKxqZW&pwXbF z3@z07N%6>zPuM$IzEAnkfRa(nZ17Wx_=_aDn+tP$pEh9I+3xg>N2eO%wjKY+tuwP^ zRq**$e0P7ev59Eq&W|13B|aU;_Xh@g7Poo+);h!cHI&EOd^aD>Yx%Y34`u-;*a#~)$@1+@(C0Sg-u1w)0Dsg8626|MdW+eM$PdVXrR_L?r zRgFrBGwu?X0)`>g?jj8QHi)QNE4=%1-?D?Rdm01v=G6L>o^|iJ;62Bc_nx>`{#5&U zf0M6pwTos^GG5i29Jwu*3+7jlZGgwRoXw@|J()bug22QVpNFllbE1}ATn|6#$OFLq z3SagJ4i&PVS3Qd|N7R&Q4UV2HiOOt7Du3-odI^ebkdUccYKv@v#^zd>ob{~Z%pai} zocp5a3~ufRxf)g@^u)GVXYN%$PNO_}a62`b0?tWMsj+b(wN4n83^zKnxYI6U1HwcU z$NE&CRgvS~9*8(o#_9R##aQy}iCLf4Bkul6oe{?!amAm!qkV_&d|R)1Q6~iG zl`!)7ToB8b(^C7{r@IX2CuX$bB42DQ2UKBaN*M=xm zZXJrt3I)Uftd?z>&qNRFi(w6Yo18xfUaFR;wZSbTtb_J1{(f%Sbcv6nVM-Nfo5TMM8_tT$RQ3&>~yP1#F)rnH8dY zLM#0;wj=I5DxVkLv0_N#4Y zndo0~Phd}uxovw#d?`s~&XSQ;ZT05Ct}x@W-9xBcU0Lnbn_ z?u;9UpPeO6IBdj#o$K^Xd16AI-q*-ihM&u#8O`1J&hGE%*a|j#ro;wDCEUr|_nqaw zKAgs0D(v-ae{bZR(ff4@D@TWQX8?C+-*I>N9smBiGE>cutK$9pN=90u#hnO0c5v8B z#{lrxdOv|*v$~(nBhSn8lDCfGki~>uGPub$WnsYxuh008h?&d&_z&zLg^1V0{9qm# z2dfug7ha5bMZ+H{MRb0^d;D zSSwQ{D#0PAMwG2MiXcsrEhL@)1Y>*&OER-!(c3m+;+P!A&r$ zL#x5zv>5l`Na%{VF&V}#Y0PycTQ$f3;j(Hz)mcr9W)vS2G znlt3nAKJ=N>e(}U(lbc*!o!_$saB7;Z`bT#@sW?;JeLmt-n_IYThADL4*T(W5hw~M ze-Q-%_Y7s8>GPgT4rlXtT@(SCJX4mo^$ZbIpE<}(?j{Pb@RPwpGNp5y%)e(BgncD0 zXz3xX@Mg|&69xhKD|^HhXa1(EbdQ2e3UVOgYeP=MO?EK|VkFILj(pqx!Xb0nYsN=LtM;pCPIr$V_01&Cls@Qi5&1o9u$AgA}Jf zb|DUD>hn0^^4X-~Fo?|v_n(HDGYFC<3SgU|r6=qW4wtQ2 zcN{$_@{PLFA943r^6h6&=O^dMQT?3-+Wi=K{1sL8Bwr@xRP{4HfR3UaO2BBO(Bbkt z;H~)ZC(3GashZ9BbVrS;U^8Op9{mMJb(DT;;jCswD6PoTzGdk%UiuIHP>hP#2R*c}cHLE?KdWCF%!4#_}_0;24y4DmX!&gHXc>F` zV3vmC`vZb(C2p90K9M0Xyq|9Zuj`okFfXE}K4tu|$|DB-1vMG*1^Rpz%^1c0;cth{ z>Tjnuiy{0cbh0R6-Vg;XUkT)WxK=y_iQ+!P%nx+jZ;<(a4crv$@Zpb@TAHo=>Ef+i z+lg5WBm!8426yK_9CFlA`v7sVOp)RD!mB&bP-2T;0E z`FGo+0$ufn#X!)Q#YGRmZc6-dyrP66B)Ib}pm}OmIn%<8o}9u1EzSX;j5+O{fD9g0 z=FoCprce=+00?u}m61W44Z~*ZCBJ;y1vzqFGUbPSl_v=PMtP?Sg%A2NAf+y4bud`?SxQzB1TJz)aM zVglJm|LGo{Bd+FDxR^v-)#}PCoICB}F8mc?0M2!iT0o$By3p?alhgyj1PgiS%{Y(z zLNz9SL(-=rIQq)J`y9AzNetPW7R{_)_L?3xahPO(e5RgK9(~A<9@e&pPTk#m#Km5~ zVNo>zY{AQ(;V0YPC|S+u8*g(%9F&;f!o6EFuU{#i&%073c59ckpsM&sHcJh zNHn<$R{N-cz8`oNMXy#voueZ5{@`<~71DXk677UQnAW3U^qD)siM!@*KU8k9rp8#z zl-0O#cs$51*eg%)R!?P**i_FP+}xhcE(Y4TF^EW@>jt@Qd9uu0>UfPb7M#yOivj1v z&H=GoEKPGNXW(;A>?wzVvp{iBfeXsYx$m{U_aiW-UrDWJ+2`{+tI0gQ&td4YjxL=; zrQGM2SD%Qh@-q+nOFnqeBR0zwOA+9S6&}|tmDBe{k#6)kxR?bfz{IvoUwZ;yuqa}D z%;}%OC;dDmX6W;Ol;xhdguQbgjHXbdp&x$0M@48r&1$oApY?~=*PN-#b|FurjAPhg zjMbw|9B1hSv{w0@&g~wZBiwt>nS$|6Q5vvf5vFn#Y|35kc8!K)rt;ZX(_k z|3uI+C$s02YsOHYxyzb0Ss=M`PV_INyK0{b=#rarxLMC*7`A-zP-l3K_ufn2tM5yP8=DiF0&($$KpN@;R`n+yoZdj4pNepEyg4z9 z!_0@<*E2R;X|n=C%6Bm%51vd=8+o#IDng^DK=E4xYNxE?*l+vXk+B3Ra|Mu(>V_l5 z%1N>e9!O(GGmzu#;wJ2kVD`|lfQf4Dn^NI?ffm_Q;m^71Gs_A9`EY% zjh$zT4!f?RM#NE#|{!O8X~D$n&#O z*Kek3j#&%;!!aW3lc$PR}Iy?W{X2WX*uOpkXeq=WHcAjfEZ09{jn+b2QI^*(jBiaCi=4-Khj- zO&OQh5H7#E8&b0n>NtX3=?nW`*o8Ihtw%)04;n;|LoqP-9zBq8K+I$s1}9KpMlyWt zfsXo!aa$oDNQk_SKBTK12`hbl}X= z#@a_WIs^uC#~SsISm%J#Fy)H)J?B~D!EAAg(5)*L zWp3sp85(@jk(HYC$l$fg_URuAQjw2j74$<2`}9e?Jt1>pgWt~Znh4l;wykfjXK@?< zaXM#96;Ju&GJYuYv%%jb-*NoJ#si`@4B#En&up(D{meu7?%uf*MKooI=ai=`|1%v9 zR{IUKrLaH#Hp`;@+}rB9vg;#%Qu9`;^gsm2<+B9da8w-biVNWx&g)T@e0vJb{sFM< znP3~xD}OZE{M1u`!%)AiS#Ij+xro=2c|Map4LZS*+qGu}IFDTjIe6ils{xOg40PMX zrGdAnyqz}v=*eLIUhK$kJf?>!-wp=K9o-VSBNrvWf2q>}{T2=$N{qu9xz!0g%`2ks0Uu^j{dC=3L+37dim3kIy}3s^DO3ZfYVm! z@DxvC(DC;Qi5O;al4T3#=}`oFONmPd{Z}6fvJZNo-QxCcAUz~-b$8!cBe<{Z%k`XO zeOWobo$EB_cZglwXcu&&Z(QU;@62gW$8G;G)eXReEk5(bjO?DN{A6>30ny2h-6(Rv zDlk~43jDO zssQ+tCt-BK86ldr@+GkL@RC0w;3HeY%vlx(Hg86p3+d~bK^vVvAoS1OKw#}t)ouA* zwoBR^D_Nz<>?O9-&x0j&3pl3gdlj1r#S&zYpE~D_Hx^6jK@4l+#6xfr_p4}C1B5ZS$IxEPC1)K+lrS|C)gzM{ZoppDxN<0k})#G&G@ zmAIVO<7WVSK!m@~*k8w4_mw}H(N*iIqW}Ov07*naRB4)o+rApX&30X%JyGDswH}G8 zo@@-hZSO$_kn_lX_vxz%xv+7q!*k-R{W5P$>NWoY=2--v6oe9W)YjQhq=9b zERUl$>bX7-5ji;kmuS9GEnp&^=@+8c8aDBJb^Y4>&v0L@9-X3?8I`f_PM9oy*L9Hr zCqZVbtv><=e#cD0_-;Dc9wi{rXRQ>woB6E#cX_Dm&j0atzV==YK|Ls!RiA=RgZu3Qr*Jwz5A04dF7QD& zTpuLPOjG`#bVYlYiwOvLhbfW|7BW92ar2j+>Cx}1p$%}UL$|{gtj-u;>o$Jtq}%rQ z)0N@A%J<9jJxuQ~1zOx?Kz-*2U$_La;n=~waV7jNJ$`7xwY?=UTCce2{cQPhjh|h` z?Nlg_q3y!(V+XhG_*q|zd-80AMzm8_dg-$aY{DW>%#?+>ej>vk$8QykslRE;bb+fk z8dQ?rEAO`C4b*SZB9i;+Y)kngA#YHiyXZL9dZ`OT_5pnJVc-1|cEvv?zC7?B!$#Wx zvVtyvl5921f?f21K1Bl639=BpSKRMdIy&CbC@SJ=77if}D{-9zsrEE^dhdzJ6{-v@ zl{1tu;_)vQQ-k0x|NYAY5-diDa92H#D_HP8(^(hG%5uH;0Bpjo-{C3|brQ%*AnV3$ z9o*|yvcOXNf@9J2(N91IvfmBAAy{VA&L0fddpYcNPDyz8Eb2m1vJ51<_~PL`&CpMq z@G zjGcU;?`IhN^$aK)gp5m-y*j-w_pKaMv(Hpbr2tjYArMz}ruRu$aTG2Ge4Kf2CK2gB z<|LOe`ip#Fe&j1q2oxc$!{K3mgs~o=N@r)+`{Nvew&c9nG~v8FJ$nwJCZQh_p*a+Na=_i*uK2=nUvNC9}9ztiA1%fnBm zAAJO@>UbE{#^YIF=B_U5D+@8qB6PHlJMBF?;ujJXH?!WvpKmAed%fk>mP(j6x<(RO~MGRDH>>d(Xvm#BZRbQ~@u@%1}6{ zWz?q}q5hETSEyk&&Ws+{g?C0s-sp|Pj~$)$8K3)i@YXj8`o?V?+^bgz2)OG4|7WdV zrH*UAJu#%Hm5Q;l9(Ihj9(GH^_mrM-IwcdgWjW;YC!7mXoZ@pYRbq$g~$8J8^Gb#Y!^IFc`KzTpun{lM;QEI#d(2uzqKKqM_9( zi`G8=PpAxkiFpA0n~zZn--JZ;NsUeG2$|-0l*z#QIa#j_)Wnu+*0o%pt?p7NnL6(D zySQcF;n4OP0fg}lEq=}vJmBzu{yHm|@@H1^4tX~P1E!5T{VuM)C_wP?3e8SK)V8DF z#bGD@?&)*Ib;RdMU!#ialb9*zB=|=Z84Pa9N5_vH{ce3NuHs4bI1sPkQ?BqOOcu$+ zuudCT`6S1Gh1(_VA^~D_;c6Y=;a_0YXQ=5eIwJz z6H5yGv9=ZIjGAX-d3M0k7us4*085>vF!1lC^Tm}qiQD$etJ$0Um`Pu}YtQ1p=&CJ= z$}27duoJ)euydx-YrPVN3wsR=0S7B~81L-hST`Ph`X!4`|3pLBWA1&vPP-xVK@7`X zHA&Y8X{TQgk6OyO!jQA&lAt2N<$fGp#17XcE~Cy1!~G#W8ryc znLLi%*qe@A(4&vTABo6ADV7LqrZD}Qr|Pa*My8!_>$J&=ADvFHcQr>E_ZbKB*T zJHf_zxJC{SH79Km)XLc-3?KI_c5tM5{Td9L zuXyt;k+<%dOwhQOGZrp|t=BV?{a`-hOlY(|6HgTrXIk_>>pSsL-}ztI&+RhT`Tqmm z+<~ZSy@HN93W)?i<~j@hF%>+{de(Jn*NE+B>>#)Q&JM1ggAk3wL@W>a{thrb#1t?E z$XW4k9s8TtcO!ap=Enhh5I?d$az{;Q+>lhtir;l95^Qz5$^Puss>Wq^A!MFES`WVm z6OgMGq3V1oLcjtG@JS!9)^h!b+%L7b>!Hq9Lc4> zk9K~BP5Iz{sLTpsehB;|{$V2GomXY}ylk&8{wvkQE@VwSBxt4{9%%rMpH|RW6ZZ^`9~!>lZW<6+ zVts5qF(0GN%v1iDY@f!;2HYjwtzPg6sBb0~5%|rMFx)Gu)E&0*_}eZERy_sq#L5qT zmEea6UEdE$e@V)WaIE^4S|i=?O@kl%=E48!^<@Hil?oK{OpmOTTP9Av>j!T{{lPcW z{Y{iP4C4n5tg+x9Cg zdi(1Hb-{bh9|?#92Vd2>>j8+J6(V$SzFIE&Va307?8o|$^gv9dA2wmF*-elYMCuEB z_QD(60uoOE>mtGooY}K?!1=*#<13ED%r+hDZP%BKNp#_>gh5c9SJ)W=ysQ{1KNTh) z^;N*>r;TOpGf@r*R>JaQ2giyXAa?ZamwjR+^ZSV|s}LE^TRiNn>@W8u`%~GO4+hp{ zFTV7bPWfrT&N(N4J#tD2BK**FdMq&FIoB$4E-IqofJ(l z5Y_uyDH0=`P+G}q;_>O;FvA-Rwn@Xv7(A^jS%74TtO{`1j9Qf_p=FLvyZ`O(=paIE;zS2Z=WwnqeUZHEC5dLvE=Klt=w28gRb90H)?mcP^#3XcFlD{_(b_CIjNO#}}y1IWFI zYuu|IWl%?(GFf178$PgwG(!dv+p|s!MDzvF>hXPZ&fpm?Lq{NId;imt)eb_gm9FgD z5?RQF7oD)mp9cJG$HE#w{P5Ct=><<=+^e`)#n|^sp7PvJVS=;KVXN;T+`pfx;!|Ax zBiu^jpQ;eUS6*rQs}+6Lg~lYQ7CY;0NYop zMeXf+e!z? ziT|WkX(^EswmwS%!zy2MoRRTg;l}F`b}@|K2NNsV{WI0Q%o`7a_@)ew`X+8sREmo* zi&;UBRN(2Wdh!Op&mW2R7ym^^8#b!&TyD@EfKFdLXlgK7D1KK_cJQNyHx`WS_^@fc za?->0pwH-L7;j_&WKZz*>z}NHau#uo1fE=(ahrpHaPn*%_MM--uMt2@UFbI}d6^-3 z+8%*rU-RhvE8>;;Z^{~GcsJplolx*E;e?TYVo$lF!_K|OJlN;!`~wdgRn^+WVcL2E zF%x&W#j=2_oe0*fWD`=L2wcWM+_~Q6X?rB-bCL1TW`4?0`4MaP-X(vbs)$8%89P8C zL+1@_-{0U^g=bGg#?vc%&v~IodXJp+VRvvAL0=J12cFQ$;f0TkJOilf(roU1B|7pk z*O>?AT(gv}GYsSf-T5;C_ef9Fp?|9CW&Tte)-=rr#qU?$z;8Fg{U0CeZ z<6Z8s17j_oDJZ{fs$*s?6XUWaKk_##XM)++tkM9IaHz4PfBOpa9pjB1GvO4l^|Ghq zZcfQ}?LQpQvep@bHuKaxQ;%pnQ{Yy9^1(;Btein`oj(x8oBPExZrn@`EZmyg=358F zVhNz4Si7#%4&Q0*d+;vqaTaCAbH{wmbXqc=@R~n{T(O_QckLUBx^}>+JNp>EN15@+ zZbtR#du$Hx*=R+qN5m~Zq{mFPUP_z3CGpxBi|je@^b#WO?0e?yAQQ%hq=V;L4tcx=($V&B#q(KojoX5Ny(CMNG~& z$(wlVZz6j~@UCO)@0jY+4HXgYx;fc*wwBw(kAZ<4XBT+zA9gw2U@JiBkhpV$ z954b*{PYjSetP1Ygu9+S)SIvmFq65c>v<40?r+ri;;>*9jsQ;j?iS43RxgS}BDrPQoHpt&m8|^6tbl3(h`|Pe^=YhjFiK3Ywpbsb=c4wa)ualK&vu*u# zOdpH-!ZqgiZ66rY*eb;F~Yv&>`IU>YKO> z;K7MKcxk5dZRwJ48?q37ufoKtzDz+T?9l8HQ)kstfoa(~17#2pABjeg<4wm@iSC3A ztc0teYkciVCfj;MZ2s2S`}%<$7hjx7NlE1o$A&Q?emk(>u+`f6)3gGr9}+G9WyY&c z_8OkODV;w9lYjrazxD54XPI@zDd1UM)$tYx-1e=n#>&0zQ?EP5wgr0yy0DLOd!w$3 z%lT97UH``|Au zm+c~bCsO_l30x6}uw!OCa)$T<*R)Blx-w|#-A5ASTtefl8jbf)V%bBE`J1-#U-m_C zFt47y8n81=TB+j$9VTGdU01)EPqT2J>0qxL1KaQd{TJK|7s5JfeH7hou|*d=%s1w0&UH=}J>$=_DQ^)* z2EHC9Pkr41cfv398@BnoA&5?jx(AX79Ul74X^#4X=P_&NAg;fX5Ye7SPbqO5J|U9p zd~8SR&(hSn+KEgzZnttzGHj!ET0t&zOPl!|V;ASBI+-w$kLvaa4|K^moNGhP3CrQn zaLzUlzKu;-vBJ@IBs>LmVS8I|Nx78 z=yc4@(src5jH!ZBzz!6h75sOvn{X%m?gZaRKwZ=W4R-X;Te*pO-1f~GHBgyXY?rFM zguwZ^F!1``vgqoYN(dE(EH0PrQU@%Z`zaLZKIu9OR7#r0{z+v#P4ji?y8O^trNzu@ zd+CW$CT@KFk1rd3-wI6naWLAp|>Bs0M7&hR$Z6-@?{>ez%XT^6GnhwIOi1-*9FWtIh5<&1Z ze*la2F0Sk+k{?W|pMI(j=0#m+c)_}XOazX&0+fS_*2=t=Ip@`91OJJKJ-qC{qU5pn z%O?O5Tf@=)Cqv?~_xXvu*o&BQ*4#*YpFi>yUseVld*@$x5@h}zmvprYcY1Btll-yq zc2{~nn{LfXfuV{AsQ}ID3>2o_y6# z##em=0L=796&6$bQu$4AIgyfQkP=82uT~P053KY%8LY1pG#v0!Kd!QefuHNR$xgSP zJ>JZ!lQXOm&-{rV^N&37k}&)*l!&%3JgNdUYk$<<=)9U;9>uoC*n)5{fNNML;kcUZ*64htDdy) zeeN-4e&TBl1aX*&Lnj;djjxTS5r9RI3=C#Bbcur?;HKOaZk>kRt~L_j=K`>;_~V;J!_AiEM4z&v z+`fs)`q`^@F^c`{{IAW&Fkg#*KOE14`dZA$@?h*QuIzi^DP^~?b#SjX5W3r!tOmTtAX+Hl}L(YYxqVK2>iofVe z=S=P^wy`#?9u`?ErSDembnF?93Joihz@J}@;w+hyZ-_Zmjf=>w!}tEHGEw#CWqr91 z>ezK*e>wW_ZVGf(D4*%v{0F%y#BY1*&~7(&!k_6op2YoQ(-raKEp+#zE4i62KZR1> z4eZ$-KlQ_ZS$9GJnp@8fl)RGLBg_2<9P5ll#&>-NkTnqYU*R@Ea@y;gDwyu;2v2>t zl__&6zHF->jD$tP%9jD?;OouAs)ID-DK`%KNeAy4(tPbVah)H=taEJ-bv?@uORrf0 zh46*T0am=%kCP~xLf487EIRANf|+q*2rS;Mw>{!QXOjJhA|vsY83#l-(9>TkdK&is zXIMwl*0k8rD zhkdX3Hy&Mh$-?Px*kk^ZLyv3STA0m{o=j8OR{zYk?kRqSzi{j;JXU~zuKCG(Ca!Z+ zyx}K*z4^xvaTW%yJ1qdEL_ItGD!6&F{>&H0!|pSWx4Y_~-#RG|kQ-OJI$lKJk4Wz4 zzQ41o-VOqdNi&>r-238mJLTlwbL4}9-Za6ef?34u6@1!BUAQ!mhWy~FS(-cxup%Q8 zH!}uyv6GLu1X2%c=g-PEApm$TykWS{pZ)|;H~lcTN+ZF?Nr34tn$hiv%yQl5vVnvn zL56z_aayM=Dvy!iBx#VoKIKm0$}S3(m-{*-Soza#>R_v88>Oyq@dO9+H_eIfPiz-=T% z*tZTNcJ#zAwFPeky+a8wI0|-0o<1Dhm2qGgeLx8>;#9p{GRO2|{ z&-KRaUZ>c^<}s67iM3in&+dmG2zMM`%f#>K2IK_B1dk+MQCyz(uKFz4xg!B-oH88d z4-IBCu-Lbbz1K6cdwi6;qnHVnW;rvCi8yqX-A9M8J)e;vas*I6J4li(Eh$e;zNkQb z$)oSpJI3O(aE_XEX9$}7|AD56G@cAq0jM9a&cbGT?9=umWa`Li$B2E5vzmNh(I@^#$rS&iYa;&_sD~2Rk}R$MLtg1G82-fbMT3r@(K>B+ za^r;{%apfGzRA`MpCeHZ*?wd#)bN4pYvY9lP*@qgY* zWvW23)?LlkKdU-wd&hj=Oqnxaqxbnjf2UIK%EZo&@B<$DJEi|l8mgh%@1*;z@Ll#@ z`Pere{mu1$bKt{FDi20V6;L+x=?|GJ2t_CcWa#$qKnLJ>i)I+YUV9&a<1HFreM^sg zbtND7A9dA(vbTh5^vm%gN14Ao&T{I>3#0eSi_c!B;DQ zGCUB@U^F6m*bvAYTXgW~do4dhTARsinK^jA;bO+{mK_7LD*a`FzA8`FgI>`X2*80I zy;d9%u*2AV=1IT1e{gkot$Dq%-Ky?hxK?_Pz4J^C$#WJV^Ue;Mn!8Z*?;!N}RGn=UG-e2ouBL?!4=3z0BxeJ94hk`TVk z^m92&)ky{rpL@Lf?|VOh+#AJD{3@4na8o^N$0y>^y<{Zp6g;fO&%OBO{22gY7DoT{ zO})VOJp0*0MQ>Z--FeV!PDQ&SP|M$uWqL^=QD&Uq37vdG^mmP>5c@UCnka_g6YZ^mdm}Bo%3_u_QL~v ztvE=}T7VR|ck-FtXa79qd}R;~)MAkQkQxw9Rt1eu{5#OYaMH^`BrD8E&<87bx$o>B zTuyBP`N{Y4Y(RtBIxKs=;F6%uP63t!A`(h3Y&xe8yw*GW#Jov{@U;cJiToSDclAJp zAN}K39;-}4V)3c0-N zkp}xPeX6+gZ~aq?@nlShZ>0K*OVn1+k5y`&i!6EL#Oom@xV+-;I~7n7#jxc2$j|Y- zx{=Q*=eRMM*>}|V!uZ*9W$}+;Yu{z?^pTryg23*IwjP9iedcfETcyc7<+t*@7yc{M zkiFDqW=(7!t?p9sv)=jBjvF}NKWzI|#}b8{S{yrhwwp`?0f5 zni(qKQQ}d>5UJ^00WcYPr#$t*;-!a6PiKO~##=Pw%GA|L2{?j?JR6T*Yv#S+)82Jc zVCGpB9%t5|o3G%6FFMTE=SWmge5)?zUS}o?E_@N=NW-;sHC_%~*B|4bU@nUVp_JkByqVTf9 zp1Ln~R=f#&hIn)8DdPzn=Svm9!t6O)RM)cuSwui`4SVN9PyD%5EUcXeR|b?L>}dsL zJG3kive^|MxJHKre8Gyp29$?*=WE^gL@6SXD!F~Pz}dMRHpn4E%m zKO2zf?C8L`Qx{|p`GFS-{DCDrOug&!!97HNmXU9y_y&?M-&97$rDLE#9IV(`@9g+% zg;)Vo7GUcY^aBKHA@!>pB;cJ^<}~w9`Hg943tELw*;F7fN4B`~TnY_DJ{oRgn{&rC z!}m&Jrt(L!Y#*2XzIt-b+V0Vx>Y=1#ZdO6rvl?2UIQ-k5e%!!;Z9D$Ga>m#%;#oKT z<8)=D>d()9_Ce69p?e{%zlW^gA5>AG1E|;>T1~ zSt$?09wPp22ag~94u}7;-VwZwNNAW|l9gUK{RM|CUwp_t8RfwqsK@^fboh>I{VbMz zd{b7SzCK9loAoear22b8GGG97G+YaW#-*Tt)~|kxC_5%?P!%d+bto^tNpmr# z!Djv$j;hUxJ9rE`D^iyo8J!%0;;=?~MGBvo0*33_7v5(jh2-4^JN8nx5Cw?&aH94r z+w~K;+f;vMfF7839u^Of0uT@!hY!N55d(~gU+E%`rho0q{U6`VbA8K(dxU#j?%Y#l z<<tx`5mGqa1R=GA?OjcX+Q~zGbYlpaMDJUc!;y z1_-qB)_E97AHgSnsNvFD=xlA|*njxWInKjhn%x-rj<&sBf%;Drgenx{OwBsW*_!po^*BHHtftG{JIL7kQOx?vLC?O5 zNo)B_rJR-%hLk^ej{#(4`{ZrzTn%W)h)?SqImj!(0>QMv~xZ0)@snpbZ_h zw=(1hxbcQ`GG~6t3|`8>oaY z`4dkBAM)_gt04%dpp4j2h_OFo`328;r0V+de&P30t5Y8aS+ZWo5SAIG|C|G?24lYb z1sVp85}V>r!hf-+*oen_1WMOz*$wwRF?ycc=*fGWmwsfIn-K0X%KXTD6F_$q`X<{i z+x&Zd@i5`x!nyLD^UjI4Eg2x^C7&?m7UVD-{~rED8Z*W)opXc&V)6$+G{&wF|3NEH zOa6UdylL>}q8+Q&z>j5IT6cJj*ZJWmAXs(y^vsMn@b?^K^&^vip@(&0T(Q8|r!{|> zv(1ryG{-8&nzQl<*Pj{3gJk^Qd_D~#9M0F|V=#hc>Zut^Kcla_M5ST?=NckOrV>;dZR_ZI3B~J4UY>4M(%XSJC73NugBI@~>O zFL3JNN30|4#3BH5?YVaNsD2R@*Bco;>Uk{uLwm2T>l=Pt;%4g(0#*M-@%oA(ipPY7 z?Y@=m0s@#g-(z@;)_^)E|IQd5Stn0GyrysN3(>ky>C3%$RH^I2z1I%a@AjL2t66O$ zRi^;YtG*KWr-1WUXxmnQ2X5z}1&?PseyX25`eOZl!Bcluzf^e)50tVv5i>eo-ARWr zywefJIrbXY+&DYaEdH9ecah)KkJl>3nc&_d?q1Rte62F)Y(8a*AlUKBcl`vQ@wWul zRdn48#Ejx$(21NK>o3?Ac$Jr*uYblZ1eiq%JT?SA`SPB(d0@exni?Yy9`i>( z+GtA(w_@QRKfsC;;4jYXYoW{A_A2aimg~f9_*Z?-pZ3rDinuKEOtj}-o0BoSpD#NfsZ;Ca znaXuPw!8T7*iU#=^SE&`eg$CINWV^3TodOJn;n4ju5{&?=|k6N^V0o5TEw)hjEWs z5p{OfYM2@LoGIC7xfXcr`$UXl*r$X#l@OjCVw}E3jK4s` zK0dr&6sh~ASDK@jUn=|IGW3`I61fdHt0M3Cs~8>Ej(bpt69b4|FmT7vfP0MB+am@D zFed)1k-q+we&XKDp16L*ev#;W?p(BA!2F;!=5s}m#yF<~^jmwzEI&x=Jj~Zv5Sjn2wyrOt*I z>5M=B5>8QNDZ0(W`TMGc+j~E(9QI%IKy4ejiP;xgqvcmIcEuCj^n}nE!O?Mjc(3dC zzEr?FbvS<47?pm$%GbL6#{O;_>m`V3933&|>632?*kgN(!70lK&betkGmex0-I3M( zZq?V-sxQBWy{TzI%;?78zRVl$`s2QPE{rlOgE_Xu&LdBo!CU8%C+-<;s2`8Ze}sD{ zAaCQ!v*{I*=RxS-^y135dGbYAt)u7Qi-*BwYCI-$c{+Jy69I8C4=?i+x4}BMl5FuRq0j2h!_A{ zJz)Cn#*{f9RdEi?hcj*KuTWl6**=Sa!2EcYi02cG@vS`954xWV%J5Kt&voi{tN?~J z-m>VSi&qKikWYAYIf>)c&h-!v7MBwvgmXNT(8xgF@s4sv%&wq1=`3$HEZBr z!3#NMP=FJY_j|`C`O8JFXgUqri12^8_O7FSi$aSP-V-X^&qQ)&hc8}WE(JElp5LP;f`e>(G{9HO z*ZLriJ)@)WNKnR_$y|BFPwpG1tQ&;@i(^y3WJ!ICwXcpTMJ;_{S=@=9Ypt ze4+fvl)iq%{8Of!E8WZW=EFJQz3AAn0+e@x$8UER!q}(yhY54yx6Qth=(S(P-&p#g z)q0lLpCGP%t@DQ_Cm52jTP*nxl+23YW1KPkS*n6D=v-BW$vvwHnQ}!xF`)y->UG<% zpQwBD{P`8%D!<0*t@eu0g>0qc7ry9~cN%$c;{8nb*&+|0HarIZxpA$y&VR1r>HGtA zv1Fqu>M=Nczo)Km5(poU*m*gRQNt%@yz$BVvkLs@M><|R1f#mg;J-BLg8=oapS5LD zG2Ys}@zLINErPqw`dGHH#z+FIlUY+tAJNFSI3LW68 zuWt&{c3JV>pmfDkmdUzP!Kv8&p|eBuhrjqDW;Gar%r8>zIy^MVqe=b zQllBif4||;4`|m*7T12iHgTy?>~1_`w{r?Pk7h$SMjRJtOz8uZ<4H_C zeUl*oMvkc)Zqsmd=j9Y@GK5rb3}kn2W>vGa?k=qoANHx^RSLRdW2dT}T^8d`|No3uSxOFFt~9Y$o)q zTJgEY6dcbUU9K`zJSG7}1A(z%^3`AXH#{8K+gydyX6RW4P#V1a7oY@i_elX3D zdZdXNxd6j2DWk@m^wUpNCgw#*WoX1Rf9p3$Wgwpv0JGv{?O6f7*CW1-t;-Hs`N?nQ zo&#qj%ZiC>gyw0FksW(g30*6QIl4XT>9c}<)s5>s{o*>W4kIEIj=uJ*36((bctLu$ zDV_Ywa+dmQzhhqdqrA~{ph|G-PWxl)gzvVrf94bEcNA07Fb1R*<5=VIp}Pd|q2t5& z2p`ylhZgyykImoW0Z!iN6@0Us`th=X;Z*E_AE`)d?YIUwez^FHYM#SkLT7z0gRe?c zzE+!cr>;^-pTWbPnx+{%f9laSJxNfL){#jBzMp;g%eL)K53YS^*sj=B$1w8x z?>*0X=H`co!J8jG!lN`ZJLaoxDSmjc27b8OX-|Dt4g;L{FURVQoBZ8YZ#?i?A}a-E zrH<KW!QrE4#P^nm_wg|tZ|>h;?Y;h}OJAtKi9ay1%$p@RC5S1Z4-o}$j>x|` z9=4?ezm<1;c>BXAu4FoKo?Q2Pad&t>U%%H)R;r$kUnsz&4|c<#{Ujbu*O5FIu~v+hYNpr zvSq>!+~UH~;|HB}(YJUItgmCcDo-0cPmpWuLIue$H5dg}oXHXjI5#%=NEO%twjyUK@#- z%ldPFYAPI`z{{tK%;A^^fXA`^)okz-Qh&O20)4?;SJojmyy+L`DKavzInS(UFY9p~ z*=HnD;!B}$sv1CiMoR5SPHQT37w~JPlq!0m!(6pPaMX``CK9m5tm+9Wjn@fP{5VdW z*>>&~khzWBOEPd|yyeU9O`1Bt-K3N$Kl%q|WEJc**M7Hsa)!)^XV1C+*$8#kou!8u zxx{{^>8pKHwfDK|GmoG4E8z#Pxe(tch>pLyOl%80e0p?-#n*+E1I7oh@@Cg{U(JT` z&?29+Su63W9-BI$jhDWy8}T5qZ+q4K{WUcU^H|fY)=f>{;W+DW6_38twe&PG{HLnc zfrGK+xu;N3KkmJ? zCOB1;L`cHpy7};W@E_qu0R^Yu9RGzL9ce2=iS_m`T{;o+!2y3B?0!SSx3b8_vZ`|yN(@5= z0T2ugQ+OJm?F2c<{b2`$k#(>x@JD01;*D};X0t3&r?4eSCLU2J*y9CG-ev9Z9(54H zvwkT^P%x`J*<&Z94|{diuZHQHMj1&H%N_y1Wkh$-9q`zN>-iqxuTlpQ2n3v5nw>0n_7ObxYe~S zB#P;~ee%M8oVuwS`)q=aEkV0As%*vkK$d3(akJW;Tq1katWdJ|`_&m=0TnItt!cdL zZazhj!X^rl)0|xuuXCZ{qNB&RP`dZ6V23s1;;VjI>V2^CXZ_H}zsfL_Z4Jw|3Q^Ci zocUy_SlAB)Y2c6MVBs8dc=&*kk5QbJv=ICPL@Iu$RnKVlnR7J%qvMkYajHr-Y6;E; zF4e_*_5TMW-N);C9aUqkJ?0bdq}L{%szIB~4U7yco~(WmZ%nKXB%P@OkkApa_)K1m z_xC%5R3K)gqU8xmGa~a*`I>NcoM*UX#?wA=O|dQBt{cxo<+E`sGgP%8)+HaDNMwv2 zUv6>vG%%fT0Y+bCG_WoK2SDO0<}~uQOXFfq_NeXZMs^?E;WGv+=g!#m85L(d53~NtNtgERSv-JKS=}vthr`M| z9ajcDGVaQbdKPMD6I1K9XL4bJkkRq6E>hGDN~}Kcs1xI}x4F>KPiiKRdsOE7EO^up zb^MUXsATK^4s<4*2>LrCP14@)RJ~WZ^+SJ|e~;CNj(V;;Vh0dXp4fS}7=Da+z+H2n znq&0s#-Hk@McxlF4EiSi%`t0tN6&kiye8*09FlqS4}5+m9ituwECo&2`#i8&mSh-A zpvcU_Y@J8^lRwm=;h(Ja5pVB@K0o5|&0MaRKCxv_gh#r{gR7fbit9eV!cekbpLbDz z-!}PIdRqI4dF;^#_6d3H%Q}xZe%OAjW3+kw9;;`@jM3GDWPY&rwnhv@Y2sP?f5i(c z{s(u!Z(>^X>jLgefoFL_(ywL7;Nw0Al@GM;L;UmOx{{c)lAo{qqZL?IEa%aj;%hE2 zV)jAjrxN6kHw_H;k=;bLXS>{uchlj+`QcmfZKt~Jnh8h)l`EWb-KYxDagHKdpJG9vAh8H0blxpSAAa#Kc=2zaQw9S|oGR)QMGO;+7-w)FomVk3PrF<^ zbO1#p#78U3JbgpMo-=hkQ;Lvw<8^NE`HaF>7l8U)5U#2AjSiC7G-C3M;I)qON7w7v z^G83o`n6B2gR1y4aw2a$bk&}ApO};6KKLK)dE!<89vi}bJ72t-sIel3ZcY&*2)w$M zw{Zxk#R!&|b@&iH^N`J&ZQ0i^V?14_`%;f-WK$5@U@Mb=Ohlx^?M>M$G zUzxz;AguFv5Y2eJtaESb*Wzc3{IwgJJ#2=`XOPhTPUAH z|MScGu3G=oGgk1^b>8u(7xt=RV7D-u&d!bR z`geIBJw#ro;wTp_$3IFlC+B!b!E)YTkVd>`6M5(L|(*fQ=jG`df4bI4i9y_J^Fgau2vu5}%vz&?$r~58I?6GV zrWnj1S5E#Z6jysD;KYo6_`!|u>v#K>rI33sb%Q-& zSM@VW_ja9tJ=g+pcG!fye&hv$Jp^o62}~ zo*2hgUzqTGeMc${uy1kF^)JEs1ssrA7gp z(QyQh6K|!1j1j{A)R72z85Hq`$LlZ9{cT*D@?56G z8D~T2Xpj349Q|$70yQb73^VTfm8d5>b@))pH{0OUCEpMD>YoS6i~6h!KkBUCG|e1+ z^8nH@k@5=@Z?ZoCu|CFFC#^7|&X`~42C81Gwc2dyYOgykUPzI|C_;hCWrDpwXqdo< zQ1O2q{g9}U0iFAiSAzgWWY9V>N*@28T|xU7-V%~9Tf*0)HGBxX00h^;b3FKQ9wJY! z=Xp}V`!X_P{lxrd#C4BVSylJ*q7R-e?Te^`wD z>`4S3^}D~!fEyb-jlE5DK#B@nt;{#NJEKZg-}mkcj}gCTyk7gt9?y;v<=RKhNeny_ z!mF70b#>vfLVD5}1MpAqrl&tf)v0#XwfaqCc$D!`I{D;;XV|gO}Al9 zxCT7G>4$piClWJYK_iZ7VDaymmy!ksMLeHJeEgK&@oqRXEQ~tHYSjO?ZSdlyjlj`b z%rTFdGcwls!M{ezPn+oM)>oCle^TRR`JpQy;wdeC)S39GlXxW{XR?7gBL7_rV7a!8EdSuud90Ilc%9pI?!L0fzEA7A z_a;tV=3CLTU1`y=kjmfW6vhS~IB`7_dff-1Q{#G*O2Ja`rliwA4bKm9rSsymz-|1_; z$#Yfon9{d^4~vFkufUAeC=SJe}4z3BlYNs5hi)!4~!vQ zmxnbY*2MXQ=mNJl1$51PICGlH6q`IaBWC1@xlSkEjNh5j0UZQXXHSLPvU!2FCT)3V9=&A{x zG3*r;`Wa9?P!NKe+6}n|(1|76>0GL@o=m7Ndr*Cn_p<$;!l()qR0Gw&8psJWMx&4F zY*6VT;8P>_-rV14E@bh~=K+9+HVPk{MH`&R^Rvz;b^ytY8u3HFwLsWB>scNn`-?os zp_=+}%BCN?5^gZkIWuE7FsOoxSxn)&@O}Yi^HCNfmeS8dEQ-n!GDJ4|oxH_o5)i|i z;{d@2$tBv$D%jMVVO42aHQtKg^e)2K^Xx5tTXPe<@ZF9WBV^!=c2(_eO;?g z;U=TZat?Mrbs5n!HV)z)!>G~v)B#6EUK>W@qQ{={#9A4N^pSn=%sMuAvIjm>jMH-j zBiYYb6&j%4`D97eh1C}iP-`q;`j0tv=Qv*YS?VWRRnVn$vMYl!*QMw-*;PyB^2|JV zvv=Ix#bZ(ihtHCaDfh;lhbb2?auJVJ&9QWG=5v}j*GhQtLc}y(rdfkD##rgK{;o?m z=;IgY)gJX_1PZceLu*8!UAV>oR|^4MiON}SSLs{P54v-k-`wbiGO6I=&uy} zzzY7IzIcPqC)SN_&(>W%8@I&qD z?N57Imrp-n^hx~7#U0;jH=Zo8J}`73hL$9)thnNPmA(+t)=8)f26; zPsGp|-G=x@9y_O>cpCBE$9vujdu)GTjLLNBU3mNh<)2#qKz)|VNC{&40Vn3%DE{2! zR_2C#l^f5`lk0vjX5;9T`1d+ED_Jju5NYv-!(+Vh!105Bj81$kfEDs}j$Nq0afmqn zk$W@`J$S!w1oC}9=U<@tl*OkmZm3ILeREJc$*RYGV@`vY57Kpq)^%Eag*8!~YCty{ zebJ9b3av%P92j5HDrf+>_Kkdv_-#i(pphedmU}?IKw5wKMVmP1NOT%>re`N>AXQ}^ z7|Hm}N!6hoew+vDB|f?=VwNALoP{3cNXYrhSQD_p(uKdAalP^G+ji$i)SQU$T~XE& z`S@WiiSyT;z*7Yu9(bzP*W}Z+j8{s`X^IKPg;S5E5JO{>P5B?so^{b{4-(eI4Xq#{ z{Wu-#qZ~vC$4DcKGNu8?Kj3H}=HWN-8V5A-l^a<6tk^owY`sPzekhcY9d?D#xc=O` z?xJ|w#%T~z1#)PV`iZdL)KLTMC*$T8t4N>qiv?;J_2_8hjRvyy>WgH8wOxSi9O<=o zW}KIvpjAQr;MRCDSsNp~9$S$ZaWnRrcTibi$i&z&l4rbJ6Vi;>5wjl72+v>PCCIYl zy3EM{Cmume#;E!!Ku?_>Gv9GK6YK01;QjQOL{Vee>~6++BV-@ykUcmTS>gFA-Y6vJ z7ag-9b#G~(j|N`&ywlR;U|j1YjNG`!6CFLR@Vxxx56<;(X8Q4(y!-nqb<#FfUB8as zNKT)T?s}VI@3VRgRn|wPg!oml6LSX2Dl`2U?J8}OOw?4T8+hPqrV@6GLGOEyvjUMq zFh}{>L0v!YrJ&JS&MO+cm_JU@vr9dFVB#dk?D3G&_$6sFB2Im5tBdoiKgO*?mf=z@4wQ|?^j+c^1$Xp z*_I=yR*#(nuLO?~Wd7a-{{G5?c~r&b4@Y0YU-wA;*pirgf0wQD?Okm`LVmSmPOeJL zYYjFy`lT%r1D_v&#)$k;i)D3b_A4-rv8bH0uTxIk@?zm;LB>8?w6<+$6Tj>O1)lWac zT4^F(Ux_(klUUAwZU4E~?$+Q=VE5{W7GX7gS?+~t_wQ9_m96)QP4)6FK?`PU4Iw4ju_UpujM>kN@6ia zo(axuJTC(mqopu>Ka)|alF=*bjK|5D`Ee)B@)5*jjIOH(EN2Qozb4$1@VJJ$R%#$8 z?5X@J&%F;$Kh@T-%l-0e$1cL_`V?ki)X$hY=%~-7R7`(Xi1lvkhZCB0ULG43`B{9t zFpoGI0}tJt$E1Ed24~a;3t*C?@{bxEl!94T?`J0RJ%J}sw3|4#`~Nqzvwi&Z{=IjX z>z`OHl=afraxS@s?9rqef3?qi5hQNLEHPfT`)tw&IFloTf5;{_##%fn;C9X#`5Vrj zbGDqf6s#1LaAtJ8=(jXv3>)>H?8*jSRer#%nW$N*O=e`WmWD-B9xwkb06BugM3xJ@;v38JYxddCifzQ;e zAI!bl8Be$Q;>^y(jDoN-=83zb!B6DGBiBEirAGFdsxl$1xF))W9p@}7K<8cyo4_KQ zxm<~OGXrPvNK8$MnUArSyKK{C{cK2R#?Sc+bmBk!8eJ?aQujYx_G-_rtUcw$3CWJO ziK}iraQN+zGtSOYhy+hm?Nd3D$M9ANJb1Sz@OU|{X5V7?vf{@G|89RgY;GNH__$8o z^vC;7ZTrlJD6Z)k(a%d<*xb9A$61MU#9Zvj_`{cID1PWZqimGvqcGl6q-<|1Mz-f-tB4gsA!n@jM=leO2sXoWpmH+r3auqW(7w$7i}c(vY<%<%CN zi`5owYheA<_^A3%)%u9<@9n_LuKWQ(YqvLeE+WS8!7Y|Od>dWX&x0g(K8<IgSW`0gA>!T|C%})8mwGb|5_1kSH<9Y)$aWm}tR4AhAXUq*Ze@O_3dJwoRep#en z_1vHgqZ_m&XA)Mz*57nB+|V|o&XQnMkz0%XZ&fn7 zerEdY(_i6bh@V%)hq}~(f7Zu;-yd}N@A|QOkC(4~=6Zxrt*|X0%;>ke5Cc4c{*D=( z>qwPm$RaL&$x{UIENLarcN~AMv9shGC*{p`Q% z7ZZUD|8E*rVPF42f4nWdkN8%b)So^>yM389K=M---V|Kx%=t(}F7|oltn;WjIWge} zqawn`MCYUU*9(w&T`%m5FMRrsn)R)q@M?_zujjNuo_5%O?F)4n>6f^S@IfE%8BnP; zB_kl7JF{%h#K(GoD8V>;+LD-!OyP4oYObF4irA?R{U}irN|C?r$>awcCQdjI+ejK2 zqtO(?r*qDV7GKQby zU9S&*Lpf?}cB9wXE;#WlGJ-*#3h)_^q+;&#cBOnH>Uh>)qU{)VonbzH=Ic>!wltD; zmFasiO?B}WJg44Ove^~1jEy(4T9RiWu&7Qoa4xYtMq=hFvEDm7UXC){*B{(WO_l7S z2QWDWh(9J9K7v)ly{EodK#0bV^0|t?`U`Y*v$dWL{>`iRlQFjnJ;A!gTnOiZyG}R) z0AfbFh}t&g%Nad|7;iZFPmP#*6Q45sMiG5|qva>9=!A2`PGLvh_ONPKwax)bw0 zG2`H2#OQ#>ledEF9Fw0Ky_Uy6Rrg)r5AcA&^eLSBrsZI;!=#BB zk-tBN(%s%y{~q)6EBxLbW3T9CoxiBU76)yeKas6fqNHS%m>+~74Ij>xc!8O}$Cmy= z7|u~S{!xWF@fYkrE%n+Sf7e47dp|cGqyN2; z>~`D!;BoZ^a~}RYr`+dkl{}qw|QbqYJOZhlx-}EAbB}Z|$MbK(qmKu41<1Ci-Bg zzd-Xm)*ESc+;}?kq0M=^?=)e_soUFr&?R(Mr}{N1-&g&xJJ@jvdIM`6)dJ;so}gEZ z5yZ2BT3`|29ZS3=Zg?1wJDMo>Sk<1}6?;TWso>YT!KpQIO^Nki%yOGs3{D|Norwgx z;}R4=eQoj4SL)%}(y6AAcKAh;y1bj{Oj5u0n?2xdI&3cuRMFwV|JGMvd-A_IkC(>-q zSS(d2JU4SV%zS|1I?mEG2alktaTArXpBb(;XN*|-()D3TUwq(kjny`~YJ-rxU$d?+ zW=+h1bzuZu4J3eyXAd-F&{;3__3WuS_@JM>09PZfO^~=GnwP_hN6w7{+kP;0;^MdR zSy0JP;lfDY?0@2%pJ+djRczVKL^9CI3}emhKl#KKDYFdiTk=4k8R ztKxw9P?VTV$eH^Y056Gn#CxrXGSm0@$=H#*Q}V0>LQIf5PM&c+vtTizFO2n*z!n>d|q} z?5^4k?HDf21HqRVMvcJ7!;d@!i(lBPF)PQu0JER$_p~J#$*uYbCNfBKe@IRo$1#rg zu}t+{wNMvgaK}H^&YECM-&D>N@vNWP#J$gsu}l|VJ%t!y6EC%AIpXB`89_~T&(j0$ zY_8t+F@R9}4;;_!IUg_K726gu&vVtQiz;1krzaic^->qukEZBh?ej+q zzG+ThL0{)@%Au{xTIZ3Ec>J3mBx)X|s5a>{KWDmp^8-OZMcke(;rk~)bREUl`ENH< zd-bP1T-MCCc#Fg?yYbAwR|EUYu{q4+}%}26uuRCg`=;{VC&iPf=5p|gN|+Q z2*?LV47*So5Fd63NL-0UfbTrwTR!By>SV=gV;0)f_w@*VpyliKwXwj-9>|? zeL2M)qh{t%`s#6Wn z54^5l9nc5~h>TvaV%7&uXSn>7SD9M@IoMd^6b=WUz?DUy+G5{NC_(DZkhIQDE@35jZUXgZ^}HL=wdjh zLor1os8o7XAz%En`Xe{BW28#o-!FtS9qLx5f|!vC=I3dTMX!YN<>%|9OyQ!J4{2@RZOFtMZ({ldg=szEoaEiJY)Lz3-C0jI0YCG_F4}BfieZ z2j1)PrmzLFv5flAO$Wul*}ZH8;qJ%vK%Ok_)4pG3ekReU0$228!5PUP<7}WRmkWi*GH&r0h&df<&q{b0Tkv&kQSrRJ>%@;}~)75J;V*#_?; z-0EshK=rq_Sz8DN4^cEpw`0dGubOX7z9fFD!pBM8lgA%-{F)-_$F9dedRgZmbjL1a z>)hN|*I9LxoX?68%>QRxW8{CzPaiZ&H2F1;PUC0ABEI2~``XW1X`O$~_4DeVTj{5J z;wI;&_ZB^-kmI(!guJKRWR7zh&U1t#ihX>Z-MOEO)(Y$Vb8cT#`R0GZEtas_ygLux zx_r$w$!08kw?kyKgU<0=?0jN=vGa39{`W>Y|GkR$O7y~R*QjTLDV=_ck&!2+jN{}r zLU>y+m7xm7ss;-J^B9>2{@zb0;>UXe&+J!UmtUaMm}|kZ^qj7Ve`DYMWut!J@BC_= z^;HO8a83ADkG%R*18LCx6WfN#xbjt3gVANrMtA`UYvOs2W$1-MZ_Vjwom}>r2MtP? zdz@%ueBXsb)GgIMbUV9Q2vDfwNf~S1>et#EOCw`R!vsF&=e3_L94~A&?Y3V!vRBlH zr+U_J*UwUY_Kwm;FkHrl(-;L5JCE2q#Tc9s99@i^%k{_VgBMZq^SjwGw+%#z1!=Cn zt_$TvNTu(W{NUSYa?0v23hpODO+4Q*`$7lAjM>P&zU6D*SU<~ma zOA`5$x$}a(U&TODUEjC|P(O_%126dS*0I-EtQ_i^497BIt@11$-QdGDflr>Ak^xYF zRjGawNaJ_bhnJbNK84b_>z5OTs-Fzj!x_Yvez?T=fve9-@GRQ9AX@Lfno6OeE5q70UdRk;i>WUTv-dP9^;h*y*YoMt1=WT(nz_EB`|GU8Iy zWAOa3rcD9+kvlmA#vu*N=s2-#ipU-@l4qYx$NM##rE6Cgv#)6yWjwCO^y%jE?2TP1 ze@1+11ApQ^2`NNJT}E_@Z9T$|`c&HeL67?7%f9pqQ$dWLa{!;X+JL6-sz;FVx~h@E8Vm9I{dGyrsU)gDokwo* zlTV-npYeE^6gh15r^~Wu3v1S$`K&Tye@TA6a_zgo?#7wG18>G=Gb57AM=pLJv-+EhZhye$Bg{Mi^O{3ZE?1M_~<88!O6t&#i8lMtN!6Hzd)zq&%Q>p z*4fV%{1$iLdscwC=fN4_!@J@TA-ohov=JZI8|`BuW^NAtF}JU?>Er88-|NtQu``W4 zsWeF9#bk*2i}Un}d>QfwW&EdWX=D;fu!m(=^*Vn@{$BvbX01QJyeC$k4`u489^5?0 z1DAwW3jp``ta`7IB{Bj3<0q_~h5XeDqi*yP|LB1^F+DyRu?fmLe;}DFpD)Sf8NoB- zfnNt6Dco!TR=@)cpV@zg$P|p<4DSX{; zG5F?bRQmm@A6vvH`a=?hp3PZ)aM*O8hX*;#cu)ur((9kKm0%-x{O065&J8{{-OuB6 zaCZ3fwQm(JyJvjrSanayUJkyNhc=+rM_dy$H8`D?WaK<_iMU z{z1;aNruO*b9<0BuV$>;68Hmm{oDxp<9!7lqikyekG{A;;UA+6DuV&UYXe-UGQh|6 z_-GVyAb2QQll~SAHtT_jl6>Ng2A&bvepn};wb4gY6B#Dcf8cwa>SE|Oe5^ezz!tj_ z?1Rb|%(a*9h0#slUCC)LT+!|9>C8|-*VO#z4|hugzdzm#K2j(=f*=3VFZZLp<;MD| zLH1v|%4_+>(_ngzk@7DlhujL0H+Z<#T8f~%g(cpFTSw3UyH4#}5gLb?<755U?YY6@ z6w#0Fh}M(hS}-=_Hsh^=Rl~zkyQ8X|fg7V&OcrN=wK3w#ni?<9N^no%Lq)1R?giGU zVm|%%oUSb--DdZ~zDzb0^pj?Q7%*sQ$tzJoG_# z1c)YH!CHrTq#n(q26)W3pGuIYmKIcGHkQ0RN(dhwdH9p|>@Uvg!@l#s2d&1{ABGgD z5tw#J7tVG9$B5;`Mlh-4W#afDU|jwa~>n9YhE>sT4)dCLAn%~M!HMb zkH+F7;~M&ADgVf0U@8x>c*4;E5GTLy{lsZwXN@v0pM-Iy3daXs;~+j#KXK09@$9LK zN?tB?CT?|3+!s*w@h{``jHuR_oa)rWfA>7$lj^9z%roHC1q-TBZ*(pr?;|{Lvj;v` zS{c)Gz4N+U%~CwH?9&7WeeT!z1~Qerzp@*RtFL4IBV#=U*P$6ZIL^+jyR;2ufmHfF zc~7;ieVFxG$!g@UwX2M(K?JxrIM*3lw@TvLP3Zswk9myD+pant29d*s$MLb*E!<{) z?{aoH!>nJTELJuD&tLxUKvyT~>^CM+Gyc7Cb(eNv)3L`3bxz?q&aQL6i8rtMh?J;| z3RNsYIeSdO8&3XHqni1Y`sYMHve+-T8@D$*h8dl1cz^}WV?ff!!Q-z=oTI4=J;qT; zCLstQj36*4j{I-^P?LrIR^3;1=<5~lXYxGox%E{=K=#LjUGN~HMZ?ZI&gEyNcpN;8 zt|JaX_{+0&ZLkt+pRc&znUA`<3d{V9HLzvN!|qX;!JE&BG0xw0;7>eE0xw|@5}Noj zo$>FyMAda6zf<*fm4B!wzgdsiR`cpY+R~r{EQx%iFJGU#@=+! zdl|oH6s$N8@sByy=Q(K}+!*8E$9{1Bxbj&Y{RNs2Q#_yPP`{xUK_BGGm6#&82Pt`c z{bJ(d^}HYP%79q@Px~61SNn&rwv&nPvxr4tqbP_@3xWs1M+8Tqh3L8Uts(V%Gs1qO zNE1*jshKfcp-lLOS#jw{JT>5tZMC;CacW8u_1^hS;J?^w)HLe#z`aPt&#U)dV%znF z~do3HK95x?jg2c)r-hi0q}Y|V3w0YO(-!( z(6fHh&icvZdb0iZ`dNWGDt-WTQ2feMA6>`QM>W7RO8ZfQa$D|$NQ~}Pp9%FJoW;$n zf-#`LA$$|#dUwXT9VZ86Hj1uUU3JtgIM*=F)D}ly0pKxaz1Xy|*!63zM|1ev#H0SM zvT{pn+2eWb=)H`WwM=Kh#iOC4Q1)@aB~umo(RXIZgU&I)s27?rjuCwjWUI*(v-7GlN)KpwhTW5Nr#w_)a`shUfPsUl{?l^E-edzQI1W{wN$M`Llg_PGO1 zyjg)abDtge4gz{2PMPRcpLaAr3&rM=j_{G+cwG}p_!HIKy&6>&Iuh`M6KXx`Daw-n!4_Uwb2Hz1p+%N}ANVDP4(%3$ zW5dWVk^aFFKECih2LHZstsL{H`14iY>;D`22n%0g6bEC37vom80?rd}8mlh+E4=o(K2=f=+wey&6Rb0Zb$;O8oR+F32qV*!M2433Vx;~v9%IleM}Y!IqPB?H58 zoj=0X{66PoQ5!Y!_l*D1w>D9I{OEpCoQ3%yr|$=>AM*I6+k*`%U-Imf>(4k9+-PdF zjQn{(Wy)zgSr-kkjwVtlN8b<{Mn|j$Fn%@4U=W#m6Q_sPf22~I>vWb@KUGxI9;)}s zcpJpjY=i84=qH7}8oU{ouHgjK4dtpnXUDI-?y~Y@yJ&;Q2c!1o#r|vAOyr%70JC3euM-oZOY-Wl z66nwCQH1)mj}9N_H=Z+m!Fz8Ku{P)T4Uys8lBdpdeXlZgW0%!1IRV;9GW6&%3dd)k zB_aox@#wdzX1-K3$zs)6nzI)A%R78HxEw%ucQJca$i%y*g5~*5k2Is|B!C-juq+z& zv%s@L_d*GYDWgMCfVyM9s?e=Ic=wYIXZaK$mvr-(7jhFPyKQ?Y#MGYyqzV*%JO)vrXwxIkaybxzx12M(6n zTFdku2!@k16fUZ`*PDWfZ)bseZbhjRZ%?${n6sPFGdQ;j&73o*ljfS-;@T|V3*R*M z<;2rC%Zw)G8L7WWja1+3wIB3(T$E~1%Y~Q#rN z*~G`o#8+Nl?Wr4t)-AqcO1->y1E_pVSnHV1>oQH!X}(T;@CPsBx_ZQmU8?Zzk#dioDUG_raz){)W}JP^iC~MK?T(>1;AH zk>qtgk~nRDNgkZ3pD$GdU11xaHUtjRIw$|Xc4Q;jZQls;j(<;#JJlI=@vBVpYPJuP z#hVzNp&_&MO53&!Y+`N!R+l%=gU%tvm1pAeiXHE;1K z6&&(o?*F^bL76}0wOFq3F;CG~U#q!oZ|LBw-e1wjCdQBV;&HNm<&_Vd-WoBZH$%&w zuh?yoI~^Rm-kW6dfxB@OIks-czdwpfbpLx*-&+U$e4#&I9#+vn%*n5%n6Df^+Z=@8 z#Lucpfx(#91-4(M ztN~Zo3V=OR+&xXl@j{9UGahT7BjAuN(;1{L(LO8XU#8O3Ui!myH|^fodh+rqp#Niw zsi|_KuVSix^{2RN#7z6O!@Vhv6RRO6>Hvm~3C)`Oh=s<`(* zwQQHlG7%rZ#)}YG*RN!K@9#e92chwmmyEu(KxJZr%~^k=1#<%Wn+C$7?fOUi?0)wn zgZgpDk96yGKI;U67v70P6#vKubNpyA$!4j*8Ka5kcFOv-bOrbyjG^DIRr}svqg zV`#!;kL57oY}a}Pm#*V9RDUF>LqkXXAchKJ61si~G+>;7>Q#PbSF6_}rwIw|&XQXTbp4w*snPyNsyp>J50v zrqb6x;t37EbT0qtw207AppX@hp8{0B z1R8R}^}$ZB)TfGttGG@`U|wW8YR|wl#Do*w(MBIzw$#(dZH+J z#zfBY7#=Q+@W?ARpz%sHkXFPPoBn6F(2IE!#J@e>Hs!bb*z0kl(Hn9m&@0Wr72 zqlZr9=5tu&zGEgd%%+aZ6oq0Q$%&1Zc-n0wC4>FTKWn2=dah!Oj{kI9)TPdE*Qf42 z(v^RB6pIhD0>-3T_Z8?(t`hQIwO^ixt5^QA=?4w|h5E4%?8Um%U?1o)sBZCWM+P5v zJu3nTynOT}Vi3mvO_&NTC=gU?s^nDQ@cUpnO`CjIFCj~uPD zrSKiESTrW^N=HZVCs+U=i)FbC%Dt z$$ZwP3FzXbDqNB4GHwfgp@;JPhTi+i7~g;!(YU4Pg2w>~?5TrnDlMmZiZNBtr? z51(T^j1R^$fHH7c7jf>Cu9o1`f!ATRHQ}v26i_xJ2AarN%8UV7+SpX4q-Z2?dKRll zaG%x3hx&7rFthklhprnpo?TPw^PyAUMio!xw14TRMOSzqLe-aIGDt&LUj-oZ^=p4N z9%|giDV*iTZh1=kL_cT_B8eyCI~q9D^Em9ms#Jx(M;`au&}R*JT$`?JJe30n$k_Lu zzXlacZKI|R*GToNA!79KIn&_U4hSSz7;$aDy?zprQNN#2nLp}Z8=w*mFy3(Tuf~i1 z?e(Xgb?Lg*{`C5@582kF&(Yqed2 zhfe(3cRMN6JtILHxGg&FJw6wlI15d~vp$7(p4zX%_xk9n&mwkTmfyp%YdDk0e%J@k zH=ca&9yiBz{V(;Xu8|GEkOaoP(PVOj4{y&th!BuUA^XSm(mqsQv;3gW{TLR?hYdKE z4@Rm!YI9in%fIQ_&^4H^Kd~o)Q>d z<%Um>L7w;AAo15BZvZ{#EAxR)@feFS2eBJZ!5gou_6_<*iB*KYcV^#*#k;1w-*O}B z7DrgXqTDJfcu5fR8zyS5ygpVuKLm_7;#xdoe>g~6?ZtzR**7?hP;bw0^6!u9+Sd8| z4mz7iuwfe+$=A5A3DG!R#V60B6FE3BV>^IQ_{_jK$5j*aRlCklJe1@y8Wc?ahjXktgOX>ca=lMcfCS2jp(|?lS}()!rMu>P5Ag4~=9o8A)QT{gn@f z2pPdszenx)nBDo?p56Rf>9e5-(OU2}N4gf7^8!{J?i*vu`tN#c`H&OetSIX|J{|a8 z!QcDpObp>$TKoe? z@VOB?wv+z`LgVR6d{&|HWaL2}a|EOr4+A6m&FOsTj$h*s3m!VHH|R=WSx1!u=JNqk zZOXyL5Z;ZzhZXqWHohvHg=h;lXNJ82zqRFhc*}Ves@{k1WRzc%=osR3YD3{lhu5m!Nte(Uybp<_>ivhed||(w%}cCfP3E%U?2vQ zF~V7(>+~bstI3w2SAKEt-^hwqt;5^IBS6P5{LZ^|e8c+`-WlG*^Z11iX)Y0?o7Tb( zy6q3YupvM9BOSa9G5+Y!@ipVryo0>x;8K?B&$E*M>^SseKdKeSy*kfw?;Kyp&lo1o zC*wniUr5B*Q?5or)(H*SquU-1T5)S~;;uLu?y<-4@nHnN7S4pzi79g1v$CfOGDd$U z!gCxxc}A6IqA?lHQ~K3}^-!1*Lt=cui5U+Z;gLWwN3yVAeeQw3nT)`3{PtgeT(t-h zFk(nd1@H$}8-(u4f1E(Jc|B;8_Y|Hf>>E0Kyo$$q#v$kr3TXw7@j7DW9QRl?Z_Rh( zj&kPZ+|u*xk@N5sj*tW~*IvI$mqI)yg`+cAHa$`4@0d`I@{EoXdrjW=^cQFj6&py5 z@ZAE*3zwLUf4qYnUyoN1F*s;!Q)0?tOD8MLQ zDT$AK0pwMP$&tX}1LN+sM{PqT5a)F~z6z-~9xkD4xYu}pB2M0Ln#IP;G#%rNM?et_ zCq4ql1wddth&a75;*nHN!HP1c3NhvrpWt{If&OEPB~U&lkHmq^O&gQgTsBbiGNRz8 zM~Z*Mk0(g)OuC#NoQt*nY&E%ZDq?q2URZYGjg=yLgZh+$&xeZ3kq7ZS029SWltdrH zGw1t8+=qr9chA=mx+le7n%q1JzgV*d&IV(aYf ze>kr5cRb)MdTb-L&S`#;@P`xLW@$zDO!f6Zipw0A>J>Zk%3-ssU~T@{{DJmb-ulo5 zFTCR0dDtuYF7+J~JSurB#Jg8S_sq$Cfndc#bb(DLUg3)O{m^RgDc)_3R%Pr3OfqSp za#kMJ;}I2EiwV8t77r4C70{Z3FmQPIQ0BlL$QT(}Mr;_J*D*dIslaDPajP+s>v|9| z1})~3x6Hv~oCU_qJUfFN_XOvN`z3fE_{s9!#4IriHIin;na@_>>zRbg*)+bIc*I z7S@&v$GZ0jevI8fq8_i@Pt4c*gtd9w*n-mnF-_ui?s6$kVOnGKFh4+W8u1wX<%lo~ zu7C7gTVRge%5Dv;Tx~dmpaYv#$4lh({x=pian6Fy2I?9oX~8^Jit1OwWa<}abqgOo zhQxFXeq0SD-kfAX*_R)&o80g|tjQpLxh@JE8*h~eeSe7YIYt*Agr@-O;AXtfuFoi&Z_@kp2c&>*H%lC*U1U%hK03}q{g~QXCp#VAr9^ZH} zLJ0BQ5gi2?(UT|MN7%54o0XZDN56#lw7zC+oEsg)8$f4N@c5>?;k!?tkH>s(n;16t zo<7)+sPVphUk5P;^hx|`T=gH-x_9b(t-Bu)6`QyF^p|7FR9rNCcGaT@CKVrXK7!$c z??nsE(pm=#FP_u=+Vge@ug@Mx76Pa2i!Py!arfa_w-7!!#m_n*B+cmVz5x}pZAzQY z?Qe0f7ls*KLmK|5t_;<9`LH0>D_`ub9CX>B6EB4dN1pjXEr2{Fx%NDt{7m6p z_UjMt<^zr|KIaL9Q6t8{7=bG;K<5ELjqrdy)_!2jxD$!UVn@un*dNvh4&Qx<$!pa0 zFXXnqXaEzM!33N$i3j<&M~oI+a5pWEiArOdrSoFD=uj*feQjtd4NYFB({M#xkO~$ z0UX}0a`rvu49UmWg7k60z8T?3kATE?jCf|ZdvsnN@@M7L$18tj(^=C-m@nXeyO%FgfPO|Gl# zKHRs?|J67au`X!N7hsuA9(y%pOrgxTRe~iynFHURDTp5|7K^5w$OG962#=t}Jlw&$ zQe=;xEA6J+ah@?;e>N^zJ2!gDa{K9Umd8iW-vU$>t>5D+LjG3MH=tr*mtn^mI|gyZ z9)Bn6I3lx|QGq|+Y@a39$~6b-MP#!|=Y+JMN${Hu`F#f>O2thFe(DxvnpcH&0Hi=$ zznvkb@G~QFaq#DSyv#X?rPc?gslGpV-TiFU8Cm@Cop)&r3(TEH9z1xCkvqn>iHC9p z)+Y#SqlsGumect}R^OEIk9lMn*CrE7HeZtA z)ckN6Kb7DS5QcDPT0u?Hb|+0Ed%5ytXo5Zb&(=lcZicrf@9y_@bR3RSdU=n#01Jdc z2AtT8fko3KGrZ&adX0AM8@?J{K%YBKL{{>ea1*j$@LuWg5pS#u4__MG`?VJ%CszaG zT|>HZg0If=8KZ{nEb`#GqySDRTs+oC0q1*s^@VsdLU-rKDT?0vb2Fe_&L62 zoSGFWJvJ2fhd(&HvLGbAIcbp3Lw5*dt{79dD)~9>w@ni<=n@49lyzR<2I;S5yUIsH`m>EkNc5r5n;71XU$8- ztNQ~Vbnd5^bs$ipCaVD)VVdkJ9uRo%*(VVN9#cGfsjgeu_vnZ*aAV4o=PCB?y6}_! zFa-GGm<7BHC^OKBt zZLHLYDdO>@ksQIyFC|9dg z{ZUnhB<9hCHS)*|S>!Yyx{zW+4lLp^*M0;ZKgH)7&%ZcNY`hGPo+-tTRQw>%XpKFC z;$(u{$M8b$z896#kVDt!ivsEbbDg2HPX~@{j(YMQ`$4%F@uW46*WFunpPEwyF~wfR zhpvQdKY#cxat+AmtGsizL9-K zo;@LpzvJW|j?rs8%;>q6s5Me7#Or+O1{UKsA3@v%6OWhYKQLD7Rk7pe3io_3qdj0y zDKpz1k%HRdoH>5G$qRMV8TV&Jt_%JbvhJn~q*Qgf~NQc)Io^ z7)6lv^NI8ELFM_xL18ldbpHboihGC;rHoUdR8ga#{FVSR)nwtFApbeE;R0j>)6%5eCRt))XG- z-}H4_wZS@+W})S;?}dv%%sQnrzY4Dd-Cqeuf=VA%TI2F9AGf1S@mQ~(p>-a7-`FDM9aECosnK2;;c7Enz%`n~_Pv@z0JEz^^W6!D& z_bb?K=-S_ZorfJbF(W?gocneDGIJveaV7ll6W?@>#^t#xwvy1tvg5mJt2}Bo_fGE-y;h2=y_U#k$K*}K)FX6$A`7^dq)3x zq!_G?k;CSNDTOhk3P3<80*McdyB5cr!OrF_E7&L(#xk9ZqV)b$6{IslJ z=`ws+aPe77rzhPq20G_V#yFV+oX_1DgAw@pAfBQW&*{vMc#5l?IgghjbABrQ@d5Lt z_+~7Ojz%`XRaD(b9}L{{E~iJW&8V!6JR>yp z#Op|o6`ft3$g023afI;^AGqaVA&wd?|9Ji589VKkx4)?aA8z=RafdyN6d$G@Jhe;t zYzVJt1kZK;On1pmyYh;M;aua0=U8RdIhB|4JUZroqG90k?prL>syzW-8=xEXW_-Ws znlCo0je}M=cH|i;*lXR_gKkGXa#p)LbH<)VKJgLEH31F<7v0Skil_E?Cq6B|2vIp7 z%5BfcKOCpocv(%?^+nOht;Ly7!y-(q5^Emc6%39g&Ie4jNnwjix^L083ABKjN!xyNDk0( zr$D`q?+JnxrC>9kfbm_Pb)zBnWR=$`oT_@Axol_S5J zu!8q|SEo7WFvwPglZq{QMvTZ4v%Lq7kc7t=fHSf_Vt9=BF*;6sj3J1BH{(Mvf!oc5 zi?;zTw^~pCZyGDptsugTc%jZId*f>m?gEb~08SysJr4@oN6r%XxmTHy&u-uUF~~CD zml!2nXnN}E2_XRCO(5cn2|m!?^Fsxjn5Pd99rjBPF%ul`OO=Ply?3skem^?4^GB8M z@O4aUL$og?CeOP4KVGf#Va3oHJVMsvl}_e!1U-Jw*$=ZciIv^MN>`pez)N|6TAT0^ z_#VCA@%v1*@O1J)Kj>P&HY%%Vp%qfQblR_qc{Xv6L1)CBvfv*d-*tdTWhxPqFB}*e z>B6->;hEsGzF;QhLx?>ie!MC5zNG>k?cB$9Ob+o~@i3zcT3NgP($LtgOyzrhsbqb6 z_Lkuz8%n4q_g({EoLL2Lf;2cWRl$iFiwFG$O;05Drft%}_3lgj<>#&!(sR5zQlXk0 z{0ol-Eh|k(hBI_Kvf#I~)a!oas^Y4Suj@o;$v$gg;xvYEyH}h%B=8@k%&_Y2#64;| zw$P2x&6ku3J}6?`4&q- z>#kao3;D|Y{`0T=R?)|xsdYU5MuS4?}Cl;Ta&#PbYPinJz&vHSP5D&Y}8@MqeFp+ik5lckT8#m76 zc}~s9FDq5p)?`~ZYp@S`;0>~^gHNW(l3vzrnehwjmva3J?3Jwl)$NXta*ay9t?xfF zgYj?sL|OIbx3wD0U)PI^BF%4<^+y~oa_$N8ci4HVBC}R{Sl?A2053yPbuCVsLiKAL zH*v9u8!E%W+u(<|o$@qyy(*utt-DnnioK&;tA0e*6#5!#p#0mK2DmgU?oh@Il>knr z`nU3zGjRhs_h=yPUFDs970-yw#K?WmW+PWq;k4_%t{buW9YW3_UveloQwlqLPOVoCf%>E#g+_wL$}$qpDP&sQ6Yr=At(q zs)5?Tp(ai|ttuJ#aZ&llLa%h^SsHLNo~pRSs(S8jXCxD^+V}v{iHaYfw-o$e`9rL- zeOa?lzQpbG556w4HG%>Fxv>HO-|VmWf78<@6_FX+o99-94m^H)6&pOdUo+-oteDBl@kg5E~#Ys=R$Oxf zAV0%^RbV3CtXzA~JZFF^jZ?J^7XiB7_pbjR=l3yKd57w|Oq_~rG$^`e8ip_rME zK>E2DiQ=L^TiqZ%qfS}zsxo}hskucWu>DgSgl$Ux4Qgn~SHUe_0o9+qeP31a;rpAS z6JJH_igU1x+RlUhz~OK2s9o$r^&;0xCoFy~w)v;-YMj3bd<~p##GEapwcZ5#EpP?I z^4}neb1>r#eEsQxSYM|5M4G>@oBz*0wcy_b>)zO9>QwVXb_A;LZ_7_#(&*&*$-ug( z)pa4hV&ASSxBB08%oKT~WXHBKpp%O5?yfq-}$l_?`uu zxpsafdywsr_Ck+N5=O#j z9x~wg;{0muS&j4QopmZ=@f)eR?N42|0{ww`CA)Z3Sl68w@rM8DXHM2+o_=?D_h)qQ^qV#7$h^(6{8}*r8Lt;cKpH@(r@xdjM5PGxB`ryT>n5=Uq<4 z(W~*)v8aIIx7RAM`qsmGrFEYYwq@ydWWe**jg}(3&s1Aph2@&fcrMoL>vjG|#HJu_ zwCIzJLoEH;8damidT5c6cee&Ke)O z-_*U4=HHwnTyH+9l#6JAucPEd;Lq2d;D5%t)^ktpy4j|AVh%T7hAo2bOAP(v8M>Ey z@NC>e>QzW%bnbqQR>?gtGLX4%@j*m3k3PXjWE?OzCV;u&h;QwEuH|X=R^Sn*&67Hk zxpsdr-9vDvG55F*DF4;csgwMoS2c##>A${RXRZGB+}(%o&8d3sw9ZuhGxKxid*#ko zu8P%xi%&i20--aX9f{}}anVAnT|D@|Gaf`{NFy^cR^rMPf6ft!z!g~j)3qmg@mXdl z(3k(+Qv99S899i-%-Q_!+7Q0unJ1sQ@$Tz$PvVz$YmC3wpP7aJVwpn*HvHN2h=Wm7 zuEsMDAW&>6Q?mIPXYScRoK*Mth|EtowZa$wG?(kH;0Y&xDpQ^%6?T$^tNT*V!>PPD z{kcDTMy*F%;T3dvFR*>{gJ&i3XNMzl_fO^TsQD>^>fE=1FOfrY=10QuP1v>Qv8#0cf3mh@qenY#A%08e zdcoc2zQ|3uYlKiI{^bRK%ruLC)~YNI<-wAj8uj|yEAjM$;*0Ek?%2-@gEuRDTJ8OO zLXRAdeZk<%i=S=I5|I2$n;63E6wD@G{f65#W22l&THmcX%=MkIBffACCF)#MM*Mo# z_Q3Vh`PWMT@bpplDdVyDeNQO-7j2u#`=$M9<*s#iuUVzdJrY;GC9ePVd#>$?he*Ub zzsWbf>xVzQ`8xLnQtrf%e`9-N7qDrq>xHX~@~jIS7k2K$066>LuK)9(b4ry%CE;{h zJ6eLByC||FWPY+`#?CdU5*j1SmyfvT$!z`(xO;OVak4UYOuXXw+WKq{XkkUVy@Kmm zPT`geLeC>#Vf-bOw6?I%861QVcI8{krIjGU zPe5PDikin~9d_;vzdzC z?OW5THrHdtZ=AlqUYb@u0$Sqwfg5jc0UD|6wKIm7z)12FD=twxufWg#{$JsyXM*kF z^I7m)c5dfDX1O5p>A*XvbL@eXhu^MbCSpY@xi3{Y{G5MdPGMBo1N7IhS;(^|ojN1@)ENxBZ$IOSrq z07B$7Sm5v6yB1F=mR8LVzy9sp{LS-c-W(ln+=y+DT12&LsgWhrIs8-pNbKw%U+zWj z-fZy{pREo=J*e5t1@uKWV^y>#u3#6vUV47<6(dl6@!VqtB#s1tqjlrBew4?&&dQ{# z)Q^AM1O1=UK)h_%ynud-lx;VehD(uU-4Z?CUP3JIm6dd*U;ikCP<`=d8(mzpokazMv7V_!^)7tnF;N_gL8Pc4`f5 z^}j1j1a9nEaUI#jM?QDB``7){117tkz2ZTv`^0BcKhLBJGxPUVYo_Tn+Z+3vRF}D4 zNHrC0_FTBJZl%1Pwot>?|Jrs7w;#CS?Om>A2Bt=;PwA7@Xv6dl1AJO9uv(7q#;F%J zd}-yt2EEl?dy00hjgwzHYuV~!+%1$Q`FGTJFL)`l=!mgW-+UVo#VZY6Z}QV05j!;F zR>M}_JzKpmXib52|9a{63ib~UmvyKU7YjaH%crl<2zXA4qVw4rpZ-A55zlgO7Hg4p zzFRzm6j*aD)%^_3@@1$o-SiMC6KC24W$eMI+#hyZQN* z&&TI?TZ7fVfj4K~AA^Uj<&CfeU~uA~UzbND5Y7W`C04)Ux_@bsm49td1wIS5?0Ryj z3_CLIwc%E!mgl!iYu(z-?+!h9al050;~9uWB;-%y=LNBGN1)0ln++5|c2MGP-KxuHhmN+r-?FOs!d`tiYIG*f zs#=U1f9vDhA6ERW&V|!-NSKL2Nl!R@0Ke{0wD!G2vt$Prf3)9(xt>Z6I9D+abt=C%)LrCPf3p5YSJ7eCvn zv}3aAJyj#~0E`UZhgedzT=n5XpX(ecTWR7AKXYSM_oVu`-z{ax=uUVxI{>G6UgQAq z*^ocU&FT;%VlHJT&I)c=I4bye^b0SALMg9(nJ;mnS$&OU$q0D=%=mp#W%w-F`Z=fO z@2boS=Ow*L~KB75tVEcrNtqp24Ex`rD@(=i_~EE0E?(VldOET>!nOZV{C%m|Z(@N9=3HV__3x(#0^JIUu_^Pxmuud%jUV+EgqpqgA;`me9eCp zP4<{qjcXib&?03YYCiva3h;lj>MPFwlh~h0`)s2R@%#dkyLT_;0WJD8&A(4ZRh7Re z;?N4;pfR6$G7KK2hr0y34DAafCB)wN^I7BGD=>rpX#Y{A(bq3zeU=zVTt_Y3@gvap z?TUf*{af3Kb*SZOsy=ztQy%U&yQ;10rqcYYYAp<=Q1#(!UOX%pefZbazXw7kKNfAr zX)Qx@ZyIVML_XT%;SU|XeiSLmY=K6IM6EhMxKb?D`Wr)d`QI${P$4E<#ZK{#Yspo> zdJ(~wD!!{O?Abt9oFW;1wcqmezTx`4(ZPX4tOXza#<>XXLf7-P`qqycr{LcCsWov7 zd&@snco(Ol3YK{I(!w{t25(7%KijD~^y(RV5}cfo<&S5iSEusA0BD)5;^MBgHwpZT z|1bHYjrrQjXKU9m*6fit_w7wsD%y|NMXZzXNSdb^fBi z^x%ea8px4nQRGkldP!iqMXZ9=cPbMf0Edq#9-R0;T5{y4+=$e_eVh3&tLWbc^Sq(s zos{2IPECkssV$J1cZ+?>cf31F@6OUxz4s?B>O%y-zR}dZay0JMfAMMuzi=BC|2Llb zuyem`fWZP-zrf;yO`YHR*wK_~CM(v&kN#(!96wa&-y)8_Tz^IF8{dAUe_ZIpy!<2K zkieJztIoyG;fIFDn=q@=R=JdR@7W`I_|?*zGIKSsJECg*M}6g$W#gXP{_EuMnt&H) zjcMAUR$>_=2frY=(!W~#a|6FBc6^mn zhFCtEVh5^kZaOR-KD}uV8N>-3D{-`To~#qQ1v2xzfF}+$;-_pyYsA;rdGFnIsHd$3 zcHsea@6=}uehQuzC*c$R6dwgo^QppqR=KkJM+hs7pZ_;jHBo&!8MlyNYn-oQ;v(VG ze0Chb%EN!ODf*Zdtf=Y_++KLY1tiZ-0$Sp7x=-hF`to{7TyzAYL3WE*BH&+a)w&nE zd$Z-r+E3W%#eHPfu19ni2~smamWzMl4fgeV0xTMd{kC}KyuC5k`MXkkX}>r8UtB- z<-2UL8!1Z_Zrdk-D08pYPD72P^dA;$u|Mc``t{PN`49-5mb;czK?io+<<=e7HsS*L zccAmEnO3_5CGSzq8;K}T`dwT{66Yf7vUt(&!O z@!Ne70?t>@UoX`cUu=_X!a9=TkGyDB%|Ojhf36aIT2!dwdB!WC2-Wn>Cw2=Kf$#1- zJ40`(4&CYRS~WL2i4_A#f?BOUp-Mtj5Qk%H1LA1`6wbuQ1$5mfekH@Rs+vDfaLnb6R6KPXaTAGZRW$wD&0qa@ zf>7F2ZM*i7sJOh8k)SvCN@8T(q?uPRiKS4+Ew&CQ{AlUDqd}|Ay7qa*4HkrqhriE1 z61k5KOeMZ+b3xUP`3vOLvhs5B&XyG=X&%27n)@`6{KQ*?{XV{mIw%6K-GAZZKKD{* zsD5pcir=QxJ^IGijpzUV@R^8O`?HUi9e#$(f>(MR4q^R*ALj&!n<$^NQLp;iINRC5 z8S|G+=w`p6QT-|%dCy)S#&v05j$dGl9v%Bar@%b1YIyN64Im}XFBTjueSrgBIAGBG z{3Bz|ZNEYXzsO#$ooSiT9IZ8j_s&K&&7O9*Hf3+vny{%ST<}`B@Pl1dVUM@;A^)vYiv5i_S4#6Nvv} zKWl19g^3<3H}H%@4tv{zE7Z53iry*Ht2jm2i7YFClR$xgbnhDT>W><-EUD~3;tKTt z%J${=-xggq|Et?UTA<@_HIX&g*13UAAgi=Qs5G;#m+;r+TI>v_4($w7hP=3Toe4Vf z@EH$azBx#+h>w=qWAcx|SA`$7ec|*V{&tP*5ixs5XC4fQa^abs2x1Dmmw0yq*(i@iQ zjen(ToPTHktJhRp<@U$AX<9#2t&OIiizaYM3@!!N-^Y&u+UFecWW~PDr#PN!km7eM zThZco^jD8ebd}v_d{L++V4Q;m+LqG7=rt~1?R=&WCO%v7_xRf?RVDlz{ZgKXe*j$c zd(mBtPCg_u(6Kzk>Wj}ZV`Xn<9P&S4HNmzbZLL^cXUPDz8>7wJ`O6wV@z0A+TTaiQ zP4dXkcB+cJxI1m`+0=sB>?Tq;ZvMQkMPcKY{LKX}HEx(WFEk5=UE}i2%E8KQar3$8 zUkQ}LCO>@pv=u76bO8lw1LAkPvf8?_+Gn@19w(srC$)~dUYcz`oqxSd{Kf6b6#>~P zlN@f6D@Fc4Vtc2T(x-n1Iy?ETq8kUxW|&9(WGl}0I}^Fv0i>ULn?N8OvJ?si{3eu& zu>e4*`QQAiQhjQw^5ORP(K4s&N1zd{iETF2v&dJ%rf?77r{FzZJSbt;p6_hcc;<_M zOP!YwH;YhrWv7(+!_Xqx#}8XsW~p{BLf+0A?Pq zSn_bv_UqsGiQ-GEA@KAqqSL+M&hT9hoiz_1ovQfBu6%n!3*dh<8DxAbvB;^~h62JST-AMYI@u5+Df7N4`@cAm^J&m0)V+=hT=wOpEdE?6)LQZab}!b=?;pZiyonH~wzVeK);H&t=-GEKc&%{z;ScZh z3SIMa&+eJLr7JPY<*k3GUF{6OyRIsk|8MM}@^=kiJk6M0g=CG_Jbzl7_|(%=Y%=#1 z{3p$w^&Fuk8@{j-k#;8P`~!adt@VLU4!>06rgbJW@J|av=%R_O>muG#E2XZR-){P5 z+raIAfieHc#WSMGw+-viCf?$MkaLc3&$XB0Zhn;8;wkWI(W*41_NT13fkIGY8i=ra zK0nF0(1EirB|K?#y3JiFMJGsk?aa)l)o_t+#bxA+ z+>h=-Mfh7&9e07?`dPYy_`0cn#Pivk0wV!Vw#tZCoPU3FKUI*FzFBycE_@4d*R@!a zZ0nWU*Vjut6Y?f#Sh-xc#rbE*nov>xpxu{{Q!hfZ59~E9Fza2Df zRKdNAK-NQsh)@OHbU83WSa|%B-rW=7wLq7g`$BJ2q9S_o#q~#zpC_jRC!n5zD+3(W zbMIEtFKc=@)fXRD*6gw#TKY3993Fi7xwz}+8%}*dZvq0<5^D<6LIJW%}APUT;j&-gm=GfFg%FIpt*%jqAhP9CAJ_CGb}e@f@NQ(_3$ z*a&9Z;sNk@)rou0Jtv-e7Mq1Eo1ZvwxJ(=tC;Wjgj7Iga0SUJVeC|`klOB2dlJbAP zXB5^jpYo5kHmWbO%8;$`^as#yoby}-Z!05Cce6vdo~sRZ3}j=0;2#P11o(0O08|{G zrCR;$>Nz1=%g^_=mVr{_S9{^D>S>@`zSM}i1 z5+E#58?HD$n>>qR>D6%fTlo@K#y**I#s`6~O!dWgtAk)53L~T+3v+oW^5N<5Us|** z7rg3MoPC)eWCwA^`S(WX(mQ@01Vl#==hyu0{;eS3cUGx-wyDni&=cR)N2Z>sY&C?E ze-ajdd+k$j@8}+R4ZPEe=h|yintF|!KR@ycRA3e0M}hyt$2<8057oT3h924eDzrjd zLfohNdLb5xzQj*n1+4xZ%v%v{UE@RA-@47eLm9a0FC!3+-txK9hrHNbpe5VlvK<%e z#tmLu_MeWwR`w(X0TU2}Uk#mg4@OL;Ryg^m8!=nfS_m{_th@ldsLB_hW3LzS;BNaw z`)C)!pbv!36(e75oeL ziF0c_f0+cC)dH61AqMw_Hb10qI7+Lfdau1AeqaB)rdej~p_%7Hi2&62+5b<>`Jd33 zlZ{o;rej0MPN>TKUck-{&JBY`1e$YETQ7dXpY}uKfW|>8K5dCBd(l-;i0hkgJ56K z(}wP#&!_gt`&gA|$0!5Z%66{IWYuiaep{GH^|e@2;Z-9(+xedNY5E(q^)k{U-)*<9#Jdhg&*vK zY{i?~{(NdgexW9(Yg~!-p2>C5RpScJpZA}6m7m@E5e=^UCeKAO z3;rp7X*fqc{i2^D)7*Kcp829iStaDerXMTwt=F!B0g1AN04iS|o{sotE3o=^LX%q& z*2CU+YJPhf8V#eJz7BF;+Qsj<@U!5(m3t#=4o|TZ$iRyJS0V&b#~d>NIc&xw zT>V5J6N&h3u}}PouXX_qc6J0GZnZxeW72jh9RrJGd@N%oS@JpD}E{mS2o0N zqS4a^9$Y+S1j2eomGBFf;tS{#q|!BPztRtK-5;6}H;3!*%gk0Lc;aNqd2efa>stOd zs|r?BJA}U;z2wEh9+8(nl$o#rv}ZFuR`#UGZSCQ)ulI{EqB*u}AHd8bgw?1pU3l@* zIhZsVar;u|;rqabM`!XyhMn=sDEuiKXtxrLH&ek1S{)D$~D5woyYM+a|~oVyi&LV{tDhdszv> z`~~uH34iDFDpedyezrd{p5ya#&p_V(4hFf|5YB%Gx)vDz_1YCKTRZWILVOi(Yl4}N zKz3dU#mB#eYmoRo&a2tG4>b#%@`EW~BG!uIvv?T+~^;-PL%t*FGOYX0G0S%8bMJ&fd# zn_Xk;I&*7qU9TeF)_A8Wp7}d#M5t_gg%EPNHxcK1!&KEnYx-87tS&xbe$CM=gZv<4 zmia&)WV3Hlb#JotSmYVni2pAF+1yq9uvZ{89sZbSK21!K`_uVEmU{>=Y?Yts$(^yc zE2X2u5^}ofzUs=!j~<;&0nC$<>+ao+q!BV ziD#$-5OY(^?F>nk*-;vL%4fdtCUPV``!)MvuE!L9bi=LK+U%N7PA+=Z^Tj#2XZO2v zmbfE5i{Je9LO!9R2hZKAj#^3S+0C%G7JvyhE`LUXQWeopY{>EzH z)>iwou2+y;ixFpApgla=ebVlc+jT{SbcS7R0jbn6(L{)h6EJ8(}5ab<y|i_XJTpmCK?jj2?$nVu@KX2J z1zo)AkGSZmx@%8G-^nXP`z2p_-cEfe%lzxbru>@u4E(HTJlsUbBIk|syzN&I^SFm# z`X^y=_m7Rw__sxN8o=s**s!+D>~_dWU(&{v0WL9MQierFDqm%J0Kln3*hXTFZX>wd{kJDB$C z^emtF)2eoG!hMNv7c_JYRLg`f3}znl%c~B@KiSD!KZ>5v-{RB`)lcaS9zgh77QW=U zGd&`?S5+tciN6Erx#wnfFn&HuuDyZ;)1MW4s<`0!=so2fiX@B$c$SO&X@5@h;nSOC zM#R_T8IKhL0f6f3KR-Yr1_8c=$)Tqo3vhJTb@~HXaeR8G{gSVE@$9P>ygmFE_OeWk zwy69R4%CTv6M1$}{nM7yANX2Yp4!ZhjJ?YD@c<;R`h-~b4F9%Qf{RVzCDxZUQT>WP z`E$Y*K~1*$E4HmOAM@0EO+JAH6bS$S7HBj`kzPQHtSlOaN^te8%|$`&QtJ^uNNR;Uvv{! z7kCQGi}Q!Ab2iaMLO@T!b@E0jK%go6?ttOWXLTRL+CxTGfw*?PL?g@{C$-9 zJu7m*@WSejO!dL>`M=qw+Wx4*supToX+^~OhN@@D9^nTMU6>_TuYJwq!?QbXM8tpI zGlgExmD{PI4N2;X=T9kpF5&A?IPj zH83K)+Gh3b)6^L&xT?d?%Y$f+6A`Z36toC|)Df@5PyXQ4J@Q4mGF4Dge#S>8_o}vt zz)qsbu%~qav`u~BJ&v*wmuVt=3 z{heb$^$oqFlhPKTJ$SueqK7Emj8*zFe`><&jk3<)p%Fprd z(@g}e8TtF_uEoik_p@4j%b`m!f=FLWS2$I#0d zdh*3F|6>1D!-Z(ILyM#otG)2eIp&$55hsFloxKU^!Q=;IGUjTe5NmvDe_vJZL9@Zu zR9{EHP2T_=H#B>=a+Y#^&MV*l@2VDD?nnG3PJgy6JTonn9-2O|2!=TSjcuH!vvSw< zgNF20UgFNlc_X}TXAgCncc1!X62D)bwSqSG>MbIRv7=-@+t8_p7wMDi)mhPCIpKrSjI+|%eJ3f2dC{(=U=wv`cEzd9_>2UV9eiDQ)uNs{lG27JDj$? ze$=Sq#KiOZ3s3<4vFfY=F{<7amFGV54DWvyipAqKoiyC z)i@rVn&14_z#Fr%+86s)ZvMmWW4C?br<^Tai(GepxjLP2y>uLvIO^+lBz)^A1Y0cg zYqbh~>(gMHcw4tqFbXaIH&$;&;j+^naL@=V30*CA#Z?_Vcz$ja0q?jD7au{K&u+pJ z>{kHbqaFJ(kC}EK!B?>6_v#~(;~=mIe1*jM%|6A}{Oh%11lhL&4d4C@oq7hUdiE{B zM%n!rg)jRlIR+QQ5UX8mXBS4*cf_F4|Vy}{6!u_mMEOw$HKi<>ERA`h1LTJSSY z@2mZtF(gmC63;nGBc}~O_u&;SbcE5R$MXq!M(gT3qB8!`&kI~Cb5HvdW%K~l^%adiRoVgrQIQ)t~#Vc<5KQguk3P0zc6`ogk1fj%-&x2xX+}3^~HA^ zO8M~L5zmp*!TW*_az(2kNJFtT?o5dnnfW{dAjH=A+iQ+j3#JdCP$ph3@CV$p#o3?= z#V5YSBj9t7Uv}A`{A`R!h>iEl<==tUShM~Hps`yki^^$3bqvbK&5oM>@&mtw&dXM_{zN2tGkM2+(HK{=vEThlXogM?A&h=07Gr=7j%ciix#X=9-zmt>`W6gS5WQl`s++7+P@d5B#N4 zU!u=AAsKcezOQg-RYuFhEV&iFt{arU4zfzbx|<8@Eu-(l73}fr#dB!N^h~ysWumRf zW|P%VVj_N7G__jsz*iXj0L$=p05a1TV`~&9-C^ZioK{8V=n^%^4&{q3KyY zXf2`Io;_M1efdhQf!xgPGjA0?DZzFA1~|B_x>E6X~~J>6ti#8{7a zrhI?8Q*PecR<5r_)A6;iUHbkz?JM5TQWSiB6jtF|^?z}iXzy|n8HJ*~_5a3d!Ms|M z7+^QhJ$m2xhOGIVJ2F)#H?R_`4_I$PxLIg4e2S0k!|Tt4uHhj+=c1#;{eoySQ4z?o zVxOiTfJ%JwQ#5MZGp+pDJ{5Z}_I;0rR<@su5jV!%lc=46=&$hRn^fS95$=2}B5r_0 zeqP|wffncAtt=Ls-&h0gFEeu7h^JqK=-9WsD`()|$XBlv*f#d7z)t}EBWc^b;=bJ0 z{gKBz`TO~r>Dad)>FZO}5IOwNEGv^@zWeNe&^Thd!J6}pt$Ri>DobakK~8)+_%wz^WZx{?dwqe zDmNPNzE^qQ{i^((aYN+%Ip{5&_jckU9e!3Xd2 zo2hEwaHqVoRWE#dWF|7;=;FIyBal^rUo3c)NmwMl;kJAP=z2)z29Lx>%ewc)zE5}N z-E&g%L^1EFXJ`9N4W2k=*ilrnIC-RsPiZ8%PNV% z7r9PH~T!yS0-QU@$M-r`74 z>lW+WC7B2B)a~2&TKFwz;!NS%%TVQOH?Msp(hop>SdY9fH*}n#^dA>n^+(iiL7M$S z{6sawoNZ{#*@qmTxIL>rT+PG%X8%6da~*mu`+2YG9Q@Sn^yS$ba*5*yK{XxlXCHee zYkgf*ZuP$k+yR|_y|gCR_kdM4)|oDT=k*~+!r%1Y3%!}%PAN`7od3qsVEcd<@j+{a zG--YPj`BKLh)xgI(S)5AT1N~gqk$4 zuy$lV!v|74V5((#y7I%;D$l4*q|&cD*!F^K8F~zLJ{ixKPqab6dXq*P>&um+oIL?F_7E3$NjAL0H|A=3^AX%=9X7!mmypDUbZw;Qq&Pks)* zs$FYkDo?z?r&|HlpU6+Exli{7PHK15r5ph|YPomh#W$-^)3Kh54gM}bNVizx{AW8+ zZ8g1m?Ws>h=m`LnaK5=HUmw)s)zYJ%o11~kc#Iy0SIKa&1Gk!h%X!hexX9F|(&`o`&zt_nQ-cwd&Hc)iLS8Q7s7}tFRp8F)VJL+_Dp5nngH}Kr(Rp|?QyCGk6$ zC%f}Tq~iGZY;LWIbF>W90)3{ddT(G=Eo-hu`Kea}M_@93TMT`wE4NmbgROZ77|vy_ zdsKP-gKHPPUV3sxtZT|o_ATy-dx{NB%I27ydW zKiSHmS>vBHYVP7TdXs8@$~yn%eJyd}JR@-Y$Gqmewazzx6sY&`H`a6B5Np$rPq8Z} zY)ca^ArKZW(&^rOyJ4!LvAC+FPUOV*1}kzA`*rCMJJdE~7QZVh-J*$!Agy3{KA^A! zlO6#(W#{0krBnHL+~@2adwE2A;^N-l00Vx39R8ZG<*I)#kmbQ{<*%%G0%r$(Rrjd+ zm3Q{9O1*e=(B^B!n_HV$W7ZwB5GrRq6Kif=KQ#9=Pjr}VRe#D5hZvbB&E(vn%j~oQ z={_+RUV&wkSs)f2*IpVCXWL@AUtcr+@RYGd?QUwyK{8$eT+H zlDLRZKPEcu+by1~KW+p5-{_M)S3@b9WFozxN_>7O4dO<)4S#MrVYxVCf66@T)iz01 zbj9Vo&P1GrJoq-J{79B<8bod?V;dzVj%DfK_k-aA?^$MycY%#oH`l4yg1*!z}dGNmp@yI zXT)O$O=E1i{&U_?N!;+ss3DD)Z^sdsA1ntW@gY$4P9I2q-=|g}s?`~z_IAcKr>W}W zRU5dRz3}wznzJ*07d)458QI2B2!G9qeX{Td2wV3H>?EZPZ@lRb0=n>5nLIoA6z}jnPT-TxE&_Bf=1r?AU_rAZ?(a@S zsfaJP0U-rY(q9AD+C@3>YMhQeO8*E<#HWSBuy%LC!>CyO`N?}I$kW@NB>+{h&DdiS z6n?S9asEY%#?W*MMA=&5-oeF+fcWsQD}b1ZYy7ruak*XuAZ#ZFFhz9p*4_$SBsFUM zvz~JVRo}?|*Le>>R#7GbGcHjHDugYtk}Ynkm-rj^#QolY$62c~RK}|>pz^Fs$=6-R zBfyvco7KPhSn;>26;CJl<@&d%{>{m5@h)-QKfT2D>bpf>P%e z|6&`>p7eF#AJ$pXqG8}J>l~>F#&6K}BhF_>b|UJ%JoCS-RGYf%a`ir=IzPSRr0@0m;E`3-WilNX`ln^BlGBcH8t{w#Q3edketWW_H% zp(btJ((O<4tp(Z{YS(%_OMQLC8n*r54e#sgyn6`j6d5f-{70Uhfwo!;xuy~R!Bv^3*pKQZ69**^@C3e@@zChp8XHO|b zm15=jCnY!a>9FDF25*5-_MoYSG*aW652yeOD9?Ykbsj!673!=w+#F{h>`&rikv<^; zn@!|(Rsy_)Hu%TDZ$#)*i)NG8o)+-=&UwZW0VHZ%UK~z5Er7rg2v971)hV!pTUm8~ z=KP~h?iXT|_a)ywn&m1XZ>~gqS(pz@Vg9f7uJ=u?wmZieOkYAWUfN0hSX90Fc7k~C zPxN<7M+f{Ll{ziBA!Syx@F~~$=^v}tnRiq?I@9Vs88h`ytE#6?h;l28ui@ALaQ95^ zOW+e8@?_@xsc@U>IeLSXhUKz?z>oG&nXI(=Lf!X!bFq1^z|jf6?vG|m8QUv;`4Aa; zxhM_1)j?C1uWF6Uiw7>xNAdy?57P-BD>4bEBajw;Fnl4|p3RAWS~QK>K|ZH6A;l+A zB=WM0^-Kxcihbh zr6%0s3beD-$Q}Dc2;M7`qJjRK-n=u0Ox?Tsa54LwDR|l`y$gG|xs3`eTr*%niSz$r zX};HLUpN{I!xvF|U~l-ks&RQ=VCI1sJ?(i)pX~4RJgd=>HEsCKEs)vo!0?B6ouH5y z@#};IKjR^trsQV_V>SI`{KMiO)$B!+S=X1_m9H1!9^hQ@d|U0nhP#q2AO8QeZ@#f! z2Yd)cT1JtT_3OH6EzNW~eOlowC(YM<#v@S=K*$NF`c2-GNVYzy#2>e8CT}Z^-GaY4 znm!$7CF}_+zE-0pMzdp=`GZS}CCMjKcSIh3;>PN8zOX8|`ow$udg=DksD~epRfer+ zX$ZyZoKX2HBhJp<9FcmOU;JU z`0bxWZ;JA@1#!=E$UR<}C;vaRFWEro*#H;*TbV6S@ZPs5I3KFUec+t?1^J6TRSAun zu6}JwX?w#z*l%*qlo{cbWe@Th#?Qdpg@W*|{m(Z)f#2*_zR=BOpQPXJ3%q-6KKy6q zwyF&&w8bTE>_0e!haQiRD&OMgeY)$?qfa;E|9briDnOX-%ll@~lT5AK=jgn`H}K_X zY2Txf5l>(UWUB-SOVpV*ToA-HPA9FzS70)p7x*K4_vXnLXR~?)`1!d9P*DK*;)9L9 z?7KYt*thZp`eJv1>jjWP{xe_#Vd1vIwWGp&rw>y zta#x`r(OnP;m~fIT!G*G+Q7hzVpqS#&n?Sff3d{T_-1Rfiovj7q(61}m0AGvO=}eQ zpwSwuwUbYF{PvXUrB;aj7e`Mb9e5==K1jI4f3^e^WX0bJ&ON#Fd8rZsakF~{yWaDm z703X>r{2efDjz{VCV$(PRXl&=96>Xhoi$_i#aGR-zV~#-?aV!5&+eK#?px%*C*SBG z5i_s&@ULXYxleqZnDxnIWD8^_7Ml19ti;R5q45BD9inv+x?IfaFS(>!?pk;EddbR> zpZDH^Ym$+_mi>o|&zGOdduC$Zu2f;0W?O&IUZJgw1K~ikEw}totn-pvpx1JF?na{G z=-qkElc|gy`8`s!%&LYGVC*KmB){R6z{Fd{!K9-pV7B@mvIxpF*Kj4_D?b z4wjyV4zN=gjZDSW{i*oMt9iZ?v)z~gRY)W9d?&A+XnW>HOw8Lw6~FtuQ){Z2sm{o+ zI((zVvnpM>^9?5>V?jV;`~o}Q#YaL>fKy0XeE2mBB@R9Ph|~lkTVTRA>UjBQp`<)7 zY6JXd^f{^^1pl+1V`~m<)|Xj6TX}qWdjI38Z^In4^f7+5=6W~hN!@(V?L(}U+ulqIs@eYC3`BL{SSaSH+bN-FKA`Z+?Tlae0rv>zWB5d3b5i6`D&FH z7`o_il>uWTp3mOY8wvk$s+0*l7#S9h@1X7RiTJ$IIECteX6Lit0Z4f0krV$`20k{R z!pX4md{#W={`5?#D%8W)boIe!6C+};>P0TN?{Wiyrre2F<9ugme;=lJumm(<9=<(y z^KbZ~iwEZ+T3}qTO5?-1_dHYh=053+e9KnFP(BeWoL}*D>%PGm;Ar0gl_yhuD)z-b z!Lx9DmnYx8Ghq>xT>ld0(y<-s&&7b+rzE~vm{#}0_f_&9oqDG@#A%P@v#Lc+7}iXu zX9Avv__;T=zy?glyT#yBsp|YwzQ+H@T+uHVZX4s2$oQBdzCdf|3U=iS1-PU-Mf|i2 zF2pHM@1u_79bBVI+)?#0gC#!EG8wT5VJ_TwlfI%okx^~TJ)4pHDg|?Ks&wb2rzUQXt(M|e=YjDIZxGAQX~HI^Do@i&FIHed_%jm9zL}*U-0H!fe0cVo#fYT$V6NG+e|m9 zbUqeL)VgT1b=Eb+dgba&d|b1N^X2_Ud+wdgU+jNsujrJ0s^QjRK+KhM_V%wmyK$}) z)qN-Iq8GlYOXVv}2%`GpZ){bse$i;9R){s&E&tFL{AK%c!O#74ERNf>*jqcgk5w@xLj+uQs@O_#;P(G>cmE zw@?0&v|piJDWq6CG!N>?h`d#p`X?RyM~lQZ(7GO^+6pOW%3RflJKIrf{z7m?mOg3|)dv@M z4pzywhO6=au;0qGGt=5>ug#A2D5MRxMc4I;ZI{{}^>}*;&A#`DJzj(w)DIm0RxyJw z7i+O~-RRACDu%a;LVx`YGzkZ56=`ep5d4;>x8bS~!5TjjBT;$qr?~t!iNXo!3o@cm z;`=^9494A?7skEfnF5y|tQAn01$>iN1ox{wi7O^QghWrW_1eRm(d_uz6<4kD`dRO! z%nXT*K)Koqlo#iJv+94t-SetovvXfY#Qx2E=tLmn9lYe_-`*1e4b17di}*& z^Y{|F+)W))`Fwnq&g5st;vemHW!~v&IBo#2M~-!H;Ze|&Z+PGTK=RnadQRx(bfjjkA*pj$SYI* z#7mrrBYr|**)Kl4IA4BmYarn8;PZ$I0P+d)<;_{|9r(lrvHJA31_%rQ4;}3s9~tM$ zy!d!@v_Iok!7yhMVo_ky$FBle2r(HS78N^ssx#Hu_*EFs`|-q$I;x4+(eqMCm`S0G zM_>~KWL4s`;snG$Tl2a0^_fDAI$Kr6J;!tI02;H#&)2>ZhaX>{ueSUxsJ~6i4uZ%J zHw)KJT#$?-gtY^=uwX4BKES7}ID3;$$49EE39r>`Jvd|YFH{Fk-UwWN54HT7%YyUT zXQ7sR!al_oom>(UxbteZ`i*sGxDBs(Y(o7YdfFS>@_8QSVl>$R3D);ptJ^;dY=!0x zdQa6#vgW&=4Y&8DR*2bYi>Gs~`#xNOW;>@PS8`kPujQ*Bc!iL}`Je6Flc7Uxo{49^ z+3n37c;IJ#DAL>19@`bnZZ_KQ0h-^L{c<(fRsM<9X6cuLpF`1$KrqU{rAc@dfQWaJUYlmLZSI>$u?qLx7b=R&sK{sIxB9~xZ=%Sf2wcJhLKZ}k6+`{AIMb; zCq)G|Q7QqDuk+!9CvN3kNzclUn@jB3n!~l9rAG1T4}SHj;%iy_W^1yF@sV<0B9VRo zsvh3a-U-c9^3%G?4XoctPYt-L)&1+G+4iTuUWL0t4_sG4BRxMLeJMVOvzWce2`Z4# zC)-4yc#1XdSVK{R5dZocXa}SURw2GZ3gG$mea$BiL4eO@JOVb-J1-wjA@&rHjRMo} z^gpk(2Q_a@k!OpSZRJ=IG1s>@n@#AsUOtQu>F4lCJiC6TMHGt4JWB&$ubkzIukrL< zYkS1su4QRt>sCdH>Wja%_$L0$tLW{Wa{Uh1@ObNROeFA zM2*X{@-DVGe)wS0)!G~HFX)vgjxRssBQnpi`DyP79yu0Ve)=O~e|))_^A02)Jw8h> zKL)z~2mqD9hvVlyMIwDdZ!A&RAug&EpRMuf4}`rDr*8H z0Q_e=&%n^7TC4D>!TFw(|ITwHkD)s9X3Qbw#L&h1;jP- z%6=>IEP^Ggbd8U^tlKju&%m}v-i&+VQtf+m>5%G$7GERP!e?BETOtvk7kHm_Dr(i7 z%s@rJjhT7zBDU?7d~VKCEptokaS5n8EB;9O7{v6?A5cV+uYThCV%Zgb5N}8161nN! zoQv3f%FSu(W^cgDfBUm)z2)pU53Oa&4$9erl76&X{hUK0cz&C*SN;_FPt0atX??19 z&#fbLb_C{cp5sL2jWGBtuSH5azxeKZqSt#LpS?QQUo=_;I{4M!yEx5Rid9Lo&A@9-Oyp#4Dm8*WIhemr(Xf2iZPhF~%l$K}|ya49ChZwCC_xPm1e>1^% zjILYtwr;j`0zynW<6B@L9pn?_=m7Z*!8FSYwlW0J44!OFgX-H48QJ4_3X%CzXyy0OLm1)#IEDqFAW*>VUNWnivgCoN1%1&N7oK41j^zOk#ux^CR&pS7V? z`T84ZVk8N$gjn&39}zwrzvA=C0j8}C3Gwi&VD)ozB-o-u{-t#zdALviSwOX|dOU2l z__NF&O=48=@XC|DQ{G2X{1pWnxbBI)s%LECV^ufHOn|I{70-uDiy#0f&X+$~xXQcx z6(vix`u6K|Wa;7Oy%UM~+r~uvaiLP}@K?F2Lr?r!&(U?+*@_mgcz(s#{o3+>4w&cN zeCINuQ{~8BM6>O)FQ8U{R1hj&4_HbjF*gfxB1Ar0cokUj16vxr!VVi?KJbKvUgpF1 zK_SOfm%wX_sA2wVf!~1SYdLehr`ft!WbwcwE)2%j^3|`nn&-o*Q{(cR z1w?eM&BsUF@R1OQduh?KeDJDIF7H8suyjiJgZqWCH2@!p+#e0de<=Bw!c%bgJd}#6 z`T2EiFzgdPJRMekvpHWN5eTt}^8EjxHOnk%HHz~7S`yy~M(5`(0LUtsz({z0${#Hv z!IOU!I3$KnRa2#s#1n4=0%)A=(N~4|gtm>_06lPhfo?4{R#+Xu|7!6yQvF}`E_{`$ z5;Z@3ABpw`p7EDzKzBy1c7Xk|tokF4GqT?3aoq6y_cms7fSue{ z=JmL>a%sLU{s=X ziVT1c<$k+b{e-_-DEbO~0BwyPaOZISjkD|D4s}!4rFb5Dr32u;@_&`}Z+lbw%`PIr zuefi%rVEbTL%Pm8{A+JI7U2>Ijf&5BDf!=Z4fK~PWv1q~KO`gFzo;&`p1xjs`g*DV z&T|)N$DNWa9mzwpabCRP+25SK0y*W>-pEi+j#^-{ZqqJ-+;~&(S;P^1u}DfXhk0Er6C~ z<~w0R#FJtn5%|!o+3G_NYbWeX`<)ubK&Wz$-c{0TAb%UxZq`n|faLKKN z=KuOLikMLGwY0L7<@meB_>N32iaYEv+BatGhqaF zR<)u$eDCWGpIQ})vWrFp0pM3#3;fD&cs<;AtSQgTecf46I&mF*z4Z9?!ZvU!b!WT~ zUqb&cmfGm;J~SY|{RY}auSDA@PZN$MEI<9Zl52L0)&OBQotrU50D@Tg)z2r~mPo}n z3;SSSW*0+u`ggS&-@1tTGLFDz;;x%s?oSZXPsAS;TA{jE(;o~xhr!<#;qRAz zFg&a(ACF%8xxmmVAE5f;C;LvPqN?w=L5`?Azh27R7j2cceUX*^yVXf4@U!4&9qLfz z_c;gB^*`-T-7k7+72FO=CKCE?QF+z>rc&{-6S*NH-{!I>tXV<%p0M`HnV9)|PqhOd zEa*&hR+^!Yt6%XUhDKZC29LbCgk4heh$lS~^5&CQPWg$GsTSfrqbfZ6MX#1AY4?Kp z%~h{|{r`Q=+M!QYT-nr%LNgBe&0?s)$E&`0SZVa_!omTD4=>Du+pvg(vjrZkX=>hk z+(i6kRjMl@%+|P484sYtEe^+)IQf|#fc-PiK)@H7?MTl2Ts4KSdj_r$Yc77dmLD20 z+OTwzKyVa3mJbzZqUNz*{F%2JmcA&x%G;jFhzJBS9trc+d#2*~^yt{v+#kRM2;6Ly zslI2*jdVFXZXmXJolBqEs)Xt*49`F9ll({9#M)o7*JAe~cW=&IS#ytMM)Ar|-vWH| z1!4$$Xr{^&PkRCs-NZjHoNa1Qb>FDMvEq2sAFFx^^Si|aqTX#K%9g_O-+``&t`Z9I zbV13WZ+<4$MV;pED~%y5&W^a>JnIm89!?RKu3pDQy;QsKh04ur$QIm~L!yRnKIa2} zsC;-G8dIq^cy(0ccM13wbub;=81hmG)ZIwWEl4qo(L+|OHzHU9J}rI%AB1S=i$CBs zT7BCvuDuf1u65wGOLw`sFQ6sg&q}{*n}FoHZbWe+-z>cDzu`yzj2?P9Tl~#B{O$NR zsaie&zQpLJ`r^-)=yUepqTjU}!o2I+7mqe{%eOu1xa+0kf|Pp~jaI+jcQyZhzE;&w z6zArJ$-v9Ss`)@EW%?dOEYC)l@a(;k;Zwfj#y&n&x@gIWwBq7VtZy;gf6=-$noLSu zi@vV$rR_*H-27$L2JC}O?wQ?d*ru~`*7d>=R+g>tb*`o! zIAQxi8C?qlp1ULrzS-nQruuQQuB;5ZNu2}Pd<=7(y+~ACmL7|AFPgvDpirqbKJ%+e zoAU1CvG?rVgW2{BOi{MRyTBFnY1PGsV`rhU+T!;D(oV(ca}ON(p)%1m4|j^o&$DwW zuC1-}-QpLs`Gn<~V2gGCTJ(JaYPn_IuwQ;$Yx|-g6=j>hu2)%DF} zoNj)+#9M@=*KzIT7AW9ncQQt?(_Hopo?2~D-aB1ht4PfKx|qn5G-Rs}2(S2tllW$< z&@;ZF))Lmkj{Bv(QfqPRrg&QSoMacfUb=t14Dma;5$ex|3&A`~mGFz-w6+2vFzU!s zjq1sg-+lw_fK4LmU08JD#)|NaC8`Iqr#@PH=vdOven7E z1}ZB3vH|rAsB}FNIa~@wB-@I_k(df)L7;^HOYYWwmLCgWX7kNUAAz$!87}dk$0zjK zZWFv_4&XCi-{xZ*r{=O?By!IH1E5lFaT4nSqB6uQ{;gm24m@SR*Wnu_@`rFfGT^cb zJ^XxY90<=G<&$?@5Ih2YOhjc3kY}mYytsjFOu7EX_1QmZu^?-MJ;P_bGT!=ifG<1+ z(O5KdnNvLHj{qHcIuSRYwCm3=aN)G_0<)aVjKG|2U{TCgFIMJ@QjPP$=-E5*V$n>n z@6jO07rGK5dACy zMR20W%HGU4fzsrZ%fg1pO6L4FSU*PIq&Uw#0(Yo8U(|hLM zJ=*u4^lnDB*9+)~;kW%8-N5qo2zjs6vgZu$drSR~nvL#yxBIxu80@k=A1#-4M>R@Y z$klZlq-kF9F5Ue2Y0&n=4sX8B_*~8GuPfErrWuWqmtvKud2tp#aNHcY#yihD9+9~} z%FWDuMSsWbkvsFjb7&o6;-DmJTwWaR5C@ZP_6sGUcFJc1i5VY}ss}f0?I6%Of1oRj z$Sr+W>qw-@^ZhuO-0`>7b`hDPN-#y62bxo_5MUj>`L@muiHxP5kNvA_S+ z64j?%=Yy;9xLD&_b#fq9ATsfj&3y`h`)<`baX-d2@GK zbtLfCn&7YeT8GYE*Bs`>n*PB1Lhg$?AD;Tx@$co)|ARI=d8rBi+?ah>_bJz^|6i5xqfze%C7jpQJURaW z&iC<=NIHB3GL9d1!p6!oJt<`z@dNoHHY1 zL7+AUCeQbIv+h&o-!Dkl=gW)_t=%8Z)-$#H(MuK3V&9W1nb%&T@JjgXDg;q|@z2&A zbzkZwqAt#_>iM}Rfb?BL<8S;eP%CYKTj}}lK-a^Ex9ODx-c9y$z-s)X|4gbaRKH3N zm1nJn-S~ZmE7}tmuT!gk5lLE0Q zZ~3Be%l*wUmo}-WRO7Dgeg12gI*{6R1d2fVS)c(z-MNzMw&hk4>l>&zx~(L(2J$Joywd)0T zsx9Xuf^&;T4|MZ^zmh+$&9GJ#`$+sD`K;$0@BomubeM4q+722>AAV~{OIt`zjD`4zc((w|JS$aH5+gZ* z6zM7^e~SaRWLq~|fp?#W+2rP)hz?Z6o_z79)qdy=Z$$bUP?-ssZEv|C;jWxNt*cP; zYivZc(z4dW-k-g4RGME^doBR^2#Eg2k+_mwsH#1-7tD&Se!%`qrdB)o|HuB;4|cmh zI<>v^Ca-vxWfqPdjzD*;;4=d<k;HqjO2%b)CBmsim>PJN$uAbrU)wr?NL*Y4`p*>SNH zj5t?cv;K&nt4B|NzC_^Bk^leMd$T3Uk|aIPD>Eyza;deed!}bF)6)PU6f9f-5#A72 zB;SMYNM4a5d;pLF;c`I%IfKSvK=iD=*PfYGMg3LvQ!~@!C&FX#i0nq4DtFW6*I!l5 z>>PJL9uWzC*x~IFt3L7teorS)Uzd(&vkv>3Z!9ZzW(J)OE*rt05`Ph<`?|i)%TiTB zYwg1=j_dp%IHr@3~ZS1N#M7}c$b3WkutQVGmW(DN0p z5}zs*@u>j~NBm9&m|GO{TGv=MJ-B}EXPUiiq}lk37f&Q8JR1uGo2BxnBXwASU#1rw zedSo|x5=LX?8I&IM+NM+;;a1;fxS-Xw)U_LyE;Q7if%)H^wxvJ!v_xzA3uA3_}=qp zhrjvsQ-6%O6%R{|xGzql7l;ZOf`Zb{8aEtsQn-Rg!aAojKV>wUDSZ({82~qdVZdB9 z7d||-b`1!OxUTbhT7~WOPdp>D*ym;Ffuyb;hfcYJug8HPB1amb-dewoOC20>z@Xn2 z0OI&q_?2M1J`)zeZF=kx&;B94^rRsjtkrI&XP(J3pEtfHjx(0{-)Y=rPtI-g5_;ZT zK{w|3U{U?MH*1~wYV9+w{<%l;?9pcYNJk$LuxVoev!Emf2p@X7Sx45TeuTUHcPcQey_@4j zfa+fCu;Jgc7Y?B|iy|%R*tZRkY9tQZUZOoaahHxHg6PtKdjwD;HQ!e$k)LYV=qwuc zUS(~8bnNO7wDvmQuP@GJPjVtJahYtfol~LsyUu%2PratDUle>s zLd+Mrye2Sn@;MiH9F&yySa*yT({aOfqI6?|6Z4z`qn4d!PoX#qhI$~UPV`Gg!J(K# zk>vw3%L>D;5!vlQ_?7_pWVeS7nRHyH2kN@d6ht=h%<^@4ZyHAp=k5ObRrb0XQ!gJk z_NL3MH;NDKgYyF$A3uC}c=GVk;lp>H9)9rb-NUC}y|PvL)T7AE2o>wbhqg=KU}s=2 zY4BZFni*&Lx_sw~V+LAdIBsghAyX8Al>PiCbQ-tbWy6LyRUAF*J%z3bpE9U+E*fUxTs z&nydtT{6Cil4YHFVZq?TCVhL%O!9Vp8*v^BRnPfn`TAKOEi-65+!+|nW}^IVesiH_ z1?rvQTG*;hh2kI{SJ$x@KZYFH$nZ7yPyy^E5KReO*_=$198)r{efN4=p$spWtRq`?!W~>E1V7uci9+)13)=14N6c7CVq$!&el7-k@X&v= zj+|SE2Qr`JOSZZpkIu;Y^j5f5Y}15g)D2UNr{_+f?-o;iEWbnuLoiVo4` zMON%FIWV(l&N5K5|t( z2E?|BNdxyFSre|4J=4&m!s-A=6^6r6|D0jH}mu^1@qs2!kdhVB}@(2_tXCkuL$n!PDcazyuUe0y!t3 z?Ry13yu%+B;@|~$nLZ|zh#YAMb0;Dp6H^O7K#o1G>jiI)%AfWcLSTKE?}Ar7y^)O_^m30Foxk#V8gEssdE?h|fg)bAr&M*`jBz!{Hvkfu2j7WlF7{s^pGZTN!be zN)Q!+45qk_6Bg6E2^aBu{yjVKCy%#AbaKXhV6Ww}N!VvPr-v%J3=ciCZVX|s3rZ_) zzQ8Q5Y#+Mvd4Qbus9@h+21D!pKSq26&UI* zjdM1Nxtq+#8=#)&EU_|f|x9)9$JJ#2jW_1FG*@#JwaLs^Ct`Ww0K-;3+G>+8xuqZi57&u}}D%mlUG zkaeb!OzU$R^JbU58E`(CCJ?2__3o;6cxih>K{~yony``DA19Uryh0#%W2tuu!qTIUR4MJavu^=!d0V;qrdw-I#Q%`MJ;*+4` z;MpB3;0OXAqjE^z-i6_UGf1KyE=A*^{8h&4H<^#jlViKxbSi+0+6HuZM*cvB z$E*WPp)&x=?O6H8(jk$H?3JReDSL>!S5-{nMm!8zMpL)NbxRL``9T8FI}RTF=z#+r zUTKo_m_3?}(EB-s9ah4&xIj=vtr-CjFuw!fo~j0d#FrgC<3|2Kus(#dZltYolfNPy zf*brce|SdzL_(7niIIP0fPeT$CLuXTLza(tc5YZmhgbS4kLv?=Mvz;C^7UgxxhGZn z2(&R#kPL|#dncS==zZu@P|W}|bAFRuwON|;nQ_r8e^gh{6xe#KA@u}$p659?UK@)r zZA)^qWdfoLzR4f)RS(34gCX;%d6_a_eVI|W)aA2>V-y2EZ8G6U)+oG=*(aUCB+p0I zSEk;75)uxa`G%LUz-c#T0U6NOg?z=62LI7>lgUFn7p`!quS!5cj2$+|3`Y$NTEWQSk%>4UTQfAIWW`>_1(;g8<`z`k{U z-+$Nn1ln9KuH<@?`Ojo+hPaJd1?%CQYENWp6vDQsY3_1I~P2Am%j(kGitTk|IR+1(#{0Tx#=;+En@Yr+Tfvs!v0U%#9J2YlkNhuFLev%?$sD4^i9nufmiXK?sSZ-!x)!!$RT+;GL*?dS!#EdgRGgcm#deT;8nO(YuhR~To zPB{FWPqoPsxWQ#W@j>YMWz}N}cH#n&?w-b8M|E+B^^lX4y(aAE8RzV*6gE}DkM7hf z4rPohxm#+#;Afo4u>H=Cce{4`(?}gmK1l57Cyq0XiRUrsv1syF{)>0*TjmcA|KP*#`3LCl+qcjCfKK*G*+HqZpSqj4nX6;|MGBbs1Q z>w&C^?XnEoPrCc8i-pSxbXft{JBU;X^U>{H7nc2lZ;qD?=bpdge|L3ef(~;=n3x^6 zqw$@Bg?=7;{OofXVjRZF$z1&E33S77U}0c=4z17TSI+-u5|h0 z!I$v;4Fb6O1YdKq7CLYVz|B~3J3`j6&69Wv+X)}$s$`gUr5QWRBY+Ffu!O{&3;fk) z((vtF-&ErsRAYqTNEvRj{(;0;D^_;4_dvEB8+Gae^1b{P3xsj>j%Aa;4SP7E__l$y zKT^VR;*FI^m^%P}x9{o4@xealkUV!uT-Z14>1N9_vD{(pc-V%W-08`40~}t)5tkC@ z66b!7m7l=IyWzn=V)S;IEi(f6%)>lT7V|M^2!NmpbVJ2e0DkZze)dR~M`M?X41BCD z7v~#iW?ZlQ(yRk-0%Jtz`4|@^ z;~S+%Jodp7Qn4dIv{p94lBnS6A&fjDf94?|@pV>?3$lC0x(!JV568s3$S8Sss(`Yk z--c?Z`#d&rGfxF5csgisGD)QWTAb{w=n*mwwa=5cHgPjgWy(O}t1$|oz8s0?214hr zdNV=yk319}mW`dj=0L3*V$}$I-4H~Vov-kY=uyDi_Q$~9e(><{Cm(#|KbHN6_Pfpc z9q0F+ymNTh9#8EX=V$h9voIGQ%CgP0F^$wghYmSwg3g%KWZ+ew-hnZKt2OgHTzhm~ z62iu`b0Tch2h-M0CK{-W?kJ7Gg!Qe54jXWE#;yH$Uhu31b%18xYOjv~m%umkMul&@ zPuq#4Z~|z!+u11X8n;P|0luBI^bc*kJ{-CBWIfRIYYguvc+INxp5JEC>(wq4!(5bP z`7ur+hE&Alj;->JY?U{D0cfaEir+fo<3hvHXm9q^^S)Z8;%r^}uK`XC58 z)}Hw)9C6Nz)sp%ivC;F(F2P3EC6*8g;0Z0T$O_Bw`T&jZ?#8TeDvcHxdo3ek^?=9E z$fDKU0L<{h)hxh2gNKyhfgqM;U?_zW)WXP=GSdrqh=b?XjJI! zA5)=c%5YlEbVv7$*iU1*eN@X|Y6+3Dm8;)x)?d{A=;_nLAKM=Y`>}nH{_%$&9iG^a zXY0qbuX2#7r7#LS$Js9S9*?;lt1@q^y&dlBu-x)Y^!DafXPHtfy*XX(1OQJTp(Gt+ zx9>5|6@cM=6CXsd!-;PVTu}1)#6Ns|HoaqT?s3>)31TfeN@Z zs7e*GGt4?N>1G|7jM9<8pHj=f-ljxnRHbp2l}nhLANBz{hLn>C1*UBN$UpSzRi5P0G)^H7M#n=O zz)N#y${MH@0}0B$F@S929u6Y>jD*XNt;_PrS6vp8X!aUwLjUOC>O)=b%{ilEdSL1u zhYl}e@e|hyAda65C_U?8AdGU>xgpzj>~ZKdKg=A78wKZ9?_-<46U5qJoma4>@D_!O z9YF*c#u|?}FcC5AMHE>0s4-$xRVBkM3f1s(YVsg#; z%&c`;EP<6jvlmfD)UYyi7&HSFeT~k;)9a#p&rYxu$#OEX7t5EC>O&$0$**G}4%7Nf zvIc~Q`C+W!#CKfkz{_2668L^ZQZz<(s;RmmsuzBU5?KjWy`PK4i;xMMI%XF?v{}c% z(|^`ej<8d9mDkEk*AcanWE;0B+AuVa0>M2ms<7 zQFDIA{NsqYO?uopgTz&~Ob3kY_-h2fN>qP1$RE1$xP%)bgM}%tgW%w`{s`HvTpJ-m z002M$NklKM2DFo3&kPm!SJx&o7FGdBq^yJ{1^*O|Rbqvmz&m4z32 zCow)^(95t14`i8(hz4K^MB#Af$R4WK8Bb`jJ|Pwbuj_EC&`A~-dQN1inP=o5l6v%6 zwDAtVfu``Okeub&l6CvgBTmItTm~>HwYZ*F&O|!S z=yyFvJ>$iT`l7B?8%s%*Reb{4E87bb5j!JaS^FDJBXgo+-w8Um`Q+};UC0ssJ~v%I z5^{?sWz+0vokQkDE#_>VImueyV-;@pO9Rb3*DJpfUXSFKqv#6S`>v7v)P+ucZIpY4 zA~#rWjIbV~G@F|(zCK@y%Z&c^#N_cpA9hl7I&?}w2Y)-GShK()JWr;gzfkDiqF)$v zekW7Hi$E=IOI^6Izy0um|6#EApFBN$|Jn9;oLlsj)G;$ik-U{~^cr=gM7v;RT62dk z+gt_mF3}x1daWm>?QKHCC>mPUh>TsMPk}wLfAk{JW!ZS~P|OL(T!P@9#>Q?(Kjz0q z6z`i(NPlLCD(jV>IptA$)iKvXO@MO-bEuMW&+t7Ac4E4Y{nC*@lA^DHvI;SKu?dMh zV`f>Hs~s2Db2Y9v0L=uclvy_QQ9p+{&zbE2kw9+0xKCkm9auV(b#bHcD!(5kG(!O! zpsAN%e0l(N%8tx9*C5-7t^U(S1PF8(`dGN^iwjmwUk`xL0udboktr@3!Q$^cj*B4f z(g+gy%|7vsQ0vx74TYa1=*|&OxJG=~C#bu?(fje-)^7r9uZ_SZ#jes5SE4$8oUZCA#{m#G=#Ttq+fX(tX8Fj z%v1emz^0xef`9D{Yr%Sg(#MJkyRt;IxMbG@IXrqEnTCXSwT!cLwuj7o(Kie+TBH{? zzUf*07i(ne9N0Y?CG(IMX6mXs)1fdA0eEC&kM=dhUQ<{^2w7B5Ub({$x~HZk>^;BR z=irnvd3`h}BQk_lc=t`2i6d6ynHG}!*1qHeaOJU?+4e&5M6;D7XLZ=OIsTUu(HC-)J3oCT(UkXZ(?88^^ z>j_G(2UFUMLEo^`WIEU+Oq#n#m3uU-SlD4|{_abujMP+y`w{AbO!}Ckdg0JgnyY{LM~xIT@21y9N zLhgIFpHLkae=#qv4PI8(#a+VU%NjRjLx`ilXzlfOXOv@+*PWp)mT&;i-Jxyvrf&B*Fp${ zGEyIyehsKxiR8Bwq;i8{@7Ket+UfmGkp`j&2JvH3NU5w z9vdS#cP3-W2~uK-*iVr~MUE1ZfJQRiSm-cR|M)YWKA~mWpeuXE&E?93K7<4knCet` zSaKal{s`cjG3*IX9`ZqtzBz}pk-}xZp+i^<#}g%teK~dPo)%rxK%NXYdswjdIB+)_ zMXCo$AluVf;Y=$y!*kSA=36igs1P@U| zkGOgtu@hHiK~NLg0&=x^{{o!q_VqyQ#q})kY{x_r&=V6-_7u2=(r8Uk`mqzgHr|lxD-kU0s#&>o_Z2QQ{zzX0Lx|%)=qS;8_MyfK`q}~>@_KNln;UY zjNP7fZC<|;#~cRx_R=@5J%NC}>i4{6^*u0MlUxUF?x}xg%6S@y;HQnisQYy| z4!?o!C2l7vN}Lua4NEg-g9$r2BRs>8Cr2iA%!-9*R=~l~#z7d4t~dDA)l2eUo$v+` zy%w54>5 z=8VYo2EY9I!ohm0R2>9_wz>H5y0Ula=ycHhZj4OMeRp}qI%fDWxi5||*&;6;4#93d ze}iEoFVOxbPWB9BpFo{-wY|{s-_9sjiWK9Xd^_2$&Z2sBoTvNXzQS`qi75|?J^c*j zLpf50RQoA+4mx;BJ>N(s)zfA4^JU*>Cij@Jz+&HzbdO+!&N5;6q|Y)~jf-rGE*5Ur z)6I`Lt=Q0EM<)|@^oYP3X!g&*+?Av{sCbsUje5^rSIwrGv$H+DN9;(6^7Gqq%zITdZxS%9M||UC1YSe}iBhauG?iRtFtI?2Lm5#5&^dJlG3egk>25K3OE^EI42#rXP1XGW;;~3s1+7 zxRh4VE5g;6tLevco+Q#yWGC$pBy~XaY;bzlae>?}P$H-`tqU zO;*`i!`gfm8Aad^Z9J2xEW~Xbg;<3>Ja*8>_-2gcu@0aMk@eU8np`pbb#5^)%tNNf z4=;<+r=|EZu5<#^fSnQkN{b`w#LsLn_WaoUSh$uS9Defi;dc0Gpe!yBH_jMvjO@&a ztG1xAakfcTB%@#3(y%|APE8U2>AXZqeq9kdnR1OjAP5Xf+Zg^|c?3=3G$uCjaUu4{%0ygwIg+ z>pnFiPT43PStGK*E*ogZ6H|T(cFb{M$!9rq%z>uXgu2t*=?9+XVGjrI?_dj^pt{mp zIkIW?1IbQ_LY1DFNW|XsFgIm}N))(dUd_afgz%0oVP|YKoBqkqwUgE+u+hc zo!~b4apZ`9n8UKQ55$cnO^zNU>61kQ?C6XcN2MzaZR~)uw7F#N!)z!goZ{S}m7vhf zSc^qLIg$u?#f876FWw4kaUD-KPMIjUg2vve`X-+R_q{O|Aecv=S?@S0NPXd0eH4oP z>ER?Ue-nT+ZxB)kjhDT}V=tcW2Y`JALeIQ#tsn~jSeJ;0rSJ}9A1Rj@go?5F=u;3K z5FsRxbzArA8sboUijd4r!3IsL2%1t=%JhVe6}E8B))faom8z9M49i*pemH5lbl{Bi zVv~=FQPq`fg3F1QEKWvAA=fK(_%kv2wZ<+BtfL&LVW=(5rbv918}Q7Uiqk=uwhX|^ zUW(6~6k}M=1ImelfI2IG)|d3Sy?KRA^Mg$z>q0wc{05pPVzW}9tjJk=pZiPwdvRp! z{z~A2WoK2r5Q`$P7uJcubvydCM~Eo^{n~`L5J)Xhy$Ll@%UCibs%4~Fbk>;HPV9`F z(6#x}vGI=*Lh`^ub?-gto9^$a4yC0QN?@+g&peVo(@LcRm$@*TQTBRa07rZ;6Z=d? zjE%T^*uG9gBis*o13c7x*QNBl%-JP%C=BQ^Sw5)2HA>u=f%)4GQ~ZV3K=nGF0mKb_ zFkpv|5!{|1+-aj&ZkO@2ynCoOsg|im-W6fuMPaX8?BH*4gqk;OoqEsSakri(;Cur|V_n?{;Xy9?y5bf2e{{)v6Y%mVK6` z-{sL~t<}=h+9QPFM?Y42_Yrdpd(?L20B2DYsbP?gEYK`V|Lr`Eb$W(QNMoaYfL^C= z`Q&Z6@v;DvGwk8noRB@B>-FN)91xSJ#)hlP01ya{QFc9MUGi}oeT@4!!AmsR7{QG= zJTYRAcT}+;h*o8D<%sw z1Rn0!4$qMKxlg^!ON_>pA**i8G>|nZ{a)`q)F-5CgMHI`0@5C^0_V(drHU=9 z2+m2Su-1rB^7_d2NY^VSS-@$pVQ+JPWRa6G67+P;ad`C{`Kr!2{Q!+LyZI-uvnFrH zudvdn>$1H>KUpR_vv`I8VpuMeIx#x+GymA3XWWqE=m9GbFBgB-pY_0B>C*v@am%Xw zM6=4+sBMNMwh{$ntxMXK-lOd)#))e!;)1nlKv$`C<-;mS$XM7Z_zQB16;?S3__P@v3lk?el#&4iUi6QZv3+$D}(?DLVPa!V$ zIQ$zsn~T-Lv~7oG5IGA_tI$ zQXvFbXS5LJbizV|x~Gw1--LR4Eq7Z}o%*p#xlU4|uJT)1vS?Y|W4c!wu(6JmpZ3~E z193yCCAcBm?`HVkqFk}tnMSgv9vjOb%1PdP$-vGFZ@(Tv!6@2`j#F{RITSrNLUY(P z-YmoMyq8(;Bj;r`X)3ZVri_rTcRTIu;lN*G9Mq79W!Qrh{TeAw3{i%)hHd8aSFv9# zxv7y6`+nrLJfau19&@Ua&EYR9W~hBN$x3?7(nui+9nf562gfxjqVM~es#Tq2s+{XO zW}E%zkau!YE-8UwXT&aCx6M9yt~IX2&|=jn8@f_lB+pp56m$6j8g}%{BX0*m<#a4o z&4GIF3Rn6-ERGN)0$aRmk790<+Gmf;j+6+F2Goh zL5XYPq5^st;E+gPQ|xAK)GhYoD5nl&HQ_WABQ=U~}eo?;>)N_UON0xybcaD1uyhM$d;TckRH-F940lJGSLEr3XL^p|o z=E{aq;5_VHb1YR1JCcu!8)ci1`6n%9cmxXPbO$X7r#UBG^N;Ig5|cdpBwdALNS?aN z)Wt3U!2H}ZrLH}47};VM`3MMRwP5fkPc2?} z*6&ru(n7eY3r8!U>T*9(1-@MYdYbF(xi;Ngy2sfLa309YYik|EHHYZlv-t>F?21>M zIqMt^p23`fn8zX~Bpmx|67kg@JR^Zi-XC_xI;TdX>B{_RA2=do%q~}>Y1kL)=1pwB7zAp$bRED9`IqPzK{<$UzO(Yl z(O_N^h1vd#Nac=WW!laJR2PkK1ZQ*uf5fqKMyL3DCBvQ>SMqHcHp#@yPOigpih{pM zK?Pb9r_M#ft{D)YbmujY$7d3c5uamtvayataIWVyqyP2oG#hyklRwi$ZQ`s=6bZmZ zafYsRRT01#laT%^pw%_^r9jU^F*2m=I6Y;$GkgQxOHpPtVi~vg;GnMQ~+L z18Y|)Ap(dFLD%oj}-Y>G}y2XM}J9Q5E|Ny)P-HbbIZQ1aLxw+w7fk z%#4=R?HebcJ09PZI@C6`uriF;?{;dGRTc2Up_dQodJcQ;ECagTo;)VYt$2@#r-(P% z4#_$a9?D72L|mWYq%=1IQR9tBZXuXDn5!#N&S8>8r}3CPQyb2&7lxbPn0S10_?tNR zH0m*i-Tk4|Iuahrjh`vx_L*;1TJN^&H)FqcbY1*&6(|eB%NFM{KRj0$DS_9!9(sn9 zJw@uuq-yjaD>8aQ?{zp+Exr9d75)a_jGjJgm}Tv)`VcV5SD340t!qdR#lL4)$vSw& zlRRwF?(=R;>VSj}?+N`@c^^33++IZ14Qu;-ni%{=KNfFbPS zOwIMRK}2$MXX@G_oV2pfjf8QNztu&&*RA}6VoKy(0~(~qdKa+l^qBQC6z?F6TB{j~ zW&{|p@qC$`cp_H(>g8^x`j;oyiO^+?$m%nFw2Qs(9qhHPl#_w@sf%$*Jt^QZRlc0Z zswXEL{4TEMKNxEt*20k~#s(j4RsOXD@Ksm#-|HD!*7#wc_NW znhAN}l}g+!fvd3^DvJrmox$`OHDR}Xl|{}tWS9a&4AwS!9(!^=I=3#4=C@9oy_&R8 z_6D=bnh=zsk;=*_JZZBu>=nPUW5_Y%tF|IPpr^m)E!@>U?Sz?}+P_0qqa#h&nSZ{Ch96?^=x@FC zz*BI}6JKeD;5f2pxtV{v_xSfdM}8u5q!8u@bJ@dyO7!S|^;6d#p8f_dkSkx0ChIjv zCDUV%wt7zhxU#Q#!#vg;ulHbiYVDGTxo1m8?;{>RJj4gitO!*X$khQ4c?zC=zzgB< zCtn)Memu>%_TbNvHDrx(4?C81yxbr7O!X+*12VePJ&^1Yip2eUs16%)tKcu?$tpKB0$*jEqy+x`PbTsBwNu3l#~4PlkI@{pYo9-j@H z0A3IM#<_v;bMBqt8)!^TrbEG6Zn*85>0FBx}XYo{o}Ie%ly~Re0n4` z^hr3K;k-!dyJ8lF^_HaTfzRZ&WkwSBMQ(qS=%Rvkggu_OX#z8z!41nfq&dqhpCi{X z&Wm#5WB!iCpUGAWoVh7sucYp$EbQpFFpk%?ARl^=Q{pY8+M!Se<<^62v%C{_XXd%4 zIVtwpc0{HC|IU~H=T|%qxO`1dXnt=BzL|ex>D$Hnh=-gtNPhHGYK5C?QUdc zR%desvwSyIjQnku>zc4r2c@>KLgSYK%xo*5348W(KtK@s8i*Ct-l!;H*XY%TzhhR; z?xlimU!%^a&3R|}h8gF!j>$&`#_CzsEKhYbr21Qfs>jm@$X zP(TdRh)*WTkWY{lxidpwL^2Y&#RY;m#yWc{5TO!=VCE$Ux*6SW{d=U%(~3ivzj4!E z1m-u<8STTw)I-q?;wW?*m)^ThP8(sTEbihAM}jwNpUEJYa!>TrGIrpCG6e!~_|dz6 zPbW^}h|P~+W{bEUJ<_KqLyMvH&$%~JB(mXnXAjR@-6HpbDt`!-FN4tQ&T+HD4Q`%` z@R2Yz+9q!+c<_LafVwnf!aZENr(xuuQ%RNw+bk=1*yBvnx@INr4(AbuhRYp9N-YSC2^Ir>YEK#fjxgDZT4SLR%$(`xScK*MjCq)5MtHZlKroLB>7)=pOpO_J077CSXb@cKX+ZDs=-UGD8PbHAXyTLJC$SO*9kTV9KMh>#aag%yJzS@-j|R)z=equk~l65lva8H(gQI&+4V9Ke$V5ZRL?!vLVDJbD=#aQ z3yoXL>T((K8kIaUyO=o1>2k(dCdFwZ31R5^X)NbKy-BsG!(LC#BE}3|i<58#ACqZce%b{$>;5{@9PS6C_(>Mu zB?oN4&|&F%$5HUOg)$>x!FKLZ`R0m|$vf9G%lOYVQU^Ix?-%eLSnUbQ1Bb2=>e+UZ z3|S}W`blJ)Gngo`^-R*|EVC?6=sjvh!GFF{`Fah~=jvzqij{PFQZeGU@e!Y>Nx>&{ z>O1+|7pV@CgtpaLp28E52JFOxFa0Wl$X0&nv39m$M~}DjU$!1m)z(Dk zB6J&I8FmGzpoq^5+@;AdciIzRMpy|(w~^ZEZG@~~>|y0wGUZPkn<+ocZ02UAn<_X} zTq_t&_V~R-#CQBLUD)c>Wuobq{=o4<%J2E%uWE9B2G6m)TO0Ajf#{zZYZ>xIqWj;f zy9~(yF@!btOyyXFEq}PN(?3DX8#?utUYoWThE83<#D0~AM_iH>!6;iC5rL0&>o~D> z8w|@xqiI~XKj+7Y5GOfZqOF_==1V+qc}PuEFo+{0&V3QtEF@o)We5jVl8gyPe;%$d z4*I!F_suRD#@t8frbGhLy-B{4yWZb4|Hv1PMW6dr^MbH8H7Qe_3*PK=E{~mT!p{Dr z$;mCPFf`yfhP&#)l$@LstbOrh-J}k>vEE~cLKI_Ndve0zXZ|URX`!6W2yGU$%(sD= z-cfHbaEYlr5NE)~j(=xd`yZe&Um(JVMjj%tqi;tdW+XDcZPe{rcJg4(a$+jY=z6U% zOKc+nQCz9O&4ptza1vr>VF3I9!tyrNVgilmtWM z9c8B-Hx9-uv)oz0Hvi}+u)HqP!SF*3=kOepU<+Tw5;>n;vVjfM>{WE1gJ7(%u{0Fe z0b}yRlQ`}DIfQrNvQeo5#%e@(Jp+P-u>dJxPds~tHycH>@k0b6^wJuhpJEO3l+^n- zD79=YsD+Eh4)!8MY=BZF-visV#aDrL0D|F zeEk`k*~Ef=ea7=ezA3d)hgM)du+O#&WWjy$Z6uUMMJF6=LU|$W6#&N$P1nI)<_CAR zkz!$2^ekVE?R@Bas)@8LO22mV?&`XY7w>fmwsDla_1u+(rc{Y!jrU*GU3GtXo}3q5 z7!kvip;$sa3$_;bv^GO-nNl zjvjN%I%!CKY^LSYW5yygsw`r$%`s#Bg#7N%4Aj2^-S32OO<|5%KiJ)`AtCNN~i>SwapZcfsht@DM|Y4w30$ zN{0q$86#tlWrnDy=NfuMj`)zEuM!8qDe<#FJ>Q}$RskZ@ccKno^vCRMvp3q? zQAtDE+H5%HCIFewMf6%hGly;M$v6ho31h9y=z^|K0F-xJmr5!>^?;AD=VP~mVy!Z| zwqD;e`1`m`U(5}Po%stLD-(gY*WJiU9r0o6`n&=aGz&C9VtktkfVlxN6W4X@n=ygl z8!hKJ>r=4A0U!G#60Ev*iI7@#G0dwkyV^rxMvT4 zc)_qQf|aUR<>OdTb5WUN+w1H_&6M)(MHkvdsu&*#|<}IA)Bjg-fCK5Fr36PzF=nZco_3^|(D9`;EuF z0&Yxv3j}>qZh`Vz6vyk}qQx!%#f&m?KJD#|vL``ZxFF1$;fZ|4?gg~1riHo*Wtj<2>y1)Ig4avIZvk3>LvGF z*J+WrimLQk4=)1D4P}xPJY)BR%W=F`G{-HddnN4IZ=O=k?y+Ao5jRhM0s}Qr;|U=b zL1=fT#ilr9jmXZI`F-&vo3n3(u6x|}Nwhk8N9gziwZ(P+(1%3q)`I6;r52Sr@B z!^`bcc)0ViX$!(>^cd#J>|QDJWn*zpF|#;@x;a9PC5U01Ll(JUX^s3+Omdp2VB32H zM1HEYkR+fkSCcyN6)SdGEMJ?FvMXm-@K0yMy-mk=KM_vB`(b<~7J!AXJA z2o?|+vTn!i{0l=a+xz7f{|@&Su&>^6uzEgKX+u}x?J%~?`aj3L=-69WgLl7 z0dBHUesIWUy7&-K2fV4HhI6>l857R6mbK>&`U=xNrNGrgI;x=@=8NAg+Z2)uKpgYW zje|AICK@;C#%g3i+q#;C#6)HRqyGYUF8U!cURbg3jUC6l!xM2^)E?iB>`Lm1Gdq)C z#gs6mvDzr_n0rEY5&E#(HO}J)S?oQVkqs|~#Eu_F$P|M^2DEezeJ#N9z#B;1%?wSA zTO1kTL$44Qx|OvD#op@n;YW&*Y^(JGjlzLcqsc^pd&KxF9^u4sC!T&UzV_IX zW$%UI7-vf)Vvm@g5Qmzg3Rn}?g6ifm;zn2U2``@FocQP11u!|PEH7pvnlQ5n3>)>% z`Q_t_E^1!$gBCEE*fd#C&olHPc^(nCDKm-3MDja7AvWIPH_%mic7UGh1q1bTNM`_7 z&GY~Obm;{4guN5?c=W5{iTiH6g&AP-p^8Nt7qhS*N9kftT#mWs*!va$UUm%7;9$h= zdkkgc#Bru^ugEi{JBhZ}bW+f*gR~M}i3u*}!uyL#W&EH!Hn8He)6(_jt zNdbEFM3=?grN7s9 zI=qa7+|+Uz{)&ER8EJb?JIh6%K!1a6a=pPwnF~YV_XSdeARRjXZts5d@m?t+(edBy zsH#0I6=xgNe^^-Oy_^8QP>+Zm~Q`d9#nub3mCnQnxVkW=27ZZxv zCy0~KEA|PRejk!>cuUGjsk>v9d5%l@=m67R1q6XZKXp77r*%+1-Kh(I=x9ggiN&W( z(x2S_T1TgWZg1o+-jN0L5MDDzmXy|EqNjopF*!FP?f3lD#rlt(7Zep&jF;uz)-y+( zB4=hVRct*bGOQ~;Fx&j%8+`^lea2ro*WF>+VZm#bq=U7e=kO{zFd${ou^|X^KF9nT ztEV2ZNSpjwXT;flcVN^#*D@z5d$mVRIkNednyh#59oEAuE*&|Kem24(%tk&!?3g2V zndH+r%cd4@h80deKX5c1^IVr8-QM(ClRP;HPqoU4Z1cAXG>~-=pA%hIuOdq3(vvak zVO?ua!k6?6l#gCxI#}nzwS3$8XmvqY1Q=qhzE<;wwz%(^}Wh&5-4h{29tbG9=rnAtnv;i)~5z^+&Ro#~(s_|Pj) zuI|Vmh*8&wkTh_CU{5i1#1YkX>|+Gc7-)=!*@eI$=s5K9BV28SrqUCh`6+{W1xFlk z%4VLhN7uzt^IA9u*b~GM?dA_h)w3qDXyzHm7)KSucA3=o0eG^&8TP&GfrhJ4jRe)t zdlG5HL9)%{C)jZF)n9QHVvt$(2;-4W=l<-|Qjjum{=9r%tj2R*5c@mTcPrMCECDiSRlF zPGI4)uDX~IkmEO2r(<&@uE8diiYC*&i}zep%`p>^b4pn`sk7>`Ju%<-#2}k=J!aWd zKaR!VyLgG1*xym1mn$_+>-7F#05G|MO#nevT_Z zejO>JhH<#jJ%=eBhyS!u(Vb~7Ml3hWa9SN5bssr<4M%id5At=O=7w^QO{Hk?GeX^U z>>jY|EYzDT%(c(^X_oK0`pXvQn)RsFnHsr|9j7(cKGJ&`_LYcAHr`pjpgd-qlt8=Q zap>TOJ|y{gzXa}nM%6?|YkjKl-VRc8hmt}l6}@n2K<|F^dF`Bd@W&vtKM(ShI*WZ5abNcG_ z*N1Pu{pRr1*RKw*zxn3yn=d~*y!`s>!>_*l#PqKZU%q-BbMM7_u?rp5;KtcMpw}!V zkg_=kz}LCnaU@`Uvd{heRI-je4_>Bk6D2sNxJ!NBE)GgI&;MqL2UdN0WU{v;3eu6uMz3Ms)Aue33tp+RAjglA%kFvSq89?ir?VzQwdTWy<;d3D zRx;!u)3GsQKax+Ol|TIBcYo!a$g*2SSg zrH`vqxQEkway5EPc(9rsTIMozU6&N3nTR-sO5fHV`xqmRN}?&{OM8nS`SA}XNz{oo z!#DQYGvs0ZvVrWTS;utKFMFX~T}i)*$3egFV5k4mdjlIik=+i#NQ5QEJ$s>;-vEyF zNB|R^dHKR)tpD+N=#Q7HS)6CyAF zgznbSdvMWGo9eNbNzFKw;HFOCIn5LQk8{lI!mA#SGs$I(*I08DPPdtP9BChh?5yQ4 z*v6h&X*7wzGcTNL8=V*aQGiKSLlHzh#Bc2xm}PJHvt#0iKhhlimvTsN<1PLUbahlc zsC2By@(@)=i za`@h}_YNOFd*8iN$alV~>P0zIXiumTEs@WTJxOP#uydW(Jp%U^pomhq?jTkj5Qp|= zJonnVeqU8GS0~m3OywSvMJM}AYqFodX0OvZ{XWR=VKbo_^1zxQm@9p^cfA5`B#3pd zk{c0UF_;<%J5RT;pFH{k3!U{)7Uf>1lcy9BNM4m<)ktS^9k* zw>%Rw-7KrcS?{u@q)(MFOx2z&-geB?a_#BZTlfYK1Ni=asK=jIXOdJ+pB(h_R&bm> zb8}6oi+-XhjY(weWZ1#u$1c75z-&f<1fy?&M-3ehbu-OGM9ntI)qiu;T~q8MMOcmy2827t2{2udYgS(E1i)YQtTf|;OKxcJz=qxnp@Lz@qS#qR z59Gi{jySu8*MoDtfAbK&h~SwVGcT8}&7J!M_w2=mzX78EX7r$5p^S!esFAJ3m0tCc zILEr4c)XtUupFh2fKHuB$It<(vzEAFTytYJI?p|HB<>hTuUN-8!jLSZCry1WKqeI+ z8wv4G;hS8J&z!mg19p_LmEbQ=2DN-p@y#=Ogz2>-0~#MV=qL(&gI5)C7s1p76i*7{ zoQRO(Z-f;$m|X(T`d9f(8tt?=>gi_sA)LJ6$cMf+j<|@R&lNvVVTZlj*Qo1j;K6zM1$tmT6~0dPa1E9Cu(z#MJm80a zjoaHZpn7#(C()P>EHQ$IZ}pB@81{_NufOS_fzT!$#vJPn9QF*L0OI@D;N@n*Dn5X{ z9Kw%ZdsJ=_6!k1b8Zcs$Y~v<>MipOV9{I!H*P-CG-;T#Adafd~T{=|$_jeaUOw< zJ(K)XEi)du=7+URqWae%T%U@eVDQyV=Bsmt3^DdSl(SORx!=)YzzeEIp| zv#-89eEQWFhfnOo^h^7O`WyS8t?#10&TptMdttk^;+%-REQNDy&T#pDn$g#lm4!-N z<{H3-Hp|cjQ8+5C81uW$o*bHZ)gzH_rllV{CipS;Wb4SPV8Yy$NG;SrUUd1v;JeRp zrxuy#l979<4abY4udQO_w?~Cz)0lF-zzQofm{2#kWucJsX7qHIK2Zbp zT^ev^K&z*zm3*&U@C6KkOADvSfJqu!3%izjyKEPgFlU-&#dQ55p4;+~lf^;5AwM+G zGKy9*{yxetn-*VV$tFnV*ptnVYfh=Kt{`>F9ndS-P$F1z58- zO2y2$SNb0QvN-%lM@aXQu0b7*KBHigPY>zfkTrrc(U0(rkDJ8ee>29p>RG;KbX<&I@j1pAC@6CYcVU&$6`8x$_}} z4(y?YMbCRUGFBXEa6r`TYBL}Sw80_fAasOafo#>++(lgcF{Q)qET}8hGEMOI-l^id z@%%IJ(C$`$Y(Flq-;w^%Zd&MfqW|kZ{zvws-CrMm_Qh`wU%YyG`14=<<>A*~eSY}g zfBKWdXRp4r57}Qho;%#!@gJvN?yNMh1;FL0F*TQn0i1k8pG-MGvl^EXr#!Vrnmc zB%6DZrCK2_V3T8tn=vV4s`@k;ZZu|*ORgl6O&LL}ADmLo@}lE_L{1&pL!*Dr&9p}T zQWO<&V-JOsJNwic(>dpi#;}l`YdG^veBoHXsRoH{nd*kbqq{_`^V2}_`xm9K+>HUL)XVj7sh6Ki7S)6!sX8+OQ z=(F}L^OFUQy|7k5{az0)OEY+)()#0}QVr+y;LUW6Z=k1W&k?0yRjbHy&+_c+vQxFE zIaV$f6T3M4K)|tQ2ZtYgx8uL@2=UnyU7^xm6QS7AzZ>s+2A({6+e^^zp2zW_eL(*9 z@cUnW>K~wW`S`^bpV*If|Lx(?gNOdDv_5FRwh!4{z|P-;_6_^{{Mj}c*tS^$7eHN= z++_3R5WDh8*ENKSHR^)sI2)yJ4tJXMsB?p!3<}WW+5{8VZHv?d@0FV)@Ju81T(iz& z(+-vS>NUCJ664uTo`3qFpHIsR@H7ik8n#GYTl{ZO^iTrR&js zGtOTy%%HKRt^~2{9?$?F3VYYV^%2}ufJ3-O?OmHX&v|rJ*E=H9s2t1r$IO`@s7Ah4 z3o4}#;3|Pzt@|x^+@kN1Eud0B@0B>)XRKsB`92S-RSmOz#hMgK zi?fVPbn|3EKc~z$j?=#b9p1f@aE^683?yaj_DYTeLykM>vN&$A?c}esTCee)?zjf%?g2bh{=#5(!)r{sg$U0ea2=I>QE~9($erTl+Z6M`PaJvb z!!Lm387i4c5Rud&FEXQ$l8M}t@e4oX{Ln3<9dm40;+eWfWH9t^ZYsvz#wJ3~7Wm@H z0&*~jq|zoS8J#EPX^45V9@1YhsLHVN#0N(^`@v0|s$(%jUe@#i>Nz0o_SE^%3T#O& zWp*eBJSlrxY{HAsjfp)b6Xc^kDPA%>T{c%?7LXiyIG4$AV6S4rP_V?+>(AV$K?7$X z7a-+zZLA4=%o8`AdmV+NuD*^9P#KziR1!q!v^nzQ34%DYNNyu;gw{XE@r}ZokFts; z1F%it;%PEQUhrW5SyK=GPV^WnA;1g6Lek45Fl zw@n?9TYIZJRdD(k>L}fFo&t+6m8vi|3ww@h55UN8Xb&VxidR?fX}~j9o^>qb`n7&jdR+26=-N-(H=J7{ndNJfUO$j?zcb|a)`eLRs??>yWqZ=7!=b5+ z7mUOuPvJb`Do+qB*~oeoqTgX`pQAokJFNqgdzOKVR(3sR8Gpujc9UH-g_jRrXFME* zSP#mWq?aqrpf-9rekX*Sz&EW+)~eo^UozRW0jw|Lj-9YQIAJ##&&j>k<-0shv%I42 zPP<{}gtJJO6%I~uOZ#TveI*?1FZ5d)=MqHO^=+CJ!H1VE?k+8JO_DXCEXG~d(Q~yL zP$`$O+tE8+*H6XuIM;jfqo9+GDrIJrtVg$R2_tIWM>lF~o8S^luaGV-3A?GAG?%3Z zr+j*CF}(dd&>Z1q@_4(=9p74!6^=H}5t49`;YAFch>pX)J66CE#O#x-uA;N*qv?a> zG~CD#Qni)53^h+Vv4v}TEXZnqvt!G(EtEI07*naRR7d}jQgMb-k%&kvEPmU-~Pj&`j2@3 zUw`%I{@1*})nEPgBi6Uh?X>n&G|ii#?+)VF>jc!@#RPOeaqPgVhXe({z(|JqT!AHg zIyZ#YL$Z#o93EJu5r^!7(-{(ad#286Frw>xlxoc7Cfx0+D=MIW=8bQm!pnJ0aB$9A z$Vn7+Gh)su*2 zEMmzMc$UFwbUAS+4KtAfQWuYo%8wz;;OM{OiZ#RhL%nma1&-J2;2m)h$o%IPW1z}0 z!Brufzs8g1yfW0rxQqzcUWM6dZz z0}bO(croqS;i(FVSm%G*01kkj`G>yx%6d93%(4;zg1l`kSmIKz^~eH%wP-6l;x_v& zfpc~^W?Sb&2SZHQ*)0RraYNT@b!aOGJI^?9jG!cL1{R?hGt^-Ij=*^QT@ZRwkNmuE zi2BQ(`R8oNbm${>R>9%zbI%@PmMYIeHuoRID~?(fB)^)vh0C}Teg|4e9k4ToHJFgs5oG3aXP0H1akK74E(R%f@D+h8 z#^@y)ixZ&>=YUZndYX$L{nkekdV#mjegK3(d%rs2oS&MAo_*N98-JT-Ko^CFb|ugs z9n%Huy~oe&N4meXzvBIi!&Cb`>OcGWU-@6_erbPrOus|jcfi$pC7MzD2U$$#-A{8i zmq}M8qyc^7k8bmN>&h`6y+Ay&8xx}Oe{RgAdMyiz-+^Z)=e}FHWU+2q9`xdoJhyFf zt*dW@Oy(GX`{&F+L>zju{EiQF%LJp}bK5K^CpqQyvanM&Iym(1PraLCV_}8lO&xz* z>gIV4Mg&NH@vYkptXDY`0?TK+m8@FD^2K?JfJ~+MXZ&^B?}`@YU;AhoAiXuMb~+{mO1ie3f@56HOAvViBX= zO?4RGJR2x=a*eFQTSQJMcXz|uu0$hvG#QK*f5s)zoKaEe6T>3z=U-q$fw1LUS~E0#03QUa#EgJL zU*~GXunz0q9#I3?B{FpTE$B>QQibZ@Hyt4#wT?Nm+U$RH4=ULBwr4CoPW5DR%iq`H zND-MLc;E%MxA(wp(os$ljQBq14VZ33(qd0}JBpMteB$${NW@fI;t+J+@lm`6p|V%3bG!=bYb70{Ns5iUZ`Xm`MFj{CHn5sLkv8H z4tXY_CL8GkH{;4O;{rikQE?6=!KhQ!*u(obr{hMJh!;k-x$^MvjE89i>UB(X^*i1f z=zi!uy(tqOejmZvc)~Z(uqY}H;-$@Ux@*8EhGQ~7`E+bDVF>ql?g8b&A%n@N@Sqp| zT*93tx}*W>U;+fk*5ym6zH|T9%z*w0EIoej-4Fany7g~Izh{4V?1LvS4&Q$B?cvwo zeBr+vt&7#XGfrzz`$tjtw2w%eg>#{#D)r!U!!6|slt*=9g3B4FzkvRS?|x{%TmI5+ zK78W8WB&SU`+agt`0DVrfo<-VpM-&NZ=|5`$D6@;c^PjEt#*6i0GZx(s;x?IJ?nK% z2G_&Koa|N72=`%U_s}Vcyfag}9skZK_#BG71CI@oS*BFJoWz(tXWHNs2>*#ArCgt3 zP9&DwWt`d1C97)vgpA%4>5m}#T=9(D#^aKH&mB|9F|Uv~E<`n7j;Jb3WHLkgbum}di|&Idu^I9%2qHiLc|Ob7lCKkT2-Dri>#2P--O*coM?WpHD>*gJ5yEzNl) zAL|6$jJ>5g=9IGxz1uP`pS;s&AD}5LEG*(?rdL=G8+u+m$X)cAzwG;t0oj%2E>~hS z%8g5vHcohkJ;=GAiyd6`U+<~{QMj}ZJ&WxH&;xeUVG0u!Oq-iIOmcD*l^f{H;s7|I zJ)u_sLX5*br0`g56y+*nN3Cq%X>alj==Y=l{zpGN{K0$QJ3M~$$iIpHAOH3Ldid3s zpB;W}5B-s`ITleJ!$b#7)d=TV~1Fuxr3rIo|rK523CUh(rVtex4iNf(P3N9HR z@@$u^qtct)aqrq6WBV_D|4$D3C&d2SAOBPP_qM+{{J%f@>EZ7_`}N`f{Hwn>eEIt2 z;S>8Y`RFy6>a_J@?7a4I{U%yS_4)MCW8;4N@Q#goJbnA|;h8<2*cv^4__n=ih?^jf z>`jE;NH~?nzO=ur{?*s7ZG7c7M>^dLoEMQ!jJc!A1Rm zonm>aIICvJ2_X!}N?Hqrxh5Yx@f$h-;o$mA1+F`UW(+4$iDXw(8eLAi4e^dlf*ts} z?9Toq@R6eyKYcX1v4{U#F51u~u1$lNHfIa}{F;%L$`gbmeKL|~II@srul#ET%yUd? zOx0Wuw4|qCrIAh7pLc9OfBf|M;T_xWAKCb}?Ptl_<4?@5J^uKi?&aGDV+(w3H~F-m z^#MaaEu(*$?+fj3lYjZe7yi>Azp{Vx{N=qL{VH#8e`&9Wn)_dT{?*}EU%fp1=H<)7&%gZg z@bfQXpMPb0eLY(L>0}nchK_Z64d%qu{Epst$RK6(JyR=CbA?+ciF+-*S6~ViFrP%r zPyVQ-SSZmAfeW9mEyR~|4#UZ&uzGaVxg1s5vsX9epGbrc_XZcq(<#OCkkk_e(FBoU z$lE~GE)jg?;9|ABpw{@|S#hmW7X z=jlItWgh_T-I6{-bKyI^F6}M$`z)W%a4lqRX)=pL^0!>j%`b?qzmBmd>AG+_uJ9&E zHoN;BoHiTYwm0-2J$-R_|EYa*Z$Hlct1mwH52{b>!@$4$)z9q5xgXk_PkRHJFW#9| zjPbwKqdVk!p?_ev(D~1Gv%Pry#QtLX6MN&U$2+F$@x(q@#0P5sDFS;#p*Oy7=U-h{ zo$=uJi<6B$BO34PP)E~Y^P;~V?{AJw z*3l3?k6{lFZxrZ7AGXxqH*dvXo{zK{l%!`i33{F54H%X_6xGDBrRXLQi? zv~Q0c@%AI_5<_Lb9j}t+1HbHgyz+mlZbsatOm(peYC-EKO?zJJsGD~BaQ8j?(DB^% z`VU^b>mRD0*-azu?RV_Wus-DJZ#Af+*LE?|8{#it+0S{{wdqUi@6|W2?N?E3f4_Qd z{15B{iF}E(H_h?EEcUnT()EUVtWM6!C^rmUPuONiT#2R{c;q+YblrHYhkf|bjSS7FZkYN_H@kM}hJ^m9!zZ=}HP4bC zz4hww%H~cVqV-27^|}yq8vE^*Te^EX){e|tJlQn*Lh2eMQBUz5r}n8n7{6z)p}OJo z{deB+8x$|>1GRiFY=8Tx574GRHmoQ=v;C^Q{F!}#)A(z{Y0keeyXITp-@4&A6Qi{byS_OUSfZd$V6WV5}jQFHss>iS%-hc?H5 z{rHQ+bGza8^b7l2Cgy*oxwkx;H_h=khE@I3R|unQIp%}O#*RK?E|vF!MRqzN$cxzI zgB5wkW20m^(SNH)HPf3_L&t10_QIwKPM(Z=ggLg=#E_2RpL`N)##68jdGzSG0`1^; z{Y7L5Vj1O=%q2!r;QI#yScqgC_$&ZN=E6@r{g+<&jK`67;^lRd&6kgcYfuBMWOJ;u zd%$E};YSF=w7^h2e^h{pF+0ingb5t>@5bMr8PFTg@7W(2d)I!H`@jEZ|J~u|_D^2@ z5C8gK`d{h(>92obFFd*{8!JD~nd9qiuR+UE+ckx{D|mb(jqTm=gLEgbDx}hg5LzW^ zJSNMFT@mUp@ZHW1PbM4mVM>2Y?89fz5C8lReth`y>pwdDvp@Xt;n((U&HwYWzdZb@ zJ^t-4{>FdQTR-ltHv)P?@X@mu{?CkQ&Od(k?&19>Pwn@rpB>(_$Gax$8#g`ppsY7u zlH*N9e5leq`(rxq;)C%w{)WNd1lSyYV{eM}Moe!A^v{`nZZ}5$&VJ|p@9am<#A0JFCEGnkydAY#I9vOY-t zXW##$!}si4%8y^Xus2ltP<6r~4y#pj@Na+lGh3U_5C8txKR&2SLX4}=XeWH ze{%s~7?paKrEd&ANye4H;!C^7Okrl$8tZ0>9{=Kx{}X$Y_!!hBqjJFemj6!v|NN8x z+s2o5XPthy(_Zz`2MfSRy_s2WH0PtvgO>t^3OVb`#N%|0{eXbi;`Y@uOy3h?Q zt+75(|Lv!r+Bd0R9sa_uWuMwlVf^$rzj2)Q#C(WV$NWwqEIfk<6xJ~wn*sA$7&qw# z_L`C!)+5(OgVy1D_CZ3AAHMtE;fHo3;vcLxBb$;v^myX{`_-&9OMUL-n>4hg5Prmm(dwqO-_o+J~*au|27Q{Erwk9^u+QV8KQR=mlLw5bdg;Mxv!h7F7_Wk@rdkxVy{+f5cL1gp( zx1W9CkAH7-{uj2tfBofG_S0oAovzAsZR`&0(N#PPf_+F60$75;VrGhp#3m4*V_7_} zC7v7s$W5@~kI^CE>~#r|QK+Jaf-xv+2{&p60w3pEuwy4FuS3`iPXX?*Sl>Wgm910v z6D4lRHdb8t>SZ?zb*n!`JtKeO#bIK22W?!}28rj7kO0rJf~DJdtG@$1=a}UzYZM*1 zYwuJu2^=S57`i_uams*5BF0T133A-?6aQN~(%9a_{MI)1UEw#F0sUUDz7hKIhrfUL z(7uWOy%!(YIr-M%Z$9~z-Bq>^RdzMv0{8#3_ohvDCFymJ}8xlNOf|X^J8_t(obbp5Cx@HyS%? z0jdgX`~Q0~|9SG9dv8_UTU7wMfm3x)W+{Q7Y-oFD~&hFZ$hQ>^DTsG!Mhx(9yvzpcBM`Mdd!e(DAEvIvFe&Mcbe z?7=MR+_vRrL7nK-cxI+Ab-Jk|qRjU*&EbB+o!sm?=*%~n-9GuGVH$7zVjG9-n`Y=( zLOb0Y(LmlQb)cW0nN|IwI$S@`I8Q3ibg`sc^}>F2X4ncsDWB8QSC)1&Dh0XB7_pO>8v?%vhB zJUbJeWCq;Ex#&d4m0b_J(gF8{PS}xE1bYO(qxKGZlMh~M@AqLY7AEbwV;SGbtwjpRfHBTT<~EQ)1`F2sEW3Ij(NVAqoDj)FVBc5B?)kG;C`x*-af@g0-+8rn)R#CQ&8 zj6T?5Gsx&M!U0Tn3Xt4E)E;m1807{7-4esLkQm?Wgi>S)78=NHZIlQq^Z<0ja41|X z;febtvjBO|a_;R<&osaP#;?YnUS=DvE-y6KH1p7(u^E;;Kk|+%kG_jXMo6QGDm58y zJi|>t+I0zRF@V6%G4|sQ^Fv8F2cNVjoc7sE{PHsia=8?fhzi6W8UP2>NqFnTi?J;J zcV7OsI?;==;bP1%bAmUsitKatOfyTuX&BJiU}+_+x8h>|^mcXjpF6O@i{OaQF5!sJF5!MWuyQ#CAXS$W zToRh`ayWI2($Z#@fWCJ4m>Q3JDhBKKUefc*vd2_uU=9;x7@sY<+ApcuEfkzWn4)%@s!%B;3BT)w%q&cRNnIH%vwetwp1f9FfV&Sos(Y%t>`@)S#%V}_oN z^wB-LVW0I+rLQ8Q{D@}H_p9Un?99IAdnb=KXXY36K3vpbzl^m0S}nm z2;fHKkT7w&%HVUx>#J#Nm1oR&iCprNUPaDOdD1zfC3`p^CieF2l=*kq3D;#e780j;Gk_nm2wSFMxLl5tyN(iTE|$Wu*1osr)KS@^2H7FatkQ{^*KE#x zy#U8MUHZY8ijqvJrv6O+T*g)7;wv-GIUXd#179~>D(HaA&o#;)xCv$GosKXw$?T7& z5qTw+NH1tf8cVnLmYKPs#9IiYp~r8#8YO$0sUj(6oYgp>0mOAv4Kz*{_x@={-=i^% zLALzDx?Ejh>Re9#_hu$pNLXpoLQayYE-hkf0B!3iKSsM34<|dm_gbho$0hQ8^vts3+gx@ z*2;>n9Xb}Bjd5p>##Et$j*X){t4y!*A8h3bt``-Wp=EKHTw>xbWm~s#%o_W|yDUj6So?&(CNXvC_!%u~k<*Gsw@j z+$Xzn9n`bPvST`P@i_zh`(P&w>-WPQzItV7Z*_sm(k0$D8j>PcHuz_I54i2w- zT7xgsv3la=LkBg>ze_gXA7y1U)6I!-UF?qI5uFo29mf7*X8zC4&&PXY@s8owOQeV;Hukcp(OkZu&1B=>7h2s&sVil>Q@Glcy93fK zpQ+e@)js-WkP1@^H!l~m7SF!mV>G#7634Ia2V!`NYe!{;9#@i-xVKNFW7l>`Gb7t9 z(dMu=W|w{F*V@z%4kJ3&YB;<&dsIvJXPP&UJsZnkKhpkferI(s8b2CmZV)G{B)g{P zUe+=&0ZIa#_hsZa#}xGJDdA-JQYK(m;tJPI@*s;s7BaKo>=X(8aEZvCQurOwBPj9x zRAjsirmjsk0^Z|c)|0qdzF#u~EJmnhqkHDr&w%rhI-fuGA0E(***A`!)XdYq<~t{z zjb+nE)XAgMJf#NpCZ=XU{c!-4byj}N?Aq46e&lHL(!E2?8%K}Dtm%hWuV_Z^M)UU< zKGm%19UTs+{hKyI2q6_VR-GdI9oN%H(PV+Kda>&YGVrrg9gW|3{v|!1Ct^k}_BZDl zXLoSd#cUIi$lIOz{o;v}v2Epz!$&mOy3+jV*)!TIv8biTI$)8`T{ca>E-3cRBU9Lk ztHn=Y-v{A+t9HYs%w`6T?dKGFAbX#d`|sBP<9DCY>Iv1^ZyY%iv&@Iok*2d(@3FN` z56b0Pdf~u~1}OG5r)Ku+Jc~QcPcL28dwHe#kDq)NW%dgV0Cm#VH)v7TmPml|OuLk3 zL#MZ5D!*X1_HXHJ#JGgQqg#M=3p}~m9xeY&k;t=@Os0_ewUC4obq_Rk-6Lef!!8hKzE(5D zn`zJ|e#Q%df|m|-j;@@{v8d(@NhmeX)#OwR3#S^N_DjbkpG<(jWE!{N>1zd9evOeqKQ zhxAY-1#GK;SEw~tYhJ_fkAnB{zARzVvt3iqY)9C?b6Wdv55ykz*AE}nfnaG{!lRg$ z7%=k; zn3X}C0L{!kzenYAM~6e7umSdCCjZ?lmtw||J?N}p;qzl{Cv9BqZQTj2X|g^Whoug) z&TO9Hkg8dI8tH|7Gg>V)-Mplw>dZ<$3Kz-XJOQo)TH4LP#7*sqX0~_l&P%byYFVd~ zF~~W&t%Loh+!%1+?1O!pa^9=9#D3ZK-28$DA)eCm?xki%dY{~ZMa8T9ADf%A`vFC z-#B(jNAer9-|R0b&2o&NgGGYVAB`(TdZD`E1<{0dtx8tFdN|t40Zm`{3BkCA^(#c1 zs?{lRO?2u=glyf+sNxxzJ>4Shh6QdSv`s2Di9GX9@`>$Z1lp6^1}wl*!}l(q(^9y* zn#s{fzZxfeZfdLc4)#^0(RM(yr_35V^%PhGwer^Erjd70Ld2s_@e8rK{WjzcZKF5I2-nu8^NwOp2<(~$Yvk(1gF zc1ZiVU(hnwh342%3THm(4aiISHY-mkkV^kCt-EHfJ&s-ek6?(stgjq8(mbOj(Jvi5tP|U> zHvijCeiZwA?`YsKn?#zgCuKdHoSiL?N6-M9pc1Lxt69fsb(H?eYhRC<=)X93rukf_ zpnrJnS`1QDh75@=V;1jX?;_a`7yJ^DDNDF(d+MY5axHEc&jbT1oTAN=;~%{EqUt;y z@Tf4$sJ)ch_>Ypy!0gu!ABw%voGWlmOO5~iM<2(j=u7%kQ>+%UT}CKwdloBY`~oi# zi9rib5p8&Ml|@~_XRe-=J^!0mUW|_abA)N@xH{op;7h z@Pvza!-2H;S)e0JnQ6b40Dfx&BLc0{pW&IEV~bsauW3=n_z=hzp1QX>Io|Aw`OG2- zNE*P-smlc6RPboGh&J$m!-pS;_RH(5sFwky18sys7cS`v;g#QV0M&lNmXF_;am>;D zic%Tic*E25d%-rlB$SY&+L+`sBgZ@3$D26ZXSij8)~TW8uM_z650o)9G!Mb@^c4{>YJd9bWg_%@U~_a(p8sp%9z8oKcjAuVu45#IwEWy*ED*Dklo7I6#7jMkR;bNGD^0~P)9hI@e zsLr%Le3LB>dx;7um8jBLBu4)(W0r2zewiWOamIZ>DY3XMcX_(lqs#6yZpF-^uuE%$V|o}D#(4&4i|*%G2GJZ z5bSb--DRg zk>H!T>FxI6vD95!ApJNS-_=wa4)_~Fmb@UB9s(c?I_e2aV(U-DTVG?3TJ0I^pol^q-p^ z$vT553$Kh`^cnyp1jpy-63h(HlSVIxn?1XKw)wAKenW?Z?NQw@8+-fHz{@&SdqmL| zb%OMdFPv{qtK-M=*xPr#rxc@L5!HF<` z_Nr;l>D2R2)RE@9#@=`6r2Ek*Wi>~XuNTC`|Na|q#_8Ap_QMafuV05jF5QZy-LC6wHru3_tNp+T>(^Iv$PDx_N;nK&9eouIrD5hWNY(0Ba7tn_wwbt4g_Q)1DFacNFz~AI zFk!%qKPrA~xEWeehwYBdaFYlu-+FBl+b3)$vVe2po(}!sirG9pA$K)$d_#@6Pv$Sj zlIgQ^7h}mbCnED%p?ms9;k_@k#8#c^2X#@g(23$vJGK9iMv}FEP>rLf?h2rUq_ntv zS*w27@Qu5+9TLK?Tll453MUNo4xb`J8wCjSOZcrnveiJ9oL*RMo*T)eZQQ&l%(rXw4_7&LOMgf`8S#gy`p#f+4mAU^)C%*<=_P$MewlebqOBdTXsI*)Jvw;FY5#JjENM-)vaj@lPLk&P#&=cMs$?s# z+tl^TiNnU|hri7B*of}fgCF~~nb|+Cl?uz6b-bwM+{>~j^P{D~J0Qr`bVNUF_=K+5 zaq!(v)oGlveT+dME!(C8&0z5+OPf&1^^>8mKV9~c4i}r%GI|CDu<>=xp5DK&_n_O$ z>u0;AnA9VW>c#2tafqPK{7x-tMd3506Y(jp&+UB=XP*Q<;;)Ms~O#?=X1a z5Uyds)76Le!^Ybx*C~L1?RR*K^{GYH{tz!fh}VrctKtTu!{F-LZu$Z@AjPlaUMLf; z>HYBdp?0{{DDyUfMfLa@yoT$Mg)-8S<@CARB9_v}cZQnIeAf`)Mz@3kE0tPFzcoOT zkrVm)WSPb(Xhj!xjuL8M=dk&kLn}Waq`PCSr=Xd>lJvy8h6Tv?#aqj2tleqOT)UtV z&)Yhg@tD>JsGQfP=__pkscMZW$Q8a9{KNVQY zck=n3I@{b6ZF2owATwd&>-rPqtAA%XKl|=}`I(n>vg)zs@6Vsn@2r-&&1s+G+@JxR zdA-bZ&;+(?jDZSR;&VjG8Rx92CUD^w<4*9>&-%d%5b^wKzk+dPUAle)AsBL*zZ29C z=Mv>Imd1cnW7p5i5U%S7)#-sMt83oY-mdK*=@0yrE}jH%ScSk+?SJ;QUx>ZJNA(?I zmU5dOk}vDUH}01-Gy1t^N&o6ot*%i0%=dR!znt=Z{%3d<&8$;S(#&4h zUw`)b=1rY;&L^+VXiq7hV&bz*zWR?wu~PQf!Ub-^5pH>=+QJ-|@L?(OtFyDQ#Qgg& zyr@I0_G)l&@0YUkTqmCuAepQh`#3>d zH_w3lEJ<0km zuHv?-5&LxBt4!lyt1f1}`6LswGW&M!j-_}@%s{##+bY4M___wRd_U8!-Fije=u{IC zmE$49>v}aByT~*>{wG)3-zpD%xx&K}gKXZA;UGt=rE zPpffcoBUk6?(_qR%#fQOXfGY#h%U@BF+;kb6QnQQys2fIx4QM3mCHgb%V4|x932Jk z<2|Tw(MPr|euT@Gq{->pOnu0oBRZ6egQWOO(RCem#do;d4z#6OBfHkZ@eWV!l1k>7 z(c`!}&Ch7RGaYDmBq!(GrDUHkm*0(kMHwp>-EwBYX3fa*{>R?WXf~a1hI2wUt152A zY3P>z)peOH=CXaqDLIhx{$kJbrwcdLM$_zlaLc!GnALQ}-!IoCVx-3+M^QR{B!e6= z8qYD7VAt@qIB#}5a!2W~0iDbeb!)%H0W zf|U47P{JlCj544fHeT(bR2R=92!2Jhu_*y<*nA96F{Za`EI*aS3}$3EM>yVK)4P(k zUVVPxw=xrT37dD1X{wktMicRtY>AbtD8~j12eM4-WO-%O$1DP0u|wqtlU9Ifp15HF zOva2e9pu0HH_S9*iHhx||z7;iVE!`8RFN z2W~CgBf4Nb_zT<8FvF(qJUmD(*ptsEb>297N}nw{(ERYs&vjD#JWP#i%B!3a&U1He9iIr=(8Os)PPB-`s}|ynMTGqbS!SJurc8a$A&gzaY} zhoaFITx5V?fgx-RBTeF>M?Vb$^`FH7HJy%}E`ocs(&Zn$@(p#I_o>dAiEu%iM7N5D z^2}i2g0`;QQ60!9rEY5Lf6jH?0GRrc~F>=m03{r9JB0 zqthhM1f9CSIQMb$sa8`^Pcz6_^0nyNPq8yP!yRpd)@96bh1Y}i@NS{bh)>bUw#=>r z<02KTEMcaY6OnPZ<&Ib&Y{HF>9UbW3e&HqUi9Xi+!Oz}_J<<0h9CNw27c&)467Kj` zm9FjEc1wb{v5663$_3wZp3xHLUwihs=7pI9(UI;y53P*xeGCdy`$#@gqYq`aX{N`wtG>Ok0l^lX-g!%RdMEfAr( zogPtrehDk06328Wi^JE)?}eRqgR?f}eQmu8UM>`EwU^{s?KYHrZX>$vrM`uvb(G7& z0HTQ^>%x~HA;SE=$c4A)6*U}`tpGW4K(55o? z)~)0e{)Sm5*nK~+N{5kt@Z!oU6iq@)aR_08mIxE{+ZIz+k`iW* zso-y_ak!uk#@V?`&FSkG)qy^v4s`u_lu;?telcYspN`_Xy0jQBotPc^*6OvDMKvJz zG{^SNHY>aDG$-~S6#mX;D(_3&>|#zOv|Vyfs^iY6Ii2`uuqur%X`$eQxVa+r!|%iz z?wTyatzWJ$lO4I2h^(~vBkAsf&0je|}mp7UdYtZbYoF1aPo!K3HUctU-Oz#W0XCx$M&irs#)pV_wQ=-ep!9fqVzgUVs0sYZAEVQ zI#`T@QnQuc!B%>LxHK2b84+2PAG#_SnzXzk_@%y)HV!FbwaarFEIFzUG~az@U;IWc zY(z(ca?H2x>DbMyj-;b8r!qu%L4%HTK9+9ZRXJ`_$4+H==XRaJAo+Ww2YWv$Z#(sw zKN^$NN$5tI$8Y^t)0O&}6Qa*A&TIDhMstTcJ1T{e`(?bvd`9K5;dWaNrrcWBowTS+ zlrFl+wlQ{QmYq)=VSB>p$uB-7q`I6{2H2F&9lx+4wv@kL&c{g!brE^OUTD4*O$U!b zkNa^*TWhVvvmYKu(hyuJj_$0@+NaL*?C#y62Q$xfGB;Up_O?|s^J7;MZ(NJlGD=aRK9^NtB$y>4)0 zes~jfs#(^4SeLNm+aDqwuxtH>>r-{0GlB|gx-wy*&mPCC0_fo7P|>ExrbMsiF^zG` za6uk}QDZqCEy?az5TT9eJ1hd2^fZ3|WoLY+CR($Wj~nT9Y%9R+_8JvGFS7cwnYz## zqxJVllW0HSpqYu)X-Si>RWF5wXVjE_hx&w>JosBkPI}1CbVRk$!u$P+R4S<$_J~-Dh5kJ<)&s;ZK_XM?X$2F_Jb?416BP)b?m zYo=Dl;($BOut{7L2?a+G7sZptMWj6fvzq12V<%$f`CG?N#UAMO%;t=c7#)NabtdN3 zIsTdItjk(1{q}{=V&;fh;Fu|mX|gXGYRE7})|00iP2CwwW=n%az&I3&Se9bMr>M8+Wn z{D%V{ep`24UAn2guQxRVx|j|_QwNUlyiR0Z(9cg&=%bs99@u(%B z+RSR8?CVF4giY73r$?s^88e!3-KBi^_Oavf38tT3z7+eRO=auYdWILXWrZDY{89A- z@!(Qs_vpR#U%&cl^YX!i>R9eCozx;@Ob_q(>zZMvbNttzej2lvKfQ9LSsPg5FjJE}pGSq<8}e)vEfu=d8GgQ^oWKqMQ%pLG80%GKsi z&wi%uNXvo*uoSxpi*Hz~y+z&rB!mh0J{J9LcoyS{uQJ_$wV znG>$*JkyD8JJ9MltDzqqX?3EZ=QMW8#M4^V+IFC)Vs?-b`Fm=yxdCyPuo6cnn;)}x z8=XO{Z85Ee=~LPh&EDu)ed_Gi7YohZJ85}njhFnjwSi!DF2hr3%kR!?7oJ)L$8EV1 zkG=qwOdn7k!jC1>bf_n}Fex*hl;f+*OX_SasjM%mbEo=IW&NhUxysCQG-6a<EuA?y(#aYRlw!k25^gXe)g|}iy}>egP7h}m-M=fH4)pD$pKKL+=@w62+>#>L zmSD_W%RMrEBW!eRd2hUX_!ATIYJKOa< zEQP(VEOW@3%B$K1=o&N1!x*jsgKn32uvDD)0)tGqp5jX$qw4217$fQW-`!v|>dCR;HtU;N~v98?};e z_p-{v&iD>Bc41Z5qGqwLi#LO6n|qCK8&lZ2Yw2S=)2)U7*u-Oy+BkhbVdL1pnsI6a zuiVb!BRgAd&;u0RH4i|r}d=1aoa@vBQ;uNlAwVccptULzO!3%K3 zEroTD%TgTfyVlDrixQ47L4*wywh01Mg^i!%N681CED1qD4r(Lp571J~FYY!=_qM4MupFA5TeuRP zYdXnvviZ!=Uj5!Ua!NBv&os}^9?|zt4@Y_G=hK);33OH@?9}hO&%C19lfBKqc;~OR z+Tx1zCqHaA@)~r9ZNt>aiI{#=wsW$$@k8W9H`40#T9g?TI0m;Ukh0&PgrBMN`>+&g zGc~|rrz68_o50V!Q{IXQfPqQiWOw8j9pKq#n!=^5lXk9*hC_e}nG^s3KmbWZK~!h+ zm!EkdW}sQ#JL$#J;ZNtUH|O;G*YE!{4(VD{W0msG(*IP}bE7`%S@bH2NQ#A)C0(if zuy>P&^u7Bl&1rRn&x*%SFJFii8gw9LHNf+&W2dwtV_)-2r=E*FsLV)TRfqGBK6oz< z#Ji`CkJDb*M}o6KBM`bW0H#wXOeBsjPcGqh z9dEfQZIonuJbNzt!%}Uk$y~n0{K>*%^SK5k{`~Bx(Sc@Qflf8sEU2z{=4;)%xu3LT zK;!e9OB$$K)_Z9|@2YLhAD@|Oe(~7R=B;B#G*GSc7F1`j@A>na`l^W3ja2D*Y15n1 z;8f-(rb>MuGvOzF6c--d%mB{-thH01CE^w+pXFxa$N>-%s$)}~2t0{Xta({1aRJsX zkP;?{B}aSpg)!jERq%=w3rX?5yNqYFLMKmL&X#hINW%|*M7nY zVhMw>3FpbZwSaB(#AdBc9*mC%lG($B+eknd3M72m^JF1T;UwD$N-sX?elCB5jOm*JDk9m;ksU@g{# zqE=y)e=WXWv3Sft{lqF3X71RtS{fo_q?NKshl2ybSW3)nGfSpfj(J5* z2r9f-k8_{$AB^ut8*D*qF<20RY|afpkKI}>%RuB@%p5Lj&tvLP^x3*N1iLD7RJf1t za7DTyZ_JkI!Kb6f?+P}pW}I23otA2M#e^P38EanvU1WuLZZ(`2dB=NX`f1IK%C5|4 z@7B9(O7`3K)Q*YYG4SAB#j<61-B)=aZJTmASz^bGHyw6no)4?z%nF=I--~F6%_+NA zm~B?s!5%E(#x@(#9$J3PezGd!t8vzt`T^5uAxyn^@Bj|E!t3K4iu-taJT z_{A+`Kh8Hg!vFcx_v64YW@b5QJ8T|C`6Biz|Kh3Fnj^bsn%{fvJF(1~LvSuH%{TAq zbZ(XsIu|HA%<%o@^WV||J{mzkd@^QLF5I}Lm&sD|m#5#+NuP_&PcNQ}m-s&Ip91fD z&%UlbY%{U9YOC^shRMr^Pc+En1l${(o_qgxDND#rEq}6TP1+K=r_?rNFt2$OSVoDncp7unZd0xu}zVfBg9w zeX{JbK3B$)`<7pMu7juHmOVm{(W>Z@Uh$?;#)+bTCz(IF%uJG&TWfjiclABzU(%03 z5PloI__WoO8ff2n=7l(@=j{vUVrjZFKdG7WG3*%0@{=KrOEQzEirJ`YL64b}4tVsl zzw(Cmc*a{8l*p?P3ppzpOaZ0Ml&q( zyLw{%wCZIhW}ctfKdXV4Jrm7<(Q*Fp>SfJjUT^-*``RC$J9rExmNN```XHZcxV7ut z0L$+#&$^g8r5y|wB+qHU@vlEU6H8csc;=&q8Prz}9aaZ=MYFe;n|6Q`{3r!-TMfdH zK!Uwxv@%N`ik7RdRRA~PHm^?GHO&tH_|oV4X%D$NaCG7r42Wf{k>LoENB>Sqh;~~* zfle|y;WllNW|a}@CpziZG+X~KfBueUM0Yp;-Phl2oaa3Spd zZ$A7mI&;e^-^^H+l90`3^|Pg`M{n(PQR;4LIq}M!d(Hp(^Y^0T|Ieqs8OyLIojqqI z5;p&pQzzANTx$OB4^PLb+(pl9XO|b(Rkrs)I7|CRYH*r%eOxA~j zH9r0}W%fhy~tcTUF)E=w7QjgB)Sy<0Ped-eG!=-|OCVp_{`kBbK+P%J;EgFpSWPQ=w9z#XNxt+6&sQkM^Q99Fvh1i1dFaSSs~mtzXmakYOK z?nYpYax)9fs(~2s-UzdG&{7^buaqqWE85=mg@*9&r~z^D#)2Bgnx9h(g4t-wmG|1O zgY5tc4Qck+v0wJE4lUcs-n0HoXMk!eEj=KtTX5lB&-_ib0 zbxbsvw4x5@UL7>XXK&Dv+0dG+>CD}(rS#+1f}4I)H$B6R8veICGtTV0V6fEu`k|B%%;)-->-bwK4vF;JpxHJokj*r z(kIU*P&5l-zcXd`s_cDPzj^(bdETlkZE4Uqi8bJaoycdN!2?Rmr0;a-W_L|#uqB-Z z0AJ3=XzQJ|QsZ)D7yR147X9k+sKRr|A#5zwzN{bH2yW>BO%F1=L%%ww)nn-&Q8zK` z%#U*(_-)z>h$4RdwlQX8hv@AVR@q~`QSd(9DEvPC!waJ!TGyJ^{!P}*;%bq@{5GWV zCs0Y>hp_UQx`ewKT#qChCFD|4b?G@>|yECRiTQ%-DP_$kGdGW^XCh$0Io|8;&*Dp5jU;Z?{DVi;% zj>@*?nVF;d9d3UA&0mWpwR_ZfAZIvPnG1XJv%SO4#mEY&qUX~abexawInX?_|FCAP zZ#VB=IID(W>a25M&a65xbZoc!qSHl3{k0<}oBbN?|H*~?d9G5RtU}^r>mFlWpfb?AWd=K1dVDHD#tUo)QvX zGqEsdOs|~yy<%GwHerBvkgYw!$qXlR;h$t=Nkw3f-;i>|vi@UQ!NPZ+$DLt>JB?9h z=gwWf*8Jhm-j0SapNVPny3!%rtJtTC!rQTaq(NNtA6_pfaiL04Jr2UmX-EDY)su6} zcbgyI);SXD72abXWnwl!m&r<*?IPs1M?peNQUE#SBHlO!IG%^j8!MCk;@s)xk3M`~ zbv*}1YLTyOXS*fQHb5oNHnPZ~T-IOVD9IH`C|rl-XV`;Jjq$$uv$LmlYVl>AYW=#- z@7Qn7W4Uzbp4Hx7K1=rQ6`kFo+4XW2*-6=w#)6PMt|OeJC@VpmK4FT8nf?j96r(gM_qTNiTv*GgmstzXyvL)xKv9=>KV zuAj8>{p^o*vi84v|Kk`eVsI;Tu)%Dfj_s|iwaq!xp-~{Bbu&DpGcd$QBM1hl#OIo) zlZlFq|4dw%CB?c>hZL82#-kX1-KyxyE8L7M;1JXR4$A=;X3Cm%Qml%gu8Zd;gj%u@ zT3!8~vOdOeRqJOj3FsszgEEDkzNn6TNO}nIFp1donXCIv8JwX|o4$a8rSaNlGeIJK z@;bhvNH>1 zI{d^Pw1BNU81n?O`4-?I;*XhU_Cc4~7KtJ!m>K5NQtWBory1pSU49rq&VD+sT+9IT z2@Jl)%Zw&V`G8H*=naMw+-L|fi@tZ~t%*m+R}<^x_iF;fN72?L-?X^)LuVX#*$@1y zeDUI7M4u)1d{XF&_N1~Gn|;xTb&~dmW};OVn9XKDXtx@A)0zRI;UAy1>KOGQS=(ak z3}27Gqx)AsK!Z7b8#teBFlLe|W7J1W%6n#!C~JIOdGoJ2L*ef5a7k_xv4s4tI-ssk zhhw+V=DJp$V?M@rc%v@w6rNcQW4o>;+}2~T<6BFz7RGj*;SR6gMF(auS^B<1UjX1+ z$Ap<39hWO*jPD_HLUyctF@wIk>sK#fik^281I;s-ZPDjv`0Dx)YfN+Khu7j@eB;%8 z`fr5d^lTLR8OJ(2hVE@OaF(^~iqBW^{pT=K81ajE0lu!2kY77= zN}b%{v%@qzV`MDbj(l=z=#RccdVR9SQ?bE(gDfics-VEa4*hihGijhi2=Rjk}%hNc92pn#8Y zODxj8wFeB^v?pIL??bNZhzd8nJ*9Hc913>!U`iT?w>9+scjr$x?_Bz{`Ss^tYi>TL z8Kn)(L?d>u28k#WZykS8pN!e7lVwlq(_A-l@}yDdMmZ~*xfVK%2^1OOD z@{llmB_`}dxQJB1@PtzCRufX_DUaPNG0DZXpqJ-5M>w3G1%+$Vq=+MM_pV5T_*{-SM z{D<%VJPr+GxqNr2Zt>(QRKTDPO?9|EnCr`v)=s#LM-P~2D+;x$847Y!(qjD(<)V92#?{Hg~=O~w? zvvm7{W}pAnhabt_x3m?E8UADxTLSuGrtC@fbScQ%SdD_5DFd!sz;%;1C~}ztD=snZ zpb8jaxgp7XgFcaTHAuXJ%D8qxx4$Y3Eq}!e9w-I|NeVBV1MhhHJpBJcx*ToT;_SZO1{8)f8+Qh6D9UeNl?1|p5)e+2kkgt>E zX3YL*3Ge|mVh`=vs~HoWN2$N)XQa2MWIv>wsm0cQ%+U`6dtg^xx@-RfQ(!<4D-pcX zWgv1504a!*oYmQiPEpDCH3-*?wrw^yv&_}v&AktI{3v%)x>5WdML0HLK;)L{hegd4 z@*QOIdfW?JaabKI9avt>jBz>$ZG2uKI~yN|Q@)v{h14N%k6a|Jtc|ILu}VzZ)U~)l zy^0QC_T`M0KPboQ+n$!}a2fP4q7h0?7|toE?0;V#JaU5>tCrslHT80iGUJAGPQ%(sY13XNcEku?o_Y347g&dkOcH+Cj$p#iw!wtJvI}&zx+ha5 z2SP5V113pOf^YDi>|r5hmbEk3^1^k%k)p|7*E?`AR$W&IJaAjuIzN7L@ssAXP9*-_ zSH9WoS6;HkV4wErPjYQn`G4!w3(bq#3k_eEiTgutgifE+COo)Jll(^F_C@gN`z ziIcPCk7pC+1)lto1Es*UHD*RD(j#0OvBZeCmF^Ka*dIX$p7_~QF`L924~Ueoe%Y`t zC!RsDnynEa*`|Y?vldvpkRQd~lBNRX)>^`$*W@M)#ha;$4 z1a-65JWb|sxB_N);$|dEb~!M-BuUhXHexI*F-cJ=x+85f0-e0ET9O;0mEQrRp!w+pjdF5-%su^p@Z-@pPd1jcwHxwv$UPT1v=5R zQn)P7WDY0&+UFp}Be^8ZBxAcQiGTOX)#l9nf<9TrL4&j%QmSl6C$gk9JSF7lB-<)E z+F`}EpyZ3F7TDF@bc(OMnTd%KN#{94N)VXJYz4=VHfjN9ULEv`2V_};uuDcad7G-r z*Wp#^4{gL%Ozh1xC7AW}d#b>*r<>+h8|RthO=shGi@tlSpi-^6wuM;O1u$a5IK z!}4}?aa>CYhPmK>+^^O`kGmxvrSzvBY-) zw^mxPubNL=?PQ5&yoj*$L$vm&!r+9~s?~nSKPGNGH)=ppPuXPSB%QpbPGDw{^BDNs zoNHA183o^%RC5h`h?T>mxEjy#{jlK=$6ZVN@w6gs>}{W4S)TZ70%%0Z@cUykW<=ph zXP(aUqD?qm}8FfN`&_MGz_YC4&GnsAbKM0#Iu(^oUpM$s?r zU&m@|T{TYaUyE+_c$UPk=)4352<~c7hA?I8t8-zi@$BV#!<7B-DBBp%@eZ4h;SbNX z9nfr;AI%Kk0xivYz2d?1Rnq`7FUR*>%|NFy1eNEwX#a#)w+sq6@$hN~>~n0!_eca# z@c5-G;d%6~!?d6TXAx!UAW8nqj z#1s#c48Z9#F|)_WeOjGn_J1DUy+2OMzHnnsFFSQaWT$(2N&H0nLFZR)Hviz6SEI8z zxJ1&1%y}fbQl1`BJEG|n``GSIYfsz3XfU7CH;(UVf32Twr@`SiDK1Ir@{Jn)y(MEzO*3NFl5}F5eYpiiT6mttMH^Pnw*r#3O~EGdwuA;10|F zvzUqG3=-JhPDY|NrfZ%g_l_EgEDiko&(CNk=z8-zFTdIR;N@?YJnSg3j=;1!&d7gW zCwt#gL;LjnRW-O*x@i=jzSgZ)Nyy}u9-?HsmNlpYw1yfhLxQe~wsDKNEF?QE zn*=a=$w$*kT#5tF#EzW6MF78^!2Yx7(opMpUuEH%I!4rG_{Y8Y0>FuV2jV~=K2yZS z>F-!{P<}*bE`NPcho~e7yi9MWg&@`Ve+|Y9_>$M65D+iM{QKSX0 zbGY`eNBSTROSqSA-;3{F$4g%KgX_?A^-ZhhMFnD88%Or+;D6DBx}1+W}PCejwJaMW%h>Zh=v+7oW8T>^_R3v_i^ z&_h09@hv(_Zo zCq$dK6{kqz3tx#+Wbg@i7F`Gx2{8zGGoAB3=z_#PkU>rE_KG%m7%i~!%{Ym$j!FK+ zs^}2Ftj`F)IhqnQ!-C|=tz&`OPMwIabik)uyRdG?$n|;*Mx%i%P7NJFz=Ou8oj93# z`?ke`#mn>N?d9k|U)KyDpNg5$!CPzUUT1ObFPtB~2en8Cv(Wrp*-WAtyr?5wPuOo> zgt?4|%ddYKWZpS7(A2^%D8=>gZGpOKS?bIQ%Iwo6pU18H(UN7w)HCDK_LW0E z^$dHT`Gz^+ajz)*IbF-R`q9aI6fU%2hIUb%d(t$un_v4jeK@J)z2lC_ z!_ieU>++?-#4xo{>>oD~HZXKG&vE%w_}iJm;VL@WLnWEv zfT*J!1%3d+Gg_CR6PMtl3IVrpCD7%lU|?q6fglWVkvGw0iU=p+U7ZrFz9%lC%ZTPP zvd#^>RzTrzMwz)^0wvx;GhU0He6Bb`et=pGlO`SOndpquA7T^aCuwh2$M}t-&uI_f?pO+YL!D-x37U<3E{KlF|MK^L(j3uuod4N3epQ|5 zJ!)u9g|d`^R1R8W8WwQdeRGqxDco49c}U-my`#cZKD#s?yEuX=-TDGhigt`SE(X@FO8fQTl?1bjjkw z$^Kr#GPi0}JL5$%f~N`lHt8!8B}qW!6W7YePvh*SI?f+lztFt>`B{Cd^^m?@ez4DE zU11ut&(9uFqxR|M?1J`7YF5*i&F8e+Dvq}s_I4mF*QT?8*Xzl~iaJ{U*as|jwn$>r z=kIcuu`-X6X?@v_@56K0>2>-eNy-c0@xwoYb~12}TUNlcVNQqj!WzWQDX&;sJIM?* zdQvYxv;RQz&)@o9b4I5&|KWRY$AL_oY>Y(*<@fZ=iM2T+Ru6rI_3%0zXc5pYB2AGP zbn-Uv1UFu$b6dw0$#sbw%>Pz`8H&PQMcQjV;I}k}j_=xToiv$^+XAZy%E^^3LehWm zv9fl}uj8gjmsz9UzJFI8%%|ehT345D>b={vv#h~jRf6l67Jtrp1FIKb$Nl8Nd z_G_?iMysJ_cTZ1zf;_X&A6~y6Gtcunv};NA;aX2*Y4cg#j_ssdQ8#&9_DwffViJQv zJU$P1uX*qCwdUIHmF7FgPpBi^6SmIa4(%0xQQyekqvh@&&n-6h`5<)fD*0XX2LU_- z25EXM#tKp%>#{wVe%RF*5lq@x6}y%ks#;$G6(1-C)Z=Hm3a_9h%AN+P?hZ1`4%elo zrH+Cy#Hc1~?V9wA+D_zsP6@x}o0ZOt`c*+mAtYJ1WHbPYkGU0H?8y0v|nyWP<*FMRq&uotZb6RE5m|L zersyh{jjwg`dGrNC2ajB>c`jc+HW3)4Z7xp>#Oz~_PDv&U^P3e7Wd_|6&+^E3i~nf z^GTGudTB4*(n2pf&}x`(QzL!vojtMKke31@t8{K@yt4$+1$-l^zp16Cd}Db&{r1qEis7z!+`4yLrybA7=y6)&eYcsJ-mNq(4Ifdi zv$XbaBtct1OS&1*;MX<`Hct>qcfC>jEun_hW*X%;-TK{864uY*%YaFTNyW6ep^nZK zoy>h`_r8hM#$tRIn#0I8vLA6`1)dO2WC0o@%oy`?dv6j(oQH5obsNjKkHnxO11LI0 zSatU;4bm{iw!Xck^;3){{V=f2zv}vCHSX5N_{KHd;Rowlo2}D=vca-qI(6(}on%Io z_p@Hr@muv>?PNGvJ!3+57a+CFJn`3Pea4x>Hjyhpa;xB7){mebd>A6}i);}yGc z+l;b$-;S1Ke8(H^@T22eGrMS3`zO@Y27uKt|5|LJu^P1>I>XoEk>lGX++q~E=z$AY z2zEnH1!~@_kA28IWHfLhHGPur_7wh@#F!?JV*hq6$# z<(PY>$o9;VGI8SC@*eORU}m`XoACx+ULIGM7PN12x%r!OKi78xuQwlD{Y0}EH{%7z za%$8dT`Uo0-`z9&kLWWzdzx>Zcu5Wa{mqLy9h#-oe!-2aQZ)4L-D&>AM?cd3tZQ0= z&8M2SHNXGnuQj`8jtphTO!&7>yx6>S=(tXoJQrDXQQt&e&U*mq_@BA9RI;eFVn%;kHq6>DU_+%od>{>$L%a8Cpy6%|Vxaa@9ttg$LSIi8q!#Ye zwt*1q((j_~Vg_scg@Zid4+PyJthy(=@axn)9k}@fu^K+X36koPCIahIn&GBf6}?U` z5|R{^++lV}kptZNsQqHmQ8`DZx(7M1Gh^34hS#Em6KGF0A6>f`+YLzXD~FDSuEC~8 zU&GHfh397u>s0UQ*wzrNvl?yMGhhTSWR5F$1gpZRGny>%9orRom7QR?Rt5~jCoP;# zr_%Xm^aM{M@AfCk4c=js z1so2y*si4uUDl;eLUI|hX)4hIeW8K2WqpN#!M5G8w0PV!a^T=Tt->1Y^k(^_uoCJg z({gx6Jf({2nVFg9`0hOu>0n+A0x)=ZOC9J}wU3&Cfx(B!*miA9E1AQ6a39^_;`Rv~ zwwv;LYKP8R(T_F|zqCE3Fj<#}W%JL=UenrUK^>1xqg;r(q$0#R1Y7*AEP`IOUqHD<@0{j%=L5F_@+a|l@K=&boWL{= zI#C%vQAt!4DVR&acJnYgX!d8?;usgeFu!SWYjLfg2XnmL+G&53W1-1k;GdbL1TmO#NN`6`t#O~yAypf2Sw== z?NXgd=W4r7EIPKdPtV~~%~92hOFM4I{#Q=lWgqPqs$bbl%WBe8$NG)1xk+}G+tPL| z#qkc0>zdz2`HU+!;alp1(Sm20cN2942T_fCp)IuvOa4pac3dXHhY$OLS&}?ShteY9 z!@v)`4r}0ta#`MIBhxp+=6!H2#Qu)kCvezqLe?a-1}t`l%{t^`1mMW`vib2m@|P}L zC$#n(1{Qwf6Mk}yV-H)-7PV_DI^N-ayy1_&TbCcF%BgX96H-yL>Wf4iKjYYMJiDl2 zzj24bOi$b@*LH-0>&hc{r8GD9R7MQ6e04nO%*aKyfc9eNSoK5usniqZ4FGr3INwp$|Yp$E);nwdl#$*S94Q zZbmMY;nB@Z!Y;u!-OBKbVVS$>LVhAPi^SV36$0SMVAD2Q$_vekW!LB>9v)^KJSMx{ zj!!h)Y5w%%x0}xvE;TKdPqh^6 zsph8_Psh?}mY*^^{np9Xnxoot{LSMpHM8ohPRGkI_bN%Pc^^y+IO2@R_FO&{P@qCkFS5;T+@s?Bio_BxG)H? zBHe#_@oezq&>pVZzND6c1R4c&ycs0eNCP1RY{89Ti79>ibxMO7bd*xra7rxDg~a)n z65GcDyNrk`l6Z=GxJ2qA|0(UjuxH_%;RQd!U~w5Mg$BdM%Wi&#LkO}|HvA|gZOmu= zE*kW;7)l*Tx`isW+=N|Jv#K={*FBjw32dddzM7~_5msf{?UFjt=NIRiPv)*P2erE3 zpq`y^F66L1VTT%{NB8X0v%FWQJKs#RI)zcvhCEDH3cH?cwIUWA<0b6%-r*Oc$tHkJ zHbizZXKT`G5F~BJCp_6W==$pPGRDzGMu(^>w?+D-DRjcRZYw`&P)7PQTG1~ddUh|Up%n1@vkzNhml;Qxb{Ue`*ZJ38@wAwpfX9Y{jZN1M5Hs`TENRpk(|NiPQce(BVy zur2M4NAV){{P@;-*Ana3mNo?6-+o%{r5zgB(m~iUaD}Z~o*0`@a@!)(Vys`{+L%SW+)*ySiG7r=KN*gmy=~6O#OV zFSw8=PLA(~j9TI|g5jlGPym*J<_S5qp9RO@T-mklO`5G!(&YF=qh8gkJ&T4JWerGhQEGVB>@w;`7zYPXLFl zpY7GDfr7vAJ95o&4A=DLhg<2ELtt@rV-oCfzxQqP)v*x|-n?df7jG?V#`jt*nZB$; zx2~@&s@yr<%Kz}H8i)r?0X-S9n46i47%kTKmNjcehmL*Ii|Rn15I;J`N3{%`CC2;i zO{W<#bw+92al!VkI;i|E-JFX9!`P#IQGRBk;XTfQI_Ni}gT$DDIl6ZyPT)o#KIKEF zIS&Iw+HT!VCvH`#44t?!E6t&4%&=e9-sxDb&Veg*((~x?xHE%=`DS`#MjA2vCV6zp z1*d&Pr_?cXq?zuncBZ8LdIr7Nhkm55M5nfib1wCX{#YpM;Hjb;{{j$${c=^QZ|8jRZiB~I*?s@{L@#uwJ-;PCjI9#smL zCCZaiSNk_f@4gsBSWN9loZ%L2wMK;vU#A;2NJk$9tb=)^c%ZFWYJaU`>oO|2b=*vDGKbCB^3^7= zgglrdiWa$z?8r1i=3WKuHIpJI(>p#ZP;<$VN_~mdmi5ulJ`G~Q|mgxjOnDEPqv^;0N z({9c=RYfo0%UTii=Vw1?j%oG4H;hS z!$D4WG?;lK25>&t;NcB*vd=Fp#NO%inz`m6C3Gy$w-OcURJtQQokS`ShV7>mi*rS! z+?FVO71%LB;Xp+jsDdZDEJtZ6Wv@dMgk#;{$loY70at~Yl9{op+53pGl9k@cTO~AW zH3&%&r7T5I%kMS1ahrFU3wfqrk3-$Como)*=t#Hu0Cisjhx?p)X1>t7afc4@oQjTN zALB`Q&;kr-VRueIk8Kk=li+?#O;UFFVeedya;Sxk$_pWmeZfnmQ%Yhc_gDi<%R2@o(cc1FZ^*-Z$e*G}k@agQ-%f7J0ot=DR z_)a|EH=}`nzuBX0G56Hczp+y*c%&Qk{SCb*G{&j=JDmc}pd^2u5sg=X8E4m)?tx3N zAMZM*4=)5gkMVopEecv%UpvFMNNi?{edFE5TePpjIz`jyEe-5-(iAi&Q?g^Vi)K{~ zDmYI(t5_fnU}zP0zMP2w@RtZ9jDS_qkA-)bUD|{@*4Aue+ICD0tNneN)p*r(kB%RW zNkv_L?00H5plzRhZ7_?$wI5OTn-;hB*EoGYc)n`CVMAQL|C27&iXlgpoOK|=l_eD) zaIU~h-j+PW>A?qmM)u57Ar3lmfN{vipP9*tkpO|BgCMxW89Bj?*OC>V#HY(VOE`Ig zpD;T67cFs%|Ccndzp2b zE2nL<$C%~VeAenS?NeUVZ1cO9&+6oE&6sIV@w__YG_uSIRp?mX)$hGa+AA%C{OI!! zi``PZ4*C)ehlp{J9Fn+Gd$gw2asAq{=QLAtJUZZku+E!5@AMllyczqLzyI%aGH#in%*b(MCiXMNQ8U>l-Y&gFHq6&63u^NeQ&xEfb|6C)Uqz(J=!| z)6vGjU{vw#z>{=Pl6=DE(js2b83gZLIv=NLw|b4|qbzWc*j^3rq#4Xsr^siCEA=j- zrN&VBcw)QQ*vm%DF-~Plbz4?abZ8(&vaY6e2IR0)lCiro6)G~qWBY=JAB!8-TA$d> zy8IlrQW9r}EysG)>ESf)>q}aNqm9Ua{Nc}HFZ4fp^^KUZGTm0Vn2II;bgC&gJb$ko zIuf>G={Bzi1|Ip$)-^3L=hWnL^YhJu-XCY@bEuEn;uY)8tI9c|w_ERh=8I-be z&;pS*<-aLQUY1-HhJXPVQXenq#XL^oMu-_i#P~NfNmAw#z!oFXN0M}7uP_5_yl*%6GC%9v4Tl{BLQ}nT;uBVE5qKEyzs*hegX2aX zGAMsb15@+2INDV9%w{HTwTrsN^~-8_YsVpw9|BccvjT~?m55x9_<*!v!G>Il@1sO{ z5imGaiOk7O_zFZg=sPh9E>VfE;=&UQ1s#Gr%z%lwgYM@u-ax^&;$0;zks7-wlH2VS z%c4pa<}kd9`~)m3|12lt`m7AsH9hN#k9j8o<0Uf)mZQ@t-9>1RPHAzUrpWK=RZ#{; z$jLeep!S1IzUn{94srZyCinv{yN*+^;K^&foGz!|NG|s-tB}WqJY9oJxCQL%6PUVL zpY`Z!oLcXe101_CmhpJHwnfgBN7OcGmlh*i&&hI&7cf7)3)KvLP3 zXs$2fgqrZ?Te3HIe*T^`2ahpp%^^zcYo2r&Bc1SFnoVIgnWfmr_szzP_NBdPpY=5k z&e7knD{P$gzoQdBc(9jsxEiO9b4oaNySmJ>#%Udtwl6xd!}x3^%yt_`1~jCN-)eq% z8pn9R?04KSJaCL>KX|KOrs#8qinzz)qH)Z20|u)my|5OI+NU)O?H5vq7bf;)bK}}i zT#aMgFP-Z$h2oEX8N%e$nRnDty}W!gJ{2^-yc`X--5Lzp>I)08a)E1SJ3$>`Zj{X( zYRIv4m(LCDTG<{emv-x&K^$LlSkg)zX02ICLG6D>eix;+a$qey(~rV4yePaSp0%{t z5}_TY(PrK3#P*YAiG6q#eli1XBdy=1K33HnZLYR%CNud}#x4H!^ZsgM*0v#~4zEAe zYf<=-8ii;0T6pW%Xsa=;F>-4E2Ia!4F0%G-knKjuN1wtiWznznz!gIO_Tfun+i)J) zr9OX-*u*vN%EW-sYnvRs-|te>4d)_m>gv(a&#VhJ|~ zUub56^zG2HXpW?LVfF+$)VzA+8O!TyWfYnqu})T}h0+vr~< zosLlXL(c1xdqm5wf9u6>H7E8RY>ukK%TjD+lqbE=o6i1j?Y;hu=iiKu?+;%6g@)PZ z|M^e;_t5tX>4<5qS1wXTq~Z6~7`&zF?DD+_Hz`#^NC|vB2RWRHiL7)`e6Z841Mg{A$!cQ~Z^G#r zPtRJ?PRwAqp$ksvl`WPS7mi6K$;@y^JMY_iC+lGZQXinI#*8=tGir7!lNnOPm$F9( z>H-M%XhtN?iMlRl)*E6hWkbnG{cAj4bk=D?6o5C}k?sFnEj2jhCnUf`rKLJG(%A}UZ5vgu9QOMa$Ldc(u<$mnv&5O z@tSj`L7?>E!9CYkqOzi9YjP`K8N+OqcpyFIx7tRJo^2@9X@=K%I(~MMLS`HIE^>4* z)rmf)PU~^aHXqcC>fW7$rvTe3-aqG#E#VdI4ThE-Q5WU1BydXnS!#x^JYaOD(cNXA z88T*vcRsZ}I?2cO%r-}~WSnK=ERp5}>`7;zYm1~8yiTN5CpulbV#b6+JxEh+MfBgP z&PR%24n=zB!`1N9aAA)$Gc24K4lReLVT1kK_B^=8gfoK#)qcX&Rj04^8^y3b3)1B{ z`%P23bzJT0NNZOB06+jqL_t)qamMoxlM3g@<6?COGmy+gPI_U%bmHq8`x9+y1mWkh zsU1$xFZtbxu}gAl|D%;W>KV?AATKUyAd=7La2VWf)k_@0h~^Kg(?Xq@4nxYHCDe#2 zMdsdpt@hC%?BebfmA$8$E6e-hfJ44(&a!R}DdRnGTODp+qjJDS(0HR<+fn6!m1~qkH-a}o%45MItq#i1Pp2NTksk{)tTU18vx%QdGp(um zal>#7!!>@*qmF;<{5D8!oNHl@IZa8Z?j`XV612k3JUGOQ<^|GH+L z>9n)NdRlv};l)d4s~d^vVO_GNBmKYp+}C18o_*b%%KhG@&*A_b8Vt@eY|l5V=d{%F zPd<3NIi^nk4_RJ(m)PBM)Ka|oaGYJQ@fxXWKH?t8wB$U&@mmeQ|+OiHp zjh6;Rh);My)Y4l+&6=ehwnXZtxV&#Lm86-b9`jWskt(aM8PVS#+e(Qx-H8VZc?AI3uHdF86 zx~>n39t_ybOz&#mJbpsu_KW71Pd(SHs0?4Zc~e_n7MmY_ays@;e{}6?bfn>&lAJA| zK!n9C(iMROjJ8Jw@8U%$^1s0KZVNjGqxGd>7#92}hQY?XRKWmtK#9Mla4x?k3|>L% zu@l@;B_``lguH}G+09Wd+b$F668gYyfL~!ewkJ{o&!S6GV9@S@3y)zrR`~N-N46F) zpz`Eep9QcT=L}3~fULDzvUj)qD3;8QD`OH25aE_O;f!-njJjMbJm6mKRlT_&(z zMsv9Lb}m{4o^GAGNkpcP^c5uJXO1`psrC2eIP6?7$^XyZn>|;Sr1xQ`R-mvJP-|CL z@6+3Gh8m7E3(25{qGq%>6xJK52tV0x{+k@(CqFo3g&!PYk15k6MQbDuV?>d&^h__+ zU0q#!p$aJMKyCj0zVG{G=E-|6fO`Q{0Vo{cp3MCA{4!6TlP8xWc=!Cr8fY0$Ys)y( zJhHIPxNs$%;-&3nocP0|^g!)(rAzOY^e-m<6c5(ic#BiH20eIF=BIA>kjCAr`N^A+ z0Y`k*(dD#J9^52@BWNoaspUFk+PSp8V&5#z9q6OzKu_2x^Tn}gn*z;FG}l4D$b)&A z7#g)aa71MVBPcVY3l`3=E!o2#-OdY|>;C-HFLTz>;TphU(k+xTyxQ!2KrGzq0E~Ph?a1gM}DQ`}^#(CZFIxT&=l{}JOqZ}>X+O=}4 zLJBYATT!+rX_QfBMjlXH={;$9%P1R(M*GYix$j^k`Z)HfxQh~$%a@Wb>WVnHCnQM{ zlNsUarS^aSo|qJE&a6&P#c5bNakqh z=>Syv+sV#17H(0J!iaP_Dht5P!H*h2e>+a{Glh6c&Vo908CA&FXn1;?qe|ZvmF+@| zD2|Hq(@$J?Jm|rW>c4#dyS9#c1k<%SRazb4L1l`Mquczwe)=L@{H%HZ+-ueucxsscF5A7 zns$LSIO-GfL2Ba4!(qA(9VeLR2<3Gu&KfT|wLMDf@`@&*Mt7veH%Q3bDgWem-S7jA zWzH2cN&i^d5`L1g0tv576JExVtVyKb$V4k5g_V4cMDw$2>>%~uN%D>i*C^6_>bLSW zU10z-%BV7|e99i#DZBa%$u1ERX(9kLI4Vw!cX*3BX%)5F%ETOYw`Jw`*G>^IkU1L(cr3mfgpUPUfmwQbaB_;hXw$`2Q389rXavJm}GxwS=FcSUuw{Kc!^!u;9 zg%_U_&F@^ijHNh7Z3#)R?3sYeTBgx)o*WrzUYnf4Y^1g3%Ir*Y9Xoda`4^vAzUHx~ zg!?Q?5Dt+~aw&GtlIVidi3k*;JKL6_l$&MOC0pV|1;wTtY5W@p(pGna#NuY6j8xdA z?J2Zx+30N$%92hIMg9sTp~4j4!{`PcVELcI4t>Mx+%Lu33)Km+sT`7T6%j&&1BYI6 z6vv;7&%zZmc&*!zEehuc$e?1fG)_vJtuBnY_J83$BefqKDg(g_ayt}8QaLE^Wv0pQ zai%TU0Cf{gmSHT-Q!P}4Kq)Gc)Tk!|JR&^kb?u8ciHJxNp%GdUh8@Wl6}{wm2!$5G zNdk4Bvow)TnP#zg669fQ(93k?$n+P$L>>etMtCO5IFn=mJkHJozH8y*s>gdX+kIOjCW12siTE+!aSmMe!*mvlVMHDBk5?!YSKe!=pKarje-`B2I#+d7Uosm|tv5n!Be!f)U-V4my9enPL4>@-caw_d8X8#<= z!^TOpTRfi2ESQ#+-4niM8s8)zI%aL@{Mx9^+&MQgign%CObH{=T=r8ejqWHp)#JTU zqI<)N?%v)3Xq`PdUm@{E!!Ck#6FSOkt5$F(0+*?*V5Y)$+Pbak>rGc@x;N!^F-xfL ztN3=h?*$;IZdd9-=9iB)E+b(@t&pCnalO*=qVP&fRt*<^?}Vj=WqgM$q{z}4ic5T{ zvW%0CY4IyAz8wf{s6h637#1KX^-`DY@wnbqw76QeCex$Ale~D!PaS*;GnIBKqMml? z)Ga*@CtJ58b;^QKR=QvS4GtL+6Ld2tZMp-u9|5O&FygirbdlQIm1>BcGPR-{IS zkw9Dikxcf5Soy`@ngbgw%BPa1(|ixcAnJGtl`_GII8C|fU&M6iyNk0nEn1|5gvm#P zFdqaSx|TnCE#}3+A`zHbi>Hh82mq0u2a3iY$8#eulg=t!id*B?3&CM63NE3+uYWK@ol3;M)u-G35jLT}@bp zgyEei(-{v=VV~N4RT7a4?lUX$m}O^qQ4s2S6sVOWecY*0n)?bV-8HynVYbXoT3Y%E z6`{0wg?FD2COkrRq#vPuxrb@oe|F^)+obVNKl!LReQKon`uSIyv*?t56CpPnn?_td zlIRy5(yn)>&o!@2oNnI1&f-_+W}AQa(a-IB(hu;tE>7nzjW+uMQ)u?5OsQ7OL6JM) zx$aQqznwf&a*~dQ&s3)Z%OhK5tZhFylD%$;?CNHHyKRB)P+r!Vs2-nW%QY3Mj!PAhXmlUc-y)KKQ}C0-S>i5es(Z zE7@D>*<3}fvS*s2k~rl7rX(0S(NjHT zc=qKh@d72=Kwwej!T8APk}{_=SlUp+lObCnv5o~sfJ$z@Km#ELOCnLEB|=)XHd<+k zZZQFvu6+p$neG#CTLjWm-a}JLQ*w3Rqujt}6LX9lCWW9`5k=9d`0Ua!s8Dmgx>-UU<3ql-^sO;RaS* zOYFN^tIF*y8xN8-+^w87VR~fTe9kndyL;!;uPZj;+&P!OXwLV93ut$=F{$mMVKtnv zZghlibdX0^Ng3aWr#r5jMWU1jr4N3{cA9mpbsyYfwpk8*IOFI}t1ha5DW{BYQSSaB zQ|45s7#@r|#PVeOzqpQ(J5-JpyeL^i`s~t}Eh{;X5$Q4P)J+fYB_3Y@+OFAHKTQwp z#8(Xltq0W4uQRY-pE<>SG1f61owIz7G>dDvpW)?zFO3;g!*1}^^`=hmhR!`CvuAeE zzMz&_dGYDE&xVTilOxa^k-fLPY@uEtc;}$>fnU$U>sn3^*PH5ACR~pAOAQvU$yx+J zT5q8s;@e~%2`-$2yPatPsyRH@YMHx{zT~jlmvwq$_mtEsb%nz9N^dNAFJsFbG0vPS zx6i@uT0K=hC-wFnXzI!%JYfw2a|Id+hp0R3?^TSan{R^MQAk)uaMG)QXmna+k^^81 z4z{}Et;@fLON8sma#G{h5-e%~%}4{PaTG=3uk4mrM5nNBgcE;4WL@G(Aqqhj8O-vJ zn6WrtXZ{_Yu$2I{@l@|LNgE;}4vdmUA_n069}gQVxcNM4=9U+mb*zu$cbfT0sKaBY zJiu~@vIvoU@VtEboPFYI65o90bZtHWT0I3RcTT>8Z%gxf*v{1YL2|&l<@HN8=L+Qa zc)(Hd94eabn*436-r2evm?v+S5Q7j(GMzxX{O+e^(yV#Rq4!c~lqx@%||Jrt7zc4o5 zjN|iHZ(=`(iJ@WaM=@m`>r3MkM)xA`7hD!I4x8SYKHL2Fzw@2#K?!+QPuPKaYLZ*xm5|+Fv?$<0RkjqA38T6#K)1F> z*%!@DhB8?)EvJQ{c5PXf{P}1lI>6jjdeEm2isy!?+>oHk|Tb# z3BBut_A60lqtH^o2&9+-&{PI^Quw4j>W=%0&=4nrsMmxCM(TwLJ>rx4Bw(8ns^gvB z*L6i6jy(J5 zvu~1C%VD|jw-=7Otx!86xLO5w3R`i8`|40#Qbrk;^=)5eW>aIW`0!xy6rB%!(r{rN zpBFlg6SBRGd&4PgLdTbVTqnJZ(LNuIKGqBY&iZAWH-eU<)vR|XFjCEf@!dT5Vu<@p zOyP4}^TQ*}8EiDhr?dMQjr59`wfC~57d&(BWbFBB+eyo}Bum-TCA1k6=(rDvw=tU$ zZ;ZEkyfKMkYtuGMOJq$acn(FOFk{MCt1T@N5pQ!dMW>3(<4#&STk#O`(g*T~ys7lY zj@gL1#woqA#EjjV1(|;LW3-65C@m}FJ8Xf}l`6Lqml-mx7mw(+5n>IrZX2#mmmNDt zvY(;evFipWx|}ZE3X5GFIB7i(2l#t2oXboJ3z9Z9(+-gyg(gybN)(~q0u~^rHZvg8 z#3mzQoyWCCK=#TW$w_pTT^S#-UJsyDQws&iwBttZsBBjz6(;PInh4tgtjr<|E7r&e7)*`^y^9+rI8~uG1@Ye-uF=>1+I0 zXm!Dr>>Z4(jo4ii-`?e~U1BBUZpFjyBp%mw7{ND&s=EYbc&_8wUgtBqkfk_7wi)El z*PauVY0~LXIby|mhu$Nb3VNlrzd#u49YUo z1}RQ*K0u`9za1@j&L>CbxGOz3gBoP?nX)N*2{Tg`u^ymEQgk(3B*VkqaJ77;~!H6*4YO*Q%h-s z>Q9HLP$u^$-Bm`EOhWbQKOsVL5|A~mqeFdXVG;XeEa4l{bJl4-evC^3{IgdaF+4vq zh7swL&86{4o9V!j=}~;f@FMnlIEgyQ@B8ld!Or|R^t^HUbn~y@|9bNob{YR_{_mQ{ z_EI>?MNaLR|1J*LA;Yaq(HKdCg-QTv7^O{Tktm|2Ig+N<*K!FzkX-FyqKUk_C~tdg zUj#Og@=Gavz_`hWyjrB$Xh^rf>ZJL>`$) zY0+7(yp3QE-N!j^l3(jjc2#zm8nQF*__w-GVE96ONj>?+l;Vge|Eyp5H@ip|2GvzR zz)nZ9sF%9-q_0jNCu1R3bkYNC#+S5GSmNt=to;Tj&2 z=U11SHGFdEWj^ei*tlYr*H`UkK8jHdjua2N6D>=>TMgivbN;%GH1mSi2z9!+_dubj zM$?osF4L6H-D`WKsaHq~WSOk#MSJk!90}BC-L{ik)=`d1rWbBV@7ig$%zX&CjCoIcV__mI$D}jhz^>8BC+t6pEefAub=l zs5No?tRMQ`_L(d`H0jFT>^2sG;>U!v$CTdH1;P>yD9%lONY`_5d-2VE*!CvoyvFX~?KQdF(6 zPK#Eugctn9g|viL@h7XwI4u*L@c|EIoa|+MGxen@ikIf}iVtWe+45(s)Wc`PfQq+LJu#GLz&a*A3|UhJq@^ zU}Pqk&~$XG`46A} z+`|9nz34! zW(S!YeVrZ}u{G0|Cnjwz^&1n@v6lKYUKFB}jZXMZ9h}O289Q@xw!}BiU&N+Ti_ORQ z?zLHs>{yOchT8d0HW*mM3A(Ka^>PigiAKe1*p!?L%&hrFj zADqTIeJ&bczIxStN0bHB87CPuu#{kA4Y5^Crx?ERW+`pBL6PT z^ThIJn$HIeb2{mkOv92U4mkw^;tAdnq=!s;oJWWcaSk)A$wk^gpjO(r0?fGdguFmg z1PBF|A8-l_oQ*7#+yo_Xk#<7aryGAMnykXNMxZIQ&?TLwsup0t zjuWYB(>jO_0WHt&KXI z5NK%!3gv3tZ}%1-+9=b97v8eB!0aHl-Vn)_0~$U-Ct(>c5B}oH-#4G$yWU)zpTR~s z>&^eX@b7J1-*+y5(7cA7v%mk^2e!U%3_HJesd4a5cs2Y-U(4U@Jl|iOw~q6_{pC;a z4OaKLDClo?cqT9f@vW(g7(JY{UD2P9%VjC;=TBB6#d^jAb(<1wI3uu$6`srCZ!)ve6TtXXX>Ib&q9(L$s|_oSRybC z@?*A&+7g*3V%gR<-ckZ3{SJ^sdGE~dSo0q0(V0_YNq)Z(cA94}igz3B1HVu1nhFDC z0|g_VY#@o%7knxS_1<0-W$c#Cpcsy39>w%u)&yy83CPY0a8SvZNQzhWS!ritJFU#r z7^jdp`B(I&zRl7yukQo}7I{*xrBCu?Pim)SzPwbY1@B>Nx|IaJCUbGp8div?02|2C z>=-?69-#ir9a(B_q3z>+<>ax?txQiIJz*o#Z%s`%=TJw#b@38LriYr>CZ~)}`5Yvi zI(4e~$8WsdeEQ&i^EWrHN6vLG&iCq;Y~?|DDzaFCT5SC!Oip@$D5XL<{vBUS47$Wu z;Nefhs%;KbYI{`X1_!MZd&&|ayAU}@l&*94xjc(Yv?E+6?J3d(GITF#q<9X99$+WNal#*2Er>!c3v$Z$kknxTSHb+cx3N z1SGOZVFJ=MDTW@`Ifbg+WPZASa<8CeW$4Lvm=Kk3YDedO^wS*JC9Rtp39BtyL)p0zuzAOjdfO8jejzHhXNIZ9&ee?vM_rgf= z(xYR|?WI}xXiO9$1|4_{fw`r+GWv~qZzU8Sq+ zJi~PRRV)MG&hF*?up;Z3q&jX^H-w*mhV!V&>7lNtoc)7fj+Hw(d;eI!dzRL|sMFmw z*Pc{b)TI>R1XH>jEn%V)e(#2*rDc3Tiz!0RDdWAwl`2KgP;v5!hH@1re20(YyjPhH zJb2W3aJ_mn8#)3!a5u> zObmF#G&8dx&2}YDi*$B&y4#cU>3C5|uImSs`tf!{cvBKKxn@7hAGQ9vG!m;KAz1R| zetRHQLP#kzUI~zX58I?}4Lf@5MBiDu-(0zWy*ZEdxBR^f2OsWl&fm9=?RA8=@eSr@ z+&BmBJn)=2n($R4z6)HaBzH_CI^~A7b~~sfOQ&W{3Ux z9kkjk#~B%qs^ibTsWsfe?>Vkk%o(#?m#NBEexY zAWKQZXKCW6Mn8zlctHmWsT=xSH?Di-Cxl72z|!h;g2I|WWH@r4%tXXUGrfx!9H|hX zH>jZ=U>AHAyCc7U?v>^Y*6;UwQ0C0?LNm)3N?6J;hZjJq1!kC;1wB-SxL)p>&D0-e zvsQ2>#{%lSb)@<PEwPdR45Jj7R27s)gJi3-h2hSDb8gpN z3bW=k^0OrYqWDUMN$K}QJ^W=$LZ3l1JY_SKiayzu6o=)taT1Uiok7>2z|!dO@CPnp zliKCJC;p_Kku1!kmcggZ;@WESn|pUqws)KVa{C%y9-eAGm_FaUj&;mG`r7;UB9ZH; z`#s2WVRQ^HESIo>nO~zU|L}y1Q$n^`I$JL=L&f{GL-ZyiT_jP9q0i*vWLjm9m{KV1 zBJi~FAWy;?Im*Jk#`E5t^C^p5dgZm$W@aOuUcMRhni5|}+tYSNp}(E` zte22Fw*X3iD3ZNmkuv39@IbO$cf5j?C<;1euM=oD*xGPlN9K_`GE%}@HAJmymmM@|f5 zZ8X>T@y)p}04%RFcFeg+7}w!F!_9mZBitN;<~s8ykImz3J#1u}yV;JPJk^}VwC(AU zaT{^lEB;rWnEx_P`r&R(iE>P+Bt3eRT5`9u(Qea`dnYib*TLw^eXEq z+k;1ZiG-3aUwoK65~%`|#Zbgq%lRPt80%oujkGUgu-h~8;!`tK=6wm+gqSjwiJEjb z^wm1{OPaoruW0l3e4{GDI>GhO6t{#?$R|&pHFt*>Z9VJ5)fL-(>;$G`_p6N$Ppmh` z(Rt>z@Dy(jv8hzOe;+0bAy-jQ439Sxe_A zu8`swuT_-kGEVvzbl7*GrQ2=Nn-aZaP+q;yt~}-*JM#%n-Egci16B0gT_$A36P5To zz2jD7yvOvWKsG9r{!|cRlw%{E^@}sYiY{Rz9~yBGS;DJSg^=D>{*gPuGh>bknP?)H zNE-;U`7p^t`4_3HmGUgIQeWurq*WG@?@SL)rXMHM--Rz(<9a{8f77Nv{n6LHjc;mR z$i^Jt7&{!VO`dNqj!!p#^4mYa2=r|8 z>$_hWj&xDd$y4kw|Lu({whF}nUpKJY>Drow$-a-@7FiAc zw3aK$cUH=CnIUSxfMWTR1*zVK@N``z{f;PVq-#$2h*bHPdAje4T>7$p$Tt}sAh)hp&lGcSZ+5)N9rpv}UnZz-cgoKc#EGwyPLOt& zQTM|HrxxZ59@so{PP~OP#V2vuTtJa9-8cy@TgVf65Gr4NASzfw2tcCjVU)?{ec$n| zOO7D1!&pTn$yfn2FDqf8C_2MhMr0>Ht0zNTP?ZdUfultx5NQK>=vdm@*5>fAmy)6Y zROgiUcDX=?F?QA^*`_=wOnQ=_PCt~UnH(~OxmM3)dJ$ULi%v?&WH6jUpVls?{w8H4 zdfK9dLRXUfknp6%5oD@+4#YWZYwf~KWWbG&ytRI*B`uG3TGS!zea8%oj#6WqxE5n}}ikA3D@~`Ctuq zKX_YF$7y?c6%fy~_)1(Eulbnnen^reGvpyS!&3X@Jin_!fV>tx(7)UqhJ`Ccu!cNdI)^poBXr${0i9W;~l5X1ryL(;n zp@;45B0gy}gO1Q7)-i9Vy!r6P&bM~s-d*cAbMA}PzL|A9hF2S)>~0~=*#TVQv4&+& zoRM$}J8JiPaMYTgvf`c%oOQ!>@PjNH=~wUbqo+e_XFfmedtcM;_(oEh|GG-cCxR>8 zE8HqwFaCW4m(%JhE4VUJrWGfT_!JjCL&A3Pz$RjR>?kiaf7?R)T(>MJEiM?$IN*#7 zCwuZi7`=ehv#-*|CZ0l^TSI%K&nQ#MSV(Vrs5T%8;q>BiGEfCR*fNpe0nwYbtlbG@ zPf`;EN=re9T}FqK5#p2u2TYbC1X}r#zHDeg*AhZ_Xa161$}k;J>UK64inHl6ro`!T zjUro9inzCeU82{Pn%D2Wf?cOCG_PP(mLsz-!@&piH>WQ&uT7n6-oQG%#r4(Zr`JAd zZZAA&{`U5j=04Vhu3@b+yV1Ssp6Z(XCAw5jC<@Z>0f8Ha{mb|NQF9L8>iy2E@25{! ziTC-2tOZ^Z%KG4OuRnaF$uwVT8zj<>72H$C7j)vIFGMR{C8V8SQJN&6m_|Ehb)Wus zyr)G=l7L5ET67ZCR@7e#GRzX3bQvZA(j9uT?w~N!ye($_-O}s{QVFJ*u2O45WPj-C zFd*RXpbr1R8y{dN_jAd#ej{9da24Mw|K$E{Os&Q23L3@?(}tu!q#CXrH{XDcjc4VYOP}oocX`W0Bs_h3YA4q{YY(v>r9!7=}WVG!v;}cwUL|Jvg@Nei& zRhV+xf;O~aGo*=X+1M7I@h-{JR#R;1_VxMs<^+uUE!-dbopnI^56+!y&aJF8uS|Z` z9A7@x%wQ_FPu-V)+@7{ouqDuL34P)e@?*|-5LVuofaJ^4!C8Frb=IF_)FFM6<6@{& zbka}iE=QX&v88;49FmE&iQKrk~@V3+uymySs0K!Ob73CkN8EUyM~ z{f?z|F0I81{-h)PpXmy@Ni!7?p15*cASsACyqWLAX}S0*2Qo~IWD!KY!0Efq0Of;p zrXMGZlhy$44@0-pnYj(G^RKLidx|omBC2(M1vUchh{%Kywd2IM4+?RCk&{SUZi2Lo zx492f2fzcU0vu(1^7tvHQ7vNxVBR*fIsiG_au&2>@sZ(ibZAduL$A}gES@5q#zWlk zX7TuvP3eBb(P&(ePw=*z9cpeEriO;fcv13?v(OlFM|0jBxiJ{Ob2^C;!pY&0<|0O( z&tT2-1g6~%VQuw(KB5`2ER?Oue$LZ=vYk^NIb&oDYw0Iv!<#7bMwTpKlYO^S>7mh-o!pJ(mA^hkmUSe^Rzr$nSdqCAj)kzs* zU0FrPnWKsMxkT8lhkORp%!RFU9a0YD0TpJ=EVv3;>57i`Q+_Xe0WUfR-Hm$)o#<1T zt)h|W2TLo_fv%M8%Oo8r=aun&nVB6lT0YsHNJ-hGns!wp-mb_!0i=b4t5;ZW;Y-7-CkKu3&5eIb=8&Vv@X9TqZSf4;r0dgr*RXDJvH2J8eYbgY z>VoO%<=@0~fFeV91J2*?zxj1^CZ04udhgrTq4*4I#FsEK{l(lJq}MPyJ!`vaU!A?1 zrgh8J95LayoVlL)9H(bvL#KDnzG5TK-#GsUIygf%)s_!<+?ede9<j3X&wpb2Y1fr=~>dD+8H`I$p&5A3kZDkI#z2R9WrR)@Aq7p1m2KYQ)#Jn!%!&a9s5ru2BL^MVH!eNKy2kYrcqksM)63-* z*cG682eU$$piM5hkLJ^*e{6`>D+IG_HuuX3Cq@cN7bV^AJCIl+yRHu^vqi#`f zTX<&PftW;R{Vr5?b*NC*#3+I)YvLEV$jsSDIC)@Eu%a_~OIDEpk$DuLT)zRVm{hH# z(1GHiR=)`027AzW%1Ih5lccKEg(x`6XayUgZlJ6)FX;EXb`y5VQm2I{;MyRLMeW z4@E7oY~!`ZxOv-Uc!5f*VGU6!Ol?Ncn-u7vq_RG^&_2#&=e1Rek+~)Y2&4@}n(A=< zb)6f?CaHU-wU?LAiPL;u}dsf&Wnn-p8-S90nNh$QS5hW&>Xc5sNp*%>B!ouo; zMP;A)TrdTN&=Am3M-7gXk!3B;Pat81VU9@WWT#kz)Zl`VI^qHunTZDk|90&OSxC2N zdZ-klvWNP86UKku<6%4>iDXC+76vw-JmuS z#v&*yOF_#~M6PS(19Dv-3bGBO6hDbs8fWlnF1ACuJ~w3A5^@8>x5R%?iF8e=P@hje zrH#+z$E;q%pn9Blx|0?p0LZXU%V2 zc++lf{J_s$d^h^Dng0bh{fFpipTvXySo0_U;18O!7%=_OlybVEyw9K4-UNr?NLk%52a}|Q$~c2T9eRnwv%NlwV$c(L}jYW zS^VZ7Rk(gK)q|Gp3;(iyCzOhOB`M<}NLf$@BVB`&TCpbr2iUIhxY!L0kV6wu)Z|Eq zj8wfXjT6w>PVq`J@WChFD{qp+^{o7k`7}PK$QcNO%%-3=cAjQe@G=N5a5|2{R>Or& zvcc`=^Uh8j^Z|d6))Huras`5iyW~HC6$aYHz;O;2wTo|GUgB`|R7M{6xy1X$Y>Jk_%h*m=KjMD}J|a|b-~7y*bC^PDMvs#Bf%U4GfNa4F8KlcUx~F08KG zr@iLa)=^%(CMA)25D$TBv$D0)AM_DO59t{95|hoDd&@Dh@;jdlYcbFnYF!-gEfOFt zt&^plqn%IEIcJ8u$aFTew8tBjMtK<$ECY{bp4j~t z#3sg=5KGLF;S!Vy!yczeddMppi(A5FmU7ZHATdD~fkT;n854A&NO0vFn~1qyg(O#8 zA}Cuh;`32zE#su9)0ykw$CUTdg9kL%$SSt!X`W%T9)5o2EsVNb(P{EqX5(bMKLe%E=>osdht&m*oHPF;+bHJ$OYy--|q z3QOK@@&pq<={G+lz2s6{5;vD}&?xL$#Pv9eor=$*&T-xIX{@W~I{QHmt~=yfeoocFMUC6hG^>hTn16*KY@!H#dHxozuX%Gc#v#%syF3 z%O+g;;klOwtT_0vTeFDp0uQ;S>1c0NO|9o9Scdm8_yo_7U{bg6=*nDPEqqDwuqt zaBChAbuC5Qf-^1JZnzR((~17Tg>nuv+$~;bm^5-ux47gm6dh&siW9fPN7v6-nSR_& z+IEyF&dcZn{AFn0W16~5G7AX!G<2(yaM%EQxc9fCej2k6RfoF^Y za_ut+04~)S0>r>CLC6-`0FeMb!=1_L3C}bphq9bx+h^Kt_P1=Jt*$}3+xn0SfSXp@ zm*Y7mdO(yH$&Vaxldw@N3;6xl-ovij?_>J*M04uo5Z&AN!SZ{sve?Yxh1t*XZSzmD z`?hW1M4PFfp~Wd0xsNylD|0gOYnzpe$c#k=3?`IfA*{9=E9uoq#!dUm%Um%eG68)k~=9h9jxyTa6ZIyWil$g z=}#=;551m$B@)%(5s3;tNx$^UUeyaan@7r^3$SYwun|0%vyQG{YQAAa?p%q_a7mW8 z17vWXq};g#cP3U1_oJmHxJVeg5g9UJ1P>@*l6?R2YqkUXprg+e#m@6AK8M9nGt82I zLZ=0wRhG>JrY*^+21hJjgMGk?ZeX zdc`&+Tf)@vd#DFLy?GPMugcj7UKrV}vfSa>Hx3tFodwyK+Wrtz(Z4?6s_MbAF?9ro^g<{2Me52GAlnDGEPjnIM*b3Ju0&d@ag0 zpKNhOCZ8uy3RuWj`6x900?(*mjN#w3<76Qkd&jH)1!yqnK45f^p&Y7gLjVw@$o!5M z7lk=K`6O&rI~eL*oY^XiERpn@M4Ut~u~zp4B%Z!!NQMMQgmL}?p7lFX%Ac!EXOil{5r9cYBSsnSvU0A?ClTfOYppYs@bo;t;8XPl;5?p!l~;1|TAmVh;Uqd3hI zjZCethB_^}!9rO>?wyr9m_09si}j723XZ~_L0vnC7dM>l>Z5go*~FX9BGx{0^RQ(s z-B`s4Q1Y<~nW5rTQ^uLDi=;?%M#}=$RdaNkBhrH$9FgJl#wm=lbEKT3?F(zGwr-L; zeQ)n5jP1zC=90;6<$|&)C2h{RyA-tL2yEhuymR=R6CcYKP`;dPupXZStG^)N7O`;4 zx+AF?>?N*P+aTG zc4K_#1nN3B8y&F@^c2=`^Z9|DY1%o2&-a~pg6~@6S**S?ci+;zs-dnC?R39>5_iof zIK3cT3EP|Ak)^U)7AqvYjQ5!fvPwm_IB^m$x_(sD{F2p=jJ{}<30#xXt&llUoFR`) z?&*96CJ}|zkxrLxYUqM1$kH2tB*5{3&MHz&iUcV2A#O>~w8#Vpm`{B?!k(<(!8+&8 zo&<%SAgG~|D|xP8JfOBHd|EP^Sx%Gu6!HsBgY=@R-z;VQQWPQbYdV;eaD>Wdn>s@s zu5{&AS{AZoKi4)q#zWSVr)$kmuYZJfNw=FHeeECGZx~bS_^@+$G}yFhr1>Ad{UdY5 z6go4!A@0Y{vy~<)HS0g?9_tJ1IP0awt&S1Mf6{oJ;~PWPD$?+v9L5>y%$U72yo$HY!bboUlYoBNDVa@0=Jq?AhB?3mJ zpOwPi64Rc5r?yM<49OCO&WGSn@7`|aF$(ls=U=hSz?`DLGq0@=&RuNYnm%hwN^W2m z{J;F-q#)-Bjpo}o25(+zkZOF4`3^OX;J#)5s>-2Qm<<8c?Cpf4N=wOB<2>!xV$d-k7LB~H zjF|>d+)R4a=%$0%ojjzM(ilQt|xxR+)L9^_xVicNd;4h4h_PrKb3OG~e z2XDS^BhMGHE}C+G{P`C)GW|3?vPHlf&lZiT9lG0&J=ErWo=sdqV zfn_%skof0sy@3}-!_8|G6L@ie>F4P9Q^eP%r*Q3#H1D68##-(bw9j)k#a(&JZE}KG zq_#_RVWtzyxHfBlDH<7hO^iret%Q72A$+Z`fQzmGg*a!5TArIe!}>EwL=lLa(TFR< zDQ(ntG739Ehy5er|CtCew}2q%vxG4rjW$}*Akuv>>n9(MijJTx3Nid!9zhqPBGlE- zXCQKAQ7Yq#Apm&bo@ogz z)Ba*35 z0R$R+G=t1?P=tOh& z$~nkkOG$a0m9d06!d=?AAz^x8A5_sMeC%+*1aC8rowRH3{KzS^8Mro4-zSH-86;mA z$zculR>3FyFtvLM*To|YW_*QC_Yqu{TXf|n79A9H_SaT&BkEQ`}Yipd41MQ0A-+ta@w~EN&U}x2~k& z<<8nf8r3e6q&JTSCnpbbbRN3)Qqv^Q7eAi#_cbc*(ggdIA zIx&oqyK6R5b`ztYT!Wk55mz?(Wt;P#V^ReMnIvQa02S9gJ7=d*zdk&7$$oE7oyYF( z=V8l9+gM90ed;}e{So4Y% z*Lc{}{Xk}Ul~A1uyDS5Yi;7kRF8p8CXR{G#`g{y^fm4kC$vYq50eH0e_A9Shc@VY} zM=DoU@~}td<&I(4t}GPvtUJGZ`Av-AjiYlqZ1!<8wx?51n{QscWShIqVcPx!{C<7! z4%Wdh_StGr^76=$wqLhIcCX&EIcbe6_iZ zvi!q0-bUR%VIz-JG04G}0{`XK^@gME>1f%OTP)ckI!hxfZXI*0pD6kd*-o;&Oe@80 zc7%TV)9nd+A;ayBQ1@=5htQyR6-Zd57`<`?2pLI<&8*n~Pis71w9u>yon(>*vl$6b zTdI&LsuFYr?a;I4C)chupWM6G{PBlh$MeP{HXy{;EDExIqI?E#l=~%946s`ee4^V&SK`VSsEl<-^-iJ zJVvSR;34(l>Jru;E@Q3QDZJSoY?Q!sACQw=xSzA5Jm~6Ys46l@z9fgdWqc4>dx3|Y z5w6SRNFGOkAK{u<$A~AFIdH`5$ur;WmuuEi$DP@EUzne6vr`o$RBm` zcNU!?PUF_d^q{`+HGt1-cF8@=Ea65w939q;r!To>0jttf6^bLDYe%@d?uuc~81$_SJ@OVKUP$fwP8Ijf#HEipkb9?!qzJPkjw(ucB)=4f$ zV4WG{pmvVvvW{~EnqSV~2r%pa9b6yWTx=cfAKS$>dxwa2e1N*n2P(hT&{m+$fOxb| z$8g#)y5AH!*^ai%>R9cK}EeYL|M9L|RW4zAc4j3)56unWLszf6?!W0*-8y#sxr#R_$<&Yu@XPnn6PG%o2 zJRbh$v#`m*b^%5dR7EWD89QWAT5*Upz`P-rhv)^@g)S<=a|<90r66ue2oL!lWE#EP z5kAQaBZ3WLoJaP^M$y-HiB5QzFTIlLCYr1Oe(=JZozJT4Xe-FD}&tbarEPh|j-^Wh%_nTW7jpmOV zTXD$)J0ILIOYMVvAS*15gsw3A6dF3|=dmH(1x(Mr7#-%H<8WUqLmEC{DxIJJBlUrqR(S1#khoumDo_OUa<7a82){2bPBv-7|XGCLD@ zmlmxP`YC25a09p-^AGJM%Pp)o?^wPNy4|I))Y3SG(SFZHhaUbsg=wnOcoFs6SPOp^ zYg@mGkRx#H=-cOqP{#*7=reXS_%h(X-nwC}WIF4K*A?<)zCmoW z+SctlqOcIH^0XvQEsd=Ncpyvl!R!$|fW&zFsgW`xH?kyD%p`PT$M=1vAit*QE3-pW90>89AxM(6)`FJH2?(Es`RO|viA6>dv)DC`Mv zdOiy<9d2bIe3H{|1h$noxUS_-l2B+hz$yb2nR1*A@AtE)AwnW_;uZ_*nE+-l63U1W zDWic(a5A_~V@1@;5F%P44OyR5+NkeAtoxrR)2yeYpE-BombYXlIZS!rc?~(tLh;LP zr#1?eaWEK+r$j0phCxCIlbLN7zvPKcsyiJ53W^gV7%V8@cHfde38$}kBB7oK%r>5k zkqCzv83}O&$P@XT^an_FCgmfsdqnWm&V@fbpJiPleb*`6rEbJwAd5lof35fw!v<(4pq^(osARam1`gov$SwrVoGzRMxC6-Qvum;M>b? zDW!~WPinv1e2_Vckp$NB5j^9wbHz@{`qMme!G}JsIbB%Cnl+A2qZ7@8Hx=U4*qhHN z&pwg^0%VKpQ#mc0oo9BU&qoLPJVxfY)|sPrv|+o4E^m%v+{e^sz65xPPo?y=J2uUb zpwgsG;>ooH~qP|qWb z+^`J19O&}M0DNj@!@mp#r(D`OJv?e1<%@WL=O$My4|bZlksBXscj9G6zj@w_o;l%8 z)*P`LLA~M&fL?XS>orHK^D>lCwDWx6;KM#&vTzzHXAmr%T!;Vs#Smv-a8?r2bfr4{ zPu-j5M^;x}GxRE_u(HFz>thNdfalQ>;%tud=(y9qQTVj1KAYvS8~!nD=5F$QIkb$O zmbr`b;qjb2&{22+K$+Olf#2LN`!+qu9$>mPuQkq+Sw)@ZEQ((Bd2^|Gs`NOn5w3xL zjC#WBTwL?mS+;X?Ja| z!#;!A6?|zWKFZq4!S?Y4MzG1x*-gh0Kl%hM73`5MTamLrX;g8P(>mQ>uC^+rrYp6` zf)d7fFL+J2dudug%lVTviYwiRc8McP8JCC>Ch?{EGA^Dnlxc*O@seg&;@u2)Mc9Sg z9|%&r6&E?vmPZ;&Yg2{|fV2brMu>Re)#DGUoE`C7&OQG`iax+8txLV*dyFPn`jBX) zvpU5jUvGhGyM*tc4}jhpW%FG^YSbjq2$Ybtajwi?5R)h! zK_|4q^>z?-+2COeEphV1t%vt?4F@LM(-NHcwi6j+=L8R6XV8)Q#`)K*qsE6V>!88l zM|ddXlt6A|w1PFI{PFWpw24hEryz6GnZJ{m#(D~}PN8$b2QYTZ_@Ko&J92|gTkMv< z$FPZ=<)7XBy!n4W|HyW*UVB3IW|NcPBX_kCY<+rBkZ+6HjQxEIMfagQ|$>Z#v?)->zT~;gxJLJzusuUub z%PJU1eiC{3?&UXZTJ*S0mG2yVPW)XW%=3kDOsU38gV#?_S)FEQhJIa#4>{HU-tv-l zP&u`h{$>4UxC$IiKXC+g8=Vo3_VGp2B$gqtuG6->ux(Q)UV5DN%dw-?qQ`88ml`NMUF zHv2gEIklQ?1~&oowqw}ZjuXJK&9LPkkiV4?=U&+u#x?W3S1)4lVzv3(J9lyI@Uwuc zc5j3VJJd4kJ}6hK{cY2++_rg`0+t;m69GiKM9`uz;)X886{HXp6;h zl1!$ljA@ILjvywS#d$2*acbwE3{H~y9pKV{Q^t%8rELhDF^qL$ zJU66#nUB(Q^h2-oKJ*cboJXA{hkBLhhA-$>F`~nV@R?<7(gHX)A{stE+)QJ1W(b`D zKKLIV`^y7mwJ77v1=IYo3Y2jq(;++oTZt#ej)=4nQB*pkQ@VrcxdV8!=fl@Abf~a0 zuR}ja>>gPM+IIuyPS-2AZkPi$^6Egd^K8?%F%ruu+PihIOnA@^j{ZJC2WSSZp(imiv#ErETFFGuvfOdg-WNGXiw z8bppraymC3hS?e9!Hymua*rS5!>+H%fLnL^>LHxNpajIp2q{sl;KG3 zGWIy&j2({3Zo+PSo1Jux9EWTj)O!-`0NV<-CES>Bej=H}32(vYym@_AMos8i- z&eoB}EDC;}iX+>+K6dh;JlZjWUAf0`uUNs0!H3Q2PO3PNvU*iVWwsYyr6)Y;1rx7A z(e%QLZudf2Kn-PF>0P$0R9VJr^~A{(#g!&)uQ+kLa+JopL3nB?fzYbgPhAb@ZL!`4 z=_ZtW_8};-dPGUR8IU)McPTYvVNqpsfl1j31+qtP6r@)yS-(S+kL;J=Py-haQb~^l zDU@kRe;_0#N?l}Ge{C80vNfr)WJYOhhr&ag$u80^(a8l?C?TE}w9udIcATtr3Z*h% zTgwr9Lwn`^jppiuTi99mKGq73H~;3Ff7rY^^-42^c(*lovgt6~s~+Ihin90r{Bn)} zR0Y;QuNehR(DDtb;Un!iEbE(E1Ylf<$?Wt*emm&bI%LGPs!KKTgq)0zx~yxPM&sbw zW0rg_Cw3nA8^J>r^WlG`>f%a+y>dxOv6FFjZul_xQ+)6FC)Yl)b=$WWAGZA>C()QP zPD3mY2n$2)tvJ$livhibh7abl8~|62IjZv?zx)gj!zXQeI!82L!4AxPDBkIVJNNQ9 zhnIUilr6<-&&%W*Zp_c)ME}vJa$z`J_q}YFIU2M<2nJpUdQIE$LA5ghHw*g>b`k#qYghlnmsf4pN4K@B)F%D8 z9<*(ce|5#Ce6-1SEmhlRXAX9A~qh#t)EpSbp z`g%#)q7BIZI+kGk`Ilc>2b$N$TUby0*3@ZB_maLHIP<_I5Ip!2h=;7Dejd54P`cmz zJKt8%d^yO@`L`~fv$fE7KVQO&BX087mXII$g;0qo6uiwhP>P~xfEfY#_-{!!mD0O4 z4dNG<@~3pV3`D3fiLU@))RT*>ldKGP<_u>Vt$~T+Nf<^W9)_h^wIvPNb%0#lvP@w? zf~c-pS%q{(DAk(rA-v1y*%^`qDV8G16FdYenbCmYU-uo$vyilu@d7Wq)Zf%If#jN5 zDdD^&%<@BdVZR9nS)mds93dm;UlfkWW#Qzak5m>YTNDO?Z6d~af+LdRgquE`l3qqi z3UU`mjjGcH>EKZbl~IFB+8IvSA-g2CP7D6L!cP6QrFA+LZ6JhK`KO8>S!iF70M;ra z&5CYs`H`MqTf&9*w7D{S3)7#TG;cq91#e|X@SugMOoyY<&#Q9mfF9Nk{B#FqS*yZS z^?>z+Al4s}Fsw)b-O53(Qixz~8u)^dkoepi++Bly7GSr$8Eb@pfY@ddb(^_NQ|IGf=sY;txRGb+|F z0&eU1>l@q7WP1-vk>hnOEB%5@P3j|((qYdQ%@mu+*}AaJIP&-irs&V3(RtR4V6!fM zBbl8_&KlY2LpJCIZIUlU0_<*t^_(N$4={b3JCn~KoW;zK)VUczJ^h+}JAiNMfWm+*P1k_eeZF-i z9#R4!xaf3IrE4V+q7UH++zOtvMH%}t#MWZ!_F?;1Z7*RUgT%MS=U720L zOL(Sx@Jn^s-IddFiLN1C6!7^FI?dd5o1cWDPY?PmAWkK}_HfQdp8wC!KC+iW{C=>W z5zJMpi6}{;qt#%+eAea4kxxN*`mPvKX73$1QaNPlaoi5=CQI24R4G%8QKyBAAy7+ z6v~ZEawRYAARd^iB$rdt4EyE-b+b8sq&!`k-fS< zY9u=8h=^3Cd`nb91c3JO2skzotLzqmsC)=Ia|j=fHL~&~ykNBfg7+0lNG-fCA-hQ2 z%Ra$(QT3KYmwxMRQ=&)_7hD&!>VFMSW>oyDXt~w!omlyan59Q+HmddI++BNH%$-+H z<2$WWBNMhZdiZdYvEzyU|q*Ea~Cmca!`Wik3 z#+&vCHd5nFlDl)~QBF!bJT~(H{l_VtYxpgrE#^09hv7&0do%w1g6C2M?zTOL9l}4K zeGs46;;!4iR(8MS<3QV~2Mc9V4;WjljCG45HkR-cbSU|r;=92J&)xQtnwS@L(2ic#Zo{ThWErQ(GOc)0^`iYEf6y@5(qW`o zTZ}*_>x+n(>^+PL^EVBw0>U|Y>hTxcjAr#tFFKJ)is({DO9b^*9}KOBEzu&>8wY{J zE8dSt^u$5XnS-G7^lBgC!fPhX&x87-a<-;#7Mr^%7j(q zr3m@?A%3Fjudeq0-q)Ib03Gkurd+LmoTUE3~v_fv=aL1cv=h_WJ(qoj8UaSnv= zR#+HO!k2~~J${Nd<5%{b?LYp;QyYnX6Qj}mWncFM-S2|vAY%Qj==_oCmxNc2B{=WSaPeGp!}@g>oXg~jFp zcG~_wKK&eD5m>{_p(QL&@TGI^h2)`Dq{Dqx`LlLaHLS(I-9#-rm%G;l)sNiqY-Dq1p5jn`WtY+-5xra~2??GmPjDoJB(%f4209tmCU6Uh3`tVZOR(frVz!rj zuFCo_wa#pv?nBfLlB&|kYq{3tSjM^HRIGtePSm(6ExNioB=bd>01+`p3g^#>cyaQ9 zfYV0#xts^9Xz&5Ma{nei1+ikgwvJ;;_vusIt(%|kIgSgfmx5a1VR|<`V5NYQiX)XO zV2YC_+Bqw6!if~G==!tMq=3bwM<@v;ds9_a`)zE>(81mA3m^Y zv)q7f63anO<2%f@mRA*XBY`hDj%y|P$sOJ=t~|JJJ6=CQ!Pv%P@Fji9_m&E9b9=VlDWBzq+ehAZjWq0%dE3+@ zG0J5rj-#_rF^&HoK8wdsL|vJkL!BSO9svHmWo~pQ{*rI6#<|-#r)57xXL$wrx-y4P z{vke#PBcHE#n~mkZ^ia=MG4!N(#4ZICbSk*r`x)P?N(3Pl%5iu_`1>bhB;TSPF-)l zeS%^3N(a?(rbVN8x2&ayMGzSsnA~+v-7DTr*0x|&&O=5sER_q_@OvsXf=C?_!2EQl z>+GyKui%oDJ?xOuM1$zYBZL_1w9F7(Y0*czQ@;}kuF5PnWiP*(2#h@iGM)rGVpIKX zGEXHb@MkFivQ0ckdq^3&7!{c!$xne$nzTF$RtLn{;6f&$Et2jD(TZ0gsTHqjx=B(E z((DEDIzKzbe4M$xaKE{OA9w2J=X$<=;f?0}=w$PQxBo$NaeNx_)3$D}oAg82^YwsA zoLW2MUalqFt!rO>Ii;GQJhbZ-r~C6zRrgC0^(n~wS`rWGa@-m1oKCu=Gg(F7jVW;- zj0iVQVn^bWnOn`T@7=`jHg+byWt%Fo^V~YrC`LJWiNj}fn+iF_!RP2x5dIvk6k73Q|)V`Y< z&(8A#UIKBGu)n(Y1vbH3Y<_;{HtO~h>r~t5OLA0M5m`r-_IeWDYE@lg%C^fX0R!<- z{Mr(P9V~1=CuAAYA^Qvi+Xc^*MGVBEZ_%|9r0+^q2-?|bhx#PGmmNJgs6peKw)aDoLe$$eJXY7=tYE$Lwq1FqQ;z`T3wd-M5nYf!qNmNuA(3=aaj`>3@?dY^q~A)Y;BFx@|^B;2u6vHH%%h7t)>ty4fXO zy0tudxt=t@sV%UARgn;G>KcTc&clKvKS8a{?Gh3s}e2wF~$6S}m4Zf6r&+pEkSswn74Ikp6o6|t= zV;Vo(-cRq}w_SX>PMEWQMo;3~wRkDPY4Y5^cqhNhyxpA9kL)z_rO-S^)R!^Jd<&hU zdziv{8#~C);(Ndxq4Tx;s$JXF+Kr#GcT;{hom+*;uHCLxTeL@QfEtTl=^o;g3*|~= zb-IR=kDH6FKYNTd@&29W&*$cvNpz%X0>7O+g|*TAPV*4z`(8PiuXU@#*j*c=%)BnS z)|nmXTMJ9gJVJi&+DD!Diq&20U;*%_f?}Mcz9-ZC5u`Yx&-akA)ar)N6duAC?g+AQsqGCBxv zTT-qQE>r6;>au2<9p^7*Z#P$Q_5AYoRXngAYtE0I#=6Jh=DoA8+jpctIDgruY+f2a zV{4^TS+&(3rgqH(ti>!#FIm-T)|9f=Yr8LFs{LPY-n35hPj6ncO_f$L;>cS41fNT^qso82v8tX%#gsoIlTiypMhSEb%Z{R1 zO>p+I>^4PsoaAaSao1)XK1WGxK|dR-g!eYhmu$vAD|uQ1ER*XgXY!dRvX z0iMe&u$C-8$!}m~ZMeIz++fTWFAx{3v;5xFH0t_r^G9#Kj^!XjnDsWkXEqt*wRsm) z&)HG_|E}G%Y1+Sj@W49K+z^c(;)?-!3P~L9D3z<~O>$ol%PrYWoqTP%F1_zF6-Aw- z23Z@mv`gAG)O6A-o0K1Az8758gnls|WY}nv185U%qy}K{Z z3+`!hk)q(1qZ467Hd9oHs985plX+VLHdoOXMQzV!neR*Z4iLMq*zxs72QcU-NH9 z-%CzEaQdI$PUR1M7qyS(&sM31F{MpqoI30D#&(ce8c5kIzOj_&h2;+Oe5j|HyiK2o zHT;}fd;%|nc&PE-4V%_64Vxp0YZ$dyMVWEbnZGZwd-a!dSSqnJkI}kSD@SgylWsVr zF)ARsx;*51hGm>I;IV!-Lc93_Z(^7xUi1beARmTDvPRYE%;7P-0Bh3`kR9@9pSMh{`+&PL8r&v)x!{op-;Kh|lkX-c9d^1KLt zv;NyRo_EWjN-po6d=d2+4I`(E_j~ZW#ay?|`mu9|YWenM`b#!$$mMqSVLv}Ayx<`Q0TrAyh&59|12Y)WXeFEH(&b%tw+*!3?+Kc-A;| z;<#OxyE!sVi&&p&8}+j;@XG@nS-w8MhNTuQZRo=1wBX+vp2 z8Q)#I503#5y~KICkXb}Sp#|0k7J!;8?H*UqSX6{be-MN~p%a@m>>bc(4H8vR zMV&T(ugqy&9dL$5s?zW3lQ+QRa*k{^`jbs)$DF+l8z|LHP#C`@d6~a1d$H@Skn$`z zIfS-I>hW0Phz7nZg%tf>GOD49%;_l^v#pDqxUc#_J*)VlA}|c7J2l@ zG3$6I8>HkphVsN_-VD)@~lOhf}oKDf)8mL9>h(Dpye+9C7{_>&=%i z)%f1>qIL4{3~xF4>+4@&y7W-_QC=_|8(E3zhh`W*|~UgatgD5PBrhIJ!>x|xc;0Y*5}aSXXk%6 zrg*dCyo63NJI$Zt#REUH_Ujo;*~g|yUtk#mJ7-T~<0N+S+#l`yx19xwV|j}x-Ca6Z?uh5;!mReN=Hj5ptUljS$4cOS?3w&QM2WAju~Nnl6=8y^3-Mo zK~3LpG+&#ZMjb!dyf=N?rg)zp^Osr@vr{4O73|3K z^HZFTeFb)K)4R_eJT$-Ez-PGlIV!z`;E1zjU2UTbQGVr@NGmBa6+oA!)JW?3z+L+v zMIC)ymVXl1JF)kIa=3JAtKwGV@~fxZ75&?NNt}8kbif90HFkaGl=j7 z+B{pNgCkUAiv^eNvx7=1C7U|Px53>U6<9q zN^mMGm|{l~EI<_oKE4C}|Ficd&z2;a}l(8zbR#0j9g>I^3v zl%KVDB2)$g(T#%)zO>081B=GOhemOxmtbJd1|9%w6~vr*!eY~stDfABr`Y6Qrwh05 zA6gx|dDU0_ZrV>{X|jC9Cfs^+w_ZrL-&RWw+jG-8xzpcthkEpwHVt}93S0HRRlO}a zT~JbPqVwYAUDc7MA7AkA+HVA3yK^(1DeOX0)%(Os^^04okK`$7uS+jjl1%E(*wzP# zGV1Hw0@-n8t$tpy9BK97mYoD?HCmI}vo@(cYq!E`eCnc)R`IkVs=F!ltgbEyF-CnN z;^VcQtm;a>PC|TQZl%+9BBhfPoxp2lPJjAIs!O&Kcj3kryWn(X^^y6#>~GiB@89Yr zVf5=GG#yi=Tm7rj{y0|nSB7--C%zS-Cq?PW$g2~6a;k87XB}%bp;qa~UPDo-jnY@BTJ8hdi5>kAvi>4VnmpgoGpRPw`KG;;ZM&N;Sm@c- zOEy8>xh-Pb%HFe)PxXtQw^cvtnPI)@*_*zRQ76;B@xJ}cl5Rov)p>QYwj2jRZ2yzK zrIjFz{E?O(Jjz)4k7KDOs`^PNUC=mo__~d8dnbW?FX|cTPi*D)(1HE-^1>L?delM~ zI(BG`YgH=7dhmTeviADrN-Oo#Qi@l{5|x?|D?jq3F?7Ku&^k8!z*d}Z*+e?xcwiUc zY#$(RX1AA{E=cL;zZB=Gi=V^=G7`6ivI6Fkym}8s02U2U z@DeSoM|z^XhCm-t!|5fBi{9RrilvWXtV`aTp>9%ytR(*}R0mlE`v;xwLS?{N8HI;oaLnNtCGY(IDbWKoR(1C2iMR}`iP4;^o< z6D};(UbE>+3_`CO0r<&o&&~#ieHVA9<5e8c4rAgaa?u3v3nE41cLf#&ok@$6GLsrD zo%q@ksrceg*v$_JcpM~&qdr{HbUu2(P^OWt;zi7$&;k@GJHAzO{#v)dqBSXntpr)A zmc?eY{wG<#-&bi`Q23gd;S2X^jqGXL_>N*{J?|-+N=F zKdt)yz*c{KjM@p7CS-a+%~Q5gcmCK(yA}3?{hjp5v?d^WA)fA<80+-0Ry2K5Zj%U= zw{23W-|^L|a{L^Xeu_%J?Q1fh)Wt2Yn&c@z{!jU+oRI7_tuc|S*@tV@xG-HYTxgle zdW<_`ir?!B+H^dJ5;MTXw{4nR_hj7aP1hN<98k8Y?x9GNN(#c&K0o1AKkFn{eQ?$u z{m`WOC41Ibw{^d0t4+skmFbl6HL2EZ$~s9t<}WDRZxcGZMcf9=Z9Ab=sXH+=vAbb; z*5vu3tt4xbcga>BH9`2$?$EevlOgdn@zca!Ez$~;Lj06cG22#QXEqe4Cd@C}#97ZE z|HohX2`i6#ygVCy!7hsY_rLWUc9G;-0|>Y-F^No~kQX}=*!+hAH0ph+JlZ86kMw{h z5h>IdKdhYDin-o|t}*<~@mT3s9=Q!RE?C&D-==@iR9(WZ(!=R4imqmO+DxluJP z@aY9|*88$eNaDse`0-t#$e`7~+IO@MsC08cFkV}BvG;PbbqVbu3nPD|Q*VCUpZd|o zOmTE^Q~wTHm+At-xx+ek99X?_RzI(KV0F^Qx%yl$EL3^iZuNHBjv=zw#93|EIMxLC zrcJf8}_IC#Dw1Lt*(BgtSHHycd{vT?HAR^u$Z08 zVeiWNNIeV_*T=Q#9`K92xboL)4{uXAFkLTj z8!Mf=f8!zuZ=u|On*UH8zEg;^v(r(!Hm`O8m>{H$H++?uhI>kg#KKPp?RuRm)rkrO z!a_GjVTMvu4dtSpv=8r#);i2H&_bq#m4>;Zo6vVj<8 zNuBi8ZqYvW-PxEf0(I5QV0xWIJ0g9+T2X-50=cc4XLnHmE*bChZ`{*E&C z#K|@y{;b6lp|`^V@kI$mSN08^$&!)Byqd(-{sTzrVc%m;>ZMSXPP~rSiMLtR`IPS= z_NC-o#X9v9R2%7`T>C`Z1G3SZ@0?S7@G6H($wSdgVN}4u$(S?huO`eVYTx$-d0#rAO32RZ?Y=LPMS zOoeDGqWY(kPo0En+-g#w$*%5{(A$pnEUE68&|?Q)H(LDI>^4f9RQNOKR_eEqRTodI znm27CebFXvSM?j}b^VtW~R6z%FzH}RUH+_F_TzVE#K zhLO=#Nekga?_L?6loy}WYu~d!3snC-K*l`GIiKiuDr#`t6X1_kYSO1^{XOenHEiOc zBgXxnwr>~-LU6Vi>1YPMWDb1j1;uzmhOI8_XtN>d+g&UD*liQY+cwUR+|V5?2Ub^Y z!mNuyx)Vc_O#K|3jv2?TRNHm=Ovj4b2A~|?@{4aq+p$Aq{hEzwzc^%fQs|lHTeeSm zeA$>hbObL#*~3p|#m<>57WKQm`NXI^&srCSC(%A=fK?~`pKU5rXtyhF8(bZ0T3s@n zYC)k5jk}SL96oyRM;iY6a(mkAF&>92|4S`-9NfXSchT3SCR$+-N?(^;^j^d$H=AyK zkSKeA5vH+d9wldHsU6Be^mV}0-t~L{k#kVO5`GzgzA*@0x~fY8udl<4GBSX^Y_*{edaf}{#zB*{(G*vs4S;rV zWztdkFf=zNlICsfuh|MGPJzmDmZw4PiNM_`+afI=y&vrZI-$CB=Z4)1c+=PMbjQ2Z zqW}Ov07*naR8mHgl-c-a4m|G2HT7^Cdy?AiDf1YuZu&Y=(_d|lQB=SfIi?tkse1ax z93^Ne%a=;EL2??4E`Tme!%5u~Tn7V64ATbOBq^$?qdKnE*vZ2+o7mdr3!k9M)LUv5 zsjF@`awkF-1Ag>?H95~EIi>W~QJL5QTz>R9&2<6|c{;|CJMm2^r6%9xX+e&v4X=m$@U4tr}}A~c#ijN zdsaENaL6$X_mrTJS0zn6#xnbO<+2&{@R1MaBr*;{t9{P+0Z99sV$>KPPL=OGtFXRy zIO7Lwf?JU_Gs7sDx>z6Z(u`uE{ejxXLSPFvC5_=o#6<(- z-8U~l%Wy)n;U^aCOXY6SDm2i?zNrUi#$N(Pag@*6pB_w)d=rC93}*^anHdG8hYPo# z8n*%qLV5FDy;6GNmqfdF*4{zAwd2{DZZz^#u_Bv_c`x(B%tpun01D z&Pg`XMeVn)xgpA0=(S7|u+R)_>MNrU%uvu~VbxsPKCG(jey@5)x^Vp5}}AY}jtLy7l% zBNiNIOoTRyp_kPxUx&BrCBe#3)W>ELR!*Rad~P@%TsYBdZ3w7QuhF4d z$IFa_fY2kC)vu{Qpd4h7k2E-3ncyUicuPJw0-u8+2Q}y#XaIpge!m3V8lDZC6rEWh z&;9oRL}fhMn*q_jF}{kYhfy6x;)E_eF%ZJ+m5{-MMpxy?L$}vy_0Vo@9Ier8Z4JDp z2c1YMKwjP%Tm^((ct8(3{8E-#CXqfXc~*R)V0Gl+)>umigwyg>nm|s<=0DgiPV9n% z-o)4l#w=qWy6H`EdcGw1>+A5Ot2(kkYSNH3u4+MHsj_iI&9-V-uV+_FlZ zI#H*+WBZA69D#HGETUitzIV5vwmEw~8ECK(O^dNZLy3ShPgqE^Xpq+XkAr zg~L@M+*8utA>oY-AdVTWx$pxh-IcicPe%r*+w}kVowxnj=x=;8oW?V%LyzS&Z%P|2K@%EJBOiJwm&}%6;Fn!8=pW!my;9#nK zUanW!*g)h`x0~Y7L5>4HF|8@_;ru1IBENkBl?iGx7f?E zSLS*VH8BT;HMSXJBO~UHZq)$^5(VO}yrf6yB{_i=4wa>H<~S6=DRD6Bn34Vo2lV>B zN=AhV?IqwM`g$rkJNe}R|DjWUN}jgFmHl6OjE7XCS$H4mRGSryn>IdXD$Zzl8F#5c zhsL7+6wo~Msu`77Jf#EUquW+~O$RlErruSJLIHu{bSy{jX^eZGl#Jh4Ms#{!wfI6# zCLMY5K?~4;4nwmJAaf)n9H`pWg$S7eAx^T36ON2z7KU)0vJW5o#$-%6fcP?UzACo$ zhB6jyC3dt6Mbur%Rb?b;NGhIGEF1=hYd3uS(_ZMxN9wntMG(C3f3PJqTY+B4oBd`q znxh{4ZIvU)NN)>l2b7<*|7jCyuvD)jpT-GAm!hSz22L_dw+3JeFh(KSi$&>GWl?o+ z1jMo3hU`{!ibt^!m;Cf~(YwES6!zhf*oR8}#g_3H13mGRn>rKCNsEMou8v<`w~wAb z0ojm{wJf!P8q{un$|GJM&5vT;yLcZh#w}V6&G3^38PF^-`@ zFS(;>=p{XV=O7+o-~-d-b54`ZYs)xu-ACgP&+dELetf$W+Xy=bh8+GZHKRf*&h!A7 zmRO@sIn}B|kPlTSJ%DI*Ug~+q&PsN1FW?fYL>JsS3j4s#fNN zOIZTqtULxIVC-|iOZ8f4sj)@^ih<@rIm3ma2&wnBBafE!{J!4XZdA{!^QIDr4P)L1 zfm2NdHW~Ca?6R-UNl9~os+Y6~W~q_W^fQi;il+<$q4@_9KJ;KP0F{(V21uO6UT8mF z#uB@cKOa=|NoeA?w-c85`lH_D&C^`gv}75Y;Q^|~AutsH6$w~z)=G4E>7_2O=z5>6 zUhtL2kX0O|&}`7~808SBqskU(xz;fyh8hlKY;{QYfg_lvQml>_mL(EWzm;ZIvgzFg zv(o6z3NNIogX(1UHJJiN?noV>xJBx(ue%-}oxZcGv7u08>J^>#(7+8P#|+vZvCcHk2|BSKV1zQK6vjkaTjGVo}}K7@4C zZc^-;=ClUhJi5V>KBP|zCiO}^E9hZY`a`7!FbE3J`M>}>C|3e#2YT3*LK=cT)e~0% z>8~`R4iE;!jb5rZ|EZ96pfy~BwT)v$9qWP(YEq+BvAat1AZCS^8pPEZcon^79h_00 z<`*!{8v#)yPe$XTwE`bj`hW8t|0gjOveOULcs5JT?7M#)MvO9AbKT0aWsU#S0(-{z`b(3;D>l5R{aUa z)raEI#$k*3k|J}JPS1H6GG8KUg0tZFEi>NsTA3gjPaCn$un)-zsMkgQSt%7Gaqd-2 zJCu)2V7yghHyG2uyo}GWh2zxtWk@z~LzbYC|HCe6HLcuw>)nzhZ`}+>8R4Bcixz#yjGiR{w@h z^mOB6n_g-s5~{Yaig1as@^YT+OiN)KSdPQlcV3DH9^4VTtsG+nC(_b61{b<7OM z?YG_s!$tqq5?Zjf1PyJJII6=(uWJFSSvHM9Fp#M?s^bXlCA#vQ3g{Jo3cwKxF7Y#t zoNpN9-Q`9Tp2J&TIBmz@;HT8>48cf_eX)}WHFewpAIWol<2iW9x~lVoPOka)+L9f7 z+!S#78|W4*z9-&I8$0AtVmH-Z-_i=Dt4vWl!B}NX!^a^2(6npT_ZSW(_Qi%#fG+uz zgDrX~X3P*CR=9_zW5;}j9$J)&L3LsgiHrcEEI%n`%vM~fj`eIG24D(XRI>}#NRREI zos-zjwBpJ(jW~1t3c)L!=FIF!5d!i=yNr*D$JU4#(-S&;cC26|cJOgz6pFAYWi^q7 z!1tjEKlFlyfD8g=8P00sM2$XX$VX{jfbv{G7%tTU3I^TzJ{b>wjBZ_@`Y=Mh#WK>4w$TR$=mdV=!fk~2kd>vE z0ZjFwS~;jS!FFnZ;G7Z+nrNZ>0X*)A=rG>tf5siyC5J(Y zGw9w~@t#D5z-Zxtxfb7znkAt-Z+jLXM0v{APrmXJ6#zLy&69Y!j&JQ0%OGzun z2sV?C`g2;yLczmQeQvpT9Aeu$kISAcKb~~|u(V}tNmd#GfDcWXX0&V*wQy;+qaNdE zl)y(HKe*q$a`d$K5iGZDhhhO{$<4!r~`Ipb*5 z!4|Ah{tug8X#JQB)F_cdFC_^%`b&B<{%Ei#MRV?xJLX`ggrZ1m;(asWwQ%5 z>XgW#!H;vwmM!F!9vGPb$hx94iNXjRjrwvIbCmYO-9JPbCPj8&{SSrx^ z(gz;-(D}3-H5y$x8$6@8h@YW1d03QN1gVGr*1`}JAAKBG?xkn#&W0C$;!@BKyOYXzbc$x3b6znGTXL(Nfv-R2MQOa(Huo?1k{TvMm&Nfx+G?Zoef>L!2~QPXuk& z)zpX}@&lIfQl;{RNxB$FJqBlhu13Goof%zs3{9a6+IsMESh8Z$sty`1|GF?&G)O{|Q_I7=_( zt^Xr%c3A0Ou+_XHakL$6qRxUR45x9VR<;)_&yab0V)n zkXbO7gV(S!mxEX18)v4BRth@2bZ=In;~G{CD(y!7WXlg#aJ+O7g;JG43?KFsrBP99 zV=45Jk0_wQN3TVRgc6IU>70dq4{gSLeIJ$}E1SSKbrh%magUV#gf2OZX>a9sN;eE9 z@FHZ`ADbo$CK~AU!;s}zF*}CQeDsq>KS`PcSz~DMT6`2~E_xho8M{p{H0SzFgPO|G z9>@5pfD14smJnEuhXG70Jx2KM%QfpGR8i15NQmIqE(B&%bwy%#vCFLHM3gQ!zO&3C z%A~At6LX530Psr-p~+1%$fgW&m7E5Z=TuCGh@B)JLFT!D^JSvJCqf3&^STsmTUQrv z4d2^Ctfs=)l&UuJ8I&MW(iB1u8pUh~`$W5A3x%P?zKbO^W(;Q!w{RPe%ni7EL#_{U zZ+x%wLOhFEU1zlGJfew_*ajDT%8DL_MFy|Bxs!R|sj%%Gb3;J9?s{rE2Q&=dBi*U) zY8Ahfibr+glJmYUdWSb$8?1Xzcj{vo*+=l>fKA`mys5LFL-$y(vQD z2O>21Xpem4J74adr>)1nM_Cx%Kocmb@q>afIX|F8Z=2jZqmPJa!iRnmrCLKAeSL>x zA!&lejY{gOR9#9I6Jd$eHqyb}qfeYu_HNhl=6hfzn zYgYx6!+~_sVCrXyTJ|5n6<7Kp9430_= zeAGt&HSk791-B?j4Uo|*!!;Fjuvw#00d`l-Qf=CH+h?+ke76=_@|nQR+K3`y05If? zXi-NEy7`}K77XmsPXT15^hyzpEQ3GyAL8OC&KOstr?BISHhN8~0`nC1`v?;0s;&rC zj&)=ChFk=#?CaQx*XpWCAB&)!>4N0L_OiRmOJY!S8Y}^XVW}~maFDpwpQ&FhKrr!d zQgECvfBXTw{Zdf+Bl?z;HCQMSSH)QjH*zXjr2^cH9}N6bqCP3>pqAXks^BNI5u*D5 zh+pP?J>jBQ5WvO<0CX}mSm>4iU^KP>bu0=_v|H;Ay(8*Ujd~q>MnBM$#8v)7fIskx zPQQ30PT+vaaA8}H(Zn)E#ZG?dr91)W{zD&g91ZBOXW^g+Zp$YoAUDmBKvSZJ-8M_i zXK*Q4NNDZj>8`;9(AX~IlQtOB`8HVGs@rdZuaRdGC+x$O?8D1>JNq2ENvAT>E;6R` zfrQ4c<2wt#l@k<~N(|P^H_lOOCb5G?IY(WV%yo^!Sf@QWVziEY;3?OF_DmE34jQ*; z)>ull(oXX_@5y-3$39A|D9N8d#3Pm@-6(a3cH*y&J%d{F-+Vb-esrFC_6Bt<0axu&4#U6pD|W?(((+ zZblxFm9QuSk8|cco>=_IoB!azuf9x1*YpU^8FHK+qL|^5`-U4Hp`DT~ezKl$!FHZ0 z*#Ajf;-`L<0_=Frf7lZ*;Y&TX2p~QMA&?5IDz!^wLWqYlur~M%*bNk>qV_w`-Isu? z0MsI$TJ9*dCr-8z@t!t*%W=&H%`k|@sMi6LX?BRRNda_1&%Uc>n4`f=VXqkkR4vT_ z;O})jPu5?bg{oJD<4!R2k`!f$LqSa@jM-g)I=eAIRefB+vG>`{aG4B9CdeQtI! zMAA+-q^tJjl%ZreK3=RoKR$)w2nS6Ia5M6SckIxi)xrG-R_BkMSRJzR=DoYCd!Ig7 zeRAv8>fVFB#<-A=eCPoZ^GrMFB^#e?SRX%hcy;3Nq1C;I4{Yo{T;27* z2dF(}iI+!*5yJlc`&Orp993A;^A#*1sC@fu7OZ)+@)Ry?AKxK3= zrA75n)G0w3%TW&M$RF+IG?sJ8-f@Vbj~?D*L?H-4>aVW@+4FFRE;5yUUAE|qbuh&5 z>!NpbQvhszFz{MQ7`;W4u2y*qcVnT*m)G)DBs80?YMf-XQJo>AI(T(2I_4wX7AK}*7m@XUcb+HYRKO;y4{uM&wK4d|g& zpYdmm(IV*)xTnGBdCe8v5jd>qVd;z0zNku)Cuij)0mrsiPEZ^hTZ1hkL5=uEns~>) zhNgwUHvb@jkmbN;?BIXUE)rZArJn-EFmXW>A72@b4-x?tVhZ2~0#b3ux@feUta`>} zjUVvMCiHrH>KRBgm@q_`t+(HfWnBIgzN#`v2Zdom04mQ~pohMPX} z2T(0%trnmiUDb5XK=Jl7y0XuP_=+4V^jx)lXjr*aFxXC*#&;TFMBS93Qx7%SQ14Wx zBTj;J#)OT&wzluL`b($J`Q-VRKmVoG)5lM(zIE}!>f)_yt3UYOTdR+5Tq|V82%(q2 z@s$N65fq^ncam$1$^@FY|HyMMy8nx}ZusQ=jSoLq-SLS#2;r&u4@9K7rcmLOzvL8M zl@L@oB(VV&R$poUgF91CK!1aKXNX%^qaAeX%&=&qamjMzL*sFuPoDp)pZuBCkpl;P zvi6yF_dxg!)O|tQOTf-+@GmJBac9EoMi_VaSrkdVG@-o zHJUNQN)|aPf9}Kd3Cvv>QEivNT2Cy5Fnk{%2FCqB$fmCshLF=T+=PU>iQJ8qgyRZ0 z6YMx9%TXvXHfMdCF%)jamv$+~uHwWu`p~IAD{z4RpkTiPog9FmQ5^Y8H2RNJ?h5E5 zMKU!at|@o#DEvpKw62L7g91L~M)5Ho(Ba?;zvxtezc@}x0Ypm0PW|OeHg(DV z?G_nOPR4DA@&nA^V#bD{Fih8XedF;Z$+OQjD7+HADU;q@G}{tia;-WaIe2h&`p9uR zsXwtgdH9G=WHnJ)-h@bFHbP%Ukl&*R53Qa*ac1@W$N7c)lTL4p5^P*&Fhgv|CD_IkKc0(?$c_GqsM09 zF&jE~I%?zd+|i>ofj(w*za2WTe|6aIVtDG<@zpD*Pg{F!OdEa6Ca0P}KiW3Nv_7`2 zVhc?%9y@f<#{9|EvnTA152LT$vAY7UU9$=FfMf=`z!m>OraLq^VXYL^2?g;EI141& zv}#n_uADK{tSAW6&QRF7ZdbqH(xA z!_BleL&d)7jT{1m5B(&|K9;`zm@x^eC8E_gh26mzHubd?1?~B2h`wQnO>{dV(9997 z*Osh~^qBIc1wi8ig(PU?B~3AoNI1A$Q9vL0Y`;wf^p_6IB?qu}{9q6tQKOG~^Pdr; zSpK6@ur!QzX5h_?S||?cexg;rRT*%&(o{M#fRs$#lzIUc2LKo&y$)#P1>FW)U7{=R6TX_Cdd>UBaZUZmY6j4twpo+sjDEq=eBmy_tPW~%rD>-}(=l-; zQcImq)s2_>#Pygw0pj2$ad7(p%#n;1zYn07_jT9jV?og4NVV(4eKtwfN&Qhc?yXFO{ngZrh|(=?5_kNgIF(m(lh5!9qSoDfQ~|B|Yq#h21e=u^zWuloiW8 zyU3v1e(%n1vkyy!f5aw|&)LNZO`vbvs`H08j(H9p69)v)97Szte|0F4kM^ZM1tO31 zmAf-r&%P13pTJosIi35>3G16@ZPKZ}(%(s=eRJbaSEuZv&r`=wtbXjd=WV6>^Q(_; z-dJ6<)m`Q3JC{E(AI*^?TdaZ4Hxw~vG$n^W#MuSw7U(y1uZ5cSp*Yr_w=u5^0YCTB z%d5|yJGc7a`t{W(c02laEyjtUoa}S4N`PkQ(aUYj z2EX}Fy|ND){e}{_@PkqWsgL4fiDoD?|G{rGc%%N5Khw|n$uHIwXU9;Leb?&&9iL_p z`_sUbjamy%C(g{Is5~}RY!Fd)7*#cy_o~^9Y7S%V41SXaJbbfH5gK$YfRQ7{4qD#8 z2!nVlsAQAl8jAORkt>=e2XC{1CXka59eOe+UdYabrM< zyv#zt7*BR9`nbe}u1W$Iepa|}pM%q$fo_;ygT@e=67`XWzpZiv8R>0-?SL}=$Hwmf z^dmtm#Sw8L1$vCW`nFfhI;Z%CeS`B{leagan}+Pl<@-aQh+u`pb2 zojr-n2&w3xt0FdJ^m4>MEn2gj?QZJn@>e_>Tp=Q>uJwmSN;-NyYuv)5tML_!lmP4-XPbFNxVee=?XX5TMWk9M2Kp*BBd&wlF` z=jTqHTHQC?uiyD#^0wzcgu}%Y@r##$3$(gJeRSgBk=2*ZKkadTbp7V) z_JeztzX!8t#u+C85jI+fDKuy!J?cTD54|+Dknet}h!p)8nLMxK ziNBj0J~N}tT8U*$^hgsLd}v^yw_O4NF!U4%sko|33tnHBT-$n-W)oT)efH=Z>HPzc z(Ns01uW#D4%yK{bC-K6eE}Xu;DemJc$XPU`UFHKb0vVeLO_(EHE70SC1sX?l02~G+ zUv}sb775}?#;haKfWXj4ukMl012KAhNflj&HqUH;y6o$A^Bu?lm7X_&SFb2Iob#y4hS5jq6fW!M6JFPvQn)=A?@gkx~i7`-~->x3JKAqL=XRzC?RijX0Q~4ZhGOb z;u+nSB7G3ZQWH|IPF)(*rGs904jjhzTrZ9el1mmgffx0j(>b!LZ!R`lZ?9ox{6Pyd zQ4)q$P4zT9&`ZT?(Zmr%E6ZQ7z{zl$-ec4h5m5z9mqi4{N=Eko`!CF z$4S~%-=XIv03xEUPi>W#WMD5fv98^|v$}ov{^}3j{`TsP58wC6^9`FE`185$-(kG{ z$86I4%ddZFb^hq_)iWng`mNso%b))1)ujhFQ>hirfAR+ZG4>7C+IM9C!PP%{>2vn% z_sP}IzVNC&k9ueIKV130Y;tqmf-J)T%5>t1_OZ|4x5=qZ&CvsnGP2U;tuvRe1GWPH z%Gql(*aUihAwf0`y91*-1~Q|Qk^MghOZ58U2RKT6 zSH2U+X@mA-k14qxCjo{9?cR}3p&RAO`}{WOo3=XpR~O#5mG=8JIsL#c%%~gtOgOH_ zwoojl@R!-2(HWmqo7YAWZ^<0B(^1AX_dtv=go zxo0H2sD98c7X9MOFRxxbbH?tv*uT1C?fT!|_=Z1TfxBr;a8-_yfo5!@S7E2#NcYfv zeSqyn(>jU?On;*^_+!lYvB-4jzX}^W#%9t1!?&84!NERgi=}|pw|5j{U;Oe<1qBg5e_G=S_bc_HT zNXcLdT3=yUZ5HZAzi7A1h0F7OaNz_q;?pp4a z@LkV782kQxX+rGXKZK0d)rFY+xu5XF-qFm1Pe#f=Kw zKVjIOfd*g(h+wBm94OGGV0X&bc5#SjyLP*)-)C%{r%_(?y5^*i0|Da!8^$H3jL&s` zgIAiVQb@}tyCT8w7|%%Wit{y~_?1X?;#;G-S1dY{(CP~(VuMXM@dXr=w;&bZ11F8X zb$uPIg%#_ugj+YzmSkkaubb?)vkCBSYJl|&s1F7KC~*T=Xmv8;saZ`)E7@Z zl{^Vmw#ZN7~DWPPSF<-*>=$ zX{A@O*`vMg&++xKr7c%34Ub!>^@i?;cI)uNhw0R#P#tbg zYLf&#)7x)q{~?>`YNc3zdegF2t`FD*qn-zrce9gaGY4mVT;d+Ihl+*ywEdkpa@eAI z=$OKFFyZB&fI&3YWK+HmDDT#}2d&q%-Fd~JRyf3)HWe~%-9mGo+rLwj{;n=72d118hyv!>^|A7z4G9HR`S)F|PP`q1c6j{5L-xx_7Q zpb2xN;iEs&@Ski+RAcmtN<8G-j8bMjBz4MBE_liG=Q68MmyE6FZKaOgBbQ^Ka>~{R z0I!v@)P@CRaNB3CK*QSTYdCa9C|pgYb)d>G0O;ydU?>S-3I}(~CGq1g-Iv>{=qym% z7X1?z>XhRkh2Q)K2Rgsa{DjKgSAIMigTqtOTQ0C`5v(^w4{5JY9oeW0UnMfG8MRcF zgyf@}g8AefzoAeRK)u^Fb=0s!rQE~F+&2GM1OnFqQGfKl2!zo;T??O*%+(ml0MPGq zU9qV`!>iKTBT>IEAd#8sA@3k0u&tLNg%=ug+fj}l?SiH~)ovMfTk+wI?Lz@RfT`cN zSFDj{m65BMZ8j5tz~g?$OLt=*w3`w~V*uICEGk?bx^%m+{&d3o&`xmg+ljGF?^gCC zu1{=F9Xe_+-a6*b3*WSr%e#I{GN+~KxORk?h-%WU$&gNvb-S?s{DhW(3B=;{XCKYe zp+hkderUIU>eO7fEl--2Hd>+7)cK4(V|m1$c|CmKkkKn!vAW|w>vQFnPTGl&gv?9z zo%f$`l?RJgai6spH0gqYUi785-n@G+CKrs6jH8;IQ6}5^Bx=O2*mNTQ-OCpZ|A6Hp zZbep3*tEauWl$&WHr6kkf7agCeSCH0_6^rRV(mL>KhvZwE)e9c%$6tlz>h1~NBdQX z*uVDK1%@LB4zIra^s~MKtqJKqYp0$EKW6z8fQE~qdLP}puegkn4XQD2v?gA9E?4b1 zVKJ#s^;U7U>HdS*ODYcpR3(xzViflIMS~wY_w?$uGw1AQnsfo-f%Wfx>u3A3@o~^E zX4sgrF?w4MM_Zgin;@z&`pZo*>(+I>cuSi4=_mc{(-oWC$+~H#0gMVa8AjXt*H%X@ z4(0I7;bV?<`OYmL9}jI3E6vWe0|pGBEmrtZ#%D&*3NvGja!ePJR-E-raZF@wU&*nA z7mYdXd+I0chZ@KFDJeGX?|MO*#<~8k+jI8UY=ziUCnXEVBQl0CkJ`9AY0so9m)c*i zo38$1q~KL;i@IV}Y*CU3E3fn-ZYSFnptFNd|MH zPYk9y1&5e20|UOf4^G?nM3<3Ssjkl`v8SPE7M}_N$_%r{RtC_%-%4RpX>b9AeJWL? z=)rI0GywFb+%}$Q7xr`g^>LuTY5$KMWVC#cf90c{otnUe-Usg1iFDOkP!Wy6HkGg$ zW22QIw?i!YQsgRP{4jR_CtRh+n7~*fR|5gxEc8&oqwiY zn?R3-BfVV*bC=24F`z{c+ZT+D-9Ex@TC^Q9;v`x!NjdVNYw%c*|D14ka{mK;0aR( z+u6#C8I3?Ay&c@`>h7hl?Zig!MSR*X^;@la=2k!Pq}zIL+DWlq!lc`a&)D0l_3|s7 z;GQ^i#8$cPx$K&qZ0m&ks@;xx#U>9zZC|FV)k%9B@>!d3>o;KaciHqlu|L^dx79M= zGDVuApEe(w;ONC(NA1nMpV)Jq@gk@=nU=mL%$iW?WdG?Cr|b>Chuw!>wB-|MYm8n9 zrpeJ2n^@>J&ytVD5<@%uI!zQ>k zK|OWo!Rm)?;;Uy~wP_;p!S%~-|A}2F&??}3tcznS={D)qYWfSpwP#FEA3o|6RK=so zvv8Fwb)axQx@mc{6>t5d7b}Izi*l_z>ut)KIG$F1Y*KX8_{yRBRu?RUr3sRr?`Fc| zBhCkihrcdV7bwniHo?^@`RixTTg=fX%1gXdOTXJ~FP1VtYMaL3n;(CitDx>B9cbn8 zp-=L3fkiLfQrqM0*2jFT3PUUV8kfS<^eWg5nWOqO4g<~g-r$0@G{X&S1OSMmJ(4SVcFWVyrx-g=DvR8jg_f$@xZ3^v^ zZB3x{T(h2YR{t|W*KtMtrhWAl`)%$M7Rznr-1f~MT)AR7f9Su-t{lE>k16P7W4dsp z=bsg~a`$dWDfD2YUN7tG4SB4E7CqDNTT{ur?SZse+hw!QY&h_D7 zCw-0e;73c*0c&2ScBs>hxlb8d0i;5qhu2xZ;b5v>Q6VNuXmHBm%pOhUy44@ACLsNp zGxuk8u*-iiMt11aHxZMs*x0SHzw-K5SO3Y^{*kQ~eYE|pM3FitDmwz zO(u1ctuZGzZ=2rRwwn4U?|gUl$M&bmM@-}*wAao)z50n4URizhnHN`IvA;im?|c6D zt&cxgee3RpXhX)U+kwAu?iu^Jo71a*_T?X6J!_NsKYRCW`?;ITtKa(e*R8zbZPcol zaP?g9Kec#YJau+;$|l}ANoOppPjupc#U@7A?%rPgi?9EgulnleeY6^;KBOOY#F4I6 z>3;E*FZiV7N1l7hE*PBf6+7imcsdFH`iJlOWc*)#^KVw~T)pfk^d>I9b=;*hGWja> zrwOQ5@PF+mes=Yxr=GP5+cp3DpZ@4~e3kp<(`S8>_n&|DXME*ZZPcor?4CV&YW3fq z{Uy53CepW8zwynlyZxVAo^IJ{ak^7Tv00n6>UiGXHm(Wjul?B1c=^KVGv1Gi>V!>- z{+plq#lpR5lc(Rc-@{gH|Mz#_^ot$Jk^25MdyBGe{r-=>{G)zB<(#!eZ^@2vZ7aGq zDbWRwzrOIUPtyL?n{W7S#djat?-emFEi#Vs^a0_jJZHD?{^F~jU%hnd%<3P%`Z>R# zqQ2Fn`jtP4G2t z`f;b*sNcNwk-hxt^6LM1>rH=)w0`$k?f0nbi>1EZ3As`<>qluA6F(v~aa6AU>F2+= z`uFYkqWS&j-+lXgtKWV5yAis@_OkW$m!Eoi_2bVz=Wp!(z3+c-_4}stRl8{NRr_sb zN%S{|I(|kte&&BC_Wvd$e}JP z$^MdERJdZX{)@kR!+(SN+Da2^^+;-}zx&N%pKJp;kxhtHM1O!SH;G?rDX3AA^4eD> z@v!17Z6T-}9Qi7SNe8wMdc`~QAB`COVcy(F#HE+{i#P(K|D=6AudibVUinL0`1BKa zqh9V^W`#z0nNp^ehQ+4>pecB@@}JQf9V+r*HUDqMZXadHTclIk_%KL7`Yt~5CFN`ywM%8>B-BZn{W z4tRot;|X(@2x8bJ_(z5%2N9ooP+uCpnr4%=bGG7q%O*R?BqjY&=V8Vdex^xB2RJUAxV>KWJr; z@~LNSb+fo)60PS*bzAR#`$ku!XY55@nsjL*s$r!UaOrKwdY+TjHrl*lX$}nDZe4dAKIirew^ecFK)&_Nxteg) z?oLo2J!6w>txUgaw_oe_;vZbTw7O^UyOP?eji70JaJ|jy3q6;t39!bBRJEF{)qkz# z>TUwebs@p?l=c*ApKkHjgz8nB^lGJ5{c5T7@uG32KG3bd`rYUoHi>-Y%voE#y|Mbl zFh@|`;;!~p3AAsP8|^=y8`IasTQO?P zt6#knO-DIXzBEB*vMd};ezkJUJ|?*?c-*&AnDYPP$rCp5KD|0?cLOK~npE=$ipI!c z-O6p{L*>ijlrN2W$@Hgf%ri9d(2`zA!n)DO_pT%|#naan!AS32ROQL?JJ~GvlWh=} z47E`{@}bLcv&yy&CV;Xgq_Hl=Ue?p9PdDPgVk-S?QpBp*iGL3w?cI9ae7pNzut6Ms zsC|6{lSdON9vnoS2*E=NjeO)IpG}}S;NYkkMh!U!DD(j2%d8x;L&7M<^%Wg7`>n1p zWFSbn#?_mZ=YJ(4%*+^9eAM5;}3h>qqokt^JpLn7*N8WmFpDX z^`P)(0LJL`BoE#AY%O1}CF?y4+6T(ae+ouE`eRvPrU1XRqxBDQfj}ZgPw>%OE<=-R>fuTIcp%2!uKtyt{zgyh zk@1fMw7-9l$usIGib-^R29vxKQ8xGoD}7OQnAKwJ-UqwkAkgb^OzJ-}Dt~ zO{O#v)bBfM68_2=tq|_Fm2~^LDEreyT9f+7TjWD>{U)NG*Nlc?gn_) zZg2ljzw#5l60EVJiT0no^L>9iwpRHyk^0)x&)Y=Res=1a=X^}5e!=qozy9@~`Ypcf zI~AWtJ^j#x?ft9QRu648`+Fxoww3PVtIwZ*+TYy#jSoNc$^P3{?UrrLTrHoM?3QFr zs+bgkrheAN6-}z1K7PzsJ^$*1_k9w6)!M9a^c7o$)vd{|T0W$=`oa&b-umQ{|BkXI zVQiX$y<|D~FTVEUzVfc0T9V)Y_Xpqi754Y8Up4!Kw!(VOZl^xE`so*6UcF%YC+#A` zckJT9cMMa@E0cEH`1k%Y=7jG~#hG)7hjJq=$M*di?I^$Y%(JT>vQ=A6a@B^nuUuZe zd+oZlD}Jw8lWR?!fAqO$?OFM!tRLg}@Sd$4fBTb9JddB4-Ai^y#7j1Deco1zpSL?D zzGf@j&l&HxzVnvd?Qz-KrhhReJ(g)0DYtQCeWywCo0l&6ME+-O@_yJR+1eL%k?1cj zT<}VX_{uBxGiT?0QhnPd)_?Sl?h>)vv~41-*~@=K2Xgx1;}%7^SB{;;~0le{w6UZREb04Dh5hZ z|0N!GvQ(Q*plKLzJ6{rcpWU7J@@R;}_Gs2SXGI**+kBl*xito>kMYmWHxo!9K^PBU zVvD@!S!J{s>6yVUGe-X-z02-5gdbxW=?#q^Nhl8-Yjx1;j~C+mUT2-JQsZh8(S!*1 zj+c?txH7s|?dz%f_oz_xlU9DlV()u!9-1aiTIs!HCz~H!zhW=+`NVEn{=`p6HHp^h zlO`yyo_Wfji#%g1k}udq`mLi80!#n^KmbWZK~%#Z`h;s^^zc{XY3_b4N7G)d8XmL> zZLDG)^i^f8Mty82%pce--q-EfZJlIm%=Rc*a$ z&va{S>aEZ3ow?!@^H;3B%F8KRY1PDQTb+<#%o3 ztjV~30!*vVTH#b~b@4$biRW$7cFEeJ@v1z;qey-=Fb^Ek(r#2~H@jUx2dtBqPw^!}gg&Fnf5u2Q90REK%60Qlcc1Gn^lV7bwa|~b=TsRubx-f9T z#>XwY5TmiCyh~Sh%DmsLVmR992_l7dVfQh7qm>;ho7tncbEP7cSN#z+b?BY*wq+#i zBi~2}h9gag@S)MC9Qh-S-lpYdj-~*;_EKFI$fnlI6iWxK&q8+`U&{!)DS71NZZ9ag zQR4K6OA?7ph}1QvsxH0}y}opNEYZ8V5lrti>hvSw5ynUhK^uJN;M#c(6$HzZwSd6! zXa`L>UiE@M>xdM9%THd=V+4+Ui!(UbjdHF~z^`%yNI|iwB)|EewjchW-rNiasM1S* zU0JGx41pBVW|czj6xZ=O@vOSB{@C&*8rbT4lGFrV+j`prfH4WoW@Ym~4WPOtzmyqc zl*s4!;EZ`1>97Q++$11z(f-j7c8x#!9sSK|EQGPHE(`e0I=(L2sZ{J$(vuE+_BBqp z8chaGSsM+(QI7Fq7eCdbXcK>EPM*?`{H563(1Iyo@u%$4Z~2eb0YiU@hKH|5FT;o1 zuMf9;&3^Zp_|q=p0xy_4k+oOwtp4t!cm4TeJrDcQ&1)6zH!i+!lZj)ifA*yxw`W4n z`E$s+omy{~_Q|LPm>ts;Mv=!ygi@V z?XEZNPJqkylUIN9!Mip=xKXf*&|6};R#|QooHk=BADU$9#ZWgd-1UjDRJ8$}=`>!s%gE^A9k_Ba{X;vS=Wo^v@)z*2woQo-nEN4 zdWQBd-+#~VJW#&XuUa)%eH=R)$C@bq_P4*~E8$=N@Pg|qTFL&x^7&P}`{2i(d%;(r z^}Mhq=(U%EzDF@ncPdLP<*e9ElXytw?d-Qx+&y-MspN=^1DSzcZ{ETLW zOAtdi=nrhPXC>`#9e~Pugqi zjW!5jDOSOf#Ejkb1lor{UEce8KslbX^+8-&N4?@q09Kr7f2A%GV1B|9JKCiX)^4LC z0;zxtDa=bh`7MBn+hGmfz3`YK5(__GU?SJ&w$D{fl<_XtpLJCoS(PT*eJ#M<3M?foNkK8__ zD!EGivAH~nzK(pkbVL*PR(*Zha%Uy-sf|`a2lvT+F2$p-Sj8A|^2MmzAT?prTe*1- zQL*2!6IDCs_EjsbDyo@!zS&oUY=!QgtxP?z)u0VqokGUPW;v0%R-5nJL@J)kjN6Ol z_eGn;YgJ0WuPoVhyQNsScH2vSZ8J-ny~c@fb(^}{=x-mtciS-ZGfrV^H+|dc)m?vS zm%o6_u(T?xms9CPey6r@7jKT%%Z;?^%!!ZsLlXi`>V1VS8-#ne-5?O*s6BeFSbzH7 z#zrz>_AlUS(s(9ZZ(r7*+N;T;-mZP|)^)$Ipw-EUGk#J^6LP(cSu4o8?OPMAleS`i zYn)ap09{_NTJ%QpTW{w$&~}22+4y5ELtmy(YG-Xm-zrO$%J(E_H;dqtW`?c z%iw@Y+cd_NSNYM(tZwO7858Ju^R^yO&@=fyCA2t{OX-P!p6PuUt~u$jWxa9T;tQX6n<(Q!Sm26CD&WGwbJ~8t%lz< zyQ>!SU5oAdUH#^?&_JdI{U~od)zEA`6r9Fy66sGVarS!aBk1dU6-WAv!DI=6PXB~N z0x8Gv;G>UxiI03%0x%kmbQnszmrw_`r~5kc&Ag$9ZJT0aiN=#GiKRukqtO5>(7sOc z#p^?;x(TW-9V4KHwN#gESGT!inuTPEhChY}|6sfF5UfUrNux1=pOCo&aw3O5iN}gS z;ZKrjP-Q>jL8rSJUILdrzTe6-- zzGu(R`i!Gbtm@E|#BHVg;HzUxV*l(VTDID(3DE_+b@rUSC0i?kdW-ab_O+k(N%Nn* z_dWYv+-s}9dH}655(x%mLU*|SCW4hW#qK0IPjmX{47#WmdS`bOJ0`*E=suaE0_pvX6RQdp>0DieP zJUwUrtUd3oKflG+CM1-M)Ot ze{0%auJusAhi<<^Y!fp*+p8CYX;Q5}-Nvows(<>$mmCLQg7)7eHyhz;(+a76_DcK9 zzT4*5b~v?;P$tbtzT~bWnhj>aIu{lyDxGI-b2X1a`%XjCWLVEAU$UP*(}e&jc91@? z{@8xTOcxAR$5y@~|2=z-TNkH%OlG^3KmBO}t!I_>^HzFxS^J45vJdRd>uoyRM;LWt zm+#(x)0|3Zfw($G=%>2m`x8AYZEx<@?c-Zl_t8%1$`6WW*j0UOIhGkHrt)J- zu9tl2_+CcfrX5w#mS{JW`Fv}pC7X;{d9eSX*6-t?KAJb0$b!R4Y&pqFb?3T8 z18O|gQW5paRb?Xm6)hY48^)NRwBZXpXFy1pb@~+A+hel1e}^@-FhtI(4$z@RUe&Cr z1b+j&;=#T{NY;=z6aj>;GW6>fZW>tfX^%K$jjN2s*Him0)A3~rQb0=vDfi-F-(pg*4gr`m!Bw0 z%>P+QRs_z^Qbg!aBL+_TkH5JGgXwe9*B{@*qal3O#u2U9I5#>$^+~i%q#v^SgK3u1 z@fA^fR`JtS%9H<0n)R2Tn4mj4 zCa5KO=W41lrFvM!s|!KCqHVK9{p+f4&WU8x!V(-Ui8)2Tj%v+C*FHq8b}) z!j9iowl~h(B-=5KDI2|=e0qGsYHv&Jvl{El%er{73(1DB0_!<)U%Br>ZLOZ}>dHao zSW3NO+Q*)5TBX-~QES<&v`-0M8lsm7GrDA3)KDFTv8G@@09-Ywq?xcVZm#AOUcFWC(I?dlKa zb8-oAYdMk@2t%8*-tHNb4-01W$2kqg6re*mzoMcJCG)g+N^ubQIE3eUypb zm5sm%QXxfs%-l#5PTvH0Do6c;jS(h|?#&dydPu&MN?Mh$=cK*N(*bJbFB$!pV;=$f z)84TL)t6#w=pfN;+xMTw1ydClOsU@gXcu(zpOzJG>QjusBaL1q5(XLU4X7hSmlF9B z9sJO$9x2a(npfw6=t{a^>6y;adjn47Q6T|g(<(|_Bj;*cjSU2403+K8nSJ9Yk1k3< z>qK06S#Ze(hW60aEguu}BCs3vQE2fLCo5wF(;a-2ZbT~v^|W-@*Exlyr$rMhk%WFw zB}WN|6WX+(>u?l|=r606JaFgj$6q*Tco`M;bFVGw9BY{5Dw zY+>AgO36lyVd<9f-?V3yPuOk2|K96Ayn5N5v;FF``rYP}t6zA5&P5g%X(SV9UBwMzizG7 z}ssR7vt#it3DCQDvwEYnf_ zvDa+l!oWIr@v~}riI^tF%CjbDUpoJcw_O)>#!aDUrCzQyNquNbpGR!6t)I*C&3s2K zX#l+G_F-&E25HCaB{FhDP^t_#_0bef&x`8~>bjsrI^4(%K#OB`;3|K&?%4#|=i9yS zaHa9u#sco+=pG30rsXuM=6kBusOCAab!@t6vHhmK^;@go|Kycd?QQWV{mtRe+KRv4 zx-I{DIhyW5_?_>6-zM8vZAJQ`KPx@UaE_Z9pAcFAaH1rM)!0-lI@PjAd1PgTi|?r^ z^n`MQ9#nN>0@RCSIJ9R}QomNe%@LCAP?+*=wMe z%CbwqqDKSz&G^!x{c10UNU`x{*IfL&T7jv=Nmx}yas|AXG+J{tv6ZMJe zKmdn6hPCos0vVdN8&pqsXsOsAFXz&Z6V>+3VD^bEi9ZAFD)7ZLl?-^67wJltVNB}I z4ndliVxx_vE$!F0U;e`aH(}I1m-tO%tPuf@Q(o)=5q<`0KZckFgWl-o@##Htv_Yse zl2}lM553pr)IZc1v|a-E7Ga$!AXvu*Tpr3svFtYQ>x^`eAR3!>nW|y#bhCH^u?)_A)Qcr+SkMP5N1J4 z@tey`J|;>=pe4S3e^`^ackT9at&Hi-+`5JQoc~^POyUpjx3>w~o5Ag=O#d0KJ9Z1L zUIw;q6ShwRe2gn-yltTY>i~C+r+D@zKBdA}-7UY>of;Q5Cj5AB9~FroER?gzSKR9C zPYin|b%%ofJiTLT2Raaz6DalON%a8W^l#ZRB!j!n)IDZ@Cf$R#)peV=>8<4|^`bNI zmVo?srR^4Ct$wy~wWPiKe8uZ4=k98Zetq_pWAeO}2sJ)@jEwMUTzI{_ua8cU4dJOB zdNy0ff_LoA+j<^)zg?{0c5c0sL4R72e#s`+dNJY;Y{gnXcjfPC$UenggQpgbn|t_J zJgHJ~w5Xm`6z+GD!y!MAgi4?zjUIAJ^p@o4EtXp|?A>gojyrUrM{kWDHL-x-G?6^H za))9OOy4}}OARZxR&iT1Q0a9UC0|uxiLY&`9?~8BqyYq^3oQf@ZlHQiNuZa4!4A3I z!W|~_FSSq|560->2)g1!fCoInPkOR1n{HkCNxRVuIO2tF{-?m@&c7W@yBJ|P`mgvC z7~HY_OC1s4Ld{RPtA-?Jj3@n#{$W>HfK-skK{-h#I`Gk@L=L@PFSMF$v?}XtQ{^-j zv4l?_(--8n_!v}s(nI1gQN%B0rIY~EVf7*Opose1Kgc*vy5cKK7Ywj9)=(eY4~-)J z(Qo}3e(S0mj!2;8iAbcWx-j5@CmNYm*VG#h_F+36?cm{o{I;>5o(X_{blR{Sz8v5aaU^W`1r{LWf8nikE&dD zZeh20l&c|MO(|Nh3^YF}P0+@lB{vk80+6Bp(ky$(CyAOU@`5M57)*caAN{cci(ffW zKJ>P3{b?etDE;$>M-vW?&ZPES^ z3PV4ScJkg~GmjUj{i|<(!*2!5Re0&>P33j>lsuWD>zb)sJMEl| zqpzodTlBu@sWb`LHvUXi3*UqzEwy2?Vm$GmF*Ufz*_gMIE5{leJqzx}pd^xJP2(T@7tz<>IM7wpaPCssfC{Bx`G#{0+i zd(pZJ;=>!a{7wk}t!Tff5onVnCCiKEuIE}kBbw!=MJfZ@J|3U@qfF@OQ`^)Y_~^sf zpOj$ryl<``er&~0dZGr;$}t|=k3IXUC)xC?9f>iGGP+!%k)P^T5Mgk}_M_MQ!-*1H zP|!n*ct?H(gkc#B3`;N^`BL3|T7JErJ5f@O5;0)~aECJl4Oxzw2{rTCp&GRNb5`OS zFUaO6jLB$uQKz|xF+?A*5*v}5M^~G-gT4m`dK?P`Pwe3|xrSY1EkpAKZSj=|wMPNq zUOK9$uA}sIiD;J_yGE!LKX}Ab{!?|@{)X3(vew5W)<|Pe{2|a$UNi8bBs&+6K0EQqO%GN{M=oOpUntlcp0 zGq)S~TgBWg4&{-*sA>IBt2ZZYf~=Jt|LG&SGc~OeAG8Tn{1)q>oJ_?;f#)k@JlON6 zcB`nbNSmo_^wwP{3{i*~4glzZJ) zty|~gq$ExcrNM|%99rc&VSlIW;)eR^l#L197OR)VY4tA=y>Pj`*zJ@(C#Ts)lJ&NfiSz>`J8W&ipU@EdaDRY(! z;)G1}u(3}TJJgG$Lf-ISYJ^#gv`AXrGK#?5nQ|IHQTR_Ml|N`JbuC-JmrR5@an%%8 zZ@PZ<%v1gw&>z`bmwmH_96NBtCs9uyi?=h!3w`v0o;&{LZYEX!jC4M4DcfVVN_^hl zW~`U}R5CJ9O-yvl_3LM!wx9j6Hvq5Os!=JlkaeSKjO7?i4Nzu+|v;sjbX9Igw-cr9hFFwZ~U%dWDE0Q$M6pcLD&1Bd+2QltReMJWstaqfTWs#av3Z7&D23nY4OT`yoe zXxR533PG(;-{wrW$iH{>vi~gE7oK|BZ9aeQDSzYo-+lDK>Vd7|;a64%?e2vyoPWwz z|4w>c7gR3TWafh1_8c#+@+M?pSSHyO!qUYTt$u6M1V`gS7erzoG~1#5uDI?)dipDO zRavL)?I#CXfq&oP*Kd#OSgHxw57{%&YUiK(?@Dq1O`$A7H4@2eXJ%>%SI(p&iD>0rd7ZbPjwwLH0HrSs=HhKZU?d> zt&KPN(JHf^zyG6me&EM~eU^Y%&zxTU)CO!~my(0${6V9LW0+W|Cb zzh%FD$G+#@Hg$_aM6Uf+n}w2PR!R_j$Y_1=^hpM))s9D2=zTH8!*^GMRRJWU`0`@< zEr4L;=jfkvR+NB&rd;A9?;>n0tI^KZ!l;dW?AuXt0^>-olY!9uI%g(899jFR3Q2XT z$j<9VsMPm1rbgPo-Vj5w{Hcy0z0A69y+2U?FUA;b;;mTvn4~n)7|!_Nf6-tmk2z@D??5j)K=7#LCILPw?6&O48nIvuANt9Z z44$zQiH@i#$0+IJIuNBo(Ey1*g7h-UM|#eX))U+|YrvBVjl>Zj^plFm{MrCe6#ZMc z6`lTq-|EKlFCdjV2qN!W(brv1ADeEDWLo8Qs0(037H_Y&CmjR+m8Xq1>G9X}&e~5> zy>62Q{XC6wEfnqlPuou#>F?aJ6MiD56V_|C3Vh?f{aj2wGb_Ej_LESWRI_@m+pM3q zTdSWj+h=ZY;SbdMTci?QmU2Ddv=nq z_TIJCcC|^jwEODr1ZgC!|Lg=q`05W`+|YzUKil-Y^@Vb!-@6tbf9h9FdOx(E_|ggY zYv=4|sVv7YpFZoWu=+h{>BI}EbO*s<{~5E>_EI@bc6GbFo)f-mxAp5M!aV=x-?wpS zCJkG<#HpC|*6VY}kJ|I^HcsrKh$gm*Nh{npZPihub6pPP4wvb9TZP8grT>q+ck8(& zInMOH7DRG|Kxw*JKxE-hGD>l2k-&V zU*T{@!E z?_*rk73yqGo;G)z3nkeI)%;q^x@)X8srl7*)u&%RZS(he`-v)ji+1~6bc?l*!&|F) z7EWIvrZvf0)nA(Hj9)pXp`%(Zp0Z>Wr%NN8Hsi z`z8BT#+c>*XMHA0Lp|y<9AdIt-w5L9gi*Hbqbx5$+Cy<3yFT%!Zfk*K!dV<72y9T* z#!m|`Kf(=x15m%Hb4rhgZ3jj@5NeYr2SD}p2wQX8%o@iZKpiL>u087syAaU@2QTYN zVMl~XJ0f>`Qyzc=*TmQC~g2{NhXP_jFa0E(&$wqrZ*SH)-oPga7o4Uu_rVAGecd-F@s2 z`#7gv;Cg7k=yEE0#+a(mGLo z!!GoG{L{A^Mzf+tUvCR(9<&aKsgEVz?!o*ehHa9Q@67gbX;ZHct<|<(4X?i| z{;7TG)K5R=7ofU8{G7e9@CmyE`Y(Rx5A4MLy@yxrj;KCT{HFDFk@_wBonBoSP`4HB z4^o*em+tcE0z~tr3l#mGxZb$jC}WJ-y{Aj z``dbLCq48g&*u#D;;;Sc!|&OrAinp`TMz&J*Zy^jwWrt?O3`w*DX^zzw5N_8dcGE8 zH43(YN>0ObIqf&rpRf(lmCck#gbCIWtq037yhh+HU};%W?i`iqWJ*C7&00sQQ*Suv z3+Fy>+gX1G@Xx;dW&2)(&$N$b>w@+*``u@?`cn}7Jla3~(ihv8hJEvmH`|*uZ`yj$ z9pum2{D0csbo!Ie|E9e`WS@Gmi)iK1?_dAOwwczC-Zc8NFTC8oVC-M~?(eq0pZ-_h z{jP1ZcOL%LcfQwf>!dJCZPtddxLZ_kPEphPp85ee~b8o}&7X29By4hxUn z9i-(zS$Cm6&a1stbVAO56u^M0T;TEoI^z(EZpQEKAoR?I@2KRX%zad*&qupKJJWbES*d_P48ccTsoO^hII6XCJNBT_9aR z>x;TRXJ6cqAKAydb)o#WUC=QT%B+h#`Cfll7qIWNyOz2u_Zy%3O#5iKF4FYP z*q^tLSiktIziuBD|4GCDmR)%0j$?i3g;6KHx=XE#zu$T3XYFpJ?tZIY-KCZO=j^W4 zC#|pARKpu~!KnUxQC`)t8G-DvPy8ucO$?i5Bc(Z3-I@=bzxN_ffEgDzlx%ig5W`uM4>1>FVIMYcZTtlGr;_FHe-+W2YvO=!FSZ?#ymGNrsE zFeS9QtNrHt@7j9!b*trrcCn-E$G%AHZ{K{wF5vCW414oO7kOW_zV2@NryKkNTE9uG3%_5pH$3z=N3PnFz#kX?*`X@f#wvFyk>rWNlG+lk+*xQEDn$`Nz5k}ij$Bj=L zUL5_?g|~ltg#zbRHR$m)>&g}GeO6|&j{SReo+aMup(JFPplVkf@uzO9Fa=cq?LN^AYNsxAZH6=h>2)t83r8O@!a9!SqwBQrKkX<3uW+#+^g};Vt z{(Ofc0t-I#!z^tQyu-VQyh~Ql-7IzbYE(D{JND7ND_ZMH$kJl$Y^%1zv|46ehuOP@ z0c7kQ)q9POAo`o}<hzOjGg z>v7+kMof?Os0aJN4R662x54`Mr&|2ZGf>KTMXVEaBb@r(22tBU6kQTg zezFqh9>o_-f*;Yz1>%Wz+Q5kC_KEtGdFZx6bn&D+yWh79H2qZ6AHV#ShyU=m{{37N z`WUfJFu!AWob~Vj{N6Y1j?G`U6Wn|!#+2T&JBYeF`L!Q@w_R}k$;)4U_~S2p<>8M$ z|K*22{LL?gp51-2i|p6zV)1|ao4>TX#qU1+H~;jXx4Y7r@m)7D@cj9v8D9j5>LyZ`Rt`|rH{@Na(biv1m{{T;7?bW-lCo0;(O z$Nh)MrdXYoEM)1_wUvM}`TB_Q(Z`I%*FR;|KV3lUx3vGaZ~t|>DE%-0@K4*9x&24K z{qHqtUDRnC{V(7AhW%~vJMB)f?iw!-hRDV@fG%$T*Kd8Z>FUB+7vz8TrLQ)s^6JaJ z{^I-JZMy&Fhp!m^efur#x9m-jcOU-8um5Gcfc&ri=pQxSe`?=$?jMcyM4DUO9sK%_ zfApZAZ~F7U`kK9Y@x(4{?3>T0`8=Q7ca8InS6{X5_{(+&`(?We|9kc+g%|B&{i}B2 zu7^~EXZlgFkF< zB>a)xiT;>D{)4an_CWXJpgvOkihXojcd7mR1)>0JqUVKmW6)T;Qq_O$6_kat!kV^* z*S3f~`IZ@hN;o7nuv;r~WYKpN*rL@y=4*c{ph|VTHvJp(O(pHmJ^t>4hkyNpAK061 zKYjSm?2hmk?ZWt<*|%vco7TM6*8lvsf7|X5>u;E~Ucd6-(=X-e14|y1g;=#JKvB#9y(CXMN0DUykQrDyHun(5F~_MH`ml7<_BTv6~|> zK#AtW$JooEU{HiPxYV88{WY^~e+#nJY|w=mCJ94moD(6a6E=2=5eNq+IBeSFEgeU| zt6|2m#%JY@b0l`^0S_VcaQ6YHCtcDIzDHaY4O+#^v^G|5f&f;XOtb`))1AIu`*xYD zaa5DH0vaQes)P`Z{Ai&)=VvrN#{PUz+%O}G*0DGJQ7-els@EAri?!pxfS)q91ihNM zXF>a)aWUTHUmQO$<~I3rvSHLEKJg)iGxfJ&?XvfwiS zew4Y7%J(_pP}_G!ud`Om45Bo?0VE&z+~z@mmwp7)W^i;`AFbazX7`nF$vWc^L!w+pr-!p5gN_{#CEw_Y=Z|0I$=rmVYX^`ZIImv89;QXf^< z9pX>^s=i*tzbROE(SB?nkJcLw8u!(QboGgVzq4AtVe_ny#On@RyAU=R{a*1awmn}n zU2PkEiO?@?qv|f~kF_m+`SXXb+eNFsiTfqH)2}yDxQPCl{mr}Xod3-muh|{HC+#EV z`ckJn@fnMZlE?MZ!m(t<(OvAHy#ImSVScS$i0clezR6u*D5f_e6#t&RLGu1D?T)wo zJ-DVpcPhVOAF0+I@(=8{zrSnSO5@l)p4eTTgUbW zg+A%<>XVOKJ#RdG&OQpPJI`9j{EVA&h^PHSZ$!Le9|eBJF1+U>;I5L{ua7E!^A@OWJ40hvq|C-+BA(_UVA{*qb5JlK<$Tk5Yf% z>egD-+~^C`^ae=#X}SZ#8@B&^)8dV5^~H0{xOh*j&VOg0Mo`VVyR47h zx8KDE6x&?$rjJ+a1o>}lj^D7lHMd${npb_Rw)B-#cbc^?>r)Q;n`W(H9S7P+)~!xm zII9Z1k@3E*%WvE7KmXX){S)i+C1KjzH8=V`g>Tus=o16qv+X6E_S3uoDdUcEi}zWY z$}mn2{b!luv099NsA=dIY0Y~;xA2rGYgMV#7u93e#o)4); z$udD>pMj^(Kw}BJKD_7FQiukjzW$BO~*_Bxg?I`|%eBbs^YV ziURBQ_$-)5!7!A59|n~=_b@!QQ0bYvt(4*EIR601$sF`7oOzm?nUOy#1dbH`u`cPf zcpsm`7cwf@lBb@7Eooq600(Wb=9Upk|+W>n7jNji`cdP_w{IE_o*>c`ug|Nc1+7v_}7>mDUKgbX7 zg?4A-w?1oM#AFweAAjLvma*3jYHCZ4H|?TZf4{1W6#Z)t1^^LB<|}O6z($(>Zc`T= z|Mj2!C-(0@wu=@!LA4XZ8Ba|5D2CJa`nb(O%WEIC);45yOY>e~=n9r`jh{T=BS+Lx2rn;S8I-?fVk>AwF<|57o1`Izbvul)_~F7sUmT|kp7(8gdd|LAnT$QmJB+oBcCvl^dR9K&#r>3h6k1=Rri-1= z*!FK9@qY4tyHM3f(v?pV`bh20uP=?WQkr0U1Hg1XZ^sU;HN6od zEnNirMXKS9D7b!EgEz!}!^W>|slO*zE&AkyxVrPKk7Bnk*t2{(E-C(-_NN8@acsNb zHm&#ULRlXRZ+UFY@9M(YiqabA@?P8XQ}(8m=ID1`e6byKzGH75XuMBsA3J{IrOlRQ zxc1-}0(xAA@)6JDlO}O_#3v7g_pN2UHe&-4p8R^ajlTPgwHDx^nKtC9;p05$Ppv_F zy%wucTU3__JYkr`(0_fFnIUp_S@3N z`@nFz5Y@-VRj=+?>OzQPp)^#c?)Yjszd7ig+SIMsRdkXIO8dQ?qoi(3qc>B-!<Di8- zh1GSCvc7F~=z>%iV_K8C*wZ+5mtU>Fu-gW-own6(KXav#|I{w5KmN;Kv5OPGz}L@U zY5v}_H#&5Y^D}!x!lW#TuLpJW!4ioU(}vX^U}-h}_igP+UvD0%-uHg-3Ev(zFS-a* z-3@OVs$1jIHkXfapqKfgptfF2r@c92^B}x(g<_z*k?$A!=5wp(XLcd2ZTV}z)E&~c zX&OiuY5F$vwx8IA9(gCB3x?hRdh0`GQ@L>q7XG zKleMp{=~eb?i{wN77ZL%p_G)NVV~Q z$SrBPsA%@H!bMzEQIKp9mCVi#=4N`p?@ z;VzhKDTu_8r%o0N_^gk*NWffB2!atWW}Lj*yk-7U7rNvLT*U*UJ&IEjt+47#TJ2F^ zQTzt6zMq(kc=t0?{lrCn*0}oqU?AAudcDStu+MYHcK?VseF7n;y4oW;#JiG*m%0)k z^mgJfpt3IFp@ZK|n;_i{`&s)oXfMqGQ}W3$78Qrz8qGznvmj6BG0Iizjr?r)Fo*e> zw~OM+$lYG;aJq}D3t-*#)<=6iM;uLC0n}=3D_6xvre?e72X;}b3%_^muJa4+uAyKv zP}WcEgj^41x&0QYbONdiKt1$jS9;_-;0>UQJU!60UKzYDa-{yjFUaF~&4j1%c$;tw z>7JENhmUSW)M)D3`6QcRPP*AL(uwTLaB+S0b$~ z`<^G&v$Hk##}EAA0%dMpJn7@Rdg!jS9?HglP19()Aku|=dxVTI(_^Twy`k~`{Bh=r zX9m)5_4xeRn>2Q@_0ERR_MWmE7rnWr$!Zsm@7cbhhh_y0^|k$E>z_Uzt&8aP2#}6# zt!q!Z3#&I`<_l|&`!qDgmPEaFQEqds2ggW_Bczut*S@vgjtde9rN-q+O4{Atv5(@T zM{LB8eeJLM69xJ7^JjXHpSBfj{$JSkR*pA*@kFq8CtR_%iyN!%`iFk8 z!&Zx-<{D33@2oRX0BM5sdR9H+RoDG}Sl(k*r5}1Y@Z)aNC20cd2FOYiB2Ubl=BsJh zlj7)RUpRNhq3?NQ`5iaK+AXBkQPg6G2dM_|xUcZg3=RWH9q_%S zhVFS>4;|XL+!@4}O?L^`u<-&r0H3Psx;EEsV^NKA^?u9T%we7k-&}1w8iF+u)nxVA z*_SzH5()}_T4mJmW$W#XzGB3I<$31%-8UoL$MX@M*uL#7J|RK%)0c+r1#U*#xa~=G ztKaMc1~@`}oW0lh2$;J^VD%Q5JpJOUFa}WTwyMJ=r7oS2y*n z+(VikIKYm0T}-#tIgsg64tC=;ynix;jCDN;_k15m72;zQ&$psnHF*q*I#RdTtE{*sJBIuvn~J-3yfEQJirHzcNkW96>@vQg(DB7 z9IO|h7q}iv(47g#*|n|;F7vxS=@Ujm0?b9b)+4V2>>^ap=>IIHGl= zWE(hQ*0_>q7xr@wB69+l==B2ToWVeNsVT@zAZM2ZQXO^e<0xTod= z53c8@V3g)|#rf%iSd>Oz?NMKfnkV7Kc1)a9H(%#<)zLTirPm))vj+ofrfIhUYx{eZ zZd>*!8ow*M#Q4nEa*(a+Lqs@XcsWJ!7I8Vlq%gC-Hg#$3c)A$8->^{He z;n0dQ>s(Q!9l3etB54wWX{vOglL-4}{ARFg=O6>0WA|jMO8BD|N~*Y8;)08fo!b%t zfLRwx-V$cY*sjoL^f1f_BrZDWwJXVnaZj%Og^-MzYO<3SURY;-a5*o_BAC*c0Rv@h za2r4F3d2UG;@B?WPFn;JbOQSd<7D4=6t78!ZGnLs-R_}B9ty%Qo4#W@(*4b*0K+z~ zjnn<;g$h#LeWORFBT|ofuiH5%W!oRAjLEx#d%F!24{i8!?4ibC;ID=N06+jqL_t(G z21XM6Sr>c&@S}xK`T}FS@H&jmzK1_$N;+i0r98MGX4ws(?dk#MIfsJ)1QLh8*2U{Y zoj$*#9ZT1^$wME#VxO7RTguwfz~ww8avneRgJbyXyo5jsGtnvB_8lFYTf~F~9mh|O z=10zO?ze5zI`$Mfc!>-^`WKrLpfEkHGt<(LdDTl78g*@``+t)VM!l-^5xov6D{Mf`_!@~Qg*%W=`wr=62+hUDq6lrOrn+h; zJObddv~cYF4Y!Dr9MOfH^3S(J9lL^`Z#6v|b>XQt{%6}Ni(UjY8#>rAE6SpMdX9Td z4J-V>Yk0F8UR=dPEMK7H6EGfQAK%(eiV@BVR`_%G#2}}UJ@+CkA$5=&`(&0+!c7fK zmBfyeX%;+pC+mw{zW7pdw9t$DLzrP@qUv4NTE(-r$x9v2TK{SEthTljTT6MInP4N2 zU5A4`;=)G=>IhMPjk_l<6|E#^)^&s)d~`qjRvDFD|HwY}Kl}>j49tYQV?CoiGZt${ z8>K}$vlpFk;9nBo=eRq4%2R+VU}g9t>!=b&_4lVLpAPDY`ur-=LzK+C6=*I);~OtqiLz0>OQ0@MtSv>79S-jb@r{3efbAt=!)`D zJoLIk`E#MYnjUvwwHXF}?5^Ow>d)VSK7Wx5DhgyO8JJtb3C~Up06X~L*^<22E_G!B zXB3&;{B?gM;$)HX^PQ26*t4BK!OFxpjQmbh(+J8D&Q zUxd3X{lvO*-S(-2Jz&Ds-@GYx6mwus-JTypgu`TM@w1K5Npa*3N>7Y+y3Xje ze&%PObG-8WV}rsteyxi_dSUd$8v8=Z*1y^}SNc8H-;q^}Eb8mAt^oJ*%)GH3Q=t{q z@9y|`s=C!e;jzZqr`GX%#)MNTk_0F$+FIWN4<7WoUD)@su9#`;bpepU3+VaDK!JfP z`59K9m(U3s{-zt;tOe<@9Z!%(KmAq)wGFioU7)R<{21^jq%DPrQ*?%BKP!;2vncW# zR)dF(%Z@qKrB$p5AW&&zoA!Dg&(tx$tVwWHu4nch>5+$rov&j(Q6vr}5+F>y z;m^8=m$uRg-Z6HVYWZ(CWjL`-WaC=bgXhd z$13z2&%kY743d88KWH09(u&zrQyy%m#7x`vukmIJM5pC<+g$`)sRP{x)ACyZpxwrb zF*S<&*!JjwbROQ`9%nZ%iytDu+SYj5(Mg?RR~-sXKaLcP$n4Lx0WTcP*qRV(V2o~5MTZ7ftRPY z0103-vCNa%4)MyFr*Uc(R}?AlJ6_M;vuFdjrF7XFi3(J^VN)fDgKbJ^L6uDP<#!`R zgt73QAnrDWCSvR+;EH4*B>nNxpgC7#L~_OwAYOOc6q^gJR+o&J`(f$>q?q{jPmh+& z+QyM-4(oG0Ro};L9Kl1lkHrP&@eAh+_67Lal0O~M+4+~k3$Z8kK((g<3o<8q26vzl zjFD%3_yB@HeZSN396GfIQsVz8YNCc%Y-Umo5vp4J5y)*C1aPtDQ!xFvgh>X5HvNkV zh!44?0P>R#Klbx($zSWjCjK1iBBiBL*70wg72+UOck*0tL#e`J9-Y$@GuS_n$45P) znxBnX{kNxEFNpd(&R3tj^YCw9|6%(^Y<)Afeh%vFK_)V@E_kuZ^%KDUV&}@0k4;{5 ztDpGwRti^A!q-$?#M2ny{0XrRg77 zNfwd@vuyn8`=ffm z;>WId{1t}{-oAy~q5ULkPK-a_8i*n@0gB~%VVIP*jO;BuQHOg$u^&B6d?fX+d?6FY#mksjmml9`sV zG4`(N2#*`PnT(wGGtFOw(d#jr?&yw%a53m<&XAB>m|@}t8$=xLW}V?5~h)*0l%-}Ac%o6QWKJxlepZ=$tt zqy68%_16zy|M3sn#q=-De!X2lXJw=qT8bT9Pt#>GNtwl0(ExxaoNliRIT?faZm9<^^9|y4gvrZOLIF{*;`tVQbEq9xjhOwtsi#;XB ziwo1-)@z3QTguEIuZJYEV1wGzB-@(A3vsMl0?J{43sh z_{r7n;vq0q|iRxWHOcGF#ia7G%B{=ZL3ION#CO;XtwguPyO9FQeTtmF;#d zsiTO5LNWWg2nKNku(JSk2yA%prsI5Ks~z@aM7Y_4$VQmH#@28V(0XciVClnGyF3sjFp}`y+oHK@;Q>KQhTL2c;MyQh z3(^n6+sb1e>~Ems0dY2M!YLt;;2@STa=i^;ZmAx5T<OPcV$k+R2aV zp%c28XkAU0V|M~F%mp@WmF>18IDX@&oa^mbri)4xfLziDgoAjvG5bE^I8LS+AGp=d zM2DS{-7sD0kZ|fm@kFP>&`lbE2-F2o!qAx~WboH{!CzZ=RUUqypB=I)Zfp`9UBrk& z9J=h9k~txeHl*VgY2;W-p!-UU2nDB)3f(pV}zNf z{0LB9!Xv=AI5A-7NL&!H(Zfb}X-@$Q!yU&7*l&nJCd=r@#}a2B%yXUqGjwohG6mfZ zMdk=1Z353aSy=$9ea4^Tbxu$l=RyqZEVqn5hZUWir$|?MIX@)KvT%?w1CQ*WgYGP2 zcc5M$t-KW&XKb=@Qm!6s;SbD1&OWVxqB`yP&C*=b;+#F%M`R~tMBO`%{TPd<7^jKQCA--nY8^Epy^S4ZlU3P7HkP@8?2_LXK$l^=R3w9b+wU@d^HI zI0sj@zo)gmk1`7V7~iXH<%*N(>1+gx{;`P$QM9U`_#G`0MY4`RVbRGr0aAtz^Hkh9 zEtTdtyrmdKF16CQ$ls;Q!Ii2QRFTI#PCM4a07&dMe7x1}i-`y=40P{ljoWs5(_*J* zUqimik*>z`;1L8#M;9ndQlFBTHwXF#2*ZHEjTP5V; za1azyRl7>{I1F@%$R;-Ws?unKE4*C)5w9@dND;@*t)GrNI(-E|LY}m=#A^$FU4<2S zU{~?n=OeJ2n**t5;bJVcEVOri;%6=V-2Y;H*tsbM1iTZa*j@b5{`-W5=8b2am5gN{ z-FbGr7dIZt@nY-Xm;n15==qWH zbccrk1lq(!2Y=$jfnD*JZ3IYgjabq1BGbA6f&Q$UUOK>|#honT1gf3D54*D#P(8H* zO0E9fSpd)$Ag@cT!D=Tw0pX#r1k?_Inizk@?`-hs(BePlWw`-oT!{`q9-Py5M5;%p z>S6c8TPi=YL&j_4dt{QNQ9tui?F#I1uPne7dZqGQZS}r!nUdl!X-renPd?cVnxEF%w(d{bj;`r-1&ofNzHIs-DkN`hzjrRi| zk(0IxGfon?T~S04uXf$`#L=OJzqHG?4+KKA5UXI?bj}C}?b@n?p8K(O?7bhpD==>i zpRDZqxp|j_#gW8K=53q+de|GC6E|$sXra}3wT0ub#)aB4HzA5Pcyi>Naok)0iM-e$ z6L6}=zSicp?gchtJ+`pzDWCC^1wVF97!Jy%Oj_bg3t!%40suRtO$-Xj&JHn9Tip+#@&8krCtTh6}xs_JuDCY!VDSer% zR#O2N&Q$hI10s5W@nc_Yb96~SImbza6bbM^zYz04w;`4mo#GC)@RlUGkq${4CyCI) z-oOWUV+2i&nEh{^<8WnnQ{3-F4oZ2)SsZcEv4znp2*)_{V`XFCm5-i7)GJVy_%~v& zDP7%JS=X!!+SzY8BQ4o0uEwbt9c*3{#(5%ky&dOCEULP$(2!$s45&(1J5m5gf}3#5 zxB$S5wpKi+snKeO$YCR_)J9JAMH~ppX{#SQH0uTXlxoZzJY>Bc?|Co)5KhQ+5w12D zX!97+y4sp*--(^n%8)-a*r-URKOES!=2kcF_L#xQXm?V?`}%QDx1HH>ZV#!EzR_0p z28t-~1~X@yFLN?4%?50svet7R57IC$jt8Vd4I66p!{<*k`bL8x53KIH-HWV@BZ~+f z9~Cb`g42rrJ9>=DN?O(M0Cn0{7B<_zm*G9) zv^{>Ds?JT?5J#c0pK<_k7^vawsfC94As^n@!nsWlYDCcY){jc#(O3R{N7_f~K4!Dm z&?+b|dif`*l-J>Nk3td`ozw%K0345KuW5jyG+MUzvacARVT{Pj2D?BDAZQh5R~?bm ziq!gMjD8CxM*YKw2zs;%ti`5n!8@PeqUm&bXHUA|XtY*FpNsldJ=a62$J$n3`ITe5pLJrWbH zHvY4=^15Beij86}qHr)*eT<=nBm&Xa#n$agC^{6dZApX-JmG<7e0ffv@{S~qoBeRyKS_-?*xv3V9do5uG9inb zw)(LnMcwx6*aH|mWT~*rd9Tk2h`H_f*}qy9S?$6gOo6m)G;H?eipQo6c}(6M<6zOp z3Bn1Prtqn@qv^rG26?p&9&{FY-H@a~w&Iy5PAJk20(khPWglQ(NZ0*Ns4^XQ9OoES zc_sbcWE{-P+4ipmNx3FRE3^qirI@|nBg|EwBlp9rI@sjtIC?|^xpC)sfL9!IM#5@W z9=L3G01`Q{qN-gdo;dp;^F~_Ya~7gQ%XrFmWa;PpK&gV22k!3ncEwBD&|g^~i8!#% z52!2?IDxcrBy#Q~Zg#Kpht7^r_*-+aLr4tEJ{4eoLI|F=lfa;Tx6r^v^H_uET)UOd;G^c;8S9{sO`#wh z`SAs$PuLPj#Brx4J4W5(bwzY-*DNKmHu4~Zc;J|ybi?nGgf6s?A{E`IDxhgefs<;z zd)lgs(s*Z9=dg0|hGFYRCHP&(cZb#&e#Auo{sy%r>-|!72Z#z;2W)@ZOb`R9es|lP zsWj6BM1fI*8ESvTuF4|!)q&~h^!M?tAA1&pC>-#E~iAF$~U z2L<5Au6Xl1q(2MFu|;qspwc4A$<*q14>;o2+B;cOk;ZK=66WLrKj&+sJMsg<{?GVE zh{k}@nI9bE24DTe?`uP4Ir!nfsJ7!jvPh>fbLS$=3Dq+9oe9D{-ERy+o$S5pCtll% zLwq`Bj3D&^Rk6K2DvYS=ugb)$ziT)many~Y1J_=|48WGeo_5K0{!|8lCUyd*4k$U* z7n)X<_Z{W;qlNUqXpNFoYq_dCVe^nLwPh6@Q-&Y*;7B__TDw`hR}YWhl# z0WeOg;Uti8-hMUH5pouGeySkbT1uLnhX$J087>oeXbd7RNFrIr50fv4eX8L=%bP=$ zjVyk6{o}7ho>kj3Oo|jjn05l0!TjK7QG-W^c=2O0VQlpc9Cx;z?=JYPDeNR%?ofDS z$nB3mgw_H)fWN@E=?fkjZpH(@#_`j_1Go2s1W_6IH|?_h16O4?^l8^;?UWLBQq>Qw z&lR5I{;P?qP_@zV1f;=B z3#-QQYX!0I^?k^rXuw^!eW=tm1Ob0f_M!AhN4>N?nIjk{=X#Zg-FeYVLGNJJbHtpV ztnV&T$(@IkTL2M|aRu zD8s^Dex$lFszSBTL~rBS-kGLu66}`7_LfO*G8zKC8Mlt2pV_P$XCqqUXizNnikv*P zSZsk90Fpu6xNZ78XI|)+5{j8eX#BL)H`}uX=5{-B=rfqGF{@C86KC+)G3JD$7moA? z{@B+3N5p}%mB|XK`}^uD1&ol(eP!S1>Px1BtqK}OAiQC02~wY*a4GxD&&RsYV$UwPQH z_52V&n>foJ15KRDYNiBU=-kH@ddy0HZs)fy$RLdRi$(zQ5TL4K+w+>&SEz#aK-8`E zd5JHMm2=s1TZ_2>wX;dLY=03&vVZNI(AqXP2qqDn1zJo5^ReFucf^rD9{@yrRu8n$ zMz`8SsZavSoUr{-U_7~2kc5v-3vrwi^Yy=hCJ+M2LofxRgFk`cPdhs`fYQabCaSu} zGN53CVh7^IELTPlazY`bKy-4RiVdE`*t9!8@n_zy3Gw8RB4=G>IY$gJanZq#V5uFw zbXOb&RDTj`=Waq32%ChnUiCr-#gTs9^e|y~3l49!c^&WmV;%VSw@<4F-)G?O;)=`Bx$k*Q0JbvEs0i?RM`eHd~2YxZtC5StMdI__-2 zcUF`<6q8sLV@@q$T1*S(Meqh0Yi{EVcX-Ug@=9G zR)X7jm-L~x`kleNa7+P*9xY{68-HGKAi%sqNPjZCLpai`ziEn_f3g%jx$x9!MjU&0E>9yYf05kJ#`3@6?+1m z7;u3-Mgy+5O)&5dMPND;>$MVV~(2m^lf9{JFlT1dIx!-2pA-Xs$SejX%4 z0K4iO+F4AF$zk}9$0`ivg6y_?c5Cdg49FP_Rs3~K=?_A+n|pMsFHNcq-bsT3FxEjG z8#vUlJ8$k)eoiJZT7(dmzxnK``RwV?Hr&WT@318a!P`X8LXCZ~`-LkbbWPzw0^AbdTw}-RFpZvXt&__By~Ng$gOM7aIR^9+G*!{N?@EJrtNdnN|jIlrV`9( zwgnmoyI?7x^5GJ1ZF5eZ#~ZCEqq`=`c%{o>tmepT0|p#q@Ej7(?5az`ynxO`p;+x) za8%7GP)rDS+>(XpH5TaJ!V5hvKssRVF~&{$bjRR}`t~T6yx8{e zNM7d(|9OiN8L-cpjsPEgHY#$+0NdEC750&OEJ2PI8Cv{3jeEMkN8ns*;h{tu{u)LU z7&bWeUFL~6cyQGY9Byd6^+?}$dPF^B@phXY$~zloX)Bye$kp-UhlgDHEzlQ3L^BAw zwMY;pSnWC|*cD&(u)!_IT>v#OKqA2$21{JF0MC^2*@d281|RIzc_8rQA5BJT0@^5+8T(g(*TN;|go z@;Vm+ybi+4O%pwzb!}__@J?C9Dhzs0Fqa22noQU*1+2Y zfji`l8nut$-9!^|4$P{Qb_GNUEJDImmOEn&gkW{U%Q@~H3sFU)?(FQc?fk2}Be}an zyxQgu$^2M-Nx@EtLTx!NSO0CB`6^OIrSV3nWQ)>bbK)GCmZtI-z@dXGF0z56)o6N(K+X?dfod;=jpM9MddAH1f*F*ieouf=p_-mDQ-y;t1Y_vVzduTk@mh@Tp z?5NwHD4Uo0!H?ZfQe8OXaq#u|!@I7Q<2mT~;qmLEwE4R_&zu29Jf`y+w{nU9Xhy?En&_fb37Hl<5eASOM5H`o#@W4a?Dph?z#f} zipS14J2in_0mNw$sQ8Qnz;SO2e;yC%P^&h6*mYrcybv8lH&6QrB>_jtKztJh-CGFV z^K?D*ya39Wy*&f|?ulNk1O416bFu6Tm4p~cA*NjkEs3UUXCf)BNQQBMOQx)*C)D6C}|22 zUEB^cYj(h%-|p((8@-*-4N8P^OtBiMmEy3;(8ulq_%l=Ni2VdTaPgv?dJ8cW761;# zcckE+Hlx;K^fM*@E7Z4H?^6yan?h zSQH279ML=x^ay{GIpg~1XY}f4?Z8LB`iUR6n=I4#2WIuBU36GK1NC~hkHA&YuKsmS z$U#}O70KN9I81ACl9uhpm{{a#4_tNOPwo`6wG;h@!!JYqL-M4A!ECVIs~?+|OxR?? zk6xBQfEFTl6;QnS?1>m0+RDYBEku9>@MC*BqQ*Wbv9zrfV-M^&j$K}^V;Zp0wW~K@ z5Il}E>WNq*=;^r33^&1syO-D{*4{O(imEocyl%5hsoCe!P}Z5=%rK#*g+J#&4#O)* zt0eT*o<6AZ68J<7Envr*ct>>gEprr;gr5xAh8UW8xs!snM=z5T;l}xn30M}-F z@UvCX9YWW-2xsLtX%*cMRvy9T;6r(c!l%vQ1aPe)JiGC&gh)jO0JQ?_t1Wuxc#!MZ ziG)U&3oc5g@;-F6C!E;}iN?}oP^+%j=!mGo1j4Pwj zNq@C@rAvS4kf*fh9#>htx6wuVzzjV$4hSwpu%oSG%xzWzxfbB(*m2A)$7{TtLiN$r zaf95o;86gFA3NF#t2S#9dW~0q*a?eHotH{0Riz2e&vN8;LG*c@OTy(2$|wbKv;CO{ zmUN~`nH*?_S_gS>b99kg*TXTf%2xZ?%~KSd>zo0U@va3p@X&ueCX{On8Y#xcwr9?| zJuASUy|P>p-7Ss)K!mFQY>wbvZaqOwQ_H}vIMOgL*tGDlD<8YRc~uy7ybua3_Z{Nt z2q1X$DCo3B&;=MLa?oc~J(7S@J2TN+$_*I0Nu&Pynu32!HTFrHf&>=_fjiq-2aB|b zRU2I0{+u6bei&*FF+|WxKS`KOi1=3^k&`wvo#5s$EW8{;uvsOPmE{JJxj_dU6SDZb zu=fDqUj%Ca8zKb!S6g`3bUbj`UwH#V^ zKxcj($s9eh`XZZ?Vxb6k@rR=?MrUwx)el6j03W@jT>y(?p6@O0fsM^+z|?R~9dqiq3u@d6Opmi0gtiSrKoF&Aqb zb$~yv%_Miy3op&=q#pZA-7VPuf5!p|M)#*g>-c>3QLmXC4}WGqjWw>Tu|H_;ied0< znQ`cg}Y zp(lUeUmVeKijF+DZ$FQf+(429cx%nc8fRXR0FS+E(Q7(0X|}pRQo@DpBiCNiJ)NcK zPH{xlesG{7Oz?(U*F6Y-LXR(|@q@i{LDiO!|x0buEkPTEXB zcT7d~-+`XoW>C?FSm6s{+EOce^^l0fwD?!X=&9({o^`OmF;6+61OmwGGq`D0j)l-C z0rt|K0v5*XxW)&i;V`6G$E*OcLvjU@n-F5)(Cj*=fDXOXD|(kWN1(ORLsX96qqznG zoYmxn%qodzernokGvWI!GACH9Dwq{6$V(2!#Xz#YnGJ0A+PaGLyy3H4u+?%yV5ish zH^dG&>A`1yDx7%rZ|o=-7OH*x&a3C}bvlzzC(`wk$T=`uwKl2%MO$$@8$3F+_=8tW z)EIk?G3dbg(kk%T0tg3LiI=ds-Lv{(Ty39Hp)OhqM;4oM@MF_1ekblP-67VNgf6(q zR`{ts>n(5+0Hdvb7DT>q7A5e-y)$Vi4_fsjQ{y{7 z^TvFE+v%o%+4dPZ?jT$X!_IvrfZ&hSwq9r)OOW3ZA^;A4$FH6igAl+((JAqManqG2 z9e9MRo$Cs~)3wO6{h6<-3a2IaqIQ$ z4(ziuXDGB26IC>@PiQp^jX|(cE6(g3UrEVvB;VL56Nn$lOc@Sr;=NU(eYE3tJ#kC1 z2@PH3fzc)|I(6`2Gi@1$hfe6v=6Z~puF$a&9-%Yz*g5iDd8A&h^qi({A= zD3TicNYnOM?vlFRPkYr zH&%_qLvpfZK$rrcPutFRKC{z__}xPmc;K*m+&$R+zz-wZmDmAE4p+0Qv%~j2I5pXx zF-Arf7p@$Ak8t3LDLcNglBUrD<{d#Jk-plI-0bQAT(jZ7)$X+LA9IP4k+zT6dFwvo zOda}o`CtxmN)T&MGcpjh$fkhf@``s7EU~9GXKgH0DRcBM=R9N3h;Uduc^E7>;(a1( zR=4c}IwTPw12(*B;|HHQ0nEw(p0y-E5c`bh@g0kUS6^(i9y}(Dvhx_sxB%?RLWnqa zro#b%0DkPN?KK@x%0j=PkxkX@gLW<_reE(4t{LnCv6UF=GAc}tjKX8wj8_yH>=j!D`JP{Bgd61zE*IU zWOYI#eM|r*$Id!W95R@%jFTY*m^a)Uqp+{FwMgu1>~(8UT<{qzZIvY*@vwJmp>C2w z%P@1>kKZ-%Rl61l4?lKk2~!|pHY#@AxP@24Il#d6@wsD3N1jc~nyES)_;q&~M-pp+ z6_<7Zmk9_Za49@@>bMwRN4Wqpl(nR|VljKRPJfKc_bk1TT&MEq3;dueL|5--5!01XNobe)&gb|gG> z;AhJ?7;hf<0|5~si1^dSQGo#NYtObnYGhlpyQk|oqvCV85kM!``Z7O}89#+UToM4F z1%M3^`@WX(@md2LRkG0%r|s*;5!h|~Riu;Xd0TPVGwKeDY9A-W82a3}9;(b0>iE+R z2b(x|jTk|m^AucPGgpV28BxAU4BS%rU3{`%xC31ic^(9a*6hTYa5e}Vh@G$Um~b|L zPpqkR!AhC!tGLU8v%Fg**^T}-D#zAp$QiLaX0Df>kni}TS_U6wEn^$&Owd5Q)rEU6 zuWPzO*GVNh5$Xu%E^8qqbAEb?)iZilDw3 zB<kzu$Kvl;-Ero#m7GQtL-%|D6)Ij@Rs26 zA{L~&Nugb>^g50zpH&8QW?j5F{Slr~c1($FpD$cFkW@&|txnegbsryAZ+HBizcZ&E z8E!~Q!c7bnAcvOnLCCq`O%{(mTXC^Uw<_#b? z#=ko_XSyo5*b)X!kgBlH+KNYyqJs~tgqQT0&Iyc_wa=LGCp&PPpinm!2N z$oJIii3N;s78V&ugdbe>b8twU0Ye)KAzCEP;v9Iu!9%CED{l2P4nl;h5OKz~9QZ0B zZ1j>3ZgyY+2UXi0u5SB^!$z;#Y@b}ywUA06Uj57uiLTBQ_-#8UVjp5X%+W4V%Y?Vg z7XlEX#hr>cu8s>mcqvpnr+`W*w)0UTEw44q6Mk?hf&)MXO2=?lhn}ya99N3e{bnr^ z`SlAY*lnltwF#FgXTflupsBZnx{b4<+&kcx>eGWB0KTsSNB}t*JG^u9}M* zT9wBhC=Hta8t8knaKO|TQZ|e+MqDi?S?n+16&oKr|A^@o1R7FJ4r%h6(QG4etA*;p zp$Gq@9pOko+1bnkW2y54|596b;%N(+0tY+9D2l?diQ~sky%H#9!lw=+o1D#nPI}tv zhuQD*yQj|~h=Uf<8d~No03@&YYB!volwc%{{YvUKD9U4&7*qE?uJv@W1wjZhtZQK1!1eekUx#*8tmIV{-XwBZj<<{Qa! zVz*8jLuQy`4BuIMAE%;g>D~5TzG^`XKsz4O7B#Lo)aM*TLrvQ zhaBTXJo7XQUewcs)V^_!(LOQgYOS{JzpgNrfl(-v4ZbudvC0I#f{Nh{bRi?qryoHA?@g}GwgW9&#^aO zU(jvGE`ePaTBZwJ_4f-AA}RQ|PL-(z?Cf0R0q6_hu#PGNsO}Sez8q2Gok!1G^y_eD z)PV&7feJ}~bn5occo5O6I%x;N<_~C<09yUnFtNd-hrMe%uJ;bHH32p>?A?LyP}H38 zGmaw#TZ9XSghi*KVXc0T)cFEebr+j8T?4Fr5)-e&#LMez-r0tCcS|A?Ab{U(qIj## zhN$sW3&O&ygakwYJB8>(3%|JcCW7}8owu$d>98t#Rv*G91%WE9VUdx0ffe(#b z>s=&6kLxf82-4!erdmf|kGplwG7D+K!7j3lKM7G!wNJ~00FcDk_qOO=(*a$x40oUr za%i1Eb}j@!W7h<1kko+?zy)98zVJXR1)@lCWjXCI_w z9s7fO+pYLfdyo!y-Ts-n#o7BhFYz@^wWGeCz4x}IZv%*ANj_(`oc&BEPa%P!Kc1G#*plh+H4#$>=A?SD?i);E9jIb zjmJ3^a_;zC`NUTLtO)MD)-;;Tpw)J6%Qm2r8u2S`Yqd#&k^7d$~i-b79e&fxU{x?i`i4dLKrf`T|(6XaNrJIp+Ss& zAntQb?Jth)dk!2&I4fWKLfB(wG}|n|5pS&OBZi-Xs5avZAd!#nr1+r(QG!%}ADe;V zr*y`jxa^ps`VjP*qP`|{#57-IAN%3*27&-j`>0w~lep)Ztr$)m&$t$-H@k9tA)SZ6 zMXcn`#ddp){Q??6bZFrd$ELEypMX0%_X$-r<FzY;J7&vMX$oVSe6cg4#3zB)E1xg%y z*TMsjy~A((+X-+y>Ks#mb1qLDoBgF{^SY=Ge2<^??q}>3ixx#eAdDZIc=&6$kKKXL zz|rFGazx-cqT;}>Ey!7C#ce#5mWw~|$mho2=md_F-7ZVd+Zrc|YNo>xdAq+d5_qwL zQs}Ul8DCWajz81W2|&eF{3gz867V`d-S54UmGDq~O|)YKm2fJRPRd26;)z#3GWdBh z9x5dSw6;idJXz<;89SZoXKwrJ_llTm=mi`n%6+Bkgi3W+y-Wsh24E2dj=jd+)BTRD z_^N{q54+z92oL)T2RjHPNMh*_XTD1K@g@bh!fdTunK@}*`!5_^3osUW7kC&o3hfDP!&OpxyqBr2-LMiG21o; z1A)CI90nK*!8$h#gLr140|1+Who5uA7k1-IUSvRzgy^OH*ba%71UCgM8kK$log1=3 zZ|W{AgqKAc0o0fELmB|stu=Dv4?(&l#GZJgQGus~kv#QLVvKz+aTswja6G}z3o-(z z2OxJq2nZHjpZqf*#4Byp!oI>CF%)g@DzJ?)-sEc<80=tjNZljvgg_!0u>ZqD71U zij-l;oxV{*hl53xscEpm*|v8V&%WxB8>>6&i?v9GHvW;P(9R#UXew)tUb8GyOJ-Va z-mGO{2#}^0g&$nB920=cJ@!X9ex$Hrpfz&T2Veci_6FUPISts>9{~;$w8u$Ly{<;G zK2kiiwJhwFahwwT6h#~-W8ql98h{KQn-+d+^TIsQoHe~O002M$NkltokCOHOzQdE7(;u}+Ve|&b_5hsTuQB%}( zw4+=Twx`xMXG3IShr4fi_S{+Bl{s28c8*q%1<64>&$aS&TR(ia4>=7cUnfdCI-!9`X0!DX@AKqBtzpa*M7NT9V9t&?hA zyy_x@xXcMDs#y9XkVKRo+ai1~A0CC$x}PPAD&?j>hXLLlXY&LfJ@nUO%v{fWq)OKF z>rgOd<@mh>#>KHD?NZ_HLXGj0rqFJfY{wgrOCbh!rylU6ha)GzZNhb&_<=Kij!C)y zF|n7%vICKgqIbCJ2M<5u8#iODD(TNbx*n>z?-Y5`EP62~`}oQ@_GASSuJ$o2HwkcH zY)ou`;3yayT=?1c-R*%599XtH9J;KjBs{vqKrRYU7&L4a2J528;ZAtO8Alyg#UY2E z^m~6rRtC&FTh_&XKrXnXfBO7qb3}$E^)keYSOBvFINqA5ZhP?HXyKo>vj7$}-AZAY}KgPjoXoCz-bL=YJF zMz&;mW~WE&C+IUyhCxeNI~zW4+9r;1g<~3if`Uzw6r)oD#RPTU)`aH_A^M!me5|U1 zpE9vo(m5qD8Jq{uR=u<2dx)(p^Co5j&ZM}eICUU2O;w@!VK~M-|(^ejD zxD(IC*Bpj!1k2JAVS^r8ga_B$1wh5L_-FJji?N|l_jD9{d{#?8i!0_hFGQI05Xw10 zc$TZ_ExH3%;w_sh(W>?>t}1XI2QOK!fS%bQiH8gPa19!jA9&a?WkN7*SJrz~3xzlF$!_obnyYEqrSYuxd+4rf5wQ@q20XnEZAEiwh zRu&0b;EecZ=dxmMcwQ$>SO z8AIq&mka`{f1n415H|Wu0Cv$J3~iB#fG@rN_Nu$PjeOO?zi-L7N9-e(ttFI3%`Dse z$P=z&OhEDh1ku))r*M0ep|8l;N1-Qo52f_@ZU2lr&M;%DWf{kVVOBZd&WhTm^}x6S zfTE6c_4hakJaY^0HVO`?!m4iIw8XJlF!<~7C2?0e99dW&h;}X>swPtXd@h1D)z1?^ z!W>#cRUAmJeH_tN9yYM1XHV&uBMngL*`^OfAB$7L(;`R){Mf|1KOz7KtUx?-o4g>7 z^Mn?dnK@VhjtPp65&DBb>yG~3?=x0)L=vuLj}kZpG(n5HZNZ3VAr5dq$-odxe|92L zZ3w#-;aj|0v3dTK1vDA+{I>=Ikeju}&2BDxYE^2&n!~K-2s;JHIJR|sW9(`Btr!y_ z$AMlcbwOC|3}uX=n1?-r5lBBkYFGm>58HkL%~*gTj31jsiOT?)T#^5&h<)>RPl0MDXA2mu6X7e5Y&0H3x+_qGlwvX3^0D?aX)u^fA7M2n?*en7LSoy{VYXL&Et2^c+7gwDhGF5*O-kOi)6~UZ+|Re=q0}j zr#&LZegZCZYPPU>Qv+FGp_pr8lOgw+O}xnE6yax%SUkJA+Q^%GQ+WFK%ogc|I1n5D zPCjJo{4DU$W16WZ)1VVAest>ix*w&-wri$CB@zO!ASR&tv4dB~0BAs$zu&_SVBffZ z`@0fl_W9}=DuT$Rmht8Id#1)OW@_Sxx4brFASty|C3f+hf0v>Ad#`FV`?LT6g^hbB zzxa;{)k~I2$L`zSJ**}CRS$coyZ(((VH6V`4jp+#RJVP3eM#P(oxaaFk#hQcxF36s zd!*~k_A(CYOTkcNJjClhLt^5|$5Go#sPX%3(p46IvQ@ipe~*Cc+tn3w^U4IYh(&hh zia84R{SArkXNpnxjXbxD0Z<<0)-1$xF5m?)&!L$R%H7RNO#mj99`JOBpK$ea7pRYa&fopVbqOXm$Gg_#FnT&KDxY zYkK0bFR&7|(=Pr> zaGOsDR2pbn;&nWIoQ?ob`f5`?`|hF(0AdGtZxO};Fo@{jzTBecI=u|{cY7`edE(3q z`yTN;wg+&|)_47!pF9Sj5N%IX0%0P&LGF`2bKhW6m`|JX+QiYYF|tQ!e`0QCZ!GyPd$4 zyxrO1kNtQlt77sbFOI|Hyk6EjMp7^!p#sRge`MA``b132Vz9h2a zM8E_8*_rs-dZhj6e6AHpe)|#+WUYPXQ-=q2w3?amEpX&leqj0^3Cmsoy?;C`)N`ir zc0V&;`@XLBWO5yK{U?0fU2}x7wv9f5t=@aNZR#?0)7LevbKLSP#{KVbTw}z(_!`#J zVzQW(T1B2G+oc;dD7T4k`M&P&6g1t%i4GSHnp4Jl9%T*#8&rAHzIJFkoHq`xKJi@HSp5)pZOH3xo z?7*HaVVO&nG$P=){8|iu6d(5uazbVV6}<%#Zoh^nFm?+lRHGCkP;k;VGa2yFa~*aX zovqG+MPw_E0MWPg0-)zQk1;FZ1OTEL^~cEpPkS;bXQh?M5Bqj)1wh=NEzT!iTFfS# zAvN3p3e=`D$dUJ564CJF;kgbm?{n;h5Bj-32!cR}{L{4u1LvET7C~Qqk#&D*_%#Ux=X0?^J##?#@+LIC&;z38XC&&3KhT5|xSx{K+37xAd z5cm1WUiwLu$)XqQ@xC`pC}`i?4d` zT_#vMbPlQV=(7(hi1E_-kER8-O+S)1;V(8qzO5$j)Po0#5^---W{BB-aIBAIuz}Qd z7-_(|04R@BmVL4`{g3p=34|E{OMVxkN*0vzTF=N-k{Ts(4{}w=Bj7{?KDL_RW5zS^ zwGLx)k73cG&c<*S^g*N!PverQ*xMfkb;9|g^#;&&K}cLIxY9w+t|R-x_{6}KuP(k% z#NaN&JfxYAlE>sfwcq3)3(W-kz%%3m(`R7EF?BziG9w)TS9?Q#XPs!#tTad?NsRS; zU1Z%O6?5&O(Zwxw-xH6W_dfPP-!mqn2TB4l7vC4=ZHTeemi6%8qa({y2IuH-XDV)7 zDxk|V>+7#KOj61gYJgjwfqZ{hm|O6cC#^SywPNC@H`gK2yc8tHTAPJ&R%V8EdfC?p zZk~tHO|cU#J_g%~L@SFN)Q&U59`#vR+3myc44zjkrXg@*U>@tLKqw2|(q{+|VG!hr z)80^>f6eo%$GWzx3*ps2C78F%t>f|{`s+S`iF$sVYr~>zl z@;GJTp=0F^KL$2s@p?U}gBlQTPkhH^MY^?4Vva?ho6!kTnj$VRg|Au(eU-T3{TV{+ z&t31vCD!tA09F_FMop=Mr{J+R(F-0U@Tt8Gu=7EYC1yQ(-%}TO{02G)14w)dWCk3| z@!0tsB>w=5KJ|=;>J%QTn=pB?jy6lhTqd>jmqORbm=TczrMxF*fulN>U^kOO36wHA zYD5AcF@T@gpK8I+*2Z|hWeI?XO0XuN}#H}VJ=XTL)o^=Ul1&4xpD zzCaGkZ=8LQcQG3<1Y<_QM;;$bJ#75pE9%rjfj14*iC3Mdtr}oE3z`aXu?Q(+$s<1F z5zKuW0sF+h!E+1W^EGw1NnpgCOVSxz25n|Tj&jO|Zhty+>NCI?##|Z5fHd%IswUKuV&%40a zT&&O61@R_C9^Sk+mMNl+#*%*RBKjFIP%8sJEzhQ0@v0d0CvN0cT&U-=mMkvKSP=P$ zsVV0Pj}dui5rn_usja#8VSpm$9C0qlE3O1QZVRnZJ66Cj0gjEE{sqI>0hHz; zAm9fMT0r-h4pfOJY6M$ci`Okvq+M|&HxkOjo}0H6-@E^jFxaCrh2OR`x~**Q=V%Vu zjym^=jfKy3=h?zf6uK@ir7iS+9Ru|Bc-)L<4?IZ^l8cej$M}?imZn`_SOx&>OQV7NBcv;2~zsdLFYsG0i%BwSkM3A&Dmbo z4>}~H_T{i)UZlM?#QFUwL-$XD}OL8u>+Z%tYS};it5#$n6q))GaF(FQSgQqJ8V&yE^y@` zDDm2XUo>78O>ci*FmrHge&DMK74Oev8RF4g6mEUo)|oNEcLco zLC^2K#(mRc5T}!8Fpq@?pu{(vUBg>>%)Qm`jzG|Q*CWf>)uX~`0C;ZtGv*>IeL08l ztU!ORo~vp+3^Lh>=(*av1CHhCbwLk)4W5n*dMu&h`F@$)Jw*79h{`)u*VF5Jgi7rI z8$lXNdtL7;c+9%d!J;Qd+C>OALUH+UeUQ!2yg4W?@f+x)1m>l6 z7LPM72!~7?jM~e|hAMUNKekNzePNia&4=uI4y%~ftYH?$&Q$(5Y(<2|MS|WIL}z~P zp{eOhlK5Q*aJvp`j6GvLoCz$-C=iQ4Vwd*I0Lm;ROMfY3GS?!1imk}-0~x<5j<5J{ zm1n&+Ab&0!fLWd<@flBl@~S=(Q)zzta2t~P;``IYFW@grH5d!xruOvOa_ISafTmX9 z(ZLqL%N|ST5bnw!^O%*Pv^NY7V4r}jXluEOde)YvVo1Uz*)@qdfJR$8e(0kR@bE_m znyjT&?UsK-lq~#8=qv%4z1W#^r5kLW>u>pU{`kfutN&^UTkv?sq}2Sb|MoefRL94z z>X8dYJ(H%N2qwd3_V)crJ+Pm#Q#B`PKfPU}^k|2?#a#J*4c`Goboh!zvwNHqzt(%v z-Pd!2lJ^%`;@1OQt6y$kN*c$Mdyf)9Y_JTE=BOH)wBw04IACmm!hZyBOb?j-y#A|p zv*>VEGt>D(IsrgSrbk}*N)UsO*s;_O3mL)zcucJ5@UvxVb^CqCR!(CEH)%bycl5f) zfhUKh8L?}AId(zfZceMUvbH(%FjmxqUoc+l*splcJwIb0gN+{m$sc+z{k6FECcoz1 z`EXb%mi#lHDroFP_hR$ztyp{Ek|m}(;@l93EAIl&vHlS;Z6-JuIy-~>&?4xQl1~{T zP>6S&h{k1wI{V7LfMb2VjMC`@t@c64ssRbNH-IK^!X^F;4~ni>&-0Lv_4`u!AkW?~ z5E{e>k%BiRf3;3HI$fE20Uml;eanXMK9PW8-K8pz_Z5xL*m+3X-2$_$&Yxd1AA`fmzJGLYij?@oo9s zKk!*+$2LcWjPYDuvzlN{e&|;hyH@%uu&DSjw8U1OHE|}a%$D_=pVDt!3iV$s-W#LO zSnq8s@7P^egYBS>j*DBRn|w2;2OT&Yz!c=(2+2^nC3kq1DfPC4raTX35KnfQ= zuquNl-7%R08lJ49n*WUW@_ro`kU^>wb=oliu@9&i2Lds~v}+b5_FPL>g1vH|VxC8h z-1!n9og^|wt7v@jUOJEtai)05 zK8vZt!v@vZ1$C+<>(lrE3aPM~oHz<)ov;n}vbZ7d0jv4G)>Ie^35EVmbaq^SFvtvn zpV&x@AbDwHM`yh@=82EadrOzF{`Hh|%o;o1;<*U}PkM{{oTTD-vdlV9m{EsE%Z=Z% zlpBs`1b`n|G(G~KAhj+X4mF$M@`Zl|qsBi}#SVHahEcu}-jW?W^C;YGFkIbY3k65g zn}TPNJTnWga>bkt^1iK^=l7~KW5*bR9q1T67>%IP*!SLxePRKK@Y+C|9soqy0yuw( zSBcdRfrwLjZ;+EF+IPt{YLJWBbuS=>=<;kbzD+wc^edH%o~JEdo+7gcYoOr@r-tns z9+hPK{(wUJZVR~*cIorBLd~gJGe;glfB?v2$qVNR3d6oAEvVt%#CKH4 ztw{GH=7**I^vxkw?5c_;KhQ{xksbewJy(7drqcYjD6FwJQB6(}Jzn%)y3X@>-=C23 zk^G`i_w%J|*MK>hZjZ`$JZ72Wj6&2@2!B%Z+oMmDwHWYQe$hMJ*Bm;4f=)c^%s%e@ znOCvywu*tF;o}~4Hi98U{LEQCKo^0*;M4J&4f;wU{8|W<+T+whIaJ(?=%ac(TdP1G zoMhd(4e*xu7T}#O@$&$vQ^g|Ub@H5#`PVuKs)z<$9X80vwKs-{5Z>7@6W$4Kb*3Xp zH_uad%NZfB<-1VD)H#FwaFGJeht?;hG4?|l!Qi2rF_B=E@Vu|je}moYmeO?4q5EE4 zqtB5m>)hwP`Zl61z>0acu&!Qz0y!U^Z<$?~^^+wA|K5I`(VB3rJ7ELU^L){HGsv~) za~*W=LLQ4OxOaK#pIek~ojz3J;sW=J{6?NK&rs-orjU0leihhvR@Y~~Gp_Nqn2x1K zh={kL?s@RkiElXSWt~RZ>EJvMzW0aB9SLHy*Xv(8?*^cbCyUkL=MANCMF5MN8s5MAPD6;M=lpJ{oC z&@a3in4SwnmYla{ue7;{y|W0u!+ovpsv1{oPrd7{&w8q@_y{XefFnl-4L?k@GrvX3st0BR}X~sxmiWuGYRey*Fz3FP&V4znI0t$d`4SlEX@!L#*Xf zz-r@2ys}L36_R7Wqw(2Hp&SpXwS#`MJjS3NYW&9WmGZx-F+JUV|0?gW89$sHqw{RJ zod=iI3*ok_%=W2e2-JzOpXyxJ4jp;olBWx6*K-`)u&mQ1fRIP)o(BoS8RXrryzpP} zR_H3@`MSs`y;^_Bh+Yn7gna6twl+>592Jo4?a~3rH|*Lae7X#GErs$@icodW3s?ff z1Vn$!Wub=5(^pQ?_uu3X71qvZw8lPZbqdnUN@D(m=l24vcuAMJAh zP8G59XMB4e&aJwkbr`FF=T|wO{ksCaWmQV;)Na)Rbs2Y9Z${-Q&ky=B25%@sC*lAw-s+F^`LzP0N{l@jpJz1bq!7WM9cVL( zvpzO~)Wi``FS61lsxyLkWgV*a<=8YreH@J7@*E+1%RbY9Y51)DZf;Er@)(v?RQyx{iU9(El}` zYtK4SzvaD7d|2A6K;rd0f?u8A(y0boC=F9zX~uPUpS&P(AuW*^iYJsX;56lsTv58!G)N45mr;8x|5=9!@9gyHyMjrIt++6?O= zxt<3vh5HRP`R`f=h3_%>vXqa3tH8xRF0%2FMUNO5eB~1e+}i`APF!yrMo0zQh_89@ znV{lJTIYc#tjqUF^>kd$%BO4rY%1ZrzpGz!?W{8vs03 z`%;4NsG}XMYtzN?#UhB1EE*2*ScDf295i1*KPsUIIAh&UOB-?=Mi^Z5 z|Iv>cU9*v&d#>T}CrS(t7klRhVzP$A4*&x_^J4>|#=pWH=W0_7PVo~L%%N9#{yK+# z6H*(xX(Z*}e%FPzF&LC>o#n9yGe2IoYU4x)S3KoQmY6Qc=Pr`{skI@#E61=p{4A;j zy-mgX#aqFF1&fEfkJS5+6_3&Ae0#3-wK#N6^{GJ%$_RU2aRT{G>VBgPGB|X~vdxxO zSgJ*Dk@f^Fhk-$O?D(g6aCx@P3yQ|T-eiGL$0DzVHZUL|ADQV#{KgMtO7E|7nH%Kd zr}xSCmH7c1ILn~L77vhcpc~Yb-0kn5XciBgINK6aKSeSrMBMWEn!jv6--(@`}xsyecyvRHpJx5 zQARWqBiQ0V+LwrxU*pLg`88+s&q4$~7jy=FxL5Rny@r!=Il7FOSb6{vV6hR$5b+Apy(19rC*UW#U!$7(S zl_36_?m#05@%6xbl4|dY-0_k4Y={HJR1`cW|IV_g$@ltG!SnB8^nTB>0$&mF z7dx>4(e`zcW*>l`F}(fY_*BkE|Fpi&6Y)9&-%;d!GQ$BQT?^I^v=k@6_v3t@yYG)b zQ)uT?dCWL^rt$F7!?_XfJ-ULdZkP1Y3LHaLtmiJ9f_&9UL#K=kuLrzop#F19$L{o~ z35?<#cyRpi9x;Tn)rM}sxm4W-FOhrHnx5|%US__(Z%j(hib;3F1l(d4%7sSG@PvM@ zlrJ9Z;aqi`fZKSq*AK)Kb z2~Gy8WRdZ>aar;G0N=P&B1Tu2Nr`gUA77UX!@CMoxDgR}ZFv?nDs@pMf9nR0eL|1l zM8n4lkKkJHobM6Xx0Wt%fgb^mwbi{u7se*$+LJ$?yR54S@JMT@7O0evmsrdX9rS=7 z4Jm9El(PZx-jH=GUyl9?Hv%r`F*t;HmeGg{#Kwb1hkWwL4%7sWcuCD3#A@gcfTL3Z zZ+X{=_@Gm%?)(OO#l74C7phbKqfsC2u5BT&e!B1fI+amHM&t>LyyW{f&2DR(# z{(Zi#M6+N|g-Wk}Bkvyj5xn@Sm@t$5-ST<4$H|%b@#L6vkH>_?(|)w#^r-i_N&@Zl zOfufr!dCkluh0z19$mIhhsNrx{i*kHztFNQQgA!Q^R;;l^JYV5<%GX#51%p;YdLaU zJod?B^ol1sx|JZ@+kj@Oh%*NsgvaRg0zh`wLj7{wF;=Z!Xguk3^UX_Ho{;PkagRg$jXVPK8mls$ z?@DmDs6i=vi`l20{nY$HXB7=UCfKG(&bPj&HoHY9q`XVV>G^Y8vCrqiG$7x?Sz*7{ za}^cGp|e>)&*D*CEBM%uYM-Y`w&r~gm|G|KS~V1QwF8yb05ph-d^wH(!yaSO zh6(&`JL6)M06cWi)n(RoABZSM!ATw?CJR97s(jyh*7EFQJk~^h?_c?`#yN3;z-mt* zu`F$fkRU#^2qj-1pry;W#{?hh3$`~>x|~~;Wr63$ArFr_XNI`fkr_T*9BCQ9gd)Qv~?vp>tXv z=tO*$LC4XzyyG%vJ&zB~i&j^rcPkq_pS$A?P~oAW zCb$&{RiB3`-biJJIy#?QXVSs*ZxlJKy4ZzB?l}Ch=(cwgIXuMiZE1l9{4Cyp#*5u?Jhgcu8sdyl+y z8bB%Sbv;FZ2Ma-BS@2hYkguJ+ArY!rG^i7MoxEG;DaJvFX9fb-{VD)_@Uj#Tra(-c z{HHc~uwBh#S3T+_q~AOv46z8FrkuA6)k9sd&m436H*yE1KR{mu8H4fIc=G(zO!)>*$Z;X&AOTuO zAF=U{n1+X4b&ldlxbPE(99qeN$pe|#(Ztx?^zo)~%8hSDWb<8zy0%1saun0y-&wyA z`^7AW)TrgmHIrm}ZNZfJgwG+?Dg{vHh4Urw3!Sy}xn>~eGKa(2=fC5O|Moe)6obXq zQHeL7p%Z`RS2^rG>9Dh;`dYd90p>y!QBaRnxH#xs%9k_`rJX%a(oBpQYz*)&2mh?6 zK>z&PnV-Zq#3z(T3_3L}p<}g}ECAz!$kVmgy?1WF6hXsV9UFWAf_Y~j@FTHuSAUUZ zy>NLrRt#&sSE2+>@B)HQI#(Wa^LE_1%C6&~L0kiV*C2x|Iv$e;PmE;)mU_6za3mDN z({-f+Ctu~_d+02(V!n1hWB}{-DeLjf4lww3cD(8~o$@%@V2_&f)E?|I#3X=yx+eqB zyw;zdUlfoC8az2q$&uswU&=^(nc4Q1JV~fJ>s-^$U-7BI9k^CBlaoCbu-HDg^eiv zMCEtir4&NFx#)~tn8i_<0>m{y7=)M9<|6(+xgkCa-Zj6gJdQQs$9XCs?;rhk;MQ+F z2>0G!ueAEtxiHYkJ=-HNb~zW&bp0%k#qVmRQ#|b%@?0+0@l6H))*_g||L8Ct4c;ml zU|IRB(1`pFr@Ci5g2i@G;lnI3IPFIXqtCMVpsx>S09;6d6v~1>uF#EO{3Aq=!n2N} zJQlka%FV?KEDz<#vlm=*w3OEfa#9{q0qJ_PQZ9hUH=gI=-SRmnV=*S{gGHWe&sm^O z-d0VSt?d0%<;D=;L+6J&z5&{{(yiEG3`d@rm%Q6P^?o+vwZ}j|T&G`H-Sn@!t6B^7 zb@_#5O4ix@@@zlZFK}O9Egez#iS_O_EM;0;TKgXHK~|Tbcuq&2JkM{QL$q}q`MZSy zf)2!QEO5W59Dd}5Gd<5&%(|fScEIyaN1Z%zpQThp7rn;4^H6Su$smM&f$5LdVAAWxjuQ64v|yoI^MPkSelhd>OD7|XLo z`vdVtfan%p=KVb{XjFG}Uw`s_u3%<`JlX3}W?cZaJ`!cSVJ1B00*J;6l=zav{Iu!& zCUwcvg_tJfiFqI3uYLR`8vbqhT3!IyvnyC#-#Byyc1%s-(e@tgI-O4%s*H^oA9rB) za$povfJ=;^aK@+pv4xKymi)&;6R7>L4bb1~V~A3v3w`Hs2K(9<5FL&Gaj zfNwb7L{ogE)ekA)smB_0{z^8gZ-XMR8aGdHwOT(a~~8+w8L>L1!@iNFi?5ykJ1HHU(f)Sd&_Y@@ zDlw<8ab5XzRf!8;tE7)s@bJmE1<{%B zd!x(sbuapiI_(&Un5Lc|hd)xlRzE1@!(VH$Bh)g#KSRJ`;fHt(EiIuUY(1^8iCQME0@ovfxB0C|auT5|lf;1Bo2x zF*qNX)5RXan|Ok5zI%xJKJnLZK!YR3qQ5nZAPQk`L$v_^%GUnU^gok~~ z&+=L*{FYyHpZW}x+C(?@L=Dh}^UW9wm^j8QF<{yoL_BfiZ_~WX=K%TuE%-5sDsLQ3 zRyg7}v?{Y7u&^Amj-T~>Yz`eKrflVpv!3j$9kwN0F3BDvsXWg( zUicUUfX4{jEIieZgetF{XC7I`!g`*4>SsxIYg~C{Sbo1}w&~a>OY6~&FczH0 z{aG)Tw(Jk>j(n|4@11KSc$UNC5eQ#-8c@In;I?NXf9kBF?>V3u4a?r#6u`k3t`f(d zIKhb?BY5Op2;e*WU0&)k1}dB-)=B<$8M$Z9570HY1desk+6qQ^z8Mz~08+>idmE2A z)$oqHS%nWA#A*Ia0p(RTlR6e$#!I$bwgB9@1b{+((V^$7bp;08C-RAffwJ!b`H=Q? zawy@@m6$sD&&H!8lVPRu!U2m0mi(OxGOnHc0S~6$i{VtB^{o36T;hPw&e&0lFK%w} zGyTQyQ+cX2kz8k9dxl>Zz7V;NTvx%MD-om-%(!3n;V+WYJ@W`5?2NC(zof;G$Kv-{ znD~HED7atyRmCSZD#vA|Oy6+_;AJA%bPizni=x&aweht=GgIG-!v;`Wq)3Im$pZH_ znWwrc?LNFlwznfci3WGQnS(MLpoxvb>VrGe!Yy!S4wcmp2=M~nI<5J@a@WQ0kf>op z|NiyV>5hI~8Cp|rbe{XSPEh`udLagkzk9%xH_BRv+t|#U`9^%^eW1V~SLn5nBfKg5 zoC)u@kKW#6=QlHzI7i@Kjzf_qSii}^A$qeD7vCgwLw+gWcf?NfF2ta2V=IpX5$kzk zgt0s`*)J^hmljc)I+(iWGlmtY)!)&r(Vm`U(+RE~l_OxUH~b9NOm*?b4%y-@*vI@R z`i#G#7!P}E!#%{#tQwP3i6ehp|A!7M>E=%wQ?8%~y^m6#X@H_LcFT`5O}w=>@Nx7- zav<@h_CX-Q4cq@e(A);cBpd!kKm!IOenDAEu$I)Ld&4od&J=7VY91HxF+D~{_ML!$ zA3CoaQl%@_#rbWaewtRtZd-nl(O}Q?2qdwQywfMKPfh#@_X(qkpNjpa8d#xv8QGt` zL4-Z)IKAdkaZ970uFG}5s|RnN%hy4yu0wY*ftp{7sP2cy;yeBTghb!~%ZTVNgBO zFGANrZNaX?zP^=BVDAqE@);w?IG~8B_x^MS?6<5MlpZl+9+Ovlcm-si3J2^aLq2f$ zwD6cb*0qUSsWWgkO9NoUx;IE*0hS8krF=e{b|BF<(+FI)aqR_%dxo>saVnyd(Zlzz z-XktS1IDsz@996 zsZy#<63)ja?#37IW1#WjF*&ipE+MR=j7$MopW`?q`VBOK#vi=Qa8vei1ewwZ(ooeY zJR)6*s4U$CtPwq~^l8wb0+Bp1^;h@=AOH}ev;e4+%`%d$9hv&N#G%cZw_>*6`XT+g_58~N^TDvYtFE6F(PTBxzeGm%h zvL2&nd4CpA29709*Nii2jYK;Zjl11d8&~-!L2`gS)`2L1?i*r=pV?d^bKW_}7;5Kx z^gXPCnAYnbU;)>z?gK*I>)(mdLi<_6Q8Q)@ zEPf=xZ7M^0s#hJFz@9))`AzMEz^BNyjStYa$R9WQV3q|!cOQ?r$b%D=1OO}m>a{-J zi28>%;Iin5PvuBZ_@@0#&&N?*2Z43z;Mn!#yu@Y2@6PSbMg01;H~lZ~lvcYjSqI}{ zH_!;U*6b0nF*qkdD6 zNaAO;=AGWx8JmU=%EjG4BLL^^Ean;V?oBoFH`#oqv^aX~6a25+)|^}hK*-9Z&-EA4 z_v^-&P&%xGb@2?n(HYb09;0LJJ`a68Fns`z!SS%vhE?GlbQX|-et}K3-m^sCuxMdV z92vS-Od2BAV8;)&z}@Uk*Y~LQ4mfRHmOQwwpP8)fd7`V1p8+2HT-z(LC)qq2ZprTi zDMWie>$K_m-h(M&kUs{5Dskf|4~Puovi@_LUlmKsjAIl5iO*524G$SF$sd};3pj}PW00dF0U+=X&Y?>t|dUrwQi>)N7De#PE=$D-x==0{~k;Wr|^I9u@><*3L1|8v;*O|$<>ckV7~M9A+KOu*X%U*;E_J|D8?HzdEMb2vw~Zm`e*icRI~^!zI1n^vm~eL zya4in?X%gRQPNWRH`cgn-2BmSUQ;z*j@-%#TmkFiL*p#m;zfCLQ~f)4kj?e2dn7bs z;_-*SRx;y}m%N;uTMOKlG4PEG54q_tQ0S4O7CG4MXNG*>slf}o)&tXDRMw7NJv@g^ zdVHg6Fov&%RbH_v(%#ZN@DAYYv!cD)B8-{!jPhySm>ahd?(M2Qqv3!>nkM9kML7~# zxuiua$Jdl;cBw_K?OpIqyy>+3&Jw5qig(yG+H@lbf%rpV^{*-_bo=CYoDgCW%vrle ziCBA2;wD5Mob*Wmpg^AZm31syp5Ha1x+3ud6+ZP;$>%PL)B_t|dZdj4wdRk%H5AEE zL?FU1eHAfYQ`GEB0t5WL<|}J1V`+Q4{@}!|gpV%1uEZj~y1_?m^;eAiqq{0!cW4T8 z*Y@foYFx9)8jGL!U7`;mPOun0Y{u*hIzNmBOd? zr+oUn)?rPcv8v2EIsKA9m{LVTaJ@X;f0MnxVuFWhR1j2 zwW2i^!X=5z^7(|b&kGC_(FdC2yngbM$=-nf+@h!nib~#;NITqM{z2W>b(JYy{Z)F! z+2F<_?_>Bof$#4V%J)tnre%MCd(1(AM4kA;^K^eC%L+1B#}m`Wt@C>S-Z4#yhoG<1 zU@Fs?IdX4dC}OD)Z^}mw-)t zey?Z_W>oOeS$y(govq=C+4*mTpX|?r{cvy>{9+X?ZYKe*>V)4 zYwfR>z6yG;Xgu9pp1&3jAQQc@#|qxyvka@j+21eMv3}pTivM#8B3;52`r$qP&&79I zzrM}<(Bg%s?@}1M{qlaYf-e(wNqg9R%?w5dVwOUl&Uq0=R0S@AUmWVkRv<*9A6uXB zb3sPozarG4N$3rv)kYP07EY|mp*xB%058Xd&*Y4rR6gA0OiXl9X-27tl0t_>~I!}7O4p<;p znb&?jlWLye!pG14x9_NXJ=h<7O*$Ib!JX$}nE*K;NPE6w&UnM|Af$k2_jt#$tGotM z-8l^QWMNbd;uF+!P@SWE6?ncl{}StY!g-uHM0HBVzP2v}f5(IU7g6;k<6=u`a~a3+ zo}Y1i1Nh`eZUVO!Pl2zY_bSnx1GsBfa$#?IRd{Hvr0{)Aguoro%qln!{G&ZYA5orM zS*stdV_zbdelqdHu6|VG*%i6?!iIMo<+afYCJz}q72{h04nYAvaA*RQCgd~rEY)D0 zwXDAV>_p2FL-T*?`t$79^5hhe#_JGh9(DyE#Sar`Fexl^97Xqf^elV|RXN(8KG>{U|!3%;`mMq%|C`?#zJ z%ii!CXjC{e9Qnozu{mAe`#0#8Z$x^IV|`e!JKtl+rw%{VV1Jk&w&$s9zW`!uJb2#K zV`%--(V4G#>p>crUNnMC1ZOGgI&l(^zc!=;Fk zghC#x#bv_p8J&&dBZDk5#9VvcSa<%b9?IVFqT_JI#^>UyUV0UiRu|}HbuNKah$Vie>Xlq*Pl+* z83>Ew(YO-ILr?PZSb=txZ zI0Ox+2IL_Sf6a2Qz(Nh{V}bL$@T`wSSwhOaFQw>$#56qCt-=e3u0Re^3LjZ#qmHFc z>}%w8zdu-H+s@Y*2r-r7a1;!mPS~{u`IU7%I?o)?Hxy@yd*yuerGoM$DBTj)U*T4v z=0TY8Xh_W7z|;N!ps=lgPL1&`Y?Wt+&|rV~Z>LAlyVRcDuQOijMAa7Xu7ws_%C=vW)fc_8gxQvl=?9CcB!dG| zIuv9n*KX*2C~p`^aE6`8QS|+C1?N+@JhG(uZ6Pr*Iq_GH6CuVKr!4nJjiSsp5praiu{B?xYpA)R$8VO8<@tQUv zGdaHH|EkA$Qf>7T2;;iMM`7W7*_Eg<_dM$%OPr+4b}g%%GrhJ$!d6F)506L|PF^2w zm9SA?hD`Mm^u^+;$=#FZ=1BS0Ho!?5o{E;vTkv$6Vf&Ec~w`jcl|V&N)59zFW&LmCAEO95d7ul60#g>OQR zd)6fj8a0o#&T`oQ+~9<}MuY zYDj+?Fb6QSxcBv@8Ug^>lc`cTl-0iy72o@(3T69Qpa1|s07*naR3BDtRchgR;$jzW z&x$;qSG67a9I?Qtn$}v1@{Gr~2^-(AhGXyFtvw{;3p(A2dC;RLVA4kaVu+B1GwsU6 ziHA;>n10IF;bR`oJ~+nGTuMAU>Qe&fc0PIPUoyntdk3HU^mRoU1;>*IN4@fw^mo^f zde2Y)tnf@>oq876IpsQ^{gc0|MeeqM#QQ!qI+f@zbPb?qQWZ28z_*r8=fvwwiE51Y z#n#n`ZcGO3M<}0>H{EO}-M$37z$Grg)Y>19#mP0Ehh=)nKTzPF;Tn2cWnT z+JK60IQoO95iuL5d=b>|O>AYo#&DgeS3kclj7=WC_5lFW0;z7pk3oDrTytevxqBA4Xc3l<5oENPZb`N)$1;DpLE0B1!pJZ$M93(m?M zHDHwKP9EF)l1SkIAW+uF)Nh~-_e)`n6O+Urjkfs1{RdwhS@sCKP8ef(tWk>~o&kN6 zkHZOGf;iU#fPY|_Nvr(_GrVf~cjk&+h)(fm`op1fOcyuetdEO7cP#34lgCdGC$_ed zpR7)N>4iqb1&PptOx?fFsYZEK!QWKx5`Ux;Gpt|#a+Y-t_kTDSbe3B&&jPv$W_4Z< z^Z~XU{B?yazPPf3mM(>#19q}N=&wZwi+32?VKkx80g>{Bt5`RtQKy#reAbZP$TkZ7$QXn)} z&f?!%y~`te;xa>iKTS*HzvjlIJL>di5@uQ`y-{V ztnGiXkS>6_3wK*~B4Xc|m-&-Fv`aASu;LER0(mZu>k%^#>qP0ehX9s^&-SxEbT|{l zJud?LdRZsB%K{mCLn>ApVK%j5`fD)5X)|@o}a?FDz4{yE41EaZgK~4r*FD)C;jS@}_nb_N~andOu z`&jcqKh|G3?pZCT_B(l_>AbG-tjYm+Cb7Tp`tma<3wXAzmvvtTEa_IlrH>yP0b9? zq+|U6DOBuj^Z^?DpRu%!p`ZLe)AgpR!=qUe$tJH8Muk{T0hOmwC2o1{Bk91?n>wEW zz`wAp;=gI8*azlfPdq07hitWrhg;A0%_Z9!|VlEfuD+`+!{RM%oM!uj5aSGRGAQL>Jc!7NCL<_Io@{W5Sqh!NQ zV&tg5SNa_U{g~%>RDZ5A=9=$zhGjHfc!_C3>`Nhjz(r}L;bXm?5}ceoNxT(~x3aVf z{*%uLzh`|yCjY$-KT~3!bvHth_-E>3(}sk{k*6g!&Ji3eIN}d2vcNq?mOQZ!)_Iiv zmW|HqbT|KR&GXd9cc*6LeV$X+B}C0*$Gg1OA7dBwI7z;{k#%Im@dqsRz<&J;_O}-G z^>%L=%<%{2LXsK06?wNj1Nu$EjzAZ^z52%^UQrn1*3j!7Gd_7@*E{o(fdv{Ad1C6n zWAy==pE&Tw4SD5j4#v<}y0|dR8UZ*c*XjOA?EZ;txr+bif2UL(F8Ey3^96~{W;GZS zQKuvEyE-_x@LVr&`4hT$B!R;E`dc*-)RQLE0DJ{A>R#oO98Lb>#KwtF+&)fexgzY8lg1rw!Bi z>=S1aG35_jQ6N4;@|lYi0fh0ALZ}=L*Eq7{Ez@@BX#G z8ejP$@Do|KL3$(*q=9S#&?5~T9byg+_A>#}?FE~?l+w|^Dn$cdFwt;OS6xmb zypA4x_Ln^Kvqj1SG$QwgwH4~Iqd8l(jyL~N`Ry7Xz3QyU>2YV`%Oz%_Ok^{HZ zryx_H-nTCqEkbPf-05YzsYfdOS>pN?4|s@AjO!^Mh=HL`hC{RzO84Vu&)PV2 zOHbFU5;`x-T(Od*Gt7vFJ1OX@Wbe~${VW5EGV#=me$!zZmQ8-3=?l^0k)P}(pV|<5 zRb=SKo-jI_)%luo+aqQ~Z4QvZGQ(0G?xkSYnErk3O9eU$Bc{nMf1yL`bylWyI$!Ij zY;{hTK0qVFlOci9qR>g4~7rCVHU`O%6)KvGYneXfXjAy_NZuf&nBrL6f%{r}MXx31#v%%x+( zIpNw?ffqI|tRCdx%C2;uoSNs+{J#Bt#qZ6+3Ss{j%{513zA@K)4lMI6?wo#sdK}QF zX9YN~BNg@VX@x)WBO8`lU~hiZ{?6CGo`C-A4#vvK|I#kF9PP*xukHMSp}0VBSI!IH z;j|=AjD+XUm`pO@?^)FTgpE#rzw6glQuFz>Lxjy@8MnOSh{3^8@f68UOD|pWn{OK&+S?QndXB!pmzB&Sdb%zx8bRPY;tr@Y53B(DC0b2v;8vc!PKRD3>+Vn&9z4<{IPWu!G4?J<$?vY;qj*|*M9o4>6f&H)L*XKA;i*&W)89u|Elqqm! z)!*~8K%a3+ws@?OWg;;6%vw^*vH?@s<3(Zuh(T!?qGyG?rAL?w)Q`E2;k_e@xy-eS zABgaC5=|=Nnpyn-^sd4etsJ@}lXYZ~-|~$9vw-@lk$iZ*{_TuR+TL$f%CgmIS!oMJ z@BT_nhk5kuv0dc)u}`{vkYNGUp!=uCwD5o*bP@$rzsgUw__pq|#6=!XPoS))Zm|ff zLz6*b6Y1ePD-BHKxHh$TDO#L)R_<3M+6N-)*;sptpn58i6EU~-^K0Kv!lI=)R9SYF z55s&aFpl-t55fw_in)fO)dU?&c{=3D_I&qBVnKq#l6OU~8;%edbYfkwDiEt~`93(a z01R37S^Kv2A3>d$PFR{39`}FE)eiIZv__%Lg}vXS)z^|>(9Ir z#hlH`N1aon+l}25hy5ZEs<7#RT`}o95=7Za9N#8vd;<514P$9R_{rcBD%YFs*Y5mbt>#u!{c+OJ%i3!OeGX3TH zuM^TPp!1936pKtb0`4)Fm5tY2siyD9@=QSa9a9kSpURL090+m*$2g)pa z$_GWsFjytTD=}~u0>jdpdx&+gF4}We_A}D!=NUo$mwy8tt6}7IWjCK?{V7XdW};8V zY$jDlv#I1+nr}L>hTj@=KH2bY-xNUV=*s^q_Ig_ zX<6Zc%mbQNEaaURF`8hoV1I7C!>aiE-B$J2KD}Sf_gdk1t1(Z^!d&5&&xkZ6UZAr? zJn9qjK#$b={vvSkXXdBKHbG63LSQm#UeW#^H|)wUZ3A-iLJmzV^3Rj;zh~NG*F*RBEfYq^)&KoY-&SNZxLFw!LFyiVii7thi?}!6MOKvi z#KCyl{iw$uI^YurFFu?TXPqdj|4m=NI2Tf};PuPnwHCg31@vbDcyPpj+%oucW3r&I zj78mJaMZDmBW@c=*kqke{&($vK5om!|+g}c?%#RBiOv>g#eEL1kV#=9Un12+oKB@IOIE)2bA{M3Fu-jf9!Ild^-|OvDBfD zaFeZaaw^yW3lYhxuOA4w=9~dp4M(FzkJPZfmTiQ<6@U*P-8)63TDaCrCGh9!*Pkq& z)lg@a=&|})EBy%SD>8uvpMEnV=Bs@?a3I3+6Vx$l?I-KWI6EXGb?)m=lr9UvOkckP zgU9t%Y|InSKGix|Cd)=pJM4lL)36$_AGzCnA3H`H>!Ws+XCJNG9-4+moMNL$9Tu^g zQ7HSECkZ*QmGjb!$F~UuA9r8^|4%(QwKwb)V>E@#Yg_%%Q=hsPRc6|;_Yg`}VKx4C z|I|DC+XTFDa8_8|WiHr^g@uMC?|5QeF9pz0om)Ad@Re`R7IG5E>`8NY-IfmmrD!sSS+XU)de&V*b4uSAm$GLpsK&H>O`1_tl zcIR(FzUT{jY{r*o`-6B~2Y0q946OvAiGV}_{o`zgsJycatt zHG=l3W58l-GliMX=rf@sr~`%mzR{E}d9F3D7pfP} zW1oupnqPfu|IN@9>5BFGH{1Lg?O)F``p*kH_J<2Q568%}WxiHM_B#0mA1Pp~ACSnC z!zUj$e)N~zd>iW++O7HE{q7eNhU(gJjeob}*VJ-{etkA%8OE1>tAn2w(w6)r^!hX7S*IheYq`}HehgPYX7Wn1;mEc}?NX1GxWk%lM3WEQ>-}>X!aOxSl|| z|6KKx0rUY{55(%rfBTFGpWyjKPhNK)37;Fis`rC93&(Q(!(}x%`xn0+Ryt$wZ}X7H zYF;^1%pQ|g-Sg;pKH&UDvmYXff)j}LgTa1j{j)wW6E@hinRc*u`VvP0aDfaP{X7MO z*96t4Gpf}W={_IH?09WoDt*zSwR+QYCdW5=6SGG0+8YW5?6CSlsKPeeJtAB(pVF{qCA5)|JZ{D$^M%RXFz}r}I4c=#^i)K3TW$ z(6epr>s5==+G;bHpGEiTYYG7}UiWimBv_M4Hw(cn0n!)x2i^C~xAyVUc=~4ZTs=pe@d|(zUBnBZ8mnDKE^r@)>Kv>Yr(J z=`US7lUx(+Q#p-KJ(hv>I(@L+KN&hx5Sf>YdwcePuhnkXzH(uhid1D`&Hqf8s^Pt^sV{DGPTo>AkfOx*Iso7W*gsqEu0Cfmq$ zr7v@E{W<5Zr@3kYcKKPYj;9h%R3RL7XS~W(lYziv-k!hKyXb)>)#_wGP^tVSoy|uP zxZ`}YJ$4>r92Q{;6_??f^fgLcw=+Bg*WPnxP|v;Wom_W0d`(jgZvDB~jn57&+j;cY zv$yocFM!AA1(4EomWgM~S;2N4WhP9Wt`n!#ci>JR^HU1!>IZZ^`fDxrW!8&Nf1fuv zvnAva%v{Cs(lzVR{NaoJ_v^%5{LO*xpCYZ99y**qeYTlphGUjyYePmVv$bDifXSNq ztoYj3<>X8Bm~xVG@1|n6p72x}uAEXCxS1N?=G2mD@PXtpa40fUz=lZ^eX=V!>*~-Umzd4D|_`nr2iQ9FM7Tj`h(4XqQ^2;D*h8Ta@1LV#h3KB-T+IG zb5)`9Dm~4s+KT(WZ?k=0=$Dv2&!69ceeltrXErzY93k?BBo9&fN)ThgVV69si&FSe zB6s?ElKL%ipDehmPWbV)w9CX_`M!2t{lC(DY2v*-UZAgBXj~kyp1;NGRRDtqnx3cr z#zJ!$k8d*mo(bHieDHtHvcl-F`hTwDsZ3wnl}mcNiBGZ4n4Ai?ya3@u^8m&l1{v;~ zFIWM4<-5!}(&`5;C&(F|o=>jze_hOme0gCI?fPBPUzYzefjJl;1)j5g>Oeb=m@zzd z9=J2VGFKnQpR>mYXg&|nQa`mJ0l?nqQP#tu62PFH_wv3Se8NZ1`=j$e^+rb9g@c>~ z${`Pq`eAQCh+Zd;5Ov~bn<(eT!)E8}ZBRjT{o|)tJM1{El-a-e<3ujT^GGub4@kBh z-={OCD?s+Ip#og)dHqHW(FKnFYJrgxT-sGJAZzU2N$@-|`sZ9GJQlTP!VhI~r=OJ? zcecTY8_BrFDU19K+(?gOrbmp zh_tIbA!W`!?REH$4=E|o*FP2Df8KweVDd)_oq6G-Rl(+5P*@xB9APA9ek5m}cof0s zsIR}X0W4&$zXYZk-=_Z7Je!azI3atf|0a1(P@rUv?}e6p?8L}PWRmim=X_>jDBv~M zp3o%Jt9(57O7FP#2<)b;^(g{6%2y)H z%po8EomNYc>8S0i%`8|jyJeopf_zfhSrx5h2@I8)r-HZCT4R0KK*s(|O@yhBT5@zH1FrKsgNK;B?6H7s#5OaZUiNDa z_Nn(z15Ro3;OIxY>e813z$nZb>l_2*egNn-(xWTw?R`d6A=dp< zyjlpawYxyY&Ch1sd+01eO!J|oVJQ*9_L#hH%G=F?n3lN@c_nZX+4p|&t3UhpuKn== zdLQT0D29o_;tzgs@pIV7{M|45+NRkMR4vW(Q)z9cGUa$1>Ymr(pd$bX|I#v(&+xBj z{MY?+KjOg>JERwr5wK%1ArJR>gCF_G&3xFA*f1j(UvZuypWnjy%9qp^(o|dG{{}a9 zBfkFIX8Yz`D-A!EhwsbuyvM;EU;RRs0?1%5v?Txd`>hN4A2;}+0CV^*kpH3l$Mn&k z5p`+G5a z^Gh9(i^ciZt6E^;faAI;Ho;roU_TFO#DLF=Uj2;KS~osAjZClec&h?AXZ@MDKR{RC z<0t%jxC1E5%swx$nip7~i3IE~_XEr)fA1%O6yzr)^V3J9c%jL1KQoB2J}3FZ-hd2p ztGmz>j-S-=v((mPUHn9Z7ce2p(Q;=zP)}{fDClXb6(>_dYTC-Jiz9#TxRisC#T_J-e~_YQ6Ru+XTJ@(d{Q=5`l3^8kcoq+9*Ey z8-f7}2T%QKrR)GJcO0>)k8rQ-5$M&vLok`+JTA>kdU;qR@U~ClXW!d((RtERlGw7$ zz0NgW_bLrv`=!1ToQ_`ys_?DL`MTGH)Kh;7WvhRWfpTY>;kl06Yjk+z@9Wv|8Tkwz z8)r5d-+;~Xh`YHFDy05geaJ%C8fovj#t?Z{=(%45=^ECXPQ}|;oWMwCKe}h!QJ-;j zo(^a7n5$s?EgY0s74OoRYgZ*|3%@gXR9ZPIo+aaF#BtYo9C&L#^6O6W^2$t6OR1bh z*RS-R+DyILX!cpS;Y`1lPrl@6rmj3BbX|&2^r4jrVg#5IzU%LEHM`_`b)z(^<6O>8 zV_l#a2LMs9#JN1Ojmzd|JdnBd-Am-s@_UbaCH`+%=a+7hVlo33 zL66Drn>exslrD=iebVUqJn-oQj`$)+nKSX}`p@<>%t6sZzdt~e`H~Is5RN}jeuyzZ z_}a@o!om`fv7r>cu?TtS8VP&(S>| z%G0lF7O(jc$3uQjT5${%F)vEfuN*51;`A$_e2_o>3f7!pSQH`-pLn=`{mXlM{MQGn zhRhLr6ko&{UB3el!0HD*Vsa(N4wCKru;48AL_7c z%=bgNAJb!w%S`V^TxhN`OCt#c>$n*IR^#J_kAX&5_e1Z`e9XaJ zdGbg5D)7*lJispUSq$4--SVhXC*Dt3WKTq6|ArU+V^z#?^Z&*ymma*I`}yWy8oWxg zKY>~OSDYQm&IZGG7nM~Rykqw~uWW1zRTTfWrT;&i=QaW(@pNC`M6TO(?SFK<>m47Tuz3SWte@&|QGGVsYeLQf&pq<=MTVac(C>^B zsh^l2SOShm*ZV6kht2QFRGN<0_v-#Yj~aN#Wi7jOFVH@};?je`df;W3@!@U)#;&SR zor=i5PP{NZBl?t{g7? z0m^No@(Pu;Jk{Z+Qhw;>tk*~IQKJfuKJ@oa8S|TH&}resx=NRY7mz%>j^Xb8d`@_M z0g~rzw$s{czopw3U`p@Z*L}7Q_U!3uDQNwgIGqBj55$6LE?t*IuGcA`_H*cBOC+7C zO{bBX?nu&;67jBL5gE+^g|D@8v?_KDU zt4{Eln?of&dr?5)MW_tI=qjB+=@kwgtQHQxtlHEC0M9y|_w@tOic^b*q-o%>a8%5Tn3sAZ)%Xafj$aThVm_bJ4@p0bz_p>L2M-e=dp?^3{11Nv&6wC&JWFAxsQo?5 zgQYv>EKEk83AM?UxW+C}LXJnjw<3gtTmp76g#RawcA>fx|C3=xQ~I;n#D905==wAP zKfsB%SzrQ(9}a#z2&W(43KNzWkV;4DgD=onx(0RiKal#PkeFrtV%|0PFZ~ba2^dR( zvc~uo*8~^t@zwIM#(7Aa)nnKAQ@D}K}{;ZOd^hD|^0R2knnSViJSeW4wU)_i=PIG+0f2uv`5gK5aJB#%80@0N!T&SQ9O-y<6PXFPp? z)&Z}XScKZ@qAHUPYp^tYy)mdm!UMT~>a-L2WUqOujGlPVG_Md;(q7W%ZF~j#1iSwB z__310bCc-i71po$3Yb5Y8Ue(qJ$7>nth==V#&&s!914_I_qh8>24by^*LyA1oIEy= zQhkq_u=gH$1M<@$7{^{wDB^AHEewH1#QTfNIv0L0Rv%{MQ2Y9uR}@o?TaMp6XUrYs zzwHT5c4wb@e^L+iJ}qRj&Wcp;0VY7Y1@~*uy4e7)KW^C260dX>Ryl*L0nh<|t;rRNa$L#aaOdJ|iq886Jq>4=? zu6*f^>p$5u9NwbM-osFF!Js{@Bok2Kpn1Hp)KX3nyMN*7OV|xEqxDxsr>Tr7phTd&i86)N~~v z#}RvcgE6YtFZZ5A4d3@R>q~(4>!No)dy?0m9js9ez<%iivsM}dXQ-1WZbW+2b+t!&o+A~U@ivA@ zOOI~?jt9pZQ{sPTnJ^{@pOruXUEC7h_QNc!5sW>%`zZCcoJLt^2Y18}Kn$ zM|Ge0iMWooPS8Fl=atHn@}JHz&$@zd^n{MwD+$NKASdpq(Ku?Cr?Zr>vdwU6Suu7Hsa5EF=@>3KmY4Ld_w2At1)i~>Zdu3eBzS9;=%Dl zAl_JbAJwbjO_xK1ZvQq2BAqpPIwDwJ4_g4#U5EG`?#3q{Hnc*ApV0G@qNwH9ZTWoF+TKWT^2wkI;_FeKLHwdFY63-p9G-Sc{*77~fmc_Dr6pMZ1D z(t#f~Qq`Zf+y7^MxzAso8UH**^6zBF5IwgG#zz?2jxK`J5!86!h0Q z@NG3grCKQy#P4uMzro3~PYlA@`k((@83AWZB)G0TnVeIylMtsQocL`|)_Grf3o#6_mL@SVzZ;+;HLV*;GTbF_xKy z@olE5Axb{tQs_;}cI5Zw9U)otE@T2avRL~0>yGLr#5M9wplz;e?A(0t_UjvaNgtlm zi4Sqsn~mthJk@^C3#av^!!<(>9CHbd(ADk?BH*lpJi5f-I8(&%SSE9_zvz$;T;y2) z^h1pGM=j!k%34J=i2k9}!ma$&tsO6X5_a3LfhK#(#rN-0Lq!5qf=@ zM<mgOUz5Qrl?DFB&&Nb#?3lVW(iM}RvD%FVGjT}z(sj5u+b1U;jf&ms`@ zpMdrAMOHYw+#}$Xr#BV=u{R=S!Qj|j&ui~Yn7sHUyzKoffrDN!#vtAMloevudCH=o z0j9$lBCGZ#kb3O%|M_1({0(#r5ldKyS$)R%vz8l*O=DA;iZ^Y{-^D~=^tI$iPh&rQ#^R0uIZ|vcsTNxEvnc2nb{wp3w~|9a6fa` zuB-nU`*%@m{~s;t7sm4b9)H*ZTEN3+iDtRB!1c^19C(yU_o-~Yx$K0)N^`hE4EGku12Cx4)hEOJPB-ech?ka)0YU!bIcVNoJ>9Ch-< zeZ7*|{1e%n|JS_Q3$}z!!m=I2j-yT0PzPu3ix5;Pktd4(T!2im!=h0 z^(P7)SH=_WZ{?VWe!n~y@~iAqF4}vZc)4Co`ERTSvi9V2kt~%gV9$H(IO^oF=$`d6 z1Ltqs@d3JqsW$?A0@rVPE6*E6;65meRUX+&@R^-DTOeK~y8kZF{b8bXv2S7T08Dnn z-SX=^teTJZ{ZrHxHT{xf{rnX{-k;@7H$a6uy7SH*f;Q~v^qfF&E_=*7?Ra~?=_?Y`oCW&96~#7MrcP6en82sJ~=G4l+J8?7z!_Q zp_3k0eXj)$c6)<<`5I~yeomnMs>b^8UrOwY3tzT#ft4Vc3Vqsp|0EpOgB_B5I_F&? zKu%EM+y2apeU{-C12wK!Uq>Z}GyAda(=LM39ZAn40B@np(w3)45=*|sW@tHwR;)cT zIAd`Mh?Caf_CSnG{*Of3j zIpC7r5!~x49}Y_J96%nN)ky=xdY%h>+2?++m%i&9ZL5Lx$?fOaG)ObiAYHF{gOJzN z=`KhA#{Pfoor!WIM~rP}Cj9@O{NmvPH~>-;AEO;pnHhE>U^-_S?%##GTVQ5F-cn8O?3Ty6F zNFbL=_=ENR$Ow)KXNM>04PP}D=t3bZfA`NPU1m3&LiC$ng{Xh6+`EMes+de*WNd5M zGR{i~x5DGK>MAvP>aWY;sIX;7JLiV|s(Ww3FSousJ9UzV17vwHdCH;iTk*?pqMb`X zb~B6BlDX;LNo1y-~OfgpUxUCt1|Rd0WeiBBg@O!lvE2mMFim4#9&<3~xp4d1tevlcWe zy-k?F+r5M!aqt%3@~_z&Oc8)8>X(FF{hv}7f)2rRoH9`d9`*iXF5K-=a&$L^1-(4@K7!5 zN>^uU+rOso>|bO1di0-G4;|UAurX}lE){)*ARcQ{0T(T@a8t$?sry|RzBKCVT6h%Q zWrlX@Z{fhdZxs`j!n$>s@T0$SB^R~qT==*BiB1@_tlNG^bBBXRabV!sI`&*==yVab z{a1Tf_zuN+q7d+t`?az%QZ?D7BP;uqrT^4x<)zG>9@zHZ*}>t5Z|m57ok3GRKu;B= zP_+J#Zn7amG<9Uf!K1qnJjDE2>o-+L^KUgrs*Nps3iV|n$?$LPJNw3cS$~{}ouwUC z(CByR?{LK7Cw}XFLP7lgYX2YTeC1D%^^)C%AIJFh$(~`F5lo@E1gt<0P06JeXXWZ@|8ph3>c0G=>x0;WL#Hs$1o0#Cj zZa5D{FH{RV-aQ^<63*0@8r)!$(lPdiIb+x}dc=LfISrjMS&`Tur?p7?$7w^%2=cqf zFqwh9E;alxcYf@_lPu+C6h)iqs;|_!ND5a(RRG9X(_H+Su_tiC(Fj9l?YKao;Nx6YRaH+| zP%CfoR)h=i^tm8^@+1-GgXpU3(mxe~)Y`U?s4lOI0}*l5UGoTYy*ERcP92TB=oLPK z%;&IyEivQnn;$%j`=JVPIvL_J*IuOmE^~ z9Am?u>zM_zM{6LdoVbm=KO@;kp1Uk3=P@QiotXe!h8b`HaF|^lh3SuAF58<&WSHs& z7bcJhIgR?v2j>A>hzADnN(GXnE>25BhYWH zk1N>U14=I{@~;ARp$vdD6(0g-V7Q;&doKM46X|rQ}<-%sboSl-tQGdJCkb^5UV#!6j3Q;)JJqy9{J zcg}~4ZUkN#zj;a^)z;5eM(CXBfM4(_?>zT9)d@kid-U&ksOxhbzNTi?q8GJ=_ThKN zKU*8rd;oC|AzeXu=&)`cb)(LLsIuZdT>Kwl(Jz~8yW<{AX*Bf}R3#QSIOG0OCn7d26>Rx0`m?`LCYJ>o!z?~}FN(QG?9Y0xV@ zjx;P4*n3t&cM9}7KXzur{pkKjhWAwSofuWPyZBE2&u~#s#@78|aRFn<=)kaV9o+VV zJ67hSAOeulI{@_bU~AMl@q4ywMsWmQ?~*xD*xme#Z?p3sE+a{dn(nL* zjWF8ScJw{|YxZfy}sng1^Kijv+wHTGw(4jSECwRx5AP z)&mRT+E5mtkJnom6>b1Jepc*{)AZGF=a)Ty=(1VF2UBzQ95O!B_6L}-W^}uuJJjqI zoxyJ}PNBEKQ6hqaI3~@{4+dv`i0H z^In83{~1S<3%yFYMOrZZBw#@s|Xz~0<#Z@`?&>_w2m!=BE(uEA6B8+kNi|y z#WYj7`g>uZrXCic9enb|b8^|N3wPPBQU}3=JkZ^gah-z`G-fuPL%#Hm4+na#YxK)I z)m=0`?5UFnVs1(WD{U(ZFo%n(GdQz2=ZKu$4az$1JaDhDDK+1$jVzMVxh1cfV3eC%vnKTJQYL z@GTN)GOg4Qd{ihbIC zTHZCxxxJ&7yHRH-?6CIvIJp}aNWL81Z2B+lC{>=d|GxHz)b(g{DQ*3mD3_TPEJN5OP1|heDr12G9j4z}UsMOQwEM312m+kJoG;A3zMgfW(JCut;z!57?f6-r zzJfML6K>4vUe7{WB`jJ_ZOLEY&1FYV)4D|wgsRGoZhFbGM?mBM}VCol1* zb!f@QwKhBwIWuexXQu3lzq=NlyNb>@i| zM00m)TOZmpOMib6%3gKtQCOHS{Rey1%2itXmvp`V!BYXLCJk+P(P!p%5wdIHJ4pY= zSx7@M087GW6&#fp14*rJzkiJD^)4Tw*=ml?h@SPnhm!b_66%|kjLSWGaL-L1>Z_qZ z_s#-b%oF#)-$2hcB%fp}^$XQmhth<;Kg2*$ zB|0lJDV>T5KnH-1A3Hj$&t*Fa{*AQ?`;Fu`Irn^~{(bd|X)+_T>I{@Tu={B1f}<+o zx$v+{#|}=7yPSq;m+$ZUVq$+%2Y1uGvwyDk>KxiM)Oko+aCO68=~j51tp)ogm2A*a z9s2fT|Cg?8ChhZSt6g$=%!^SDlZV~e&5296DT_inRLEfPD~>OZ`XUED?0S#aogM$@ z*Rzt~nN@zC`FkQ?R135eiO!(W@nc7?-CmBR7W{#*QviViKkK$PJ`jxq3F5!?G69CQ z_mR=zXWe!Pw;dh-yVjXRvPhap(ng5(kxll`zWVONkCc(2?X#-SX8BAU6JRE@^Uv@- ziP>oc$3`e6pz{NWf8H4Q;s^Ls>n`}ryUW8%)b?Wshkx6*4!+mB$VU8(G%F4HuPTi% z4H#IdY{r_c)D(cT;^(sBCl1aGPgq#_qFIM1zqAw|^-&KiiO_4s0Yf=%1n@WR>Ui_b zIDM^=pRYC5-StUPHd=`6FWNSaBaPjTGEHX-2|G_%s?5BR@;L(fNgvnHAv8;RzjR;{?E32%?T9Ji9p7>_N zFVu{4b-abYCnT4}`XG90(LZ~mkEYC?4fAB*iy!Pg54doW7yVp|4>Lp=R%|J-Zw1dn z_MnI-~q0cI9A+S?|Z=M+4a*uc>Glt1I9ECXH-AAPr-fX@Y$yk^V?=Fm>NS|wy#nlR zK9rT6y2xt*z$PxMCAsuw+ z45aOmpyq&sd>Kd?oW0s#$CcMK$?yLEK&$)+a2|i#Ew$(M(#{=?&Gu9SI(yII!OmK< zOha!%bW;A^Se3ziMDR#fV1YNwFX`3&FXbog&(^{2DElGXCaV%%V7^nfQkhrEAvlal zB0$>p*`j#3kp+3*YbKa7jAD`eo3G!_|AO8buSSkcnz0Rj3`}>^7z6i=`UvG_lN>;y zV#Q7>)@OcjA6xITeT?P%(bPrP-=E<_jBI8FQJ)#O58%5i*+LYByX0Xgn`j4iKs;VP zUMD% zrKCC@SKa>~X!29@#-k^$pK67HpV;}a12{M6`pG{pU+`5|<|qwnHqMRmcuR9!%ev>lP~C%1|S)FJ#%EHgqJV# zUFHpK=Gp0puQ;bK)S*!kSYjDf&m`hoCo6kc`Rs+xVlq2aa`?2%2{Uxu6S}S>20*?dE`tNncAwaY+-7oG9m;E|qM;Em6V~Nc1 zeoa9eHS5YvJce8`UkKAt`H2fVlOP=LrrCPqB9eIc@L$(Ph7abAyt8jy>YEmw0WwhR zNq@OlBpuls9v1Yo3hqhvu(9$8$^F%&B1{GhtVI}p=D721o%Sy0d;u#B_SOIO1Xe-b zM`$Z}K-%+oPSt+KptBCDg}8dkLQ*%F;$-%Q{pOksoCVz{uN0>Maa!w3U+N4w^LF}3 zv_GHftQ<+m(f?LMxWysgC>kgYSN@0~ABS)2FxKp!I+N}0jAQSO zgf{|WLCi4{<@PWy&Y*xh6oU8PF_I_&H{XQtA} z(2ldOnEU=S@wt#@t+U9G-}!f-DW6rrk|*KH^&{%6rxR{#4>vHkdztOjje_(G~ih*L!jA z;p+1Q`g_@7eg8^+rbZ-o^q*eIPhIE_>evu;pcXn!lEuc)ATB3%%Q6dJF*i8kv`$%Y zVl(uc7qRO#vr7K$*R1OWwLlLwE#KPH!A*jd_)o02sgeu-E)AXgP@Ita0>IxRzT2aW zyZZ0W@lihN$+)G~$1`?ThPQR>&&qg+gN&7VX}t!3QmQGFdfFtzCjXU6FEQm(4pG=yw;i38_~+|pd>-+=2vz)c z4fX?cv{g?mxP0h##cwn4h9%$Xq~ij~-=hG=4>I*kg%6u@z(7Q<7c&Mb0*@Vi`_VTJ z{GDBX`4q1wLm>y7G)dK!$?>YT4!xf&IgP~z&Uh+4Le`6FJc#MJ=$XnF@`tUz_ItVF zN-}Uf!664+;6mSCQ0EFKR_f)6A-U>IRucFlqRbGn0;>t&i205w)2<`Msy~ENm-Nh7 zI5AFK$n3VfCs$bVy#_ED4Da8rtvWXw7r1QnWH&CtleW~05Y_iC7vL!C7=}D^0HzX! zxeUotU(NuQJp|(VY5pGG((P7EMKNvnayl9co~85S(#pY>ZN2<&7~7` z{^+Io&lvu0cKeef!YHpsT=_2nojXbkK+>E}Y3b;{m;V^Q)1cSjlBn(1RAJc&DtoVe z@e&5ja@P&}Vo$?O|G=ST-j{3TY5rgWR`bHZnc)zV_$=?T|G?^eQD`AEFL?-HO?#(@ zfPk&bzHQY_d7x*74V@MHl`#-)mjs&VT-|JMfZaT>H#03d;zo#5+}SIA1VGOW%bh~) z&BYOh>*pADt#;z(Q)shvSy}L*w2dpL}@(pH@DMSo;&D46|o3U=1Wd8r7uulr-YoJSZ6 z^@l#Tu)?N*zuL`V@Ewb5uR463rYX<#HnR-C&ajEN_;KV~aVZpj>4bsLw7c93EcrN_ zl*cXQu7B|R-qVL%nfXaU(kjgLzuDh`<`7()iLu@ddk>vi6rQ~Vn2sJYjKJix&CY&>Q))`$7K^rc6(kdcDHl8fxwyD?<2AkBin%! zCjU&ka36bl)(nw6Ut6EPpO64tK%>8R{;mH+4R<*{S&PEv#%@{Z4CCzP^LqlRq^Df> zChpu>X4y+mAs4073(C;`>F;z+pXk3mg^az>n z^296YT)C6Me&4E0yVCDt|2Wg^{NvG2BcP|{9e}^=Y2aRP_eBD}OMDTP0E7@6=y(2Y z=ZiI`itkd~r{I14s>)Qo{>&C_S@BG`2eix7ZSQRuAAs?VyOfWM&+JBW==K}vXoIJCsTv?&`+>p1V-VuF5i=ALz=It;cJh^-4;uhgKpJKr zGNFTqu=FkLfU0Z*QwRSDx1UhxEoJdH(Xcx1(H*hO17<^yEl5Iei} zE46gS(s|$|hI;EsP7(x;I5OGlq9q{@M7BQ8Eqj2;K}g%fLOeC%2j6xqtn5nW!E7s) z%OshyL$?0h9{Y@ssPHP1xL|8sOA{4xztacx2aDxA=3-n)2M#|>ZO3x1v)<`}r{)XE@0&A-QW2w-h#pT#V3de^+$D; zY`R`fVMhF6O*_iny7P;~UJv^>?963KqCI!*Sy~x=aRqa|M+z94%X_yxJ|lCJ61x)+ zR%|9vbJ}W;oFK*%t_Y@HD4>L<>Q4Z8c-f!j1YDTy2~&r{kDzu79_EPy$3 zjq-`Q>*}beu>I|s66vab3%J>! zlu5Vw!$Dm{Q#R|tbtoVrvh#->K0@dW9Y6Mt4@BEj&wz?M(_5c-$dIeq-D3lrh>~-V z(kG?zOINhk{aaY(U!+Btd0EoF=Zw4UC&Banv)T-!eR>FfGy}PaKD;#-c7V>KcSmLz z_s2bp_^M=cI$Bt{4xh1_+Yx4W*MRTdYsOJ_o_*U-I9c)MikTUwITM;j&H_TP9QY}O z&BtDakb(Hkmpb^(lVRITnqp4?`vF}y6hyV75JHfIH5Ujdd*K6)Q(52-!Y6+RT3}J= z40GYQ6Ho-y!RDZPd1o~{k>%0gd;CaL5q$b`%kVr z_ZMptohbC@f$aANQW|E6-ZVFCSH|02RolMxLGcA~SBd@w%5Mp!j@NYk;S#yX`2c{R z>=?$nTNo}EhhJj-(m`@!-9F?L^K9k)C(#`flI~I?#X}js?_2LG{=N*KV`4f)Ga2*` zu7qz|Xg=!a91+h75BgYfy|;ur9QHmfBni*ej{nuGI#dmGs(e#^q|5dLFZQeM2dger zhReE3!w!D?@9Z0Q=nA?g-d>PU;NPi*!-q`Fxe^I8EA|up``Q=lqP?#)Qs-%rU|;5+ zQ<)7-hQ6)4o{##$-|=jF-v`dRuDDlORIzgF57nnURrCH;kMm6N*8=dt4^Op}o6i@= zeIo`3zyJM0B}fPU%NfD|Agbyg9 z9BtUy@gSSH8#MV~p|8qcabaO@;I0{Oswtj`fQPZN!rS=+zx~<3C4Fay0Z4i?K*RGga8?9T0{DVJ7!a9RvjP&<0McLfJ3Rr!LA%%8XzfopI7jrw z=Xw117;Jlq_jK2L6#D~gr*DGo$^2>V{9&lNvVb}l7k%@ddevX%bFgTAkY%Y)y$4a$ z9g);ceDg)a^>k~b->B-7uv}3Ye#Ku1 z(u2K#2*qRu$rCtb@ULFUNnPZ_j{cJ?xpz(I_5FMsI7z=0k8YwM#*>E?SgeZ9`FF&xpfh!LHZx4v=c^`U^_{VOSmK>u(F5acOr z9+(R7!$McmR|qx9hy253DIM28HOx}oziOgXaJaFfsM^STNHfPipY9;xByNi~_-D3Qurw*E696 zz|R*``0v-sn2!>e6?L)V_w!nc9o^zifdK3c7*P@dP(ht8$H^*0d)<8K z_~Zc}sE+L{Jnte3JtDwSF2CKWgbvfw#g0!?rc4I>n=nxw9e}!qm`4{75%8tI)Z`-! zwd$?>cpm42kF>R+6iPTN0qD7s4tL$da3l^|%*@m8Avg_N*IqW{c)dX^fa77x1Rq%* zg))e$1tGzxp63z4wE$4erczM91CrQeR8kS|%x1itnWGk>x(-e{vu2OCP6YWj6MF(K zD{b|e1YsuV{1LeG0*4m=RmbxzVn>1}o)tT5+EJnT=z2K-Ns#oNJ*1tEeJAZkg}-*& z&QUY^UO|`#kooiZ4ZibKfTu(w|LuAMytJP4wPt{=}CjTJn4pJzO* z)X_*R$B1=9@{oy3`Tflo^(oxcLMq(k$>4IQpINi_RsTc0ort>VgNZbpm5Rr~T6`Io zb#c}@KNZG$tuO88Futty83`(?pnO&m;YY6(#|{8c{sdTqGSR;@_hjSxuU+H!PzItH z`{O?6cCUXNy7V-^2aO|s29SndR@5FQR(F!vs`ubT=T5Gl63 zCbS&2sTo*0gsd4y_+6f5-|)QoLCVp{pJVE%)Y)f1@Kkx6B^)`(nj65%^jP3zt=~Y? zg@Pn23%1HA=O6W=vRx^MtBOq^2OB@};EHEE0Q|9Ysmim`tycp-C{V*rc{|(<{8n8b zvZ_cIj8|Z*bX^dkcRK0(OeL%1XovDyJM4?nB%JaWX}=|WH~%f9A0(lM4~7BPNTHbW z^bfi26=1a@yC_AJLCTF4og2A>>@Cn|3HY?m(x&}uGFiXI^7Uxypi5TlU(fI*2Fm#i z39;6Si>d4Wq3(veWNb5?Y=PH1@_BB;1%i^L@Xr%)83(&+MN5<(`X@XJjJKVe(PK3D!V7Hz%q_LD54 zsfPKMJsn(2Jh`g!JWWllllI`+CW%CrpmLmuvU|0TH~s`)X~O{*ROb^Kyhb+igWYSP z6MM}Kg2+=pbAz$ei?1gDrg9iBgC=dj_k%)bSVHxb?3Xs6MyF8nkQej{a~M0 zeV6EvCr>i9V@|~% zNh7PJiB7m?%rH{BH|wdh%wARRnKFnv9-gzJ_LqGH!prbk%btP7pTfqKzY>M8>|_T; zpPc2UjX><+h}*cn_nZmOIQdk8{sbx&V6FAAIOj$p@GESO_{8(A3bn6_x z9%jVXVJ9%#TB^-a7Gre>Uei61>6kM9_m*AA)_wR|_s+dp(@WSX*=ntBA7 zT_4r>A}&rC9H7C7>!1IcUC(Z@PumA%!r2@`(eZCPI$t8$-O>TPy-s>k60vo#*jcd? z*Lq0%KE?eC{OecMv8&xWRdn@OUc27pqdbxW!_JC*#lVKYo#$sK=<^vr-qGYNh5+R7EJX&nUnVCp=FIj!C~kG-Ka@ecZK+Lk}j>p4Rhx z%$(p@i3hWF?5tP+)dKcsSJiRT758-9xvn3eEgS%qXK#EV%IB+*&`=sw+?3-%3e8lCMqbvMG!(!f$aFO^*&D=Uv4jC z7X$g>%`B+kU7xIh>;8y~K;}Z#&_pM^U%iEUn7`w|b^frhKm8OBWcp9lX8O4QI716; z>fPA`+4-@=+F8!V+CFeH0(&ISAPrz+<)3juee*ua1VMiUw?yP}w_y{`9GOpjE)T{d zbkK(#206k4{-_8e@p%Bibbc%XhRo*zJ#a>$oDtB^6>e&UqnOH}G2us_Ez>j`K;E5G{;!pwYaQyYu7htaVG1tY5BM8xemCk%~KO-Ek-J9HR zQ*npv6qs_wCv)W|)7xu@M^5&iDEzmyS!ym>r=rUO)x)*UuY1{jDa09L{cAm-{*vV3Ki;%W#F z-lMW}vSJkvbJOu~oULw89(@jBKdFPY4n{RXV6MQXKiQl&ntppA;b$!aM(e;CWVew-qn$i>hNQy>>6e{LI6*E9dvLwSe;N&`eR17 zr%W$~GA~Txg#Y+U z(MN`lQ@kwVYn^>hS=7LcV8xF9WM#~vc}o_}Nag!%x|=MnT$!Fzvp)!_27JOX7tgjA z;7h`5Sie-#-yxXIqSiS^eA5PQuJ6T-7=`drEwfVSBb-#>muH;6P=D#2%9MV}uP%q- zesi8pyIaD)?Ucb64D>r3{dw94n^=TsVr`B2FmeGHzmb*OH={b_} zaI-?Q{dab7_}^RwRw9D#Tt+*+%et@P) z@Vn4`BZlwp#>^80z9|+i4--!g%1%NGL4UgLfUVyVbO(WWD~_)>iUAk#)F1P~XQ+MA z5SQP`>)P7oB}FrRq|WSwU_vX0RqH;h)_nj42)Tc|IDHV9&CL)$!l?Ja(_=;7*tZ}3 zasz_609PR@i$qi@6tX;5SyuK~=-5@^!o#f@-eBmx51bj#%*7^{_ClFvH!Z)wCt{W zi%=3wB9pBTzfYMPj|ZY$!ot9RJ=NzxrpxeyrMe@{e5%@3YY?RHmUS8!F4WK z*UEoJt$Db!3P>nJ+L2qo!(m^%+YB8rbo%9@Krep?oc&99;x6{g&;J>q%5k9BB=mWU zn}XQ^uKbTCDM;5v1wjB$wUsBc^+<5R-Q*_^Wj$NLQx*8uL)y5T^n^cHiGVrvZC3vd z8MuvM7-YeZ&U$CZuUx{A3VZ5)uP|F(Q)w?9e6HBbeAL;Wugo&Oq*?)&{9({TB!9tk zn?G>CI)6zn)P4T~JaMg2Sk_U6qBl}f^;PDqEBj!claO?})fcl}4TlGbQAx z?Am|C?VVAKXDFg9bn9tmVgG?8OLlLy;Ba!Hdd19~_>D8XG*;UOpHr9ufdfO~-Juj< z1ngDi%3vtTasKAlPNV~jm0TIX&s$~hwpto7p5Jux0V>@E-FM}&9U{-*2v0^*aH^LD zV&X3V90}wH!DX%+KYZ|fIX^_P)>*yTmp!;TBS}NqY*g{RS*N0+G44I^&~BfbNR&T7 z69_!3@a2(h`d=wOYbJ8j9F^3JE{?>f{nFXR0OKT*43Jg0DHHeVb?4mrtMk9bziZk0 zTe08ZBi-COqveMIePIt*-b@V-lu%Wn8<%vQDfzV?{-L{HLGOBrWoO*_yNF%@qQgI- zo)^{d&hh&P7kg}_)aME}nJO=4#^mlcrQmmfFWGnYnQi;Nl<1Se)G+J%B-M9@?Xuve zOz9hg4mKfmK1?TEP|5nUWm$r%KeE9YykaElwbO1rP+xt&R@s%jCgaB{{-zqBVWTKw z>l=p-zVs6Cyt#tj=@hhW8;iaZaGJ1h9K72O{`Kq3JmuGCzGirme-CTxn{d-@{aL*8 z0g@tll1$mbm$x0g0WeHvV(=-Z{ry9Ba81-^{|R0Bcg0=px7A3B-4gBNFFgh9io`ALag|{RL1Vi?`RUtFXto~vW0_Xmpe~V8%TPH&D2gm% z=OUiox4(H|Vghl>T8o_$UdVfUcsQf`%*w0y_$V=9yrP0L{<;B)q5zMdPZuLY_ZtaX z&p>_?S(^ZF=HHt->Wq{W5OKhJ-Vt!8b5DurCco}a(&KauVofGrHZBhq{3?j((1dH+@k$h|Fh% zNKRtdX(?xNT=k6RKmaf^yzD(SLB$9Pmj-kQh-dG%PVY5@+#`5)X-t6pR7zdM;otVw z0}0pc-`j7HZnkIo(a z*rlug!ZcWBHtQv1j&F2rTtG@p zL(WMxR%{k^)xDG4Dw`9WX$}>;C5jf(dw|+!LEf9!6%5^L!~_ldWK}sV_9=)0QT5V% z&~@v(o~>78Sh*h#fd-(BHYzXAdy~$yV2N8o-ym&d8$zU=in(g_W*- zcof%o`weu1Wo{w@gD8AI{XT0@W$2IVz{`CF6n18`_ZEB6GfRN38%q(DU@4|d#-CoR z;Ga(XMm}l+^22k=^vWtxf4l-a93e+Nx;Hzo3+ihcT(1*||dJxawRb5ge2EM`VgR3)t2>9s4d7`^g$7SJ-WD9ceRceP?{N zuDU)}L;W3Sy50AXxB>Gqe;o?xsx@Yl2L2uc{8e8C0L&G_#Z&PuyfXm8X9@9xj{+#8 zzbFTeAHzTs&4lXO0gyy7-~F*hl(8h9_~N}73fd4P&t-e&T=rEgb-8PQ!RAVz0OAP_ zRwEi5E3{P5Y!SIA9lf4-SMG?!4<6!vnuLXw;i8{2+1|HwRiHf;hTQ>AU;0vuOvV$x z{mDR(tL#VQ3!VV*+n$}|uKzld@Ke-f1rYTx->k0C%>SAn2t1*afA1%!x$hdp$4Wn) zpZ*g^frMq+GyB-K9)}w#y z!L<8lR^%Z+QRw(tw;#Rz+k)qg5kD*OiC-$H%UP(wl83;IC^L4}^e>e>Tzj1_fXwp@ z)4d9d5+a+(ehm{jd@Vad6bv2th^g~5bC?r6I9B3e$4-s>cO%*!66b{Rfp49NV7~H) z|B5G$Jm@fF#8Qi`2mzR<#1aw?c-ePK^jZy1h*P8jT{`!S_8u_%qD`TwR{->7#sI<6 z!PVc(PXn;qvy%05zuffHhy7m1Lq_z7#M(S>`@!|_!*WIqFAfr+Lo_x3NUQi0?5HHj7v`#{6tX|J%Fd!%g+uTXsGP}%2$b&pl@#R zB3G)_JGIao_|QwMe;FcX({|$EJywHJYv+&H)-xh0i+`jLg0sKORX%%G`AKjI0I>kz z%A5xDI(q<DrArV z8dR8G+aA`}zmu0bUZ3$p8Ro_8fm!cexwJErfE_-GWnPp6Hd$Pbn7l99o(a40TUX$; zP1#95^(Gcoqd!~z6yY}o?e1#IALECHaH6-49XvWKUnJ1?%K2J$=ytlTD{N!Yp?nk* zzCiXPGa`O#RXtRTOgFM4|5SbCqb8MyoW2b~r$pfBd)@f$f0sTiaK}#=<|K|M*xaMg zD=z|t79kmOrSN$`-SsqJ6D&Pd8b|e2A48ScEuqbvA=4Lqa#OA+Pe}tVXkjyF^At9c#19B5_d-%WV~q1iPwx>?oE_#4?DWo4H1c3?>Xb4N zI&j3PAAGV&RwOp~q~kO3f!sXA6U(Z&MoIH&rjRHa4XYzu^n#e3u{f@oAN8fUyE&1j z_oBHx-{mbPLLTCQ!4s&Mp?n)x|6Y3n;e&^DoN7$5)|kkYZVEunb?4{FHXGbwXa5iL zdrHFzDwz;u{F(cLlbx7}K|WR|8v&_%!6xJOn5^lD4`uKF(SOzwOLrWR+;uvYTd3bl zh$0Sq)sVp2fXp)Dxe-iLg`2SLwO7f>2fHAW7a8ajGaYaHu7_H&v+l~_ zg^#$#wdIsxYzFtE0=|J=F21Mz-w{V5>fdMToWF5+JrC|txuayM{j8m)p}j^#=Rf`A zXS?A=2Yne6hRUCn5eN|kRX-hsqv}dr>nCpSccm}>5Qu93vY;Mui6-cRS%yn*Nd^p_ zBZm|NfN|dfrn=7vzX^t})1UFg)t-6)-$RsM$@^23S;Nacg)PJmFFH7~S4$b#`NOmG zfG56qO8`-Z6tG7myjU6Twr_poDhmZQVYs$#8-Ph6l0DS=cR4pmb31f!!D=zVQ)x{I zp7XSG>&dWkc3)9V?OWxKh`k^15RVS=-fw=Zm|1fBLyH*ilCeT#dY-*8FVAIg&rRc9 zK>dc90Y@u*zFM589f63flqF8Hc^hcNK`W^I?I4Yd~ndn0=1EDJmdv zMP0zOvyiCtZAiikZ{px3?1D?;W#=sHy}#UhsB7h6hw7|VIcK7CP87+i>p^nIz)M~YkdT#ct{x8w&fi@B#9D55>9vE( zQPf^r8C2pfb4b?(CS5qKE~_#VN|tUf$3qgP0O8cCpijM=JtL?5DY7pHeY%M+=6w`S zEq8-|6!X9~H*?C+vQD4Ld|AK^^S+UC!^h7`oM`phdRXu6`2XJP?t1IL)m`oiHwnt% z17MelF5H6+l?lj{1;;LV`mXYMlJK+6c-j7)uQ^Rk;FFWsG<%rPz8AOaAP%0zk9(O9 zmC%jdI=Ca)6z)@;D&p?&JO1nX%!K^&RowSMEkaOVqERPAuC0U1uv);~`3v|+P>=}p z2U$4e$E2iW1x=(@81cbGj4=J$)~yrW5kDjC>Sxw(tZBD9(?}*2KNIZ3!Dq>gBWBuu zM5dW20PQX{Wxf};d5D8&``>>dT_eBOq6&Alsn@<{Mqs;uY_k3iG)?o}0-m}NCji{$ zUR-H;LDm1~ zlxn>kLqi9i4Jb#}J<3NUv_g^_JCv8FW`^TBCzi6R4*=;1mCT@H-5hwK*nZB)wxieI zUrT~(*0xBZ-zj5LEs}}2lf%!`^4R6c7jmHDeehCvWl`2WW1enV1>Tpf9tT^h9;qq? zYo8nP?an?ilEJa3!MagHPXM@U`mE=1S<2{Sb&Y_7kaeV#Sg@P?gn_HWTRbPiuR%9u zgyt-9!jPTAoDNN#6#4IB9GiT|@c5eK__)U%IURVuZU5Q;=%UsLjS%iSv?!~gE#CJ6 zURL)OTVO?3=Qx2!{>IA;6)RqJYOLE>Ih5%7sxSV`3POl7gp#1ExWsV z*V#=z@Zj>GwE9%KGQ1hmg)CU|EIRt%bpeB}$?&vAQsB(zXTYZDgC<%rl69UQ$BSdW zz`?*eF{c~|e&fJCpP1dHyf9~7b>Md7!d2XfW1iH1dY@}O9VGHTiZzVG3?rCBv7y^~ zH;he5%58s3R}3Yxk}q}CO|xgk=MA{HmUdCPLeu7py>Z{E{K-)HH%IshqdsmOo&Ry% zV=GZwA0w!nGI#%d?}_-nmjpMgg-b*A0r4#5CoJ95z<8aBH z1fwDVtbyu8W|zzJ+MDw`mH;qD?eg7t!#z$i=no z6-FWq3B!J*?w>J66DUkH|+Na9D&v-ahor)A&Gej=YVDQJj}b)99I8-paH;x zzE+03%=QCE1lE%Q3gXL4eVJL2JKxq9yl*-(Y z>ObmAokrq zDI$C4=V={T+e1XGJ;y+@A16w6g@t*e!`nQ`oAYNnh1>QjJS%PDEa9x{nm=L5)4ySW zIP153HF=g=TfW(P&mu5%eTGV(kAfme z(pS1BJ~Qq!!`^oOz}Kaf#8poY5+33%`ElH?X9OB>_&Mtg68-9rNbFf=&!dS$A|WdQ z7U8BGTOAM6Wm10m$rEx@mOXi;M_j}+*GS@o5xn04Qb6iSJMv=*g36zKtp^U;GyklR zK-ugCeW}S~72pbBzhm#*>2n8h}D{2N(0BfPrPeS~q!&3nvtaPXgwZDSY9J=0!sB1m*qgMd+kbs0< zzTRZepX{f0?f*Q2d9%9F-vUpR{RSG5wV^*J9o<>l(EAWZC7^a)%&PV>0j1Mg62L=T z@h#l=_p1$}C;%1lOG0(3cx)&X%baer-m^M{9caK%#x zC@lc_N>BmGlLxJy6co`C;Hh~07Xywb>s{QPebVs-x}T=T7is&L%Kq!=D0S0+(rg@h zXK%?v-nt-*GX$V%t|b`lJ*g*-Z7XL*ot%NAsk25jysV4rG9!B5I37DO%UhfUMy{yf zF0c6U33-*zcxi!y9skzvrlyS7SIp4PkZ*vvA`AAY%xsY^ai;brIcd5p!MndCB@c5Y z(V{P2&Ix{gXuQ_5D+-E>6$8YXY4cG)!7{G)4IY3DtaO9{^kCENANu#akm8@A6yO6; zU^=0dj!fKJYY%dujNp!=@&%yPsmD`se1q6TtU4jYn9cq{cWbx|Uqa9v{ljLM1yfsO zI`g+iNKxWqCg`jX5=e%H=j?Dc31_YFG+bx_dQ**tk2w|WB6Q5f<%Z>F01R=ti{nyg z8dm?@D=#~+cv0z#O+4v3>2ZyyD6iY50TG2>4C&ao?Dv}g0(63F{@~r|9!FbucMi@` zmH8OhlL&w78Te$mvSL|GZpH_xVFbg+nYnL@z*slk=^dH@;~%$CS&t})?$h5Tns$9x z(u?m-x}vbSqc`hPK;Sxm>V)N5VX5zheBgp5$GgD~^&J_Y=EYC&>M#4{>=Oq%-E|4W zMxC`EWB2x>zDw(4ex7) z_Quv{x@2GsneM`i0UY`!%y>>lb-4D7dTPw@R{Rpk2M#?~RNNgMo}Iq)XXGUi#$?Jq z*?Yb(MEwM_Dt5}!{-4>#dFcXO0*(1s{|+>Vx{E@}VP=(UM3=p;8PFAR8Mrs)!be=C z7orrrurB>4)Z2=GBKb`YYGO_|upct-9pjeN7d~}n-IPy#mfi%_>&?r1P~+WZB~%N4jlktGE<$C(CtHS&;}aI_!h<7D>+}orGq0I-%0H96 zvoOtXuHlYur`viJc0+Lc<+K710T$96&Q$P$eS`@c2qI1-R#2z+J2WxREXd!%{R)np z#4LNKp@pvw?I90|As1-N=)17iC-oz_k&mTESp&&8r{^_1W`31b^~_GcInT3p@lyin zv7`SHD@9Rk=va4&C+a(=3^N(;D!~8hRr#rD)-vSz0k3j496d|@Yw2|@4@!q_T>TyT z%)l4T`iZ~3v_xdtGqJ+lc-jwTSlUiRTR>`k%garqG4Z6TnRvnh zVA)8tT!@)&u)jK1W zBk2^G=2*KhPo+2f!p3VP$EmvXN*!|;qimNQXXi%%m#w-PL!hS!(wYfmTEk`eQYbwL z=DWbSLzwx^JM&uuh0T1FRO#9LF)Q~Ku!IV6fl^n^oj>B9<#)fmd8MRs-4ygQbI(h5 zFuRX(>|g@YPTa;9tb_=-G?WJHIS=+3Gwa2kj>w`Z$T07C(#=a5iO;34z3hW~UgrVH z3Q@L0TqbCga0Bb2c@q$eWl0EQ?El*8h>cYFe%i@*KprSWZTz;QckM*5Hp0`G8Ba@K za*C+dWMeJb?@zYEVK}Zh=D%Bks_ev;p#^4(g&Cvc=kp`>zCYihpEu4FMAZ~RRLNN? z*eYQDH7sH3+NZD#JXv95O)tTiZ6!bP=kS$}*a(7x^c9zZ@QoB1cv@J+N(WidyElLXZJ|%L=nKd z`|XWH*J}>iDhFCVDBW&;*DVfv;3h4eW@;&Mz=5WI=|2OhiZjgKb12Sx z)0v|+l8H3rXdk<=?E;whs_$2S1KkaMQlj6zE8C7vnmhc)f&XuPV*(6~p`yPL@|&HWvqXtP?LAFZ|eu$N#hIu149Z z>np$-QHy?GL{Tq?8X)0OroX?cs-_l=vsPL0KKjE`NwVpFn zZtVQPUjM&io@n5J=vb`@@5zcE^+%iWE_k@=nNNKXp7!x1G(!hvur#IVrknt!%9nBp z+<@v61pTF6ZIUDXHIKwW37)5M^|;V*PGXJ9UCJn|{VWlJVK=9{vR=G5NGt~Kyx zbbG|gBMyH#U-&VBYF(Kkx7OXKYmq1d4f*f_!UB5w*l{Sl*y|9kh|Y1I)|Bks$UV;99Uh81TQ|v-j8z0U)}u(daYRh*Pm=nQoPnH* z$^MUdph-6sl)d~jp0ILu{R#8s3@Pd~SBlzzL~yqHzJ~(}@}|ygDt8GESm+$}njl4u z6dq@9?C(P|H(GGUlgrnvOS0TrAIa(;yXe$^;G@RcTLVR!JWB%G3x8;C2{CKB?iMV{ zYOllr_j7FK@LI0J@#xwM05IniZ3Tvfi67VG#AV{F%JPQ9fO}v$d+ySk@g6X19ySl-&5U@6 zc!*Opj!GhuF#5aY;F@HuG&%gECPg21RpP}bugeD zW!a;5t!_Vf%wqqDBVN`Gioo`&{>mN>KG)0d>=6@jAtz0FIY(tkK(c0j)7(IJDkKZ= zn}+q6YUQWCsyKRA73@u6qqJ;13s8#8SyVZh{@iLT6uzd}<2txn1RJi{A8+uWv+9UF z>}ow7hkbw%)kD^NK&ukGIo`W_55eShUVHP|)6G~jKV?MtsYPXBV?Xw_N1v6Zcrz~R zVuIq-tN)FpQB#S+C{TZrFy&|YyM6LU0e|n`Kr@&*3v61q9erXBS%BP`3O)e;DK!Cx z$-K((E5fWoGCPT^qNXgJ4kYWf^{{^S{XG?YmiwO+_f1pZ2Y?Y_XFF!PDGR@KIJWOC z+>CjNJd@MhOA`G9Naw7+za6n_2lVGHS((8|)@xU`^+0|1J5|mKI72^E!FPA`AwF9C za?22XDEmJY%rMm+rySdVbLvgkEcRTs-soo%Y;)p|Wbt5!bMMO5!Tp)*tai#zss+l( zfp0rH{`ao%em4HSc@7-ONRAU)C31`a05kGQL_t(L`oDeEJebj*&DK9QtIPifn&%Qb z9Xj+piQx$XPYkG&pXj%pZw|6wIFlTEw06b85jcCQ=0Xpmo=6=;Ls@wEG~{5RtLoM~ z3hEpb50XCXiAf6B!9!8@a*%7So^sSKcdBJ|jLpdn_Lh!#Y9c#G>mVPcu%6jQe>fhP z2A!dJT4s~*G@Xwe;CYIJy$U7QTES18%3+s6CMXKkX;B%<=@uiAMQ6nE_NFslXT;1`#x-zMc&Xz)KOT5@ zYW&z2Zn^9HRsVrMVeG2SFX>{5Jt31;vJsE&7%4RDdoSHR9_aT9+0*8^Os+kV-8lF= zHux)PQbjm5cLi=2U*bHu8$~K3tBFBU!ri_UOyT(oTb-Dul4Sq1#{xFN(JYJ>lSRWF z9)i<(Xn!TqUdc3Wv%Xuh~fu`VXyTaP)cH2biamf|)oIy0R@-|3e+Htt# zk*3w}F}?;ZDD^KqIQ8$0_7;4Zmf<^7;4RFt<5*W}T7)~)6!vRm z%=p6+@j*qF6VQc*G!l6ZV$F`rOqNTIoSw;U8yJ$c-JGWEx(`leaDzt$)sH zPjZXu9_hLD7BI6rgO!{H2k2bOKCTeQ4Gj1Q_b9!U8G9P2r-311CqH4}Mn<1$*mF{q z%&Y`Xo4^p@4}!Aq{u>q&ds20J{DW(cz@4x32FRq(B6pct9_}TYyZeV-DY2`biI)Q2 zYjsi&NSvZAK5?N%&T>d#ZV9-X3-s()9V`{47l71*){H(toC8>K_`<-vsf+$S(m%*Y z4>H>8{Mo^HoXU7l>Hv3u;SvCj)sb8a9*`Daf&p^&9A#?rtmf?MDaBke=EFhyfFNoXj@<3#C0g zMsR1x|FxAJVOO-Wonqd8+3@3&O|Q*C<=6xD(H);zCZpK!O*=B#{;liWr7z5Y5zkGW z)W}K!Ml15POV;a;+M++In3&HW_s)#iZ1nFbl%oQ>Sf>qn+EoGeDNoFnXVp^{?t5NT zLS!6Vd zHQdBmusa|bs1p0h5q*KKRM}rYp~&C_UY@d5URTU|Oelau7}ayWYVQPh78@1+!S%EVbPx&+CH+ZXSr(0EIA4e6 z*tEb0Sh`%c#)IPoP@g&6VtVzS2gUVDgisxjAy#ucBFMU2*X<2EQBCI-{{D5<;COQ- zX}}3mNCS>K+w8o89VP}&|Eb5lAAW*NPt))O?|2U?LsX(b^WT8r%<-FjI2&JqRkI6~ zI|NWwRMy~LE^a_(bi3(oLJ6nWWu{|(^yNX2oMlN9w;=kXYu9Ca^PKkP_SBU}_Ud1< z{c(`XLQ8O~*-yRBM4$vLNdor{uGw-Qzj?73=T$8c&pip9xK4lOIg$>g^M{eB?4ppx zo0ibIo-E1(ZCV~^RY6n&b16y}sGoDzUI;ACM9)M~t`Eh69PM^fA|wP6QUi7e%c0ky zglWBV)()(3MFk?wF06UCp*s)!!W|4pmOz2UMFz8nh{JT{%BT#B*?T#OTYSvHy$}|Q zS!G)>!a|HM^@>d1KEqj;V(mpH!o&J7lex&xiO4Mc=8#~m)J0z;N{T*P83Yb>0p%gKc3~}02pZm-%7dXkGNAHy_Bv~XMV$bedZQ|swL@#p3!9u!6uT9vGpET`BOWW!G`F7L&*@XgZ6o4BcXdtkBW1b zC@ifaYX%f75xEcsYEH$+E9a7DpEOktFdV6q2{(hg*a3JN^YY!`tlrLAME2*`*#maY zF0`!u12mRDX=N&BPn^l#C;XqTU5P}ZKQk%vr-}c8fy{)VqyKEH$~r4Res2|frxiyh z)AwG!|8QTO06WMoJmGtG0JeYA%}w5^r*Fson9dv?6-NOKf zil6dl`TmPr6JXPpxl|2ZufidugD-LQ?GAs0{rCRg1nwuGvoa4D^5ck&7K#W?opk*H zi(lBk|BJ5F$?SOQpXhQAX;a@(ZX7yzc()z@OIDJRoFv%M|Lv>h!HixroB9EopR{>a zz~L=>et1v@IQ-OEb`F0B#BTCoH1^h?b(lR`g~2Lt19mDJBS}A0^&pR#;W%nA1s%Wj z<|ccK>@#P8yQ&Iu;nF`JvI!#<=E6f`86$V+q{IEBuKwNy+!5FhN4#&EC)(IBS==?h z$PDXT1L#)ysAdYX64U(k{?Gq~MS+Te zfVm`IOWjL?D)Cw@(C~hGgfjSecDd*_>bBEmU_FI~)q`NtV$ zE?Gl`sn*P2wyew?ouVbdT-$TGaed3YUcnxrkSnnq2vcY!E# z1t0{Y@0*$6cP|&WS-n1U;*WiXLu)t>dxOny43KzObev&Tx8F0wma)_&Tt;{e^e-5+ zE?cA0wf9A9HEUfo(Gntbl307okTA=a2DMqO)*$Mve-(q|>`mJVO|^R8R{j}DFk ziZ87}Yx$hUShBQW#XjbGDJKo^?S?D65!*lASvWl1u)9u-731vohCTE?@688-^{h*j zjvZ!4+S|d&06ZYs4dYk?dgenPgcjpC&m{t0!+OGVCWf;O;4Ea$AplFIAcH z^hB~Vitrq*n3QGx@BhaAn> zCa&P_x2d|pVm(~S$rFb^>;VY|)#cKt9X~AYulx}g?@^raRj(=d#7tcZ$L>)mp3JH( zJq*PcDeC;3BNAYfuqJla5re)u(@S`(@gjL82tRlBqw;(6 zU2%7ruE>9fWzORVI#xOk&P@GL`3FipQ_KQS&wtv_PGBD)j?=R$*g@eR$%@DRjO%;- zGwYPUH_MN+(aPNh;opUUFMkOe`JevZ1l~CTouSLYGTS^`Q$A^D{eMIeFYWMSf1l5^E2qiRXNmK20fh!W8~UTE9}XLyA74a@Fa|2m zJtt%gLkxp6tKfpT$P4-rxMvRv1NosAT^?6#K&7af_;?Rn2Ikb8ue z!~Q{MGaa6JwS=BET7Gn20nb_zF(??LkwpOTo_}Ntd*_&X$UPbA_dF0DNry7$X6EAZ za%=(_U-#P#g;u#j?%D=>h&hY;`)vScI&27jOjr|~<2e8zFGyB-$ipOv0C z5j)J@B4w}T>T}CkVD`mL9Z_hV87QfQHDIRs*?E*ZBa?UM;d3!3(qq5m51!lJg;yC` zxrtGYDjvD?K+#x?@LO z!IbwNGAd=tl1Nu|;UqCw>;bGgIVX@-UEqkXHHe`H5ByS(J5)3UDAMFqIVbpm!CycU zSTw%cljI7WJdwuB-T|DW0d1Ta-5x;_Im3=|U6NTHXK%@0wWE){B~~lAh_iU-u^?YT zO5v*D6jv%h8A`&$TVA(}J|o2y=R_2A%cA<78;1iK=c z;#lP%Utr4eqyJ57xyx|lIulbpl44Z)baOFt`Pbg{}VBz7zxBzlM4mek0(jCjdrvIQ4TN-%k<7{yG zY8=qGj`PePrP@hJEFp!u?Hy$BGLvpS{Md`H!prH)`E{xqZxO%HVi zlw;Wmto&i<{L=f%itEy*UijdHhBVBHY!BDT0igxDxpXFPRmdTKloSq=>pqx8_>!{n z&NR})HNk)gvIq}CS4_zDUn?E;iL)lOlgWAeGEV!a&wAz%-grV=!3>uCjxvUa;1&D- z+dCKSIF^)IuQ}^~|7Sh~!3Sh^x8<8I+jcLzGJzlh$m*6<8t=Ky%7F?B&Yqq-Z2;}c z4K}AK@8gFB zRz=wp>x|mm@v!3$=UYbinSSEQ;Cx@<{xSX5U7;w5`@T^X)&%(uXzZIE0HBk}yzBlEqNF_ABHvD$y?w#Jc1QOeEZ%)8j z-}GrzVM%fG*V~C;>0Bo2=wNrh)evXGz1=jqiR}D<4I=?^g1DFk8Ignw$=8j2KAU0o z{_LS1$%v2dbtEsW@fBQpltq5Omz_Xj!0h1sZhL^GR87GLo@O16+R<^PQ?kl zSVJIFz{Q%|a6-kJ3}FC;(Kg-#9T5DW_d0VlEc&i~IP-^DUGz6QVf?JE>VZaCip1Z; zE_Za^pFKH+VWF<(dvi0nf`0AaK)=~c0p6VQjnfd6q`wSg?+gELc`^a!^(6EGE>2ET zd;dzBYWL}~C-y4$=lq*>?|+W%&&&G7nJ8TcsMp@x^LRy$Ll=TQvB#`0rt21Fj`;(=5@L59C^)yH&iMflO*@7eTK z9=fdLp~Il6tJ3HGiFz`y)e8xK6>#Y12o1=2#x_3}^580QA6Ma1D+P+>EihSk z`01}y?9UV>k%&09L+OntUyOyXg|lV$K0~;praTkQN`Cab$Ko)#_poQEMmAoh z#0#Z4`U^3E#GfqHXV;XFA`N7MLm^8&GYh%&T?zMa$0@KrZ|<`(YlAefNR_rCv0v{! zS9@kiCy_;=G;a$Rd>Jo4`kdR?({6I+*+*`Y#l0Q*W>j>&N*6xP4J_m5>(|y33pR|L zKXaVV9wACBR`bV|c|lOZ@WR()EF4VWlAYY@*=87JpAr!*eAl%^G87kdm=AjR_XIqv z=;0Y<42M5Gg@4!HU^a#IVaC*lAlKO%S1cWV9dFy=#}w~ha6tos!A{(Gz2-R=GN=ASp4DiF)hF@3-0 ze*t-)2KF1ZYji;O{#DvD?2n9pWB2}*-n;ofRs7w$dSBwto!Niq*4uO1`}aMa4`Zh# z4utjg-*y*BpdJ4wcoM5Cx#NM{Ch9^ zk9;wV-_;y^pE5i|DdKlE^{4P?KFo$$y>D;v1N14#PyWdA6FBAcPv(_}zu%3}4S{!^ z=!^~XetVP>&j=0|PlF$y=A11YN!F%FLfDQTS0Fg=Cl%es1F%TTPnh}jqmBEC2|}^* zbOqO`t*0r9@B|T5tVmN_g=llTzaigagxUEa+Wd-yL38L7>D)U`sI=8Oo=SwL-8d>9 zTI~r9?fH$f45blQ3Ar!-FlZN(O@DwA$J0&|h$qRk9ees$28tE z|9kfE^*z$vP(*Q&Sk1x*irQ~K_YXSV2#Lkl`S@$4ldcZ|p2~u+`dX*+&;HSIIARa_ zI6xRfABh)b)seEKq`!q{Bfk=b+jC6+z~;T@zz!=fWkT0mrK1_|e#e2^jzC|Bj5?NXWoe9dIMbhOgS~bMS@{yrj9i_| zyOTMxnrbH(xzpw*4flx}#{2L%I~PHo6pP`Q$JLBX;{BOGF*nwH4J$#?;h9Q{rvaM} z)XXlUXVW{O_oHvuL;rW%-Htb>1n)J)h>#$geQ60*1$0LAmzmY4;+aKiteaV#6B6s* zZ^gY&bx6`!m(qoY`K*uE1al8wlW!vE!4YVGZlt0OYT9~F9`kwBHzRzu=IXf=Z!X~} za7hf9-Gd11efNWm)!fs+L+hO~SZLA{M%v;$0@3=}dxxuyA)MXCa8*@_C+J8%LIFQ2 zco-O`A_|H+7?yuzx{uyCm$LI?0|JHK?pF1C^>!dg>)uz2(#P(i3{Cf{Z>*kXE zW3SClrhNZi&nLia_NhHbe$%_tzqQ|ozV{;ErLg~|=tl;>TtOe%{10kHK~8J2(C7?O>ESju&A-M6&&K;b&-k(Vgh!E@X)s#SAhx=$%kH;Z5MGT z+NMreN54EpFtejTh_KzAygY@~zZp!_;;=o~+yw<}j!1hZt@KnW+$g$6U(L|Jai)MQqg$u(@Hc0A1T=Yl^#RtRWsS~l~kxeC~n5wFjUW?7{xp>2XINM}VnW~9M2C10}z2@h6gfb&>X3FT=PS$xIC^CVu>o%yqw%^=UWAv{W zQoYB~!e$tC2jHb+Y#6^i{`Iw^D=6U?UrhP>2mjpc4+U47w;%qhzY4xllrw%h!^6se zuoo`2>Ageg4`=-0{EvRf0xe26s+C1zx54-30PI!b)pImn>GC{hF5Du zG8oKBZU$e&#fEyS&r0HBdU-n`MQjCeZsNjcT=B6qL1ekd%JkA)id7_Wd~I@$Vnj0~ zFBz-ek%zYbI|~)S-*pX*cZE})UiV1vdZ-rv^lv(J_`2u%qhbnqDA*I|`;xwxTHD31 zCFzPVhaqR!x#v#e)6{q&`bEzDImpe?wJIYF>?E*pxQf`N^10h0kzYC8du1=kP%me~ zmR&k32z^J(iiH%ihBtQM%y_|T#N~{5)wyJP%Uvvo)0y)?xF3R?H3($s0#1>tMLm*Z13gUlCSp4`IHw0?Iu5^kpGbAJu~c|Yk}iDWF<_c+rv|fcsi6pY~u4RfD;B+ z71a~O<)s3wFxIx0z?(95K8Ff5n1h6x`>G}`-@D~2AYixRe_?7Ki5*4IIfu#no zDnU3$T|=H>`mHlIIOjfI(Wv@(8hHT01XuM>-@x#{vU`40bwauK$m@Y#6R7IC8P1(N z^uf(BF(o7Eq;aLwcgWR0^-(`h74QC`F>jn-3ZW);M{d5csgKI&j5YijihESFDgb)< zLoma6k*z;*3A>+iSHglM&^Gs6xQ>^2jYz(&xpCzO%bZR)8dYoLpYb8Y?-tZV^#kNS zfH`m@l3=E5c-TcudfUZI{qEIZ?lstI95WY42()d|(Yp8w=jmpPbT@0HWIPVNN{^W5 zL7r=^lyHtshVhE{_8xnYnm4Yk6zR!OaQy1W%$`=i_o~shldEYXsp^@5L{P6F%xlHd ziVG&Zs2W}<-fJRo0DbcY$*p(W=y86fbC(N@`vIG|I6W-tF2F4}Hb5G}j>K?b5ly;q zrhPagvix0-5~3JG>%G_ZBR|NAq~dYO6F;5%$=i+`MT8+9Q>XN*05pM!#NG~IG-`#VPt(5y~ zl*FMjvn}x1Z(<6CPGzZ|j3`;Jd*GMNZ=wxe*D84K7YL2voPA?Tv+BtuE|q`khrjjB zL;qFv6i{*rUv&9{M8r}rwc;!iD^R7UKlWw!8PDbdj%=nT)k%gWr0L@xEhjc3c)ZsP zWR}!ryt64b`wDnw5DKfL&bq--)0!>$NdYe6w&5$gb*j5ej!XJ^FP8LL=)WgYJa6?+ zUNay1(R%?rF9lW526+z;B{APKUwlT~@yY95Zpi=I{|1_QRCBb@b9&`(AR7i;Lr&h> zHyPM}t0%zlKW84CnACSA>^VKHdk-GpGoBq2{`+1i3%yxo!>Ikg*Es>^rWxslZR_QA zkC>EA!CnN6A0N3|*$cOMh{^x&^&go42S~a5!14XQ6}4OCt=o+X$d|qM&unsjZI#Ni zYM&|I75$p>r)Ixu1^+#gE|x$${%u}PU=Q=z{$=7{8qkcmzx+eUSQF83@N0i&$>Y~O zadNZ~`HV=PvzbzH7Sre>E@i=jkm9qdwXe8N0CQj#>#+Eim!a1p8ijnqt3_O#}_J zjK6HS9Y21TSW-)&ti^uBs6nhj=o0%h95TZ%7?9I!0cJguIt+` zj{4!>NtBBFKNa1#)n-1{!v5d?{$Gi^si_USJX3)2*kl`CRw@_HOCJvi@E6bMAL(n5 z)Kf&oLqE_rPi?HPQ_w5UruE72N*?dMJT^;Ou$L$;d80x(eW~Ri2(A+l>1fKFX zkhnu>PqGUJ91R_KM~5FAs2Tt@0ry(>xhr3V7hMX)r8sj|v7b9USh|?n^uS9gb0dRr zy2Ci^N1hB9ruFHisNrannGpd0LW{HS;vlNkHFWcJzE@G%n0-0Tf}d36EN{6wGdG;a z;Gk2O(N=2DP-x-!Z)W6P&R4G{DE6{(bB7)099;9>+XPEE85j6$!$0>%Cuk#l9nO7*9z(JPkX5YS zb5Dro96({SfC#l|2TZ)zxi{QEafy+9xz@}1ulvcg1jP;`Z0K6QIO+cu9DOpK@bq3% zlLhntN;3@7B4kZFM&A>p{|CK*gJl)N-mkHwo?`vk6NIVQ0tb4Gj=MfzL#GNl&sq9a z!4#Za+&?ox~5$8??D^R3O`*e#_?vZ!)qsdmS2@lL4z z6CXPM6HzpKI!A_k6UIWI`JiNWP6|u| z8~d$Nz=I?2_V-`~diczhJ&+AMhW1!LGEu|3R8R4$dq% zk>A`Pw34~^9JE&UlK-B)a3A_&PKe$%{(tN13H+X!>GseIHf(zzui(hx_L84mFvNs? zwO8!-yr|&)m3~jXKgjZIxSL`RUH;n!dwdfn9KZtHI^_LZHLNv-ydb@kpe%~y6 z|Lq}c%^{U&nLM1L6#fl#=hYV-o*?kVq4TaUN;gitmRkBr&J(CGO3YOC zL{)hdk;O$m6s+mjMGk@W)Y3uMIY9hE=c_{92kvqp-A^`Jydoshe_dBD zbGvHpZEup`mbRRd0~dc5qXn;PmwW+v`T~4<$K%!d^Td(7!51<72re3Rl;I$wJK6no zReNDF=+n!1_;SKwq&MQ`+4gWM2rg6eVnynMk2zisY-%RkEIebg_11JQq0-adcDmreeqf3VA_cs~qg9vzF5 zOH6%ML>z49#(kCHxNycfBRNY9KP7o3FSFB?@F2%i=_bM;2wD3b=<^_+Zsvlsk|K7{ zg@L;KS$C(9Lq@Hi5j{I_T+C`Un zeYog9_DRiIYxQgDFtFjSxo|HW?npXop?l9sF{j(FThn;g#93Ec^B#YNL;PTAXV92; zJXSl$r#nPA-BVRfzGtFFfb|}Nt^GrsyIT)@bsZS=(Sg87cr7P=#UoZkg(^R}TFuwM z4&lcUc95jkTLR?|XZo#~r*z{qr^$-lX*u5j-g^TVW8jn18tzF5{ITsCi=pOUsDPGc zEmOndOu7arHD9A2rs26dIC_~GI$2*{&U}@-Uk%Aks2pG4G_y)DacEXXCsw@dxfdFc za`>U6_rCY;{3`M&m-#`>n%FSXj*R$|+Z9J27rfX`2%_^F&8z>2Xa6zZT-SWcFKu!6 z_W|0+-#&Y6xpMCcknR79cHxW>*e4+w`KUMdX;&xrj1<$Wfv1^e+m+Y;Uy(LlC6ZtM z>Cb_-Q$Kt~>~~#U$gGNy`G9-3{d|AGfWWpN5sG2x_(!Zg433J; z+02=N&$Q}Y>3wHM?_@7pL*hy-lGZj2>7_N7N1U1Qn-4W{XurAkJ|Z<|?nRXFW5e+ZH~M5)=WF~1nx2^p zHNLgJnI{0GfFZqY{D14pJeb$V%;7#E)LB)9_Ebkz+ur-{i9f@W_x4IQt1|ZfZHB)C zanIzo-?>d{^zJzWY@%Gnj`nw|l|JK(N*!R=kzs~rxBbtG3DE(y~?6K!o)GUZ< z)e8n&-@XpxsC_%*t3>Hm^*o!vZ>n<-a}R6FONtnNR{X!IoL{5)QpNmQD}QMA`~Y2N z#ZTb;rmud0pHF~t+(*k3hrTll0FL`}=13$w6>Lm~;t5qc9&z@>l@KfB5CwrlRHvt~ z3f>9hqJMss^?-cQKTp^CvD*D4>w$=3N4w`x^*d!H_LfZn`x}7DAe<6mq|H#4Zfy-` zMsXa6ejiUi&AAyGU%GwIz<)a+2>h;U?}2bu#P}tYq~xP^8G0_Jcs?IXZb$Rkd)naB z{vIqd{CqD{gXpm0;G1#Ij8pH}qO*N_e zw>M6TYr#<)u&y2)1_ z{u>w>6Hz4HxXtsbR_>*aizHt6(R20{U7y)iL5itPBl?P6@#?O_Z2B{%^}QK?6J*MR z^`LLZe1<&~Qqso3m#8pujmzPu*t>#PxH>z*;mK6+={zD84nX|LEW+f3H$#c^#WZza ztzd3-3BcTSX`J<3R_84ZZC5GFD$p5BC$r~66Nrf@wD0K1sbabLTG1NEXK$f1(gEYR z4vn=Q?)3t?uCC5r%7ZM|zyQ1B&-_&Yzv%?h9J5&}fy%s^Ma-l=0lrInmn9l$>f_98 zX5!;bqQ-kN`h3nBt8RIn=koeD))lumu9?(iVcNm@w;s4k1lRfHhaNa9I6eyt-uiET z_HduH{=Kn%f1S#yrfgUQ!CxQ)T+w9-ErS|E*+Qa|ztC&@h{-VMj z`{&vJGnxBPR5UgXeD5f}7YFYjv8rhX#sIj7Xb?a4zjeN#Xcj*{pFQzsdnQawHhZb* zuWa_Q{h}`_Re9RK(+e4fbj#qUgxkLH;BNoB*oaXSBKWcYt)779!mK`aM*aT-jXuvQ zrW^_?4}tsactpUD^koAdUOW)Q(}I%VVbG1hM#7nRnO%@jD%9cN`O4^b@Yvi>LYkdE zs;`HC1CA)gUHw~R)SEX0F1S;wqQ+D|j)yk`ayG&&ca7ANQLqogDK*%LFZ|_VMx9u_ zBiDUo-cDvencjIWOR+n{CkgE}cdsW``UFZ`JevzgvL;N?vU68YZUs(%E^#p~Nzzq$ zhSk$7J=K!GoQW%*0~d&Ip2t?(Tp~Q}nQPToZxpJ{5|nl6@f2~+!@Rr~XqP-H;x5JR zau8&NfH_|M+dWSKyAKL39{jk`&v0C@^8lowz&WDwT?GVjb4VoqbngY_9!cDyJ?z4E zy9C>z%FVA`yDN|P3v+%*Ph8a+$xbV~V&bIsjHKO@NmE?ANv^1^As zyKLJPvwq2F;eBXVa2PoES_N)LH(&AAc7Tp<7&4&JUZ6S0aK<0(1GjpCZ}L!BdYKq$LRsj`lVMq9{0M3(h)2T2yndMYroylVNqq^ zJ0oKpD?oVqUaw5!q%UN!K$D619n%$_sW5@-VuGOw;xLll+euA3f*B8kIBY8~T~S;> zR0IeawGZ%%SDsOFTZ^2`XbnD#hUeCTM>5PC^;3E{><<*c70T`E$@a;#XTt1#Mu9F) z*OJv;$z?LHK3pea4(}>Xsg;NK1&xj`UuTQl;{?0da%hRn_`_V22QpBR*0ZJ_FxXuI z0VjJR_;kMB_oQ+FA@I;xiHBcRH$&F49fb>oaWIqN-0>QR-EX$mhp$$V#*6pt)M@{n zMt_d#YJ#GfKFSX-=MTWuN3ESGPZ(`mfjGL?i5rxeP6U_zhb#4iy7i*~-;+I|2-Yn5 z;e#SwQEC(-_TG(c-Lq`Ct>qr#-k7B11chAJc;Orm1w^`vq=^em(>)R2eiaYnq#l3b zqtKLCINQax^qIqGk7u;4MqiKp7qW`Fadg##JM>#+6(=Hr3`B2u&+Z>_rXsJ1N51su zo(AE-*Z2+e-6J*L>nmI}*>?(dJ4D2wZ_j2EAHLqjes}@@AwgzE$s8*7 z&#gBrEU*)2-En*y%O2dvtx{G+`b!mi|8bRkh|+Fx3qCuP9VZVT-?3x=)hqXqhHww+ zuO7_RJagLbFZOTGjayWtdw+>^?8cxjJZh-l9AC?RR{3wNkU?#kXY?DVIQBE_r}-#~ p6aKV9zL+qFtNAdSz5lp!|347!#OVW&8uS1F002ovPDHLkV1mA-iRJ(R literal 0 HcmV?d00001 From 7d8642c9fbf7c249e08177efba2babd71d4e6ae6 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 10:00:38 +0100 Subject: [PATCH 16/23] Fixed typo in banner url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c1403b4..7d2512e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Logo](httpsgithub.com/pusher/blob/master/pusher.png) +![Logo](https://github.com/HackHerz/pusher/blob/master/pusher.png) # pusher Send push-notifications to your phone from the command line. Uses [PushNotifier](http://pushnotifier.de/) to receive notifications. From 0eca806d46ca254b4d8ad9fae94b250964947d72 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 11:42:17 +0100 Subject: [PATCH 17/23] 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 - From 9d58fbece61429a8f4dcbae649764c04ebf8808b Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 11:43:49 +0100 Subject: [PATCH 18/23] Updated README to recursive cloning --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d2512e..1567e94 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Send push-notifications to your phone from the command line. Uses [PushNotifier] Make sure you have **libcurl4-openssl-dev** installed. ```bash -$ git clone https://github.com/HackHerz/pusher.git +$ git clone --recursive https://github.com/HackHerz/pusher.git $ cd pusher/ $ make $ make install From 5b6d35a58394bec517e79180b291fe920a141ca1 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 11:46:59 +0100 Subject: [PATCH 19/23] Changed version to 0.4 --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 23c4176..2ad18fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,7 +53,7 @@ int main(int argc, char **argv) try { - TCLAP::CmdLine cmd("Push notifications to your phone easily.", ' ', "0.3"); + TCLAP::CmdLine cmd("Push notifications to your phone easily.", ' ', "0.4"); // Values TCLAP::ValueArg idArg("i","id","ID of the device.",false,"0","string"); From 20a492cfe94161fda7692801f64176d45b6afa0b Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 12:08:33 +0100 Subject: [PATCH 20/23] SDK Submodule updated --- src/pushnotifier-sdk-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pushnotifier-sdk-cpp b/src/pushnotifier-sdk-cpp index 1503203..b1250df 160000 --- a/src/pushnotifier-sdk-cpp +++ b/src/pushnotifier-sdk-cpp @@ -1 +1 @@ -Subproject commit 15032038ebc9df984f650181c176b1694aa3f33e +Subproject commit b1250dfd76f161916745ecbcc82f9b52d3418345 From f455814c6a564085b5b9d872394e0c0ac4cc5e04 Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 12:36:41 +0100 Subject: [PATCH 21/23] Update LICENSE --- LICENSE | 675 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 668 insertions(+), 7 deletions(-) diff --git a/LICENSE b/LICENSE index 985eba7..94a9ed0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,13 +1,674 @@ -Copyright (c) 2014 HackHerz + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. - 1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Preamble - 2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. - 3) If you modify the source in any way, you have to remove the authors PushNotifier API-token and replace it with your own. It is the same for the package-name. + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. - 4) You have to make sure that the modified program doesn't break the PushNotifer API rules. + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. -THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS β€œAS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From d0a1fa6029c8bf77fe9768f381e254493631f36e Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Wed, 10 Jan 2018 13:34:44 +0100 Subject: [PATCH 22/23] Fail2ban example added --- examples/README.md | 1 + examples/fail2ban-notification.md | 17 +++++++++++ examples/pusher.conf | 51 +++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 examples/fail2ban-notification.md create mode 100644 examples/pusher.conf diff --git a/examples/README.md b/examples/README.md index 9ae1197..525582a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,3 +6,4 @@ This is a collection of examples how you can user pusher. Feel free to contact m ## Server - [Notification on SSH login](https://github.com/HackHerz/pusher/blob/master/examples/ssh-notification.md) +- [Notification for fail2ban](https://github.com/HackHerz/pusher/blob/master/examples/fail2ban-notification.md) diff --git a/examples/fail2ban-notification.md b/examples/fail2ban-notification.md new file mode 100644 index 0000000..372bc81 --- /dev/null +++ b/examples/fail2ban-notification.md @@ -0,0 +1,17 @@ +# Notification for fail2ban + +Copy [pusher.conf](https://github.com/HackHerz/pusher/blob/master/examples/pusher.conf) to **/etc/fail2ban/action.d/** and insert this snippet in your *jail.local*. + +``` +[ssh] + +enabled = true +port = ssh +filter = sshd +logpath = /var/log/auth.log +maxretry = 6 +action = pusher[name=ssh, dest=ONQ] +``` + + +Example is for SSH and change ONQ to your own Device-ID. diff --git a/examples/pusher.conf b/examples/pusher.conf new file mode 100644 index 0000000..9ff8e68 --- /dev/null +++ b/examples/pusher.conf @@ -0,0 +1,51 @@ +# Fail2Ban configuration file +# +# author: hackherz +# + +[Definition] + +# Option: actionstart +# Notes.: command executed once at the start of Fail2Ban. +# Values: CMD +# +actionstart = /usr/local/bin/pusher -i "[Fail2Ban] : started on `uname -n`" + +# Option: actionstop +# Notes.: command executed once at the end of Fail2Ban +# Values: CMD +# +actionstop = /usr/local/bin/pusher -i "[Fail2Ban] : stopped on `uname -n`" + +# Option: actioncheck +# Notes.: command executed once before each actionban command +# Values: CMD +# +actioncheck = + +# Option: actionban +# Notes.: command executed when banning an IP. Take care that the +# command is executed with Fail2Ban user rights. +# Tags: See jail.conf(5) man page +# Values: CMD +# +actionban = /usr/local/bin/pusher -i "[Fail2Ban] : banned from `uname -n`" + +# Option: actionunban +# Notes.: command executed when unbanning an IP. Take care that the +# command is executed with Fail2Ban user rights. +# Tags: See jail.conf(5) man page +# Values: CMD +# +actionunban = + +[Init] + +# Default name of the chain +# +name = default + +# Destination/Addressee of the mail +# +dest = default # change this to your default device id + From d2fdc88270948a3fed464e9ff97ef9638af9e34f Mon Sep 17 00:00:00 2001 From: Daniel Stein Date: Sat, 17 Feb 2018 14:49:15 +0100 Subject: [PATCH 23/23] Added acknowledgement for contributors to README --- .all-contributorsrc | 39 +++++++++++++++++++++++++++++++++++++++ README.md | 16 ++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 .all-contributorsrc diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 0000000..1def304 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,39 @@ +{ + "projectName": "pusher", + "projectOwner": "hackherz", + "files": [ + "README.md" + ], + "imageSize": 100, + "commit": false, + "contributors": [ + { + "login": "nlohmann", + "name": "Niels Lohmann", + "avatar_url": "https://avatars2.githubusercontent.com/u/159488?v=4", + "profile": "http:/nlohmann.me", + "contributions": [ + "code" + ] + }, + { + "login": "jamesterjim", + "name": "James Rose", + "avatar_url": "https://avatars1.githubusercontent.com/u/1824603?v=4", + "profile": "http://jameserose.co.uk", + "contributions": [ + "doc" + ] + }, + { + "login": "HackHerz", + "name": "Daniel Stein", + "avatar_url": "https://avatars1.githubusercontent.com/u/2885148?v=4", + "profile": "https://hackherz.com/", + "contributions": [ + "code", + "doc" + ] + } + ] +} diff --git a/README.md b/README.md index 1567e94..262e4fc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ![Logo](https://github.com/HackHerz/pusher/blob/master/pusher.png) +[![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors) # pusher Send push-notifications to your phone from the command line. Uses [PushNotifier](http://pushnotifier.de/) to receive notifications. @@ -70,3 +71,18 @@ pusher uses a few third party libraries. - [json](https://github.com/nlohmann/json) by [Niels Lohmann](http://nlohmann.me/) - [TCLAP](http://tclap.sourceforge.net/) - [simpleini](https://github.com/brofield/simpleini) by Brodie Thiesfield + +## Contributors + + + +| [
Niels Lohmann](http:/nlohmann.me)
[πŸ’»](https://github.com/hackherz/pusher/commits?author=nlohmann "Code") | [
James Rose](http://jameserose.co.uk)
[πŸ“–](https://github.com/hackherz/pusher/commits?author=jamesterjim "Documentation") | [
Daniel Stein](https://hackherz.com/)
[πŸ’»](https://github.com/hackherz/pusher/commits?author=HackHerz "Code") [πŸ“–](https://github.com/hackherz/pusher/commits?author=HackHerz "Documentation") | +| :---: | :---: | :---: | + +Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)): + + + + + +This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! \ No newline at end of file