From 2bd4fc839f62ddc6551e5abbf0ee0d6d22c54ed3 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Mon, 26 Sep 2016 00:07:49 +0300 Subject: [PATCH] Initial commit. --- .gitignore | 10 + .gitmodules | 3 + CMakeLists.txt | 30 + Doxyfile | 1890 +++++++++++++++++++++++ cmake/FindCURLpp.cmake | 23 + doc/customdoxygen.css | 320 ++++ doc/footer.html | 26 + doc/header.html | 44 + ext/rapidjson/allocators.h | 263 ++++ ext/rapidjson/document.h | 2146 +++++++++++++++++++++++++++ ext/rapidjson/encodedstream.h | 270 ++++ ext/rapidjson/encodings.h | 712 +++++++++ ext/rapidjson/error/en.h | 74 + ext/rapidjson/error/error.h | 155 ++ ext/rapidjson/filereadstream.h | 99 ++ ext/rapidjson/filewritestream.h | 104 ++ ext/rapidjson/fwd.h | 146 ++ ext/rapidjson/internal/biginteger.h | 290 ++++ ext/rapidjson/internal/diyfp.h | 258 ++++ ext/rapidjson/internal/dtoa.h | 217 +++ ext/rapidjson/internal/ieee754.h | 77 + ext/rapidjson/internal/itoa.h | 304 ++++ ext/rapidjson/internal/meta.h | 181 +++ ext/rapidjson/internal/pow10.h | 55 + ext/rapidjson/internal/regex.h | 678 +++++++++ ext/rapidjson/internal/stack.h | 230 +++ ext/rapidjson/internal/strfunc.h | 55 + ext/rapidjson/internal/strtod.h | 269 ++++ ext/rapidjson/internal/swap.h | 46 + ext/rapidjson/memorybuffer.h | 70 + ext/rapidjson/memorystream.h | 71 + ext/rapidjson/msinttypes/inttypes.h | 316 ++++ ext/rapidjson/msinttypes/stdint.h | 300 ++++ ext/rapidjson/pointer.h | 1329 +++++++++++++++++ ext/rapidjson/prettywriter.h | 207 +++ ext/rapidjson/rapidjson.h | 569 +++++++ ext/rapidjson/reader.h | 1694 +++++++++++++++++++++ ext/rapidjson/schema.h | 1979 ++++++++++++++++++++++++ ext/rapidjson/stream.h | 179 +++ ext/rapidjson/stringbuffer.h | 116 ++ ext/rapidjson/writer.h | 413 ++++++ include/telebotxx/BotApi.hpp | 32 + src/BotApi.cpp | 88 ++ src/CMakeLists.txt | 24 + tests/CMakeLists.txt | 28 + tests/Dummy.cpp | 10 + tests/TestGlobal.hpp | 13 + tests/config.json | 5 + tests/main.cpp | 88 ++ tests/photo.png | Bin 0 -> 276277 bytes 50 files changed, 16506 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 Doxyfile create mode 100644 cmake/FindCURLpp.cmake create mode 100644 doc/customdoxygen.css create mode 100644 doc/footer.html create mode 100644 doc/header.html create mode 100644 ext/rapidjson/allocators.h create mode 100644 ext/rapidjson/document.h create mode 100644 ext/rapidjson/encodedstream.h create mode 100644 ext/rapidjson/encodings.h create mode 100644 ext/rapidjson/error/en.h create mode 100644 ext/rapidjson/error/error.h create mode 100644 ext/rapidjson/filereadstream.h create mode 100644 ext/rapidjson/filewritestream.h create mode 100644 ext/rapidjson/fwd.h create mode 100755 ext/rapidjson/internal/biginteger.h create mode 100644 ext/rapidjson/internal/diyfp.h create mode 100644 ext/rapidjson/internal/dtoa.h create mode 100644 ext/rapidjson/internal/ieee754.h create mode 100644 ext/rapidjson/internal/itoa.h create mode 100644 ext/rapidjson/internal/meta.h create mode 100644 ext/rapidjson/internal/pow10.h create mode 100644 ext/rapidjson/internal/regex.h create mode 100644 ext/rapidjson/internal/stack.h create mode 100644 ext/rapidjson/internal/strfunc.h create mode 100644 ext/rapidjson/internal/strtod.h create mode 100644 ext/rapidjson/internal/swap.h create mode 100644 ext/rapidjson/memorybuffer.h create mode 100644 ext/rapidjson/memorystream.h create mode 100644 ext/rapidjson/msinttypes/inttypes.h create mode 100644 ext/rapidjson/msinttypes/stdint.h create mode 100644 ext/rapidjson/pointer.h create mode 100644 ext/rapidjson/prettywriter.h create mode 100644 ext/rapidjson/rapidjson.h create mode 100644 ext/rapidjson/reader.h create mode 100644 ext/rapidjson/schema.h create mode 100644 ext/rapidjson/stream.h create mode 100644 ext/rapidjson/stringbuffer.h create mode 100644 ext/rapidjson/writer.h create mode 100644 include/telebotxx/BotApi.hpp create mode 100644 src/BotApi.cpp create mode 100644 src/CMakeLists.txt create mode 100644 tests/CMakeLists.txt create mode 100644 tests/Dummy.cpp create mode 100644 tests/TestGlobal.hpp create mode 100644 tests/config.json create mode 100644 tests/main.cpp create mode 100644 tests/photo.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc0513e --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +build +build-tests +cmake_install.cmake +CMakeCache.txt +CMakeFiles +doc/html +ext/curlpp +lib +Makefile +tests/telebotxx-test \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8df21da --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ext/curlpp"] + path = ext/curlpp + url = https://github.com/jpbarrette/curlpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0013aac --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 2.8) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) + +project(tetebotxx CXX) + +option (TELEBOTXX_BUILD_TESTS "Build unit tests using Boost.Test" ON) +option (TELEBOTXX_GENERATE_DOC "Generate API documentation with Doxygen" ON) + +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ext) +INCLUDE_DIRECTORIES(/usr/local/include) + +# Build library +include_directories(include) +add_subdirectory(src build) + +# Build tests +if(TELEBOTXX_BUILD_TESTS) + add_subdirectory(tests build-tests) +endif(TELEBOTXX_BUILD_TESTS) + +# Generate docs +if(TELEBOTXX_GENERATE_DOC) + find_package(Doxygen) + if(DOXYGEN_FOUND) + add_custom_target(telebotxx-doc ALL COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_SOURCE_DIR}/Doxyfile COMMENT "Generating API documentation with Doxygen") + else(DOXYGEN_FOUND) + message(STATUS "WARNING: Doxygen not found - Reference manual will not be created") + endif(DOXYGEN_FOUND) +endif(TELEBOTXX_GENERATE_DOC) diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..cd78b77 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1890 @@ +# Doxyfile 1.8.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed +# in front of the TAG it is preceding . +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "telebotxx" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, +# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, +# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields or simple typedef fields will be shown +# inline in the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO (the default), structs, classes, and unions are shown on a separate +# page (for HTML and Man pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can +# be an expensive process and often the same symbol appear multiple times in +# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too +# small doxygen will become slower. If the cache is too large, memory is wasted. +# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid +# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 +# symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = YES + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = include + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be ignored. +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = doc/header.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = doc/footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = doc/customdoxygen.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript +# pieces of code that will be used on startup of the MathJax code. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search +# engine library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4 will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images +# or other source files which should be copied to the LaTeX output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files +# that can be used to generate PDF. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. If left blank docbook will be used as the default path. + +DOCBOOK_OUTPUT = docbook + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = *.h + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed +# in the related pages index. If set to NO, only the current project's +# pages will be listed. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# manageable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = YES + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/cmake/FindCURLpp.cmake b/cmake/FindCURLpp.cmake new file mode 100644 index 0000000..5cd9784 --- /dev/null +++ b/cmake/FindCURLpp.cmake @@ -0,0 +1,23 @@ +#~ finds curlpp + +find_package(CURL REQUIRED) + +set(store_cfls ${CMAKE_FIND_LIBRARY_SUFFIXES}) +set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".so") +set(CURLPP_FIND_NAMES curlpp libcurlpp) +set(CURLPP_INCLUDE_PREFIX "curlpp/") +#~ set(CURLPP_INCLUDE_SEARCHES "Easy.hpp" "cURLpp.hpp" "Info.hpp" "Infos.hpp" "Option.hpp" "Options.hpp" "Form.hpp") +set(CURLPP_INCLUDE_SEARCHES "cURLpp.hpp") + + +find_path(CURLPP_INCLUDE_DIR NAMES ${CURLPP_INCLUDE_SEARCHES} PATH_SUFFIXES ${CURLPP_INCLUDE_PREFIX}) +find_library(CURLPP_LIBRARY NAMES ${CURLPP_FIND_NAMES} PATHS "/usr/local/lib") + +set(CURLPP_LIBRARIES ${CURL_LIBRARIES} ${CURLPP_LIBRARY}) +set(CURLPP_INCLUDE_DIRS ${CURL_INCLUDE_DIRS} ${CURLPP_INCLUDE_DIR}) + +include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) +find_package_handle_standard_args(CURLpp DEFAULT_MSG CURLPP_LIBRARY CURLPP_INCLUDE_DIR) + +mark_as_advanced(CURLPP_LIBRARY CURLPP_INCLUDE_DIR) +set(CMAKE_FIND_LIBRARY_SUFFIXES "${store_cfls}") diff --git a/doc/customdoxygen.css b/doc/customdoxygen.css new file mode 100644 index 0000000..81cbda9 --- /dev/null +++ b/doc/customdoxygen.css @@ -0,0 +1,320 @@ +h1, .h1, h2, .h2, h3, .h3{ + font-weight: 200 !important; +} + +#navrow1, #navrow2, #navrow3, #navrow4, #navrow5{ + border-bottom: 1px solid #EEEEEE; +} + +.adjust-right { +margin-left: 30px !important; +font-size: 1.15em !important; +} +.navbar{ + border: 0px solid #222 !important; + margin-bottom: 0px !important; +} + + +/* Sticky footer styles +-------------------------------------------------- */ +html, +body { + height: 100%; + /* The html and body elements cannot have any padding or margin. */ +} + +body, table, div, p, dl, a, ul, span, li, ol{ + font-family: 'Open Sans', sans-serif !important; +} + +/* Wrapper for page content to push down footer */ +#wrap { + min-height: 100%; + height: auto; + /* Negative indent footer by its height */ + margin: 0 auto -60px; + /* Pad bottom by footer height */ + padding: 0 0 60px; +} + +#top { + margin-top: 25px; +} + +/* Set the fixed height of the footer here */ +#footer { + font-size: 0.9em; + padding: 8px 0px; + background-color: #f5f5f5; +} + +.footer-row { + line-height: 44px; +} + +#footer > .container { + padding-left: 15px; + padding-right: 15px; +} + +.footer-follow-icon { + margin-left: 3px; + text-decoration: none !important; +} + +.footer-follow-icon img { + width: 20px; +} + +.footer-link { + padding-top: 5px; + display: inline-block; + color: #999999; + text-decoration: none; +} + +.footer-copyright { + text-align: center; +} + +@media (min-width: 768px) and (max-width: 991px) { + .adjust-right { + margin: 0px -5px; + } +} +@media (min-width: 768px) { + .nav > li > a { + padding-left: 9px !important; + padding-right: 9px !important; + } +} + +@media (min-width: 992px) { + .footer-row { + text-align: left; + } + + .footer-icons { + text-align: right; + } +} +@media (max-width: 991px) { + .footer-row { + text-align: center; + } + + .footer-icons { + text-align: center; + } +} + +/* DOXYGEN Code Styles +----------------------------------- */ + + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.code, a.code:visited, a.line, a.line:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; + font-family: monospace, fixed; + font-size: 105%; +} + +div.fragment { + padding: 4px 6px; + margin: 4px 8px 4px 2px; + border: 1px solid #C4CFE5; +} + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; +} + + +span.lineno { + padding-right: 4px; + text-align: right; + border-right: 2px solid #0F0; + background-color: #E8E8E8; + white-space: pre; +} +span.lineno a { + background-color: #D8D8D8; +} + +span.lineno a:hover { + background-color: #C8C8C8; +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + +/* Google Custom Search Engine Tweaks START */ +input.gsc-input, +.gsc-input-box, +.gsc-input-box-hover, +.gsc-input-box-focus, +.gsc-search-button { + box-sizing: content-box; + line-height: normal; +} + +.gsc-table-result, +.gsc-thumbnail-inside, +.gsc-url-top { + padding-left: 0px !important; +} + +.gsc-selected-option-container { + width: inherit !important; +} + +.reset-box-sizing, +.reset-box-sizing * { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +@media (min-width: 768px) { + .google-cse { + top: 50px; + } +} +@media (max-width: 767px) { + .google-cse { + top: 391px; + } +} + +.google-cse { + width: 100%; + left: 0px; + z-index: 9999; + background-color: #222; +} +/* Google Custom Search Engine Tweaks END */ \ No newline at end of file diff --git a/doc/footer.html b/doc/footer.html new file mode 100644 index 0000000..f2fa204 --- /dev/null +++ b/doc/footer.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + diff --git a/doc/header.html b/doc/header.html new file mode 100644 index 0000000..18d353a --- /dev/null +++ b/doc/header.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + $projectname: $title + $title + + + $treeview + $search + $mathjax + + $extrastylesheet + + + + + + + +
+
+
+
+
+
+ diff --git a/ext/rapidjson/allocators.h b/ext/rapidjson/allocators.h new file mode 100644 index 0000000..8cde8f4 --- /dev/null +++ b/ext/rapidjson/allocators.h @@ -0,0 +1,263 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return std::malloc(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + std::free(originalPtr); + return NULL; + } + return std::realloc(originalPtr, newSize); + } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while (chunkHead_ && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + if (chunkHead_ && chunkHead_ == userBuffer_) + chunkHead_->size = 0; // Clear user buffer + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); + + void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + void* newBuffer = Malloc(newSize); + RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + */ + void AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity)); + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/ext/rapidjson/document.h b/ext/rapidjson/document.h new file mode 100644 index 0000000..2cd9088 --- /dev/null +++ b/ext/rapidjson/document.h @@ -0,0 +1,2146 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::iterator, std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +struct GenericMember { + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator + : public std::iterator >::Type> { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef std::iterator BaseType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + //! Pointer to (const) GenericMember + typedef typename BaseType::pointer Pointer; + //! Reference to (const) GenericMember + typedef typename BaseType::reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef typename BaseType::difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } + bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } + bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } + bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } + bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } + bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +struct GenericMemberIterator; + +//! non-const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; +}; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template > +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { + rhs.flags_ = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { + static const unsigned defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_ASSERT(type <= kNumberType); + flags_ = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \see CopyFrom() + */ + template< typename SourceAllocator > + GenericValue(const GenericValue& rhs, Allocator & allocator); + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { + data_.n.i64 = i; + if (i >= 0) + flags_ |= kUintFlag | kUint64Flag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { + data_.n.u64 = u; + if (!(u & 0x80000000)) + flags_ |= kIntFlag | kInt64Flag; + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { + data_.n.i64 = i64; + if (i64 >= 0) { + flags_ |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { + data_.n.u64 = u64; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + flags_ |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(flags_) { + case kArrayFlag: + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(data_.a.elements); + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(data_.o.members); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(data_.s.str)); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + RAPIDJSON_ASSERT(this != &rhs); + this->~GenericValue(); + RawAssign(rhs); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(flags_ & kTypeMask); } + bool IsNull() const { return flags_ == kNullFlag; } + bool IsFalse() const { return flags_ == kFalseFlag; } + bool IsTrue() const { return flags_ == kTrueFlag; } + bool IsBool() const { return (flags_ & kBoolFlag) != 0; } + bool IsObject() const { return flags_ == kObjectFlag; } + bool IsArray() const { return flags_ == kArrayFlag; } + bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } + bool IsInt() const { return (flags_ & kIntFlag) != 0; } + bool IsUint() const { return (flags_ & kUintFlag) != 0; } + bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } + bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } + bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } + bool IsString() const { return (flags_ & kStringFlag) != 0; } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(StringRef(name)); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(StringRef(name)); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + Object& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); + } + } + o.members[o.size].name.RawAssign(name); + o.members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(data_.o.members + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) { + // Move the last one to this place + *m = *last; + } + else { + // Only one left, just destroy + m->~Member(); + } + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); + return pos; + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + for (SizeType i = 0; i < data_.a.size; ++i) + data_.a.elements[i].~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return data_.a.elements[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + data_.a.elements = static_cast(allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + data_.a.elements[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + data_.a.elements[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(data_.a.elements != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } + + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((flags_ & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } +#endif + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (const GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x100, + kNumberFlag = 0x200, + kIntFlag = 0x400, + kUintFlag = 0x800, + kInt64Flag = 0x1000, + kUint64Flag = 0x2000, + kDoubleFlag = 0x4000, + kStringFlag = 0x100000, + kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, + + // Initial flags of different types. + kNullFlag = kNullType, + kTrueFlag = kTrueType | kBoolFlag, + kFalseFlag = kFalseType | kBoolFlag, + kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, + kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, + kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, + kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, + kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, + kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, + kConstStringFlag = kStringType | kStringFlag, + kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct String { + const Ch* str; + SizeType length; + unsigned hashcode; //!< reserved + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode + // inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct Object { + Member* members; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct Array { + GenericValue* elements; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + Object o; + Array a; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + flags_ = kArrayFlag; + if (count) { + data_.a.elements = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + } + else + data_.a.elements = NULL; + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + flags_ = kObjectFlag; + if (count) { + data_.o.members = static_cast(allocator.Malloc(count * sizeof(Member))); + std::memcpy(data_.o.members, members, count * sizeof(Member)); + } + else + data_.o.members = NULL; + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + flags_ = kConstStringFlag; + data_.s.str = s; + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = NULL; + if(ShortString::Usable(s.length)) { + flags_ = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + flags_ = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + data_.s.str = str; + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + flags_ = rhs.flags_; + rhs.flags_ = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; + unsigned flags_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template , typename StackAllocator = CrtAllocator> +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + +// defined here due to the dependency on GenericDocument +template +template +inline +GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop(1)); + } + break; + case kStringType: + if (rhs.flags_ == kConstStringFlag) { + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + break; + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/ext/rapidjson/encodedstream.h b/ext/rapidjson/encodedstream.h new file mode 100644 index 0000000..87c9067 --- /dev/null +++ b/ext/rapidjson/encodedstream.h @@ -0,0 +1,270 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/ext/rapidjson/encodings.h b/ext/rapidjson/encodings.h new file mode 100644 index 0000000..cc676d8 --- /dev/null +++ b/ext/rapidjson/encodings.h @@ -0,0 +1,712 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + *codepoint = (0xFF >> type) & static_cast(c); + bool result = true; + switch (type) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define COPY() os.Put(c = is.Take()) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put((v & 0x3FF) | 0xDC00); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, (v & 0x3FF) | 0xDC00); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(is.Take()); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/ext/rapidjson/error/en.h b/ext/rapidjson/error/en.h new file mode 100644 index 0000000..c2315fd --- /dev/null +++ b/ext/rapidjson/error/en.h @@ -0,0 +1,74 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/ext/rapidjson/error/error.h b/ext/rapidjson/error/error.h new file mode 100644 index 0000000..95cb31a --- /dev/null +++ b/ext/rapidjson/error/error.h @@ -0,0 +1,155 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/ext/rapidjson/filereadstream.h b/ext/rapidjson/filereadstream.h new file mode 100644 index 0000000..11aacbf --- /dev/null +++ b/ext/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/ext/rapidjson/filewritestream.h b/ext/rapidjson/filewritestream.h new file mode 100644 index 0000000..8aeac86 --- /dev/null +++ b/ext/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/ext/rapidjson/fwd.h b/ext/rapidjson/fwd.h new file mode 100644 index 0000000..9c0466c --- /dev/null +++ b/ext/rapidjson/fwd.h @@ -0,0 +1,146 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// document.h + +template +struct GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/ext/rapidjson/internal/biginteger.h b/ext/rapidjson/internal/biginteger.h new file mode 100755 index 0000000..9d3e88c --- /dev/null +++ b/ext/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include // for _umul128 +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10u + static_cast(*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/ext/rapidjson/internal/diyfp.h b/ext/rapidjson/internal/diyfp.h new file mode 100644 index 0000000..9a62c2c --- /dev/null +++ b/ext/rapidjson/internal/diyfp.h @@ -0,0 +1,258 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) && __GNUC__ >= 4 + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & (static_cast(1) << 63))) { + res.f <<= 1; + res.e--; + } + return res; +#endif + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + unsigned index = (static_cast(exp) + 348u) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/ext/rapidjson/internal/dtoa.h b/ext/rapidjson/internal/dtoa.h new file mode 100644 index 0000000..d04ae21 --- /dev/null +++ b/ext/rapidjson/internal/dtoa.h @@ -0,0 +1,217 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-static_cast(kappa)]); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (length <= kk && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + return &buffer[length + offset]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer) { + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/ext/rapidjson/internal/ieee754.h b/ext/rapidjson/internal/ieee754.h new file mode 100644 index 0000000..6890f89 --- /dev/null +++ b/ext/rapidjson/internal/ieee754.h @@ -0,0 +1,77 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static unsigned EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return static_cast(order) + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/ext/rapidjson/internal/itoa.h b/ext/rapidjson/internal/itoa.h new file mode 100644 index 0000000..01a4e7e --- /dev/null +++ b/ext/rapidjson/internal/itoa.h @@ -0,0 +1,304 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + if (value >= kTen8) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/ext/rapidjson/internal/meta.h b/ext/rapidjson/internal/meta.h new file mode 100644 index 0000000..2daad96 --- /dev/null +++ b/ext/rapidjson/internal/meta.h @@ -0,0 +1,181 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/ext/rapidjson/internal/pow10.h b/ext/rapidjson/internal/pow10.h new file mode 100644 index 0000000..1d2dff0 --- /dev/null +++ b/ext/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/ext/rapidjson/internal/regex.h b/ext/rapidjson/internal/regex.h new file mode 100644 index 0000000..6c1047d --- /dev/null +++ b/ext/rapidjson/internal/regex.h @@ -0,0 +1,678 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef typename Encoding::Ch Ch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream > ds(ss); + Parse(ds); + } + + ~GenericRegex() { + Allocator::Free(stateSet_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + + template + bool Match(InputStream& is) const { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) const { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) const { + return SearchWithAnchoring(is, anchorBegin_, anchorEnd_); + } + + bool Search(const Ch* s) const { + GenericStringStream is(s); + return Search(is); + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + template + class DecodedStream { + public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + + private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Allocator allocator; + Stack operandStack(&allocator, 256); // Frag + Stack operatorStack(&allocator, 256); // Operator + Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n) || n == 0) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = 0; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + + // Preallocate buffer for SearchWithAnchoring() + RAPIDJSON_ASSERT(stateSet_ == 0); + if (stateCount_ > 0) { + stateSet_ = static_cast(states_.GetAllocator().Malloc(GetStateSetSize())); + state0_.template Reserve(stateCount_); + state1_.template Reserve(stateCount_); + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + case kOneOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + + default: + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n > 0); + RAPIDJSON_ASSERT(m == 0 || n <= m); // m == 0 means infinity + if (operandStack.GetSize() < sizeof(Frag)) + return false; + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == 0) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag *src = operandStack.template Top(); + SizeType count = stateCount_ - src->minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src->minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src->start + count, src->out + count, src->minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const { + RAPIDJSON_ASSERT(IsValid()); + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == kAnyCharacterClass || + (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) const { + if (index == kRegexInvalidState) + return true; + + const State& s = GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) { + stateSet_[index >> 5] |= (1 << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = GetRange(rangeIndex); + if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + // For SearchWithAnchoring() + uint32_t* stateSet_; // allocated by states_.GetAllocator() + mutable Stack state0_; + mutable Stack state1_; + bool anchorBegin_; + bool anchorEnd_; +}; + +typedef GenericRegex > Regex; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/ext/rapidjson/internal/stack.h b/ext/rapidjson/internal/stack.h new file mode 100644 index 0000000..c1beaac --- /dev/null +++ b/ext/rapidjson/internal/stack.h @@ -0,0 +1,230 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/ext/rapidjson/internal/strfunc.h b/ext/rapidjson/internal/strfunc.h new file mode 100644 index 0000000..34d4703 --- /dev/null +++ b/ext/rapidjson/internal/strfunc.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/ext/rapidjson/internal/strtod.h b/ext/rapidjson/internal/strtod.h new file mode 100644 index 0000000..fd4b01e --- /dev/null +++ b/ext/rapidjson/internal/strtod.h @@ -0,0 +1,269 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { + uint64_t significand = 0; + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < length; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10u + static_cast(decimals[i] - '0'); + } + + if (i < length && decimals[i] >= '5') // Rounding + significand++; + + size_t remaining = length - i; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp - 1; + RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); + v = v * kPow10[adjustment]; + if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + unsigned scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + static_cast(kUlp); + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + static_cast(precisionSize)); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { + const BigInteger dInt(decimals, length); + const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result; + if (StrtodFast(d, p, &result)) + return result; + + // Trim leading zeros + while (*decimals == '0' && length > 1) { + length--; + decimals++; + decimalPosition--; + } + + // Trim trailing zeros + while (decimals[length - 1] == '0' && length > 1) { + length--; + decimalPosition--; + exp++; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 780; + if (static_cast(length) > kMaxDecimalDigit) { + int delta = (static_cast(length) - kMaxDecimalDigit); + exp += delta; + decimalPosition -= static_cast(delta); + length = kMaxDecimalDigit; + } + + // If too small, underflow to zero + if (int(length) + exp < -324) + return 0.0; + + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/ext/rapidjson/internal/swap.h b/ext/rapidjson/internal/swap.h new file mode 100644 index 0000000..cbb2abd --- /dev/null +++ b/ext/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/ext/rapidjson/memorybuffer.h b/ext/rapidjson/memorybuffer.h new file mode 100644 index 0000000..39bee1d --- /dev/null +++ b/ext/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/ext/rapidjson/memorystream.h b/ext/rapidjson/memorystream.h new file mode 100644 index 0000000..7381e0b --- /dev/null +++ b/ext/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return (src_ == end_) ? '\0' : *src_; } + Ch Take() { return (src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/ext/rapidjson/msinttypes/inttypes.h b/ext/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000..1811128 --- /dev/null +++ b/ext/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 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. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/ext/rapidjson/msinttypes/stdint.h b/ext/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000..3d4477b --- /dev/null +++ b/ext/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 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. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/ext/rapidjson/pointer.h b/ext/rapidjson/pointer.h new file mode 100644 index 0000000..63790cb --- /dev/null +++ b/ext/rapidjson/pointer.h @@ -0,0 +1,1329 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "internal/itoa.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = buffer[i]; + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + ValueType* Get(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + return 0; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return 0; + v = &((*v)[t->index]); + break; + default: + return 0; + } + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root) const { return Get(const_cast(root)); } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c <<= 4; + Ch h = *src_; + if (h >= '0' && h <= '9') c += h - '0'; + else if (h >= 'A' && h <= 'F') c += h - 'A' + 10; + else if (h >= 'a' && h <= 'f') c += h - 'a' + 10; + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(hexDigits[u >> 4]); + os_.Put(hexDigits[u & 15]); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Get(root); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer) { + return pointer.Get(root); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N]) { + return GenericPointer(source, N - 1).Get(root); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Get(root); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/ext/rapidjson/prettywriter.h b/ext/rapidjson/prettywriter.h new file mode 100644 index 0000000..adfff4f --- /dev/null +++ b/ext/rapidjson/prettywriter.h @@ -0,0 +1,207 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of ouptut os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kStringType); + return Base::WriteString(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndObject(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndArray(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + Base::os_->Put('\n'); + } + else + Base::os_->Put('\n'); + WriteIndent(); + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/ext/rapidjson/rapidjson.h b/ext/rapidjson/rapidjson.h new file mode 100644 index 0000000..9cb40a9 --- /dev/null +++ b/ext/rapidjson/rapidjson.h @@ -0,0 +1,569 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overriden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 0 +#define RAPIDJSON_PATCH_VERSION 2 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#ifdef _MSC_VER +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && defined(_M_ARM) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. Currently the default uses 4 bytes + alignment. User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#if RAPIDJSON_64BIT == 1 +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#else +#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2 optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. + + To enable these optimizations, two different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + \c RAPIDJSON_SSE42 takes precedence, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#if defined(__GNUC__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) x +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) x +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(x) new x +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/ext/rapidjson/reader.h b/ext/rapidjson/reader.h new file mode 100644 index 0000000..f2670b6 --- /dev/null +++ b/ext/rapidjson/reader.h @@ -0,0 +1,1694 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +#endif // RAPIDJSON_SSE2 + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(is.Peek() == '/')) { + is.Take(); + + if (is.Peek() == '*') { + is.Take(); + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + if (is.Take() == '*') { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + if (is.Take() == '/') + break; + } + } + } + else if (RAPIDJSON_LIKELY(is.Peek() == '/')) { + is.Take(); + while (is.Peek() != '\0' && is.Take() != '\n') { } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (is.Peek() == '}') { + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(is.Take() != ':')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Take()) { + case ',': + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + break; + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (is.Peek() == ']') { + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + switch (is.Take()) { + case ',': + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case ']': + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + break; + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l')) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e')) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e')) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Take(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + is.Take(); + Ch e = is.Take(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) + os.Put(static_cast(escape[static_cast(e)])); + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + unsigned codepoint = ParseHex4(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(is.Take() != '\\' || is.Take() != 'u')) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + unsigned codepoint2 = ParseHex4(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + } + else { + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif + + template + class NumberStream; + + template + class NumberStream { + public: + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : NumberStream(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + // Parse minus + bool minus = false; + if (s.Peek() == '-') { + minus = true; + s.Take(); + } + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + double d = 0.0; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (s.Peek() == '.') { + s.Take(); + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (s.Peek() == 'e' || s.Peek() == 'E') { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + s.Take(); + + bool expMinus = false; + if (s.Peek() == '+') + s.Take(); + else if (s.Peek() == '-') { + s.Take(); + expMinus = true; + } + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (exp >= 214748364) { // Issue #313: prevent overflow exponent + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, + IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingElementDelimiterState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState + }; + + enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + } + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); return; + } + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/ext/rapidjson/schema.h b/ext/rapidjson/schema.h new file mode 100644 index 0000000..e47f89a --- /dev/null +++ b/ext/rapidjson/schema.h @@ -0,0 +1,1979 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include // abs, floor + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +#endif + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(variadic-macros) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidKeyword = keyword.GetString();\ + RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void* ReallocState(void* originalPtr, size_t originalSize, size_t newSize) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + factory(f), + schema(s), + valueSchema(), + invalidKeyword(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + objectDependencies(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (objectDependencies) + factory.FreeState(objectDependencies); + } + + SchemaValidatorFactoryType& factory; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType objectRequiredCount; + SizeType arrayElementIndex; + bool* objectDependencies; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + requiredCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false) + { + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256 + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = GetTypeless(); + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + requiredCount_++; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + } + + ~Schema() { + allocator_->Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + allocator_->Free(pattern_); + } +#endif + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = GetTypeless(); + else + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } + else + context.valueSchema = GetTypeless(); + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + + if (enum_) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + else + oneValid = true; + } + if (!oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); + if (count > maxLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + context.objectRequiredCount = 0; + if (hasDependencies_) { + context.objectDependencies = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.objectDependencies, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + } + + SizeType index; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (properties_[index].required) + context.objectRequiredCount++; + + if (hasDependencies_) + context.objectDependencies[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = GetTypeless(); + return true; + } + + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (context.objectRequiredCount != requiredCount_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + + if (memberCount < minProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + + if (memberCount > maxProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + + if (hasDependencies_) { + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + if (context.objectDependencies[sourceIndex]) { + if (properties_[sourceIndex].dependencies) { + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.objectDependencies[targetIndex]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + else if (properties_[sourceIndex].dependenciesSchema) + if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + context.arrayElementIndex = 0; + context.inArray = true; + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + + if (elementCount > maxItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + + return true; + } + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + static const SchemaType* GetTypeless() { + static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + return &typeless; + } + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + if (!r->IsValid()) { + r->~RegexType(); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + return pattern->Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) + try { + return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + return true; + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType requiredCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = buffer[i]; + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = SchemaType::GetTypeless(); + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + else if (schema) + *schema = s; + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator +{ +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(outputHandler), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + valid_ = true; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { return valid_; } + + //! Gets the JSON pointer pointed to the invalid schema. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + } + + //! Gets the keyword of invalid schema. + const Ch* GetInvalidSchemaKeyword() const { + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + } + + //! Gets the JSON pointer pointed to the invalid value. + PointerType GetInvalidDocumentPointer() const { + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +RAPIDJSON_MULTILINEMACRO_END +#else +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if (!BeginValue() || !CurrentSchema().method arg1) {\ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + return valid_ = EndValue() && outputHandler_.method arg2 + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = outputHandler_.StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = outputHandler_.Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = outputHandler_.StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, +#if RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void* ReallocState(void* originalPtr, size_t originalSize, size_t newSize) { + return GetStateAllocator().Realloc(originalPtr, originalSize, newSize); + } + + virtual void FreeState(void* p) { + return StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator()); + return *stateAllocator_; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext())) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + if (CurrentContext().valueSchema) + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i]); + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext())) + return false; + +#if RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + + uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + if (context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) + RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static OutputHandler& GetNullHandler() { + static OutputHandler nullHandler; + return nullHandler; + } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + OutputHandler& outputHandler_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + bool valid_; +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + if ((isValid_ = validator.IsValid())) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/ext/rapidjson/stream.h b/ext/rapidjson/stream.h new file mode 100644 index 0000000..dd2783b --- /dev/null +++ b/ext/rapidjson/stream.h @@ -0,0 +1,179 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/ext/rapidjson/stringbuffer.h b/ext/rapidjson/stringbuffer.h new file mode 100644 index 0000000..41c8dfc --- /dev/null +++ b/ext/rapidjson/stringbuffer.h @@ -0,0 +1,116 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/ext/rapidjson/writer.h b/ext/rapidjson/writer.h new file mode 100644 index 0000000..ffb6cb5 --- /dev/null +++ b/ext/rapidjson/writer.h @@ -0,0 +1,413 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return WriteNull(); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } + bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } + bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } + bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kStringType); + return WriteString(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + bool ret = WriteEndObject(); + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + os_->Flush(); + return ret; + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + bool ret = WriteEndArray(); + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + os_->Flush(); + return ret; + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + static const size_t kDefaultLevelDepth = 32; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + char buffer[25]; + char* end = internal::dtoa(d, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else + if (RAPIDJSON_UNLIKELY(!(Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + OutputStream* os_; + internal::Stack level_stack_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/telebotxx/BotApi.hpp b/include/telebotxx/BotApi.hpp new file mode 100644 index 0000000..300b29f --- /dev/null +++ b/include/telebotxx/BotApi.hpp @@ -0,0 +1,32 @@ +#ifndef TELEBOTXX_BOTAPI_H +#define TELEBOTXX_BOTAPI_H + +#include + +namespace telebotxx +{ + class BotApi + { + public: + /// \param [in] token bot's secret token + BotApi(const std::string& token); + + /// \brief Send text message + /// \param [in] chat chat identifier + /// \param [in] text message text + void sendMessage(const std::string& chat, const std::string& text); + + /// \brief Send image + /// \param [in] chat chat identifier + /// \param [in] file opened image stream + /// \param [in] caption optional photo caption + void sendPhoto(const std::string& chat, const std::istream& file, const std::string& caption = ""); + + + private: + std::string token_; + std::string telegramMainUrl_; + }; +} + +#endif // TELEBOTXX_BOTAPI_H diff --git a/src/BotApi.cpp b/src/BotApi.cpp new file mode 100644 index 0000000..9b7e76e --- /dev/null +++ b/src/BotApi.cpp @@ -0,0 +1,88 @@ +#include + +#include + +#include + +#include +#include +#include + +using namespace telebotxx; + +BotApi::BotApi(const std::string& token) + : token_(token) +{ + telegramMainUrl_ = "https://api.telegram.org/bot" + token_; + + /// \todo run getMe command to check token +} + +void BotApi::sendMessage(const std::string &chat, const std::string &text) +{ + // Construct JSON body and istream + using namespace rapidjson; + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("chat_id"); + writer.String(chat.c_str()); + writer.String("text"); + writer.String(text.c_str()); + writer.EndObject(); + + std::istringstream myStream(s.GetString()); + std::cout << myStream.str() << std::endl; + auto size = myStream.str().size(); + + // Construct HTTP request + curlpp::Easy request; + std::list headers; + + // Content-Type + headers.push_back("Content-Type: application/json"); + + // Content-Length + std::ostringstream ss; + ss << "Content-Length: " << size; + headers.push_back(ss.str()); + + // Set options + request.setOpt(new curlpp::Options::Url(telegramMainUrl_ + "/sendMessage")); + request.setOpt(new curlpp::Options::Verbose(true)); + request.setOpt(new curlpp::Options::ReadStream(&myStream)); + request.setOpt(new curlpp::Options::InfileSize(size)); + request.setOpt(new curlpp::options::HttpHeader(headers)); + request.setOpt(new curlpp::Options::Post(true)); + + // Perform request + request.perform(); +} + +void BotApi::sendPhoto(const std::string& chat, const std::istream& file, const std::string& caption) +{ + // Construct HTTP request + curlpp::Easy request; + std::list headers; + + { + // Forms takes ownership of pointers! + curlpp::Forms formParts; + formParts.push_back(new curlpp::FormParts::Content("chat_id", chat)); + formParts.push_back(new curlpp::FormParts::Content("photo", "value2")); + + request.setOpt(new curlpp::options::HttpPost(formParts)); + } + + // Set options + request.setOpt(new curlpp::Options::Url(telegramMainUrl_ + "/sendPhoto")); + request.setOpt(new curlpp::Options::Verbose(true)); + request.setOpt(new curlpp::options::HttpHeader(headers)); + request.setOpt(new curlpp::Options::Post(true)); + + // Perform request + request.perform(); +} + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..f1d3b50 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.8) + +message(STATUS "Configuring telebotxx") + +find_package(CURLpp REQUIRED) +include_directories(${CURLPP_INCLUDE_DIRS}) + +# Add required gcc flags +# For Windows we suppress all warnings because of Boost garbage :( +if(NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wall") +else(NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -w") +endif(NOT WIN32) + +# Put compiled library to 'lib' directory +set(LIBRARY_OUTPUT_PATH "../lib") + + +set(SOURCE_FILES BotApi.cpp +) + +add_library(telebotxx SHARED ${SOURCE_FILES}) +target_link_libraries(telebotxx curlpp ${CURL_LIBRARIES}) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..b41c194 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 2.8) +if (POLICY CMP0037) + cmake_policy(SET CMP0037 OLD) +endif() + +message(STATUS "Configuring telebotxx-test") + +add_definitions(-DBOOST_TEST_DYN_LINK) +find_package(Boost 1.54 REQUIRED COMPONENTS unit_test_framework system) +include_directories(${Boost_INCLUDE_DIRS}) + +# Add required gcc flags +# For Windows we suppress all warnings because of Boost garbage :( +if(NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wall") +else(NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -w") +endif(NOT WIN32) + +set(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/tests") + +set(SOURCE_FILES Dummy.cpp main.cpp) +add_executable(telebotxx-test ${SOURCE_FILES}) +target_link_libraries(telebotxx-test telebotxx stdc++ ${Boost_LIBRARIES}) + +add_custom_target(test COMMAND telebotxx-test WORKING_DIRECTORY ".." DEPENDS telebotxx-test) + +message(STATUS "Configuring telebotxx-test done") diff --git a/tests/Dummy.cpp b/tests/Dummy.cpp new file mode 100644 index 0000000..311b906 --- /dev/null +++ b/tests/Dummy.cpp @@ -0,0 +1,10 @@ +#define BOOST_TEST_MODULE AllTests +#include "TestGlobal.hpp" +#include + +BOOST_AUTO_TEST_CASE(Dummy) +{ + PRINT_TESTNAME; + BOOST_REQUIRE(1 == 1); + BOOST_REQUIRE_THROW(throw 1, int); +} diff --git a/tests/TestGlobal.hpp b/tests/TestGlobal.hpp new file mode 100644 index 0000000..b6924b8 --- /dev/null +++ b/tests/TestGlobal.hpp @@ -0,0 +1,13 @@ +#ifndef TELEBOTXX_TEST_GLOBAL_HPP +#define TELEBOTXX_TEST_GLOBAL_HPP + +#include +#include + +using namespace boost::unit_test; + +#define BOOST_TEST_SUITE_NAME (boost::unit_test::framework::get(boost::unit_test::framework::current_test_case().p_parent_id).p_name) +#define BOOST_TEST_NAME (boost::unit_test::framework::current_test_case().p_name) +#define PRINT_TESTNAME std::cout << "\x1B[36mRunning unit test " << BOOST_TEST_SUITE_NAME << "::" << BOOST_TEST_NAME << "...\x1B[0m" << std::endl; + +#endif // TELEBOTXX_TEST_GLOBAL_HPP diff --git a/tests/config.json b/tests/config.json new file mode 100644 index 0000000..2fa7fec --- /dev/null +++ b/tests/config.json @@ -0,0 +1,5 @@ +{ + "token": "277513413:AAEXK65dnCM-pEX9j4pjqlI3N6YQGRFjoCQ", + "chat": "@zettai_test", + "photo": "photo.png" +} diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..404f1ff --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,88 @@ +#include "TestGlobal.hpp" + +#include + +#include +#include + +#include +#include + +std::unique_ptr bot; + +const std::string CONFIG_FILE_NAME = "config.json"; + +std::string token; +std::string chat; +std::string photoFile; + +struct TestConfig +{ + TestConfig() + { + std::cout << "Performing tests initialization..." << std::endl; + + using namespace rapidjson; + FILE *pFile = fopen(CONFIG_FILE_NAME.c_str(), "rb"); + if (pFile == NULL) + throw std::runtime_error("Can not open config file"); + + char buffer[2048]; + FileReadStream is(pFile, buffer, sizeof(buffer)); + Document document; + document.ParseStream<0, UTF8<>, FileReadStream>(is); + + if (!document.IsObject() && document.HasParseError()) + throw std::invalid_argument("Can not parse config file"); + + // Read token + if (document.HasMember("token")) + if (document["token"].IsString()) + token = document["token"].GetString(); + else + throw std::invalid_argument("Config error: 'token' must be unsigned int value."); + else + throw std::invalid_argument("Config error: 'token' not set."); + + // Read chat + if (document.HasMember("chat")) + if (document["chat"].IsString()) + chat = document["chat"].GetString(); + else + throw std::invalid_argument("Config error: 'chat' must be unsigned int value."); + else + throw std::invalid_argument("Config error: 'chat' not set."); + + // Read photo filename + if (document.HasMember("photo")) + if (document["photo"].IsString()) + photoFile = document["photo"].GetString(); + else + throw std::invalid_argument("Config error: 'photo' must be unsigned int value."); + else + throw std::invalid_argument("Config error: 'photo' not set."); + } + + ~TestConfig() + { + std::cout << "Performing tests cleanup..." << std::endl; + } +}; + +BOOST_GLOBAL_FIXTURE(TestConfig); + +BOOST_AUTO_TEST_SUITE(TestBotApi) + + BOOST_AUTO_TEST_CASE(DefaultConstructor) + { + PRINT_TESTNAME; + BOOST_REQUIRE_NO_THROW(bot.reset(new telebotxx::BotApi(token))); + } + + BOOST_AUTO_TEST_CASE(SendMessage) + { + PRINT_TESTNAME; + BOOST_REQUIRE_NO_THROW(bot->sendMessage(chat, "Hello from C++!")); + } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/photo.png b/tests/photo.png new file mode 100644 index 0000000000000000000000000000000000000000..f5cada05f4656d7b673fc8931ac38b6d07aca6b6 GIT binary patch literal 276277 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 z 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

hkqFF&o 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

uh@N7=N3XX7XI+6xgZ3;1zSDq2Tp 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

SwsN`p=;iy)#U` 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

fT7axW@M7c!6({8CWzFVs9Wnu8e7TiBeFM<-)$ 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

Qc(+2LjU3*jqmS# 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 literal 0 HcmV?d00001