From f9ce8319e9a1d762984616cadc353ce6e70a6ffe Mon Sep 17 00:00:00 2001 From: Relintai Date: Tue, 24 Nov 2020 15:41:18 +0100 Subject: [PATCH] Initial commit. --- SConstruct | 616 ++++ compile_linux.sh | 1 + cout_server.cpp | 80 + libs/HEADS | 2 + libs/brynet/Version.hpp | 3 + libs/brynet/base/Any.hpp | 30 + libs/brynet/base/AppStatus.hpp | 47 + libs/brynet/base/Array.hpp | 129 + libs/brynet/base/Buffer.hpp | 189 + libs/brynet/base/CPP_VERSION.hpp | 16 + libs/brynet/base/Noexcept.hpp | 9 + libs/brynet/base/NonCopyable.hpp | 16 + libs/brynet/base/Packet.hpp | 435 +++ libs/brynet/base/Platform.hpp | 9 + libs/brynet/base/Stack.hpp | 175 + libs/brynet/base/Timer.hpp | 167 + libs/brynet/base/WaitGroup.hpp | 65 + libs/brynet/base/crypto/Base64.hpp | 105 + libs/brynet/base/crypto/SHA1.hpp | 496 +++ libs/brynet/base/endian/Endian.hpp | 140 + libs/brynet/net/AsyncConnector.hpp | 89 + libs/brynet/net/Channel.hpp | 20 + libs/brynet/net/CurrentThread.hpp | 55 + libs/brynet/net/EventLoop.hpp | 444 +++ libs/brynet/net/Exception.hpp | 41 + libs/brynet/net/ListenThread.hpp | 58 + libs/brynet/net/Poller.hpp | 266 ++ libs/brynet/net/PromiseReceive.hpp | 156 + libs/brynet/net/SSLHelper.hpp | 174 + libs/brynet/net/Socket.hpp | 230 ++ libs/brynet/net/SocketLibFunction.hpp | 329 ++ libs/brynet/net/SocketLibTypes.hpp | 71 + libs/brynet/net/TcpConnection.hpp | 1196 ++++++ libs/brynet/net/TcpService.hpp | 91 + .../brynet/net/detail/AddSocketOptionInfo.hpp | 27 + libs/brynet/net/detail/ConnectorDetail.hpp | 161 + libs/brynet/net/detail/ConnectorWorkInfo.hpp | 368 ++ libs/brynet/net/detail/IOLoopData.hpp | 67 + libs/brynet/net/detail/ListenThreadDetail.hpp | 169 + libs/brynet/net/detail/TCPServiceDetail.hpp | 198 + libs/brynet/net/detail/WakeupChannel.hpp | 139 + libs/brynet/net/http/HttpFormat.hpp | 222 ++ libs/brynet/net/http/HttpParser.hpp | 318 ++ libs/brynet/net/http/HttpService.hpp | 342 ++ libs/brynet/net/http/WebSocketFormat.hpp | 239 ++ libs/brynet/net/http/http_parser.h | 3206 +++++++++++++++++ libs/brynet/net/port/Win.hpp | 31 + libs/brynet/net/wrapper/ConnectionBuilder.hpp | 191 + .../net/wrapper/HttpConnectionBuilder.hpp | 44 + .../brynet/net/wrapper/HttpServiceBuilder.hpp | 39 + libs/brynet/net/wrapper/ServiceBuilder.hpp | 167 + libs/rapidjson/allocators.h | 284 ++ libs/rapidjson/cursorstreamwrapper.h | 78 + libs/rapidjson/document.h | 2732 ++++++++++++++ libs/rapidjson/encodedstream.h | 299 ++ libs/rapidjson/encodings.h | 716 ++++ libs/rapidjson/error/en.h | 74 + libs/rapidjson/error/error.h | 161 + libs/rapidjson/filereadstream.h | 99 + libs/rapidjson/filewritestream.h | 104 + libs/rapidjson/fwd.h | 151 + libs/rapidjson/internal/biginteger.h | 290 ++ libs/rapidjson/internal/clzll.h | 71 + libs/rapidjson/internal/diyfp.h | 257 ++ libs/rapidjson/internal/dtoa.h | 245 ++ libs/rapidjson/internal/ieee754.h | 78 + libs/rapidjson/internal/itoa.h | 308 ++ libs/rapidjson/internal/meta.h | 186 + libs/rapidjson/internal/pow10.h | 55 + libs/rapidjson/internal/regex.h | 739 ++++ libs/rapidjson/internal/stack.h | 232 ++ libs/rapidjson/internal/strfunc.h | 69 + libs/rapidjson/internal/strtod.h | 290 ++ libs/rapidjson/internal/swap.h | 46 + libs/rapidjson/istreamwrapper.h | 128 + libs/rapidjson/memorybuffer.h | 70 + libs/rapidjson/memorystream.h | 71 + libs/rapidjson/msinttypes/inttypes.h | 316 ++ libs/rapidjson/msinttypes/stdint.h | 300 ++ libs/rapidjson/ostreamwrapper.h | 81 + libs/rapidjson/pointer.h | 1415 ++++++++ libs/rapidjson/prettywriter.h | 277 ++ libs/rapidjson/rapidjson.h | 692 ++++ libs/rapidjson/reader.h | 2244 ++++++++++++ libs/rapidjson/schema.h | 2496 +++++++++++++ libs/rapidjson/stream.h | 223 ++ libs/rapidjson/stringbuffer.h | 121 + libs/rapidjson/writer.h | 710 ++++ main.cpp | 80 + server | Bin 0 -> 1145344 bytes 90 files changed, 28666 insertions(+) create mode 100644 SConstruct create mode 100755 compile_linux.sh create mode 100644 cout_server.cpp create mode 100644 libs/HEADS create mode 100644 libs/brynet/Version.hpp create mode 100644 libs/brynet/base/Any.hpp create mode 100644 libs/brynet/base/AppStatus.hpp create mode 100644 libs/brynet/base/Array.hpp create mode 100644 libs/brynet/base/Buffer.hpp create mode 100644 libs/brynet/base/CPP_VERSION.hpp create mode 100644 libs/brynet/base/Noexcept.hpp create mode 100644 libs/brynet/base/NonCopyable.hpp create mode 100644 libs/brynet/base/Packet.hpp create mode 100644 libs/brynet/base/Platform.hpp create mode 100644 libs/brynet/base/Stack.hpp create mode 100644 libs/brynet/base/Timer.hpp create mode 100644 libs/brynet/base/WaitGroup.hpp create mode 100644 libs/brynet/base/crypto/Base64.hpp create mode 100644 libs/brynet/base/crypto/SHA1.hpp create mode 100644 libs/brynet/base/endian/Endian.hpp create mode 100644 libs/brynet/net/AsyncConnector.hpp create mode 100644 libs/brynet/net/Channel.hpp create mode 100644 libs/brynet/net/CurrentThread.hpp create mode 100644 libs/brynet/net/EventLoop.hpp create mode 100644 libs/brynet/net/Exception.hpp create mode 100644 libs/brynet/net/ListenThread.hpp create mode 100644 libs/brynet/net/Poller.hpp create mode 100644 libs/brynet/net/PromiseReceive.hpp create mode 100644 libs/brynet/net/SSLHelper.hpp create mode 100644 libs/brynet/net/Socket.hpp create mode 100644 libs/brynet/net/SocketLibFunction.hpp create mode 100644 libs/brynet/net/SocketLibTypes.hpp create mode 100644 libs/brynet/net/TcpConnection.hpp create mode 100644 libs/brynet/net/TcpService.hpp create mode 100644 libs/brynet/net/detail/AddSocketOptionInfo.hpp create mode 100644 libs/brynet/net/detail/ConnectorDetail.hpp create mode 100644 libs/brynet/net/detail/ConnectorWorkInfo.hpp create mode 100644 libs/brynet/net/detail/IOLoopData.hpp create mode 100644 libs/brynet/net/detail/ListenThreadDetail.hpp create mode 100644 libs/brynet/net/detail/TCPServiceDetail.hpp create mode 100644 libs/brynet/net/detail/WakeupChannel.hpp create mode 100644 libs/brynet/net/http/HttpFormat.hpp create mode 100644 libs/brynet/net/http/HttpParser.hpp create mode 100644 libs/brynet/net/http/HttpService.hpp create mode 100644 libs/brynet/net/http/WebSocketFormat.hpp create mode 100644 libs/brynet/net/http/http_parser.h create mode 100644 libs/brynet/net/port/Win.hpp create mode 100644 libs/brynet/net/wrapper/ConnectionBuilder.hpp create mode 100644 libs/brynet/net/wrapper/HttpConnectionBuilder.hpp create mode 100644 libs/brynet/net/wrapper/HttpServiceBuilder.hpp create mode 100644 libs/brynet/net/wrapper/ServiceBuilder.hpp create mode 100644 libs/rapidjson/allocators.h create mode 100644 libs/rapidjson/cursorstreamwrapper.h create mode 100644 libs/rapidjson/document.h create mode 100644 libs/rapidjson/encodedstream.h create mode 100644 libs/rapidjson/encodings.h create mode 100644 libs/rapidjson/error/en.h create mode 100644 libs/rapidjson/error/error.h create mode 100644 libs/rapidjson/filereadstream.h create mode 100644 libs/rapidjson/filewritestream.h create mode 100644 libs/rapidjson/fwd.h create mode 100644 libs/rapidjson/internal/biginteger.h create mode 100644 libs/rapidjson/internal/clzll.h create mode 100644 libs/rapidjson/internal/diyfp.h create mode 100644 libs/rapidjson/internal/dtoa.h create mode 100644 libs/rapidjson/internal/ieee754.h create mode 100644 libs/rapidjson/internal/itoa.h create mode 100644 libs/rapidjson/internal/meta.h create mode 100644 libs/rapidjson/internal/pow10.h create mode 100644 libs/rapidjson/internal/regex.h create mode 100644 libs/rapidjson/internal/stack.h create mode 100644 libs/rapidjson/internal/strfunc.h create mode 100644 libs/rapidjson/internal/strtod.h create mode 100644 libs/rapidjson/internal/swap.h create mode 100644 libs/rapidjson/istreamwrapper.h create mode 100644 libs/rapidjson/memorybuffer.h create mode 100644 libs/rapidjson/memorystream.h create mode 100644 libs/rapidjson/msinttypes/inttypes.h create mode 100644 libs/rapidjson/msinttypes/stdint.h create mode 100644 libs/rapidjson/ostreamwrapper.h create mode 100644 libs/rapidjson/pointer.h create mode 100644 libs/rapidjson/prettywriter.h create mode 100644 libs/rapidjson/rapidjson.h create mode 100644 libs/rapidjson/reader.h create mode 100644 libs/rapidjson/schema.h create mode 100644 libs/rapidjson/stream.h create mode 100644 libs/rapidjson/stringbuffer.h create mode 100644 libs/rapidjson/writer.h create mode 100644 main.cpp create mode 100755 server diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..57e5e59 --- /dev/null +++ b/SConstruct @@ -0,0 +1,616 @@ +#!/usr/bin/env python + +EnsureSConsVersion(0, 98, 1) + +# System +import glob +import os +import pickle +import sys +from collections import OrderedDict + +# Local +import methods +import gles_builders +from platform_methods import run_in_subprocess + +# scan possible build platforms + +platform_list = [] # list of platforms +platform_opts = {} # options for each platform +platform_flags = {} # flags for each platform + +active_platforms = [] +active_platform_ids = [] +platform_exporters = [] +platform_apis = [] + +for x in sorted(glob.glob("platform/*")): + if not os.path.isdir(x) or not os.path.exists(x + "/detect.py"): + continue + tmppath = "./" + x + + sys.path.insert(0, tmppath) + import detect + + if os.path.exists(x + "/export/export.cpp"): + platform_exporters.append(x[9:]) + if os.path.exists(x + "/api/api.cpp"): + platform_apis.append(x[9:]) + if detect.is_active(): + active_platforms.append(detect.get_name()) + active_platform_ids.append(x) + if detect.can_build(): + x = x.replace("platform/", "") # rest of world + x = x.replace("platform\\", "") # win32 + platform_list += [x] + platform_opts[x] = detect.get_opts() + platform_flags[x] = detect.get_flags() + sys.path.remove(tmppath) + sys.modules.pop("detect") + +methods.save_active_platforms(active_platforms, active_platform_ids) + +custom_tools = ["default"] + +platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False)) + +if os.name == "nt" and (platform_arg == "android" or ARGUMENTS.get("use_mingw", False)): + custom_tools = ["mingw"] +elif platform_arg == "javascript": + # Use generic POSIX build toolchain for Emscripten. + custom_tools = ["cc", "c++", "ar", "link", "textfile", "zip"] + +env_base = Environment(tools=custom_tools) +if "TERM" in os.environ: + env_base["ENV"]["TERM"] = os.environ["TERM"] +env_base.AppendENVPath("PATH", os.getenv("PATH")) +env_base.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH")) +env_base.disabled_modules = [] +env_base.use_ptrcall = False +env_base.module_version_string = "" +env_base.msvc = False + +env_base.__class__.disable_module = methods.disable_module + +env_base.__class__.add_module_version_string = methods.add_module_version_string + +env_base.__class__.add_source_files = methods.add_source_files +env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix +env_base.__class__.split_lib = methods.split_lib + +env_base.__class__.add_shared_library = methods.add_shared_library +env_base.__class__.add_library = methods.add_library +env_base.__class__.add_program = methods.add_program +env_base.__class__.CommandNoCache = methods.CommandNoCache +env_base.__class__.disable_warnings = methods.disable_warnings + +env_base["x86_libtheora_opt_gcc"] = False +env_base["x86_libtheora_opt_vc"] = False + +# avoid issues when building with different versions of python out of the same directory +env_base.SConsignFile(".sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)) + +# Build options + +customs = ["custom.py"] + +profile = ARGUMENTS.get("profile", False) +if profile: + if os.path.isfile(profile): + customs.append(profile) + elif os.path.isfile(profile + ".py"): + customs.append(profile + ".py") + +opts = Variables(customs, ARGUMENTS) + +# Target build options +opts.Add("arch", "Platform-dependent architecture (arm/arm64/x86/x64/mips/...)", "") +opts.Add(EnumVariable("bits", "Target platform bits", "default", ("default", "32", "64"))) +opts.Add("p", "Platform (alias for 'platform')", "") +opts.Add("platform", "Target platform (%s)" % ("|".join(platform_list),), "") +opts.Add(EnumVariable("target", "Compilation target", "debug", ("debug", "release_debug", "release"))) +opts.Add(EnumVariable("optimize", "Optimization type", "speed", ("speed", "size"))) +opts.Add(BoolVariable("tools", "Build the tools (a.k.a. the Godot editor)", True)) +opts.Add(BoolVariable("use_lto", "Use link-time optimization", False)) +opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False)) + +# Components +opts.Add(BoolVariable("deprecated", "Enable deprecated features", True)) +opts.Add(BoolVariable("gdscript", "Enable GDScript support", True)) +opts.Add(BoolVariable("minizip", "Enable ZIP archive support using minizip", True)) +opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False)) +opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "") + +# Advanced options +opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False)) +opts.Add(BoolVariable("progress", "Show a progress indicator during compilation", True)) +opts.Add(EnumVariable("warnings", "Level of compilation warnings", "all", ("extra", "all", "moderate", "no"))) +opts.Add(BoolVariable("werror", "Treat compiler warnings as errors", False)) +opts.Add(BoolVariable("dev", "If yes, alias for verbose=yes warnings=extra werror=yes", False)) +opts.Add("extra_suffix", "Custom extra suffix added to the base filename of all generated binary files", "") +opts.Add(BoolVariable("vsproj", "Generate a Visual Studio solution", False)) +opts.Add(EnumVariable("macports_clang", "Build using Clang from MacPorts", "no", ("no", "5.0", "devel"))) +opts.Add( + BoolVariable( + "split_libmodules", + "Split intermediate libmodules.a in smaller chunks to prevent exceeding linker command line size (forced to True when using MinGW)", + False, + ) +) +opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False)) +opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False)) +opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", False)) +opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "") + +# Thirdparty libraries + +# Compilation environment setup +opts.Add("CXX", "C++ compiler") +opts.Add("CC", "C compiler") +opts.Add("LINK", "Linker") +opts.Add("CCFLAGS", "Custom flags for both the C and C++ compilers") +opts.Add("CFLAGS", "Custom flags for the C compiler") +opts.Add("CXXFLAGS", "Custom flags for the C++ compiler") +opts.Add("LINKFLAGS", "Custom flags for the linker") + +# add platform specific options + +for k in platform_opts.keys(): + opt_list = platform_opts[k] + for o in opt_list: + opts.Add(o) + +# Update the environment now as the "custom_modules" option may be +# defined in a file rather than specified via the command line. +opts.Update(env_base) + +# Detect modules. +modules_detected = OrderedDict() +module_search_paths = ["modules"] # Built-in path. + +if env_base["custom_modules"]: + paths = env_base["custom_modules"].split(",") + for p in paths: + try: + module_search_paths.append(methods.convert_custom_modules_path(p)) + except ValueError as e: + print(e) + sys.exit(255) + +for path in module_search_paths: + # Note: custom modules can override built-in ones. + modules_detected.update(methods.detect_modules(path)) + include_path = os.path.dirname(path) + if include_path: + env_base.Prepend(CPPPATH=[include_path]) + +# Add module options +for name, path in modules_detected.items(): + enabled = True + sys.path.insert(0, path) + import config + + try: + enabled = config.is_enabled() + except AttributeError: + pass + sys.path.remove(path) + sys.modules.pop("config") + opts.Add(BoolVariable("module_" + name + "_enabled", "Enable module '%s'" % (name,), enabled)) + +methods.write_modules(modules_detected) + +# Update the environment again after all the module options are added. +opts.Update(env_base) +Help(opts.GenerateHelpText(env_base)) + +# add default include paths + +env_base.Prepend(CPPPATH=["#"]) + +# configure ENV for platform +env_base.platform_exporters = platform_exporters +env_base.platform_apis = platform_apis + +if env_base["use_precise_math_checks"]: + env_base.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"]) + +if env_base["target"] == "debug": + env_base.Append(CPPDEFINES=["DEBUG_MEMORY_ALLOC", "DISABLE_FORCED_INLINE"]) + + # The two options below speed up incremental builds, but reduce the certainty that all files + # will properly be rebuilt. As such, we only enable them for debug (dev) builds, not release. + + # To decide whether to rebuild a file, use the MD5 sum only if the timestamp has changed. + # http://scons.org/doc/production/HTML/scons-user/ch06.html#idm139837621851792 + env_base.Decider("MD5-timestamp") + # Use cached implicit dependencies by default. Can be overridden by specifying `--implicit-deps-changed` in the command line. + # http://scons.org/doc/production/HTML/scons-user/ch06s04.html + env_base.SetOption("implicit_cache", 1) + +if env_base["no_editor_splash"]: + env_base.Append(CPPDEFINES=["NO_EDITOR_SPLASH"]) + +if not env_base["deprecated"]: + env_base.Append(CPPDEFINES=["DISABLE_DEPRECATED"]) + +env_base.platforms = {} + +selected_platform = "" + +if env_base["platform"] != "": + selected_platform = env_base["platform"] +elif env_base["p"] != "": + selected_platform = env_base["p"] + env_base["platform"] = selected_platform +else: + # Missing `platform` argument, try to detect platform automatically + if sys.platform.startswith("linux"): + selected_platform = "x11" + elif sys.platform == "darwin": + selected_platform = "osx" + elif sys.platform == "win32": + selected_platform = "windows" + else: + print("Could not detect platform automatically. Supported platforms:") + for x in platform_list: + print("\t" + x) + print("\nPlease run SCons again and select a valid platform: platform=") + + if selected_platform != "": + print("Automatically detected platform: " + selected_platform) + env_base["platform"] = selected_platform + +if selected_platform in ["linux", "bsd", "linuxbsd"]: + if selected_platform == "linuxbsd": + # Alias for forward compatibility. + print('Platform "linuxbsd" is still called "x11" in Godot 3.2.x. Building for platform "x11".') + # Alias for convenience. + selected_platform = "x11" + env_base["platform"] = selected_platform + +if selected_platform in platform_list: + tmppath = "./platform/" + selected_platform + sys.path.insert(0, tmppath) + import detect + + if "create" in dir(detect): + env = detect.create(env_base) + else: + env = env_base.Clone() + + # Compilation DB requires SCons 3.1.1+. + from SCons import __version__ as scons_raw_version + + scons_ver = env._get_major_minor_revision(scons_raw_version) + + if scons_ver >= (4, 0, 0): + env.Tool("compilation_db") + env.Alias("compiledb", env.CompilationDatabase()) + + if env["dev"]: + env["verbose"] = True + env["warnings"] = "extra" + env["werror"] = True + + env.extra_suffix = "" + + if env["extra_suffix"] != "": + env.extra_suffix += "." + env["extra_suffix"] + + # Environment flags + CCFLAGS = env.get("CCFLAGS", "") + env["CCFLAGS"] = "" + env.Append(CCFLAGS=str(CCFLAGS).split()) + + CFLAGS = env.get("CFLAGS", "") + env["CFLAGS"] = "" + env.Append(CFLAGS=str(CFLAGS).split()) + + CXXFLAGS = env.get("CXXFLAGS", "") + env["CXXFLAGS"] = "" + env.Append(CXXFLAGS=str(CXXFLAGS).split()) + + LINKFLAGS = env.get("LINKFLAGS", "") + env["LINKFLAGS"] = "" + env.Append(LINKFLAGS=str(LINKFLAGS).split()) + + # Platform specific flags + flag_list = platform_flags[selected_platform] + for f in flag_list: + if not (f[0] in ARGUMENTS): # allow command line to override platform flags + env[f[0]] = f[1] + + # Must happen after the flags definition, so that they can be used by platform detect + detect.configure(env) + + # Set our C and C++ standard requirements. + # Prepending to make it possible to override + # This needs to come after `configure`, otherwise we don't have env.msvc. + if not env.msvc: + # Specifying GNU extensions support explicitly, which are supported by + # both GCC and Clang. This mirrors GCC and Clang's current default + # compile flags if no -std is specified. + env.Prepend(CFLAGS=["-std=gnu11"]) + env.Prepend(CXXFLAGS=["-std=gnu++14"]) + else: + # MSVC doesn't have clear C standard support, /std only covers C++. + # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features. + env.Prepend(CCFLAGS=["/std:c++14"]) + + # Configure compiler warnings + if env.msvc: + # Truncations, narrowing conversions, signed/unsigned comparisons... + disable_nonessential_warnings = ["/wd4267", "/wd4244", "/wd4305", "/wd4018", "/wd4800"] + if env["warnings"] == "extra": + env.Append(CCFLAGS=["/Wall"]) # Implies /W4 + elif env["warnings"] == "all": + env.Append(CCFLAGS=["/W3"] + disable_nonessential_warnings) + elif env["warnings"] == "moderate": + env.Append(CCFLAGS=["/W2"] + disable_nonessential_warnings) + else: # 'no' + env.Append(CCFLAGS=["/w"]) + # Set exception handling model to avoid warnings caused by Windows system headers. + env.Append(CCFLAGS=["/EHsc"]) + if env["werror"]: + env.Append(CCFLAGS=["/WX"]) + # Force to use Unicode encoding + env.Append(MSVC_FLAGS=["/utf8"]) + else: # Rest of the world + version = methods.get_compiler_version(env) or [-1, -1] + + shadow_local_warning = [] + all_plus_warnings = ["-Wwrite-strings"] + + if methods.using_gcc(env): + env.Append(CCFLAGS=["-Wno-misleading-indentation"]) + if version[0] >= 7: + shadow_local_warning = ["-Wshadow-local"] + + if env["warnings"] == "extra": + # Note: enable -Wimplicit-fallthrough for Clang (already part of -Wextra for GCC) + # once we switch to C++11 or later (necessary for our FALLTHROUGH macro). + env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wno-unused-parameter"] + all_plus_warnings + shadow_local_warning) + env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"]) + if methods.using_gcc(env): + env.Append( + CCFLAGS=[ + "-Walloc-zero", + "-Wduplicated-branches", + "-Wduplicated-cond", + "-Wstringop-overflow=4", + "-Wlogical-op", + ] + ) + env.Append(CXXFLAGS=["-Wnoexcept", "-Wplacement-new=1"]) + if version[0] >= 9: + env.Append(CCFLAGS=["-Wattribute-alias=2"]) + elif env["warnings"] == "all": + env.Append(CCFLAGS=["-Wall"] + shadow_local_warning) + elif env["warnings"] == "moderate": + env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + shadow_local_warning) + else: # 'no' + env.Append(CCFLAGS=["-w"]) + if env["werror"]: + env.Append(CCFLAGS=["-Werror"]) + else: # always enable those errors + env.Append(CCFLAGS=["-Werror=return-type"]) + + if hasattr(detect, "get_program_suffix"): + suffix = "." + detect.get_program_suffix() + else: + suffix = "." + selected_platform + + if env["target"] == "release": + if env["tools"]: + print("Tools can only be built with targets 'debug' and 'release_debug'.") + sys.exit(255) + suffix += ".opt" + env.Append(CPPDEFINES=["NDEBUG"]) + + elif env["target"] == "release_debug": + if env["tools"]: + suffix += ".opt.tools" + else: + suffix += ".opt.debug" + else: + if env["tools"]: + suffix += ".tools" + else: + suffix += ".debug" + + if env["arch"] != "": + suffix += "." + env["arch"] + elif env["bits"] == "32": + suffix += ".32" + elif env["bits"] == "64": + suffix += ".64" + + suffix += env.extra_suffix + + sys.path.remove(tmppath) + sys.modules.pop("detect") + + modules_enabled = OrderedDict() + env.module_icons_paths = [] + env.doc_class_path = {} + + for name, path in modules_detected.items(): + if not env["module_" + name + "_enabled"]: + continue + sys.path.insert(0, path) + env.current_module = name + import config + + # can_build changed number of arguments between 3.0 (1) and 3.1 (2), + # so try both to preserve compatibility for 3.0 modules + can_build = False + try: + can_build = config.can_build(env, selected_platform) + except TypeError: + print( + "Warning: module '%s' uses a deprecated `can_build` " + "signature in its config.py file, it should be " + "`can_build(env, platform)`." % x + ) + can_build = config.can_build(selected_platform) + if can_build: + config.configure(env) + # Get doc classes paths (if present) + try: + doc_classes = config.get_doc_classes() + doc_path = config.get_doc_path() + for c in doc_classes: + env.doc_class_path[c] = path + "/" + doc_path + except: + pass + # Get icon paths (if present) + try: + icons_path = config.get_icons_path() + env.module_icons_paths.append(path + "/" + icons_path) + except: + # Default path for module icons + env.module_icons_paths.append(path + "/" + "icons") + modules_enabled[name] = path + + sys.path.remove(path) + sys.modules.pop("config") + + env.module_list = modules_enabled + + methods.update_version(env.module_version_string) + + env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"] + env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"] + # (SH)LIBSUFFIX will be used for our own built libraries + # LIBSUFFIXES contains LIBSUFFIX and SHLIBSUFFIX by default, + # so we need to append the default suffixes to keep the ability + # to link against thirdparty libraries (.a, .so, .lib, etc.). + if os.name == "nt": + # On Windows, only static libraries and import libraries can be + # statically linked - both using .lib extension + env["LIBSUFFIXES"] += [env["LIBSUFFIX"]] + else: + env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]] + env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"] + env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"] + + if env.use_ptrcall: + env.Append(CPPDEFINES=["PTRCALL_ENABLED"]) + if env["tools"]: + env.Append(CPPDEFINES=["TOOLS_ENABLED"]) + if env["disable_3d"]: + if env["tools"]: + print( + "Build option 'disable_3d=yes' cannot be used with 'tools=yes' (editor), " + "only with 'tools=no' (export template)." + ) + sys.exit(255) + else: + env.Append(CPPDEFINES=["_3D_DISABLED"]) + if env["gdscript"]: + env.Append(CPPDEFINES=["GDSCRIPT_ENABLED"]) + if env["disable_advanced_gui"]: + if env["tools"]: + print( + "Build option 'disable_advanced_gui=yes' cannot be used with 'tools=yes' (editor), " + "only with 'tools=no' (export template)." + ) + sys.exit(255) + else: + env.Append(CPPDEFINES=["ADVANCED_GUI_DISABLED"]) + if env["minizip"]: + env.Append(CPPDEFINES=["MINIZIP_ENABLED"]) + + editor_module_list = ["regex"] + for x in editor_module_list: + if not env["module_" + x + "_enabled"]: + if env["tools"]: + print( + "Build option 'module_" + x + "_enabled=no' cannot be used with 'tools=yes' (editor), " + "only with 'tools=no' (export template)." + ) + sys.exit(255) + + if not env["verbose"]: + methods.no_verbose(sys, env) + + if not env["platform"] == "server": # FIXME: detect GLES3 + env.Append( + BUILDERS={ + "GLES3_GLSL": env.Builder( + action=run_in_subprocess(gles_builders.build_gles3_headers), suffix="glsl.gen.h", src_suffix=".glsl" + ) + } + ) + env.Append( + BUILDERS={ + "GLES2_GLSL": env.Builder( + action=run_in_subprocess(gles_builders.build_gles2_headers), suffix="glsl.gen.h", src_suffix=".glsl" + ) + } + ) + + scons_cache_path = os.environ.get("SCONS_CACHE") + if scons_cache_path != None: + CacheDir(scons_cache_path) + print("Scons cache enabled... (path: '" + scons_cache_path + "')") + + if env["vsproj"]: + env.vs_incs = [] + env.vs_srcs = [] + + Export("env") + + # build subdirs, the build order is dependent on link order. + + SConscript("core/SCsub") + SConscript("servers/SCsub") + SConscript("scene/SCsub") + SConscript("editor/SCsub") + SConscript("drivers/SCsub") + + SConscript("platform/SCsub") + SConscript("modules/SCsub") + SConscript("main/SCsub") + + SConscript("platform/" + selected_platform + "/SCsub") # build selected platform + + # Microsoft Visual Studio Project Generation + if env["vsproj"]: + env["CPPPATH"] = [Dir(path) for path in env["CPPPATH"]] + methods.generate_vs_project(env, GetOption("num_jobs")) + methods.generate_cpp_hint_file("cpp.hint") + + # Check for the existence of headers + conf = Configure(env) + if "check_c_headers" in env: + for header in env["check_c_headers"]: + if conf.CheckCHeader(header[0]): + env.AppendUnique(CPPDEFINES=[header[1]]) + +elif selected_platform != "": + if selected_platform == "list": + print("The following platforms are available:\n") + else: + print('Invalid target platform "' + selected_platform + '".') + print("The following platforms were detected:\n") + + for x in platform_list: + print("\t" + x) + + print("\nPlease run SCons again and select a valid platform: platform=") + + if selected_platform == "list": + # Exit early to suppress the rest of the built-in SCons messages + sys.exit(0) + else: + sys.exit(255) + +# The following only makes sense when the 'env' is defined, and assumes it is. +if "env" in locals(): + methods.show_progress(env) + # TODO: replace this with `env.Dump(format="json")` + # once we start requiring SCons 4.0 as min version. + methods.dump(env) diff --git a/compile_linux.sh b/compile_linux.sh new file mode 100755 index 0000000..1b1d587 --- /dev/null +++ b/compile_linux.sh @@ -0,0 +1 @@ +g++ -o3 cout_server.cpp -o server -Ilibs -lpthread diff --git a/cout_server.cpp b/cout_server.cpp new file mode 100644 index 0000000..7eb5a65 --- /dev/null +++ b/cout_server.cpp @@ -0,0 +1,80 @@ +#include +#include +#include + +#include +#include +#include +#include + +using namespace brynet; +using namespace brynet::net; + +std::atomic_llong TotalRecvSize = ATOMIC_VAR_INIT(0); +std::atomic_llong total_client_num = ATOMIC_VAR_INIT(0); +std::atomic_llong total_packet_num = ATOMIC_VAR_INIT(0); + +int main(int argc, char **argv) +{ + if (argc != 3) + { + fprintf(stderr, "Usage: \n"); + exit(-1); + } + + auto service = TcpService::Create(); + service->startWorkerThread(atoi(argv[2])); + + auto enterCallback = [](const TcpConnection::Ptr& session) { + total_client_num++; + + session->setDataCallback([session](const char* buffer, size_t len) { + session->send(buffer, len); + TotalRecvSize += len; + total_packet_num++; + + std::cout << "------------------------------------- PACKET -------------------------------------" << std::endl; + std::cout << buffer << std::endl; + std::cout << "------------------------------------------------------------------------------------" << std::endl << std::endl; + + return len; + }); + + session->setDisConnectCallback([](const TcpConnection::Ptr& session) { + (void)session; + total_client_num--; + }); + }; + + wrapper::ListenerBuilder listener; + listener.configureService(service) + .configureSocketOptions({ + [](TcpSocket& socket) { + socket.setNodelay(); + } + }) + .configureConnectionOptions({ + brynet::net::AddSocketOption::WithMaxRecvBufferSize(1024 * 1024), + brynet::net::AddSocketOption::AddEnterCallback(enterCallback) + }) + .configureListen([=](wrapper::BuildListenConfig config) { + config.setAddr(false, "0.0.0.0", atoi(argv[1])); + }) + .asyncRun(); + + EventLoop mainLoop; + while (true) + { + mainLoop.loop(1000); + + total_packet_num = 0; + TotalRecvSize = 0; + + if (brynet::base::app_kbhit()) + { + break; + } + } + + return 0; +} diff --git a/libs/HEADS b/libs/HEADS new file mode 100644 index 0000000..e129af9 --- /dev/null +++ b/libs/HEADS @@ -0,0 +1,2 @@ +RapidJSON 0ccdbf364c577803e2a751f5aededce935314313 +brynet 5ddb2cb2b46e5e7730327217932d0d3daf8e5b2a diff --git a/libs/brynet/Version.hpp b/libs/brynet/Version.hpp new file mode 100644 index 0000000..97f985a --- /dev/null +++ b/libs/brynet/Version.hpp @@ -0,0 +1,3 @@ +#pragma once + +#define BRYNET_VERSION 1008000 diff --git a/libs/brynet/base/Any.hpp b/libs/brynet/base/Any.hpp new file mode 100644 index 0000000..6e3d58a --- /dev/null +++ b/libs/brynet/base/Any.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#ifdef BRYNET_HAVE_LANG_CXX17 +#include +#else +#include +#endif + +namespace brynet { namespace base { + +#ifdef BRYNET_HAVE_LANG_CXX17 + using BrynetAny = std::any; + + template + auto cast(const BrynetAny& ud) + { + return std::any_cast(&ud); + } +#else + using BrynetAny = int64_t; + template + const T* cast(const BrynetAny& ud) + { + return static_cast(&ud); + } +#endif + +} } diff --git a/libs/brynet/base/AppStatus.hpp b/libs/brynet/base/AppStatus.hpp new file mode 100644 index 0000000..88867fc --- /dev/null +++ b/libs/brynet/base/AppStatus.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef BRYNET_PLATFORM_WINDOWS +#include +#else +#include +#include +#include +#endif + +namespace brynet { namespace base { + + static bool app_kbhit() + { +#ifdef BRYNET_PLATFORM_WINDOWS + return _kbhit(); +#else + struct termios oldt; + tcgetattr(STDIN_FILENO, &oldt); + auto newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + const auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); + + const auto ch = getchar(); + + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + fcntl(STDIN_FILENO, F_SETFL, oldf); + + if (ch != EOF) + { + ungetc(ch, stdin); + return true; + } + + return false; +#endif + } + +} } \ No newline at end of file diff --git a/libs/brynet/base/Array.hpp b/libs/brynet/base/Array.hpp new file mode 100644 index 0000000..0b0624f --- /dev/null +++ b/libs/brynet/base/Array.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include +#include + +namespace brynet { namespace base { + + struct array_s + { + void* buffer; + size_t buffer_size; + size_t element_size; + size_t element_num; + }; + + static void array_delete(struct array_s* self) + { + if (self == nullptr) + { + return; + } + + if (self->buffer != nullptr) + { + free(self->buffer); + self->buffer = nullptr; + } + + self->element_num = 0; + free(self); + self = nullptr; + } + + static struct array_s* array_new(size_t num, size_t element_size) + { + auto ret = (struct array_s*)malloc(sizeof(struct array_s)); + if (ret == nullptr) + { + return nullptr; + } + + const auto buffer_size = num * element_size; + + ret->buffer_size = 0; + ret->element_size = 0; + ret->element_num = 0; + ret->buffer = malloc(buffer_size); + + if (ret->buffer != nullptr) + { + ret->element_size = element_size; + ret->element_num = num; + ret->buffer_size = buffer_size; + } + else + { + array_delete(ret); + ret = nullptr; + } + + return ret; + } + + static void* array_at(struct array_s* self, size_t index) + { + void* ret = nullptr; + + if (index < self->element_num) + { + ret = (char*)(self->buffer) + (index * self->element_size); + } + else + { + assert(false); + } + + return ret; + } + + static bool array_set(struct array_s* self, size_t index, const void* data) + { + void* old_data = array_at(self, index); + + if (old_data != nullptr) + { + memcpy(old_data, data, self->element_size); + return true; + } + else + { + return false; + } + } + + static bool array_increase(struct array_s* self, size_t increase_num) + { + if (increase_num == 0) + { + return false; + } + + const auto new_buffer_size = self->buffer_size + increase_num * self->element_size; + auto new_buffer = malloc(new_buffer_size); + + if (new_buffer != nullptr) + { + memcpy(new_buffer, self->buffer, self->buffer_size); + free(self->buffer); + self->buffer = new_buffer; + self->element_num += increase_num; + self->buffer_size = new_buffer_size; + + return true; + } + else + { + assert(false); + return false; + } + } + + static size_t array_num(const struct array_s* self) + { + return self->element_num; + } + +} } \ No newline at end of file diff --git a/libs/brynet/base/Buffer.hpp b/libs/brynet/base/Buffer.hpp new file mode 100644 index 0000000..f1ed8b3 --- /dev/null +++ b/libs/brynet/base/Buffer.hpp @@ -0,0 +1,189 @@ +#pragma once + +#include +#include +#include +#include + +namespace brynet { namespace base { + + struct buffer_s + { + char* data; + size_t data_len; + + size_t write_pos; + size_t read_pos; + }; + + static void buffer_delete(struct buffer_s* self) + { + if (self == nullptr) + { + return; + } + + if (self->data != nullptr) + { + free(self->data); + self->data = nullptr; + } + + free(self); + self = nullptr; + } + + static struct buffer_s* buffer_new(size_t buffer_size) + { + struct buffer_s* ret = (struct buffer_s*)malloc(sizeof(struct buffer_s)); + if (ret == nullptr) + { + return nullptr; + } + + ret->data_len = 0; + ret->read_pos = 0; + ret->write_pos = 0; + ret->data = (char*)malloc(sizeof(char) * buffer_size); + + if (ret->data != nullptr) + { + ret->data_len = buffer_size; + } + else + { + buffer_delete(ret); + ret = nullptr; + } + + return ret; + } + + static size_t buffer_getreadvalidcount(struct buffer_s* self) + { + return self->write_pos - self->read_pos; + } + + static void buffer_adjustto_head(struct buffer_s* self) + { + if (self->read_pos == 0) + { + return; + } + + const auto len = buffer_getreadvalidcount(self); + if (len > 0) + { + memmove(self->data, self->data + self->read_pos, len); + } + + self->read_pos = 0; + self->write_pos = len; + } + + static void buffer_init(struct buffer_s* self) + { + self->read_pos = 0; + self->write_pos = 0; + } + + static size_t buffer_getwritepos(struct buffer_s* self) + { + return self->write_pos; + } + + static size_t buffer_getreadpos(struct buffer_s* self) + { + return self->read_pos; + } + + static bool buffer_addwritepos(struct buffer_s* self, size_t value) + { + const size_t temp = self->write_pos + value; + if (temp <= self->data_len) + { + self->write_pos = temp; + return true; + } + else + { + return false; + } + } + + static bool buffer_addreadpos(struct buffer_s* self, size_t value) + { + const size_t temp = self->read_pos + value; + if (temp <= self->data_len) + { + self->read_pos = temp; + return true; + } + else + { + return false; + } + } + + static size_t buffer_getwritevalidcount(struct buffer_s* self) + { + return self->data_len - self->write_pos; + } + + static size_t buffer_getsize(struct buffer_s* self) + { + return self->data_len; + } + + static char* buffer_getwriteptr(struct buffer_s* self) + { + if (self->write_pos < self->data_len) + { + return self->data + self->write_pos; + } + else + { + return nullptr; + } + } + + static char* buffer_getreadptr(struct buffer_s* self) + { + if (self->read_pos < self->data_len) + { + return self->data + self->read_pos; + } + else + { + return nullptr; + } + } + + static bool buffer_write(struct buffer_s* self, const char* data, size_t len) + { + bool write_ret = true; + + if (buffer_getwritevalidcount(self) >= len) + { + /* ֱд */ + memcpy(buffer_getwriteptr(self), data, len); + buffer_addwritepos(self, len); + } + else + { + size_t left_len = self->data_len - buffer_getreadvalidcount(self); + if (left_len >= len) + { + buffer_adjustto_head(self); + buffer_write(self, data, len); + } + else + { + write_ret = false; + } + } + + return write_ret; + } + +} } \ No newline at end of file diff --git a/libs/brynet/base/CPP_VERSION.hpp b/libs/brynet/base/CPP_VERSION.hpp new file mode 100644 index 0000000..fb65c91 --- /dev/null +++ b/libs/brynet/base/CPP_VERSION.hpp @@ -0,0 +1,16 @@ +#pragma once + +#if (__cplusplus >= 201103L || \ + (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define BRYNET_HAVE_LANG_CXX11 1 +#endif + +#if (__cplusplus >= 201402L || \ + (defined(_MSC_VER) && _MSC_VER >= 1900)) +#define BRYNET_HAVE_LANG_CXX14 1 +#endif + +#if (__cplusplus >= 201703L || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) +#define BRYNET_HAVE_LANG_CXX17 1 +#endif \ No newline at end of file diff --git a/libs/brynet/base/Noexcept.hpp b/libs/brynet/base/Noexcept.hpp new file mode 100644 index 0000000..316de5e --- /dev/null +++ b/libs/brynet/base/Noexcept.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +#ifdef BRYNET_HAVE_LANG_CXX17 +#define BRYNET_NOEXCEPT noexcept +#else +#define BRYNET_NOEXCEPT +#endif diff --git a/libs/brynet/base/NonCopyable.hpp b/libs/brynet/base/NonCopyable.hpp new file mode 100644 index 0000000..5de4493 --- /dev/null +++ b/libs/brynet/base/NonCopyable.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace brynet { namespace base { + + class NonCopyable + { + public: + NonCopyable(const NonCopyable&) = delete; + const NonCopyable& operator=(const NonCopyable&) = delete; + + protected: + NonCopyable() = default; + ~NonCopyable() = default; + }; + +} } \ No newline at end of file diff --git a/libs/brynet/base/Packet.hpp b/libs/brynet/base/Packet.hpp new file mode 100644 index 0000000..8cd451d --- /dev/null +++ b/libs/brynet/base/Packet.hpp @@ -0,0 +1,435 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace brynet { namespace base { + + class BasePacketWriter : public NonCopyable + { + public: + BasePacketWriter(char* buffer, + size_t len, + bool useBigEndian = false, + bool isAutoMalloc = false) + : + mIsAutoMalloc(isAutoMalloc), + mBigEndian(useBigEndian) + { + mMaxLen = len; + mPos = 0; + mBuffer = buffer; + mMallocBuffer = nullptr; + } + + virtual ~BasePacketWriter() + { + if (mMallocBuffer != nullptr) + { + free(mMallocBuffer); + mMallocBuffer = nullptr; + } + } + + void init() + { + mPos = 0; + } + + size_t getMaxLen() const + { + return mMaxLen; + } + + size_t getPos() const + { + return mPos; + } + + const char* getData() const + { + return mBuffer; + } + + bool isAutoGrow() const + { + return mIsAutoMalloc; + } + + bool writeBool(bool value) + { + static_assert(sizeof(bool) == sizeof(int8_t), ""); + return writeBuffer((char*)&value, sizeof(value)); + } + bool writeINT8(int8_t value) + { + return writeBuffer((char*)&value, sizeof(value)); + } + bool writeUINT8(uint8_t value) + { + return writeBuffer((char*)&value, sizeof(value)); + } + bool writeINT16(int16_t value) + { + value = endian::hostToNetwork16(value, mBigEndian); + return writeBuffer((char*)&value, sizeof(value)); + } + bool writeUINT16(uint16_t value) + { + value = endian::hostToNetwork16(value, mBigEndian); + return writeBuffer((char*)&value, sizeof(value)); + } + bool writeINT32(int32_t value) + { + value = endian::hostToNetwork32(value, mBigEndian); + return writeBuffer((char*)&value, sizeof(value)); + } + bool writeUINT32(uint32_t value) + { + value = endian::hostToNetwork32(value, mBigEndian); + return writeBuffer((char*)&value, sizeof(value)); + } + bool writeINT64(int64_t value) + { + value = endian::hostToNetwork64(value, mBigEndian); + return writeBuffer((char*)&value, sizeof(value)); + } + bool writeUINT64(uint64_t value) + { + value = endian::hostToNetwork64(value, mBigEndian); + return writeBuffer((char*)&value, sizeof(value)); + } + + bool writeBinary(const std::string& binary) + { + return writeBuffer(binary.c_str(), binary.size()); + } + bool writeBinary(const char* binary, size_t binaryLen) + { + return writeBuffer(binary, binaryLen); + } + bool writeBuffer(const char* buffer, size_t len) + { + growBuffer(len); + + if (mMaxLen < (mPos + len)) + { + return false; + } + + memcpy(mBuffer + mPos, buffer, len); + mPos += len; + return true; + } + + BasePacketWriter & operator << (const bool &v) + { + writeBool(v); + return *this; + } + BasePacketWriter & operator << (const uint8_t &v) + { + writeUINT8(v); + return *this; + } + BasePacketWriter & operator << (const int8_t &v) + { + writeINT8(v); + return *this; + } + BasePacketWriter & operator << (const int16_t &v) + { + writeINT16(v); + return *this; + } + BasePacketWriter & operator << (const uint16_t &v) + { + writeUINT16(v); + return *this; + } + BasePacketWriter & operator << (const int32_t &v) + { + writeINT32(v); + return *this; + } + BasePacketWriter & operator << (const uint32_t &v) + { + writeUINT32(v); + return *this; + } + BasePacketWriter & operator << (const int64_t &v) + { + writeINT64(v); + return *this; + } + BasePacketWriter & operator << (const uint64_t &v) + { + writeUINT64(v); + return *this; + } + BasePacketWriter & operator << (const char* const &v) + { + writeBinary(v); + return *this; + } + BasePacketWriter & operator << (const std::string &v) + { + writeBinary(v); + return *this; + } + + private: + // 为了避免直接<<导致没有指定字节序导致隐藏BUG,因为此函数设置为私有 + template + BasePacketWriter & operator << (const T& v) + { + static_assert(!std::is_pointer::value, "T must is't a pointer"); + static_assert(std::is_class ::value, "T must a class or struct type"); + static_assert(std::is_pod ::value, "T must a pod type"); + writeBuffer((const char*)&v, sizeof(v)); + return *this; + } + + template + void writev(const Arg1& arg1, const Args&... args) + { + this->operator<<(arg1); + writev(args...); + } + + void writev() + { + } + + protected: + void growBuffer(size_t len) + { + if (!mIsAutoMalloc || (mPos + len) <= mMaxLen) + { + return; + } + + auto newBuffer = (char*)malloc(mMaxLen + len); + if (newBuffer == nullptr) + { + return; + } + + memcpy(newBuffer, mBuffer, mPos); + + if (mMallocBuffer != nullptr) + { + free(mMallocBuffer); + mMallocBuffer = nullptr; + } + + mMaxLen += len; + mMallocBuffer = newBuffer; + mBuffer = newBuffer; + } + + protected: + const bool mIsAutoMalloc; + bool mBigEndian; + size_t mPos; + size_t mMaxLen; + char* mBuffer; + char* mMallocBuffer; + }; + + class BasePacketReader + { + public: + BasePacketReader(const char* buffer, + size_t len, + bool useBigEndian = false) : + mBigEndian(useBigEndian), + mSize(len) + { + mPos = 0; + mSavedPos = 0; + mBuffer = buffer; + } + + virtual ~BasePacketReader() = default; + + void useBigEndian() + { + mBigEndian = true; + } + + void useLittleEndian() + { + mBigEndian = false; + } + + void savePos() + { + mSavedPos = mPos; + } + + size_t savedPos() const + { + return mSavedPos; + } + + size_t getLeft() const + { + if (mPos > mSize) + { + throw std::out_of_range("current pos is greater than max len"); + } + return mSize - mPos; + } + + bool enough(size_t len) const + { + if (mPos > mSize) + { + return false; + } + return (mSize - mPos) >= len; + } + + const char* begin() const + { + return mBuffer; + } + + const char* currentBuffer() const + { + return mBuffer+mPos; + } + + void consumeAll() + { + mPos = mSize; + savePos(); + } + + size_t currentPos() const + { + return mPos; + } + + size_t size() const + { + return mSize; + } + + void addPos(size_t diff) + { + const auto tmpPos = mPos + diff; + if (tmpPos > mSize) + { + throw std::out_of_range("diff is to big"); + } + + mPos = tmpPos; + } + + bool readBool() + { + static_assert(sizeof(bool) == sizeof(int8_t), ""); + bool value = false; + read(value); + return value; + } + int8_t readINT8() + { + int8_t value = 0; + read(value); + return value; + } + uint8_t readUINT8() + { + uint8_t value = 0; + read(value); + return value; + } + int16_t readINT16() + { + int16_t value = 0; + read(value); + return endian::networkToHost16(value, mBigEndian); + } + uint16_t readUINT16() + { + uint16_t value = 0; + read(value); + return endian::networkToHost16(value, mBigEndian); + } + int32_t readINT32() + { + int32_t value = 0; + read(value); + return endian::networkToHost32(value, mBigEndian); + } + uint32_t readUINT32() + { + uint32_t value = 0; + read(value); + return endian::networkToHost32(value, mBigEndian); + } + int64_t readINT64() + { + int64_t value = 0; + read(value); + return endian::networkToHost64(value, mBigEndian); + } + uint64_t readUINT64() + { + uint64_t value = 0; + read(value); + return endian::networkToHost64(value, mBigEndian); + } + + private: + // 为了避免直接read(uintXXX)导致没有指定字节序造成隐患BUG,因为此函数设置为私有 + template + void read(T& value) + { + static_assert(std::is_same::type>::value, + "T must a normal type"); + static_assert(std::is_pod::value, + "T must a pod type"); + + if ((mPos + sizeof(value)) > mSize) + { + throw std::out_of_range("T size is to big"); + } + + value = *(T*)(mBuffer + mPos); + mPos += sizeof(value); + } + + protected: + bool mBigEndian; + const size_t mSize; + const char* mBuffer; + size_t mPos; + size_t mSavedPos; + }; + + template + class AutoMallocPacket : public BasePacketWriter + { + public: + explicit AutoMallocPacket(bool useBigEndian = false, + bool isAutoMalloc = false) + : + BasePacketWriter(mData, SIZE, useBigEndian, isAutoMalloc) + {} + private: + char mData[SIZE]; + }; + + using BigPacket = AutoMallocPacket<32 * 1024>; + +} } \ No newline at end of file diff --git a/libs/brynet/base/Platform.hpp b/libs/brynet/base/Platform.hpp new file mode 100644 index 0000000..763118c --- /dev/null +++ b/libs/brynet/base/Platform.hpp @@ -0,0 +1,9 @@ +#pragma once + +#if defined _MSC_VER || defined __MINGW32__ +#define BRYNET_PLATFORM_WINDOWS +#elif defined __APPLE_CC__ || defined __APPLE__ +#define BRYNET_PLATFORM_DARWIN +#else +#define BRYNET_PLATFORM_LINUX +#endif diff --git a/libs/brynet/base/Stack.hpp b/libs/brynet/base/Stack.hpp new file mode 100644 index 0000000..339bad3 --- /dev/null +++ b/libs/brynet/base/Stack.hpp @@ -0,0 +1,175 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace brynet { namespace base { + + struct stack_s + { + struct array_s* array; + size_t element_size; + + size_t element_num; + size_t front; /* ջ */ + size_t num; /* ջЧԪشС */ + }; + + static void stack_delete(struct stack_s* self) + { + if (self == nullptr) + { + return; + } + + if (self->array != nullptr) + { + array_delete(self->array); + self->array = nullptr; + } + + self->element_num = 0; + self->front = 0; + self->num = 0; + free(self); + self = nullptr; + } + + static struct stack_s* stack_new(size_t num, size_t element_size) + { + struct stack_s* ret = (struct stack_s*)malloc(sizeof(struct stack_s)); + if (ret == nullptr) + { + return nullptr; + } + + ret->element_size = 0; + ret->element_num = 0; + ret->front = 0; + ret->num = 0; + ret->array = array_new(num, element_size); + + if (ret->array != nullptr) + { + ret->element_size = element_size; + ret->element_num = num; + } + else + { + stack_delete(ret); + ret = nullptr; + } + + return ret; + } + + static void stack_init(struct stack_s* self) + { + self->front = 0; + self->num = 0; + } + + static size_t stack_num(struct stack_s* self) + { + return self->num; + } + + static bool stack_increase(struct stack_s* self, size_t increase_num) + { + struct array_s* tmp = array_new(self->element_num + increase_num, + self->element_size); + if (tmp == nullptr) + { + return false; + } + + { + size_t current_num = self->element_num; + size_t current_stack_num = stack_num(self); + for (size_t i = 0; i < current_stack_num; ++i) + { + array_set(tmp, i, array_at(self->array, (self->front + i) % current_num)); + } + + self->front = 0; + array_delete(self->array); + self->array = tmp; + self->element_num = array_num(self->array); + } + + return true; + } + + static size_t stack_size(struct stack_s* self) + { + return self->element_num; + } + + static bool stack_isfull(struct stack_s* self) + { + return (self->num == self->element_num); + } + + /* stackstack_pushڿռ䲻ʱԶ(ͨstack_increase) */ + static bool stack_push(struct stack_s* self, const void* data) + { + if (stack_isfull(self)) + { + stack_increase(self, stack_size(self)); + } + + if (stack_isfull(self)) + { + return false; + } + + array_set(self->array, (self->front + self->num) % self->element_num, data); + self->num++; + return true; + } + + static void* stack_front(struct stack_s* self) + { + void* ret = nullptr; + + if (stack_num(self) > 0) + { + ret = array_at(self->array, self->front); + } + + return ret; + } + + static void* stack_popfront(struct stack_s* self) + { + void* ret = stack_front(self); + + if (ret != nullptr) + { + self->num--; + self->front++; + self->front %= self->element_num; + } + + return ret; + } + + static void* stack_popback(struct stack_s* self) + { + void* ret = nullptr; + + if (stack_num(self) > 0) + { + self->num--; + ret = array_at(self->array, (self->front + self->num) % self->element_num); + } + + return ret; + } + +} } diff --git a/libs/brynet/base/Timer.hpp b/libs/brynet/base/Timer.hpp new file mode 100644 index 0000000..363d48e --- /dev/null +++ b/libs/brynet/base/Timer.hpp @@ -0,0 +1,167 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace brynet { namespace base { + + class TimerMgr; + + class Timer final + { + public: + using Ptr = std::shared_ptr; + using WeakPtr = std::weak_ptr; + using Callback = std::function; + + Timer(std::chrono::steady_clock::time_point startTime, + std::chrono::nanoseconds lastTime, + Callback&& callback) BRYNET_NOEXCEPT + : + mCallback(std::move(callback)), + mStartTime(startTime), + mLastTime(lastTime) + { + } + + const std::chrono::steady_clock::time_point& getStartTime() const + { + return mStartTime; + } + + const std::chrono::nanoseconds& getLastTime() const + { + return mLastTime; + } + + std::chrono::nanoseconds getLeftTime() const + { + const auto now = std::chrono::steady_clock::now(); + return getLastTime() - (now - getStartTime()); + } + + void cancel() + { + std::call_once(mExecuteOnceFlag, [this]() { + mCallback = nullptr; + }); + } + + private: + void operator() () + { + Callback callback; + std::call_once(mExecuteOnceFlag, [&callback, this]() { + callback = std::move(mCallback); + mCallback = nullptr; + }); + + if (callback != nullptr) + { + callback(); + } + } + + private: + std::once_flag mExecuteOnceFlag; + Callback mCallback; + const std::chrono::steady_clock::time_point mStartTime; + const std::chrono::nanoseconds mLastTime; + + friend class TimerMgr; + }; + + class TimerMgr final + { + public: + using Ptr = std::shared_ptr; + + template + Timer::WeakPtr addTimer( + std::chrono::nanoseconds timeout, + F&& callback, + TArgs&& ...args) + { + auto timer = std::make_shared( + std::chrono::steady_clock::now(), + std::chrono::nanoseconds(timeout), + std::bind(std::forward(callback), std::forward(args)...)); + mTimers.push(timer); + + return timer; + } + + void addTimer(const Timer::Ptr& timer) + { + mTimers.push(timer); + } + + void schedule() + { + while (!mTimers.empty()) + { + auto tmp = mTimers.top(); + if (tmp->getLeftTime() > std::chrono::nanoseconds::zero()) + { + break; + } + + mTimers.pop(); + (*tmp)(); + } + } + + bool isEmpty() const + { + return mTimers.empty(); + } + + // if timer empty, return zero + std::chrono::nanoseconds nearLeftTime() const + { + if (mTimers.empty()) + { + return std::chrono::nanoseconds::zero(); + } + + auto result = mTimers.top()->getLeftTime(); + if (result < std::chrono::nanoseconds::zero()) + { + return std::chrono::nanoseconds::zero(); + } + + return result; + } + + void clear() + { + while (!mTimers.empty()) + { + mTimers.pop(); + } + } + + private: + class CompareTimer + { + public: + bool operator() (const Timer::Ptr& left, + const Timer::Ptr& right) const + { + const auto startDiff = left->getStartTime() - right->getStartTime(); + const auto lastDiff = left->getLastTime() - right->getLastTime(); + const auto diff = startDiff.count() + lastDiff.count(); + return diff > 0; + } + }; + + std::priority_queue, CompareTimer> mTimers; + }; + +} } \ No newline at end of file diff --git a/libs/brynet/base/WaitGroup.hpp b/libs/brynet/base/WaitGroup.hpp new file mode 100644 index 0000000..1d44fcc --- /dev/null +++ b/libs/brynet/base/WaitGroup.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace brynet { namespace base { + + class WaitGroup : public NonCopyable + { + public: + typedef std::shared_ptr Ptr; + + static Ptr Create() + { + struct make_shared_enabler : public WaitGroup {}; + return std::make_shared(); + } + + public: + void add(int i = 1) + { + mCounter += i; + } + + void done() + { + mCounter--; + mCond.notify_all(); + } + + void wait() + { + std::unique_lock l(mMutex); + mCond.wait(l, [&] { return mCounter <= 0; }); + } + + template + void wait(const std::chrono::duration& timeout) + { + std::unique_lock l(mMutex); + mCond.wait_for(l, timeout, [&] { + return mCounter <= 0; + }); + } + + private: + WaitGroup() + : + mCounter(0) + { + } + + virtual ~WaitGroup() = default; + + private: + std::mutex mMutex; + std::atomic mCounter; + std::condition_variable mCond; + }; +} } \ No newline at end of file diff --git a/libs/brynet/base/crypto/Base64.hpp b/libs/brynet/base/crypto/Base64.hpp new file mode 100644 index 0000000..135a77e --- /dev/null +++ b/libs/brynet/base/crypto/Base64.hpp @@ -0,0 +1,105 @@ +#ifndef _BRYNET_BASE_BASE64_H +#define _BRYNET_BASE_BASE64_H + +#include + +namespace brynet { namespace base { namespace crypto { + + static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + static bool is_base64(unsigned char c) + { + return (isalnum(c) || (c == '+') || (c == '/')); + } + + static std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) + { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (i = 0; (i < 4); i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for (j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while ((i++ < 3)) + ret += '='; + + } + + return ret; + } + + static std::string base64_decode(std::string const& encoded_string) + { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i == 4) { + for (i = 0; i < 4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) + char_array_4[j] = 0; + + for (j = 0; j < 4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; + } + +} } } + +#endif \ No newline at end of file diff --git a/libs/brynet/base/crypto/SHA1.hpp b/libs/brynet/base/crypto/SHA1.hpp new file mode 100644 index 0000000..197d3cc --- /dev/null +++ b/libs/brynet/base/crypto/SHA1.hpp @@ -0,0 +1,496 @@ +/* + 100% free public domain implementation of the SHA-1 algorithm + by Dominik Reichl + Web: http://www.dominik-reichl.de/ + + Version 2.1 - 2012-06-19 + - Deconstructor (resetting internal variables) is now only + implemented if SHA1_WIPE_VARIABLES is defined (which is the + default). + - Renamed inclusion guard to contain a GUID. + - Demo application is now using C++/STL objects and functions. + - Unicode build of the demo application now outputs the hashes of both + the ANSI and Unicode representations of strings. + - Various other demo application improvements. + + Version 2.0 - 2012-06-14 + - Added 'limits.h' include. + - Renamed inclusion guard and macros for compliancy (names beginning + with an underscore are reserved). + + Version 1.9 - 2011-11-10 + - Added Unicode test vectors. + - Improved support for hashing files using the HashFile method that + are larger than 4 GB. + - Improved file hashing performance (by using a larger buffer). + - Disabled unnecessary compiler warnings. + - Internal variables are now private. + + Version 1.8 - 2009-03-16 + - Converted project files to Visual Studio 2008 format. + - Added Unicode support for HashFile utility method. + - Added support for hashing files using the HashFile method that are + larger than 2 GB. + - HashFile now returns an error code instead of copying an error + message into the output buffer. + - GetHash now returns an error code and validates the input parameter. + - Added ReportHashStl STL utility method. + - Added REPORT_HEX_SHORT reporting mode. + - Improved Linux compatibility of test program. + + Version 1.7 - 2006-12-21 + - Fixed buffer underrun warning that appeared when compiling with + Borland C Builder (thanks to Rex Bloom and Tim Gallagher for the + patch). + - Breaking change: ReportHash writes the final hash to the start + of the buffer, i.e. it's not appending it to the string anymore. + - Made some function parameters const. + - Added Visual Studio 2005 project files to demo project. + + Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches) + - You can set the endianness in your files, no need to modify the + header file of the CSHA1 class anymore. + - Aligned data support. + - Made support/compilation of the utility functions (ReportHash and + HashFile) optional (useful when bytes count, for example in embedded + environments). + + Version 1.5 - 2005-01-01 + - 64-bit compiler compatibility added. + - Made variable wiping optional (define SHA1_WIPE_VARIABLES). + - Removed unnecessary variable initializations. + - ROL32 improvement for the Microsoft compiler (using _rotl). + + Version 1.4 - 2004-07-22 + - CSHA1 now compiles fine with GCC 3.3 under Mac OS X (thanks to Larry + Hastings). + + Version 1.3 - 2003-08-17 + - Fixed a small memory bug and made a buffer array a class member to + ensure correct working when using multiple CSHA1 class instances at + one time. + + Version 1.2 - 2002-11-16 + - Borlands C++ compiler seems to have problems with string addition + using sprintf. Fixed the bug which caused the digest report function + not to work properly. CSHA1 is now Borland compatible. + + Version 1.1 - 2002-10-11 + - Removed two unnecessary header file includes and changed BOOL to + bool. Fixed some minor bugs in the web page contents. + + Version 1.0 - 2002-06-20 + - First official release. + + ================ Test Vectors ================ + + SHA1("abc" in ANSI) = + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + SHA1("abc" in Unicode LE) = + 9F04F41A 84851416 2050E3D6 8C1A7ABB 441DC2B5 + + SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + in ANSI) = + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + in Unicode LE) = + 51D7D876 9AC72C40 9C5B0E3F 69C60ADC 9A039014 + + SHA1(A million repetitions of "a" in ANSI) = + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + SHA1(A million repetitions of "a" in Unicode LE) = + C4609560 A108A0C6 26AA7F2B 38A65566 739353C5 +*/ + +#ifndef SHA1_H_A545E61D43E9404E8D736869AB3CBFE7 +#define SHA1_H_A545E61D43E9404E8D736869AB3CBFE7 + +#if !defined(SHA1_UTILITY_FUNCTIONS) && !defined(SHA1_NO_UTILITY_FUNCTIONS) +#define SHA1_UTILITY_FUNCTIONS +#endif + +#if !defined(SHA1_STL_FUNCTIONS) && !defined(SHA1_NO_STL_FUNCTIONS) +#define SHA1_STL_FUNCTIONS +#if !defined(SHA1_UTILITY_FUNCTIONS) +#error STL functions require SHA1_UTILITY_FUNCTIONS. +#endif +#endif + +#include +#include + +#ifdef SHA1_UTILITY_FUNCTIONS +#include +#include +#endif + +#ifdef SHA1_STL_FUNCTIONS +#include +#endif + +#ifdef _MSC_VER +#include +#endif + +// You can define the endian mode in your files without modifying the SHA-1 +// source files. Just #define SHA1_LITTLE_ENDIAN or #define SHA1_BIG_ENDIAN +// in your files, before including the SHA1.h header file. If you don't +// define anything, the class defaults to little endian. +#if !defined(SHA1_LITTLE_ENDIAN) && !defined(SHA1_BIG_ENDIAN) +#define SHA1_LITTLE_ENDIAN +#endif + +// If you want variable wiping, #define SHA1_WIPE_VARIABLES, if not, +// #define SHA1_NO_WIPE_VARIABLES. If you don't define anything, it +// defaults to wiping. +#if !defined(SHA1_WIPE_VARIABLES) && !defined(SHA1_NO_WIPE_VARIABLES) +#define SHA1_WIPE_VARIABLES +#endif + +#if defined(SHA1_HAS_TCHAR) +#include +#else +#ifdef _MSC_VER +#include +#else +#ifndef TCHAR +#define TCHAR char +#endif +#ifndef _T +#define _T(__x) (__x) +#define _tmain main +#define _tprintf printf +#define _getts gets +#define _tcslen strlen +#define _tfopen fopen +#define _tcscpy strcpy +#define _tcscat strcat +#define _sntprintf snprintf +#endif +#endif +#endif + +/////////////////////////////////////////////////////////////////////////// +// Define variable types + +#ifndef UINT_8 +#ifdef _MSC_VER // Compiling with Microsoft compiler +#define UINT_8 unsigned __int8 +#else // !_MSC_VER +#define UINT_8 unsigned char +#endif // _MSC_VER +#endif + +#ifndef UINT_32 +#ifdef _MSC_VER // Compiling with Microsoft compiler +#define UINT_32 unsigned __int32 +#else // !_MSC_VER +#if (ULONG_MAX == 0xFFFFFFFFUL) +#define UINT_32 unsigned long +#else +#define UINT_32 unsigned int +#endif +#endif // _MSC_VER +#endif // UINT_32 + +#ifndef INT_64 +#ifdef _MSC_VER // Compiling with Microsoft compiler +#define INT_64 __int64 +#else // !_MSC_VER +#define INT_64 long long +#endif // _MSC_VER +#endif // INT_64 + +#ifndef UINT_64 +#ifdef _MSC_VER // Compiling with Microsoft compiler +#define UINT_64 unsigned __int64 +#else // !_MSC_VER +#define UINT_64 unsigned long long +#endif // _MSC_VER +#endif // UINT_64 + +/////////////////////////////////////////////////////////////////////////// +// Declare SHA-1 workspace + +typedef union +{ + UINT_8 c[64]; + UINT_32 l[16]; +} SHA1_WORKSPACE_BLOCK; + + +#define SHA1_MAX_FILE_BUFFER (32 * 20 * 820) + +// Rotate p_val32 by p_nBits bits to the left +#ifndef ROL32 +#ifdef _MSC_VER +#define ROL32(p_val32,p_nBits) _rotl(p_val32,p_nBits) +#else +#define ROL32(p_val32,p_nBits) (((p_val32)<<(p_nBits))|((p_val32)>>(32-(p_nBits)))) +#endif +#endif + +#ifdef SHA1_LITTLE_ENDIAN +#define SHABLK0(i) (m_block->l[i] = \ + (ROL32(m_block->l[i],24) & 0xFF00FF00) | (ROL32(m_block->l[i],8) & 0x00FF00FF)) +#else +#define SHABLK0(i) (m_block->l[i]) +#endif + +#define SHABLK(i) (m_block->l[i&15] = ROL32(m_block->l[(i+13)&15] ^ \ + m_block->l[(i+8)&15] ^ m_block->l[(i+2)&15] ^ m_block->l[i&15],1)) + +// SHA-1 rounds +#define S_R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);} +#define S_R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);} +#define S_R2(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5);w=ROL32(w,30);} +#define S_R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5);w=ROL32(w,30);} +#define S_R4(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5);w=ROL32(w,30);} + + +class CSHA1 +{ +public: +#ifdef SHA1_UTILITY_FUNCTIONS + // Different formats for ReportHash(Stl) + enum REPORT_TYPE + { + REPORT_HEX = 0, + REPORT_DIGIT = 1, + REPORT_HEX_SHORT = 2 + }; +#endif + + // Constructor and destructor + CSHA1() + { + (void)m_reserved0; + (void)m_reserved1; + m_block = (SHA1_WORKSPACE_BLOCK*)m_workspace; + + Reset(); + } + +#ifdef SHA1_WIPE_VARIABLES + ~CSHA1() + { + Reset(); + } +#endif + + void Reset() + { + // SHA1 initialization constants + m_state[0] = 0x67452301; + m_state[1] = 0xEFCDAB89; + m_state[2] = 0x98BADCFE; + m_state[3] = 0x10325476; + m_state[4] = 0xC3D2E1F0; + + m_count[0] = 0; + m_count[1] = 0; + } + + // Hash in binary data and strings + void Update(const UINT_8* pbData, UINT_32 uLen) + { + UINT_32 j = ((m_count[0] >> 3) & 0x3F); + + if ((m_count[0] += (uLen << 3)) < (uLen << 3)) + ++m_count[1]; // Overflow + + m_count[1] += (uLen >> 29); + + UINT_32 i; + if ((j + uLen) > 63) + { + i = 64 - j; + memcpy(&m_buffer[j], pbData, i); + Transform(m_state, m_buffer); + + for (; (i + 63) < uLen; i += 64) + Transform(m_state, &pbData[i]); + + j = 0; + } + else i = 0; + + if ((uLen - i) != 0) + memcpy(&m_buffer[j], &pbData[i], uLen - i); + } + +#ifdef SHA1_UTILITY_FUNCTIONS + // Hash in file contents + bool HashFile(const TCHAR* tszFileName) + { + if (tszFileName == NULL) return false; + + FILE* fpIn = _tfopen(tszFileName, _T("rb")); + if (fpIn == NULL) return false; + + UINT_8* pbData = new UINT_8[SHA1_MAX_FILE_BUFFER]; + if (pbData == NULL) { fclose(fpIn); return false; } + + bool bSuccess = true; + while (true) + { + const size_t uRead = fread(pbData, 1, SHA1_MAX_FILE_BUFFER, fpIn); + + if (uRead > 0) + Update(pbData, static_cast(uRead)); + + if (uRead < SHA1_MAX_FILE_BUFFER) + { + if (feof(fpIn) == 0) bSuccess = false; + break; + } + } + + fclose(fpIn); + delete[] pbData; + return bSuccess; + } +#endif + + // Finalize hash; call it before using ReportHash(Stl) + void Final() + { + UINT_32 i; + + UINT_8 pbFinalCount[8]; + for (i = 0; i < 8; ++i) + pbFinalCount[i] = static_cast((m_count[((i >= 4) ? 0 : 1)] >> + ((3 - (i & 3)) * 8)) & 0xFF); // Endian independent + + Update((UINT_8*)"\200", 1); + + while ((m_count[0] & 504) != 448) + Update((UINT_8*)"\0", 1); + + Update(pbFinalCount, 8); // Cause a Transform() + + for (i = 0; i < 20; ++i) + m_digest[i] = static_cast((m_state[i >> 2] >> ((3 - + (i & 3)) * 8)) & 0xFF); + + // Wipe variables for security reasons +#ifdef SHA1_WIPE_VARIABLES + memset(m_buffer, 0, 64); + memset(m_state, 0, 20); + memset(m_count, 0, 8); + memset(pbFinalCount, 0, 8); + Transform(m_state, m_buffer); +#endif + } + +#ifdef SHA1_UTILITY_FUNCTIONS + bool ReportHash(TCHAR* tszReport, REPORT_TYPE rtReportType = REPORT_HEX) const + { + if (tszReport == NULL) return false; + + TCHAR tszTemp[16]; + + if ((rtReportType == REPORT_HEX) || (rtReportType == REPORT_HEX_SHORT)) + { + _sntprintf(tszTemp, 15, _T("%02X"), m_digest[0]); + _tcscpy(tszReport, tszTemp); + + const TCHAR* lpFmt = ((rtReportType == REPORT_HEX) ? _T(" %02X") : _T("%02X")); + for (size_t i = 1; i < 20; ++i) + { + _sntprintf(tszTemp, 15, lpFmt, m_digest[i]); + _tcscat(tszReport, tszTemp); + } + } + else if (rtReportType == REPORT_DIGIT) + { + _sntprintf(tszTemp, 15, _T("%u"), m_digest[0]); + _tcscpy(tszReport, tszTemp); + + for (size_t i = 1; i < 20; ++i) + { + _sntprintf(tszTemp, 15, _T(" %u"), m_digest[i]); + _tcscat(tszReport, tszTemp); + } + } + else return false; + + return true; + } +#endif + +#ifdef SHA1_STL_FUNCTIONS + bool ReportHashStl(std::basic_string& strOut, REPORT_TYPE rtReportType = + REPORT_HEX) const + { + TCHAR tszOut[84]; + const bool bResult = ReportHash(tszOut, rtReportType); + if (bResult) strOut = tszOut; + return bResult; + } +#endif + + // Get the raw message digest (20 bytes) + bool GetHash(UINT_8* pbDest20) const + { + if (pbDest20 == NULL) return false; + memcpy(pbDest20, m_digest, 20); + return true; + } + +private: + // Private SHA-1 transformation + void Transform(UINT_32* pState, const UINT_8* pBuffer) + { + UINT_32 a = pState[0], b = pState[1], c = pState[2], d = pState[3], e = pState[4]; + + memcpy(m_block, pBuffer, 64); + + // 4 rounds of 20 operations each, loop unrolled + S_R0(a, b, c, d, e, 0); S_R0(e, a, b, c, d, 1); S_R0(d, e, a, b, c, 2); S_R0(c, d, e, a, b, 3); + S_R0(b, c, d, e, a, 4); S_R0(a, b, c, d, e, 5); S_R0(e, a, b, c, d, 6); S_R0(d, e, a, b, c, 7); + S_R0(c, d, e, a, b, 8); S_R0(b, c, d, e, a, 9); S_R0(a, b, c, d, e, 10); S_R0(e, a, b, c, d, 11); + S_R0(d, e, a, b, c, 12); S_R0(c, d, e, a, b, 13); S_R0(b, c, d, e, a, 14); S_R0(a, b, c, d, e, 15); + S_R1(e, a, b, c, d, 16); S_R1(d, e, a, b, c, 17); S_R1(c, d, e, a, b, 18); S_R1(b, c, d, e, a, 19); + S_R2(a, b, c, d, e, 20); S_R2(e, a, b, c, d, 21); S_R2(d, e, a, b, c, 22); S_R2(c, d, e, a, b, 23); + S_R2(b, c, d, e, a, 24); S_R2(a, b, c, d, e, 25); S_R2(e, a, b, c, d, 26); S_R2(d, e, a, b, c, 27); + S_R2(c, d, e, a, b, 28); S_R2(b, c, d, e, a, 29); S_R2(a, b, c, d, e, 30); S_R2(e, a, b, c, d, 31); + S_R2(d, e, a, b, c, 32); S_R2(c, d, e, a, b, 33); S_R2(b, c, d, e, a, 34); S_R2(a, b, c, d, e, 35); + S_R2(e, a, b, c, d, 36); S_R2(d, e, a, b, c, 37); S_R2(c, d, e, a, b, 38); S_R2(b, c, d, e, a, 39); + S_R3(a, b, c, d, e, 40); S_R3(e, a, b, c, d, 41); S_R3(d, e, a, b, c, 42); S_R3(c, d, e, a, b, 43); + S_R3(b, c, d, e, a, 44); S_R3(a, b, c, d, e, 45); S_R3(e, a, b, c, d, 46); S_R3(d, e, a, b, c, 47); + S_R3(c, d, e, a, b, 48); S_R3(b, c, d, e, a, 49); S_R3(a, b, c, d, e, 50); S_R3(e, a, b, c, d, 51); + S_R3(d, e, a, b, c, 52); S_R3(c, d, e, a, b, 53); S_R3(b, c, d, e, a, 54); S_R3(a, b, c, d, e, 55); + S_R3(e, a, b, c, d, 56); S_R3(d, e, a, b, c, 57); S_R3(c, d, e, a, b, 58); S_R3(b, c, d, e, a, 59); + S_R4(a, b, c, d, e, 60); S_R4(e, a, b, c, d, 61); S_R4(d, e, a, b, c, 62); S_R4(c, d, e, a, b, 63); + S_R4(b, c, d, e, a, 64); S_R4(a, b, c, d, e, 65); S_R4(e, a, b, c, d, 66); S_R4(d, e, a, b, c, 67); + S_R4(c, d, e, a, b, 68); S_R4(b, c, d, e, a, 69); S_R4(a, b, c, d, e, 70); S_R4(e, a, b, c, d, 71); + S_R4(d, e, a, b, c, 72); S_R4(c, d, e, a, b, 73); S_R4(b, c, d, e, a, 74); S_R4(a, b, c, d, e, 75); + S_R4(e, a, b, c, d, 76); S_R4(d, e, a, b, c, 77); S_R4(c, d, e, a, b, 78); S_R4(b, c, d, e, a, 79); + + // Add the working vars back into state + pState[0] += a; + pState[1] += b; + pState[2] += c; + pState[3] += d; + pState[4] += e; + + // Wipe variables +#ifdef SHA1_WIPE_VARIABLES + a = b = c = d = e = 0; +#endif + } + + // Member variables + UINT_32 m_state[5]; + UINT_32 m_count[2]; + UINT_32 m_reserved0[1]; // Memory alignment padding + UINT_8 m_buffer[64]; + UINT_8 m_digest[20]; + UINT_32 m_reserved1[3]; // Memory alignment padding + + UINT_8 m_workspace[64]; + SHA1_WORKSPACE_BLOCK* m_block; // SHA1 pointer to the byte array above +}; + +#endif // SHA1_H_A545E61D43E9404E8D736869AB3CBFE7 diff --git a/libs/brynet/base/endian/Endian.hpp b/libs/brynet/base/endian/Endian.hpp new file mode 100644 index 0000000..c797e83 --- /dev/null +++ b/libs/brynet/base/endian/Endian.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#ifdef BRYNET_PLATFORM_LINUX +#include +#elif defined BRYNET_PLATFORM_DARWIN +#include +#endif + +namespace brynet { namespace base { namespace endian { + + inline uint64_t hl64ton(uint64_t hostValue) + { + uint64_t ret = 0; + uint32_t high, low; + + low = hostValue & 0xFFFFFFFF; + high = (hostValue >> 32) & 0xFFFFFFFF; + low = htonl(low); + high = htonl(high); + ret = low; + ret <<= 32; + ret |= high; + + return ret; + } + + inline uint64_t ntohl64(uint64_t netValue) + { + uint64_t ret = 0; + uint32_t high, low; + + low = netValue & 0xFFFFFFFF; + high = (netValue >> 32) & 0xFFFFFFFF; + low = ntohl(low); + high = ntohl(high); + ret = low; + ret <<= 32; + ret |= high; + + return ret; + } + +#ifdef BRYNET_PLATFORM_WINDOWS + inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true) + { + return convert ? hl64ton(host64) : host64; + } + inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true) + { + return convert ? htonl(host32) : host32; + } + + inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true) + { + return convert ? htons(host16) : host16; + } + + inline uint64_t networkToHost64(uint64_t net64, bool convert = true) + { + return convert ? ntohl64(net64) : net64; + } + + inline uint32_t networkToHost32(uint32_t net32, bool convert = true) + { + return convert ? ntohl(net32) : net32; + } + + inline uint16_t networkToHost16(uint16_t net16, bool convert = true) + { + return convert ? ntohs(net16) : net16; + } +#elif defined BRYNET_PLATFORM_LINUX + inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true) + { + return convert ? htobe64(host64) : host64; + } + inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true) + { + return convert ? htobe32(host32) : host32; + } + + inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true) + { + return convert ? htobe16(host16) : host16; + } + + inline uint64_t networkToHost64(uint64_t net64, bool convert = true) + { + return convert ? be64toh(net64) : net64; + } + + inline uint32_t networkToHost32(uint32_t net32, bool convert = true) + { + return convert ? be32toh(net32) : net32; + } + + inline uint16_t networkToHost16(uint16_t net16, bool convert = true) + { + return convert ? be16toh(net16) : net16; + } +#elif defined BRYNET_PLATFORM_DARWIN + inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true) + { + return convert ? hl64ton(host64) : host64; + } + inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true) + { + return convert ? htonl(host32) : host32; + } + + inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true) + { + return convert ? htons(host16) : host16; + } + + inline uint64_t networkToHost64(uint64_t net64, bool convert = true) + { + return convert ? ntohl64(net64) : net64; + } + + inline uint32_t networkToHost32(uint32_t net32, bool convert = true) + { + return convert ? ntohl(net32) : net32; + } + + inline uint16_t networkToHost16(uint16_t net16, bool convert = true) + { + return convert ? ntohs(net16) : net16; + } +#endif + +} } } \ No newline at end of file diff --git a/libs/brynet/net/AsyncConnector.hpp b/libs/brynet/net/AsyncConnector.hpp new file mode 100644 index 0000000..8d7dd75 --- /dev/null +++ b/libs/brynet/net/AsyncConnector.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include + +namespace brynet { namespace net { + + class ConnectOption final + { + public: + using CompletedCallback = std::function; + using ProcessTcpSocketCallback = std::function; + using FailedCallback = std::function; + using ConnectOptionFunc = detail::ConnectOptionFunc; + + static ConnectOptionFunc WithAddr(const std::string& ip, int port) + { + return [ip, port](detail::ConnectOptionsInfo& option) { + option.ip = ip; + option.port = port; + }; + } + static ConnectOptionFunc WithTimeout(std::chrono::nanoseconds timeout) + { + return [timeout](detail::ConnectOptionsInfo& option) { + option.timeout = timeout; + }; + } + static ConnectOptionFunc WithCompletedCallback(CompletedCallback callback) + { + return [callback](detail::ConnectOptionsInfo& option) { + option.completedCallback = callback; + }; + } + static ConnectOptionFunc AddProcessTcpSocketCallback(ProcessTcpSocketCallback process) + { + return [process](detail::ConnectOptionsInfo& option) { + option.processCallbacks.push_back(process); + }; + } + static ConnectOptionFunc WithFailedCallback(FailedCallback callback) + { + return [callback](detail::ConnectOptionsInfo& option) { + option.faledCallback = callback; + }; + } + + static std::chrono::nanoseconds ExtractTimeout(const std::vector& options) + { + detail::ConnectOptionsInfo option; + for (const auto& func : options) + { + func(option); + } + return option.timeout; + } + }; + + class AsyncConnector : public detail::AsyncConnectorDetail, + public std::enable_shared_from_this + { + public: + using Ptr = std::shared_ptr; + + void startWorkerThread() + { + detail::AsyncConnectorDetail::startWorkerThread(); + } + + void stopWorkerThread() + { + detail::AsyncConnectorDetail::stopWorkerThread(); + } + + void asyncConnect(const std::vector& options) + { + detail::AsyncConnectorDetail::asyncConnect(options); + } + + static Ptr Create() + { + class make_shared_enabler : public AsyncConnector {}; + return std::make_shared(); + } + + private: + AsyncConnector() = default; + }; + +} } \ No newline at end of file diff --git a/libs/brynet/net/Channel.hpp b/libs/brynet/net/Channel.hpp new file mode 100644 index 0000000..979cd5f --- /dev/null +++ b/libs/brynet/net/Channel.hpp @@ -0,0 +1,20 @@ +#pragma once + +namespace brynet { namespace net { + + class EventLoop; + + class Channel + { + public: + virtual ~Channel() = default; + + private: + virtual void canSend() = 0; + virtual void canRecv() = 0; + virtual void onClose() = 0; + + friend class EventLoop; + }; + +} } \ No newline at end of file diff --git a/libs/brynet/net/CurrentThread.hpp b/libs/brynet/net/CurrentThread.hpp new file mode 100644 index 0000000..b977360 --- /dev/null +++ b/libs/brynet/net/CurrentThread.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include + +#ifdef BRYNET_PLATFORM_WINDOWS +#include +#include +#elif defined BRYNET_PLATFORM_LINUX +#include +#include +#include +#include +#include +#elif defined BRYNET_PLATFORM_DARWIN +#include +#include +#include +#endif + +namespace brynet { namespace net { namespace current_thread { + +#ifdef BRYNET_PLATFORM_WINDOWS + using THREAD_ID_TYPE = DWORD; +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + using THREAD_ID_TYPE = int; +#endif + + static THREAD_ID_TYPE& tid() + { +#ifdef BRYNET_PLATFORM_WINDOWS + static __declspec(thread) THREAD_ID_TYPE cachedTid = 0; +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + static __thread THREAD_ID_TYPE cachedTid = 0; +#endif + + if (cachedTid == 0) + { +#ifdef BRYNET_PLATFORM_WINDOWS + cachedTid = GetCurrentThreadId(); +#elif defined BRYNET_PLATFORM_LINUX + cachedTid = static_cast(::syscall(SYS_gettid)); +#elif defined BRYNET_PLATFORM_DARWIN + // warning: 'syscall' is deprecated: + // first deprecated in macOS 10.12 - syscall(2) is unsupported; + // please switch to a supported interface. + uint64_t tid64; + pthread_threadid_np(NULL, &tid64); + cachedTid = (pid_t)tid64; +#endif + } + + return cachedTid; + } + +} } } diff --git a/libs/brynet/net/EventLoop.hpp b/libs/brynet/net/EventLoop.hpp new file mode 100644 index 0000000..012ee91 --- /dev/null +++ b/libs/brynet/net/EventLoop.hpp @@ -0,0 +1,444 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace brynet { namespace net { + + class Channel; + class TcpConnection; + using TcpConnectionPtr = std::shared_ptr; + + class EventLoop : public brynet::base::NonCopyable + { + public: + using Ptr = std::shared_ptr; + using UserFunctor = std::function; + + public: + EventLoop() + BRYNET_NOEXCEPT + : +#ifdef BRYNET_PLATFORM_WINDOWS + mIOCP(CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)), + mWakeupChannel(std::make_unique(mIOCP)) +#elif defined BRYNET_PLATFORM_LINUX + mEpollFd(epoll_create(1)) +#elif defined BRYNET_PLATFORM_DARWIN + mKqueueFd(kqueue()) +#endif + { +#ifdef BRYNET_PLATFORM_WINDOWS + mPGetQueuedCompletionStatusEx = NULL; + auto kernel32_module = GetModuleHandleA("kernel32.dll"); + if (kernel32_module != NULL) { + mPGetQueuedCompletionStatusEx = reinterpret_cast(GetProcAddress( + kernel32_module, + "GetQueuedCompletionStatusEx")); + FreeLibrary(kernel32_module); + } +#elif defined BRYNET_PLATFORM_LINUX + auto eventfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + mWakeupChannel.reset(new detail::WakeupChannel(eventfd)); + linkChannel(eventfd, mWakeupChannel.get()); +#elif defined BRYNET_PLATFORM_DARWIN + const int NOTIFY_IDENT = 42; // Magic number we use for our filter ID. + mWakeupChannel.reset(new detail::WakeupChannel(mKqueueFd, NOTIFY_IDENT)); + //Add user event + struct kevent ev; + EV_SET(&ev, NOTIFY_IDENT, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL); + + struct timespec timeout = { 0, 0 }; + kevent(mKqueueFd, &ev, 1, NULL, 0, &timeout); +#endif + + mIsAlreadyPostWakeup = false; + mIsInBlock = true; + + reallocEventSize(1024); + mSelfThreadID = -1; + mTimer = std::make_shared(); + } + + virtual ~EventLoop() BRYNET_NOEXCEPT + { +#ifdef BRYNET_PLATFORM_WINDOWS + CloseHandle(mIOCP); + mIOCP = INVALID_HANDLE_VALUE; +#elif defined BRYNET_PLATFORM_LINUX + close(mEpollFd); + mEpollFd = -1; +#elif defined BRYNET_PLATFORM_DARWIN + close(mKqueueFd); + mKqueueFd = -1; +#endif + } + + void loop(int64_t milliseconds) + { + tryInitThreadID(); + +#ifndef NDEBUG + assert(isInLoopThread()); +#endif + if (!isInLoopThread()) + { + throw BrynetCommonException("only loop in io thread"); + } + + if (!mAfterLoopFunctors.empty()) + { + milliseconds = 0; + } + +#ifdef BRYNET_PLATFORM_WINDOWS + ULONG numComplete = 0; + if (mPGetQueuedCompletionStatusEx != nullptr) + { + if (!mPGetQueuedCompletionStatusEx(mIOCP, + mEventEntries.data(), + static_cast(mEventEntries.size()), + &numComplete, + static_cast(milliseconds), + false)) + { + numComplete = 0; + } + } + else + { + for (auto& e : mEventEntries) + { + const auto timeout = (numComplete == 0) ? static_cast(milliseconds) : 0; + /* don't check the return value of GQCS */ + GetQueuedCompletionStatus(mIOCP, + &e.dwNumberOfBytesTransferred, + &e.lpCompletionKey, + &e.lpOverlapped, + timeout); + if (e.lpOverlapped == nullptr) + { + break; + } + ++numComplete; + } + } + + mIsInBlock = false; + + for (ULONG i = 0; i < numComplete; ++i) + { + auto channel = (Channel*)mEventEntries[i].lpCompletionKey; + assert(channel != nullptr); + const auto ovl = reinterpret_cast(mEventEntries[i].lpOverlapped); + if (ovl->OP == port::Win::OverlappedType::OverlappedRecv) + { + channel->canRecv(); + } + else if (ovl->OP == port::Win::OverlappedType::OverlappedSend) + { + channel->canSend(); + } + else + { + assert(false); + } + } +#elif defined BRYNET_PLATFORM_LINUX + int numComplete = epoll_wait(mEpollFd, mEventEntries.data(), mEventEntries.size(), milliseconds); + + mIsInBlock = false; + + for (int i = 0; i < numComplete; ++i) + { + auto channel = (Channel*)(mEventEntries[i].data.ptr); + auto event_data = mEventEntries[i].events; + + if (event_data & EPOLLRDHUP) + { + channel->canRecv(); + channel->onClose(); + continue; + } + + if (event_data & EPOLLIN) + { + channel->canRecv(); + } + + if (event_data & EPOLLOUT) + { + channel->canSend(); + } + } +#elif defined BRYNET_PLATFORM_DARWIN + struct timespec timeout = { milliseconds / 1000, (milliseconds % 1000) * 1000 * 1000 }; + int numComplete = kevent(mKqueueFd, NULL, 0, mEventEntries.data(), mEventEntries.size(), &timeout); + + mIsInBlock = false; + + for (int i = 0; i < numComplete; ++i) + { + auto channel = (Channel*)(mEventEntries[i].udata); + const struct kevent& event = mEventEntries[i]; + + if (event.filter == EVFILT_USER) + { + continue; + } + + if (event.filter == EVFILT_READ) + { + channel->canRecv(); + } + + if (event.filter == EVFILT_WRITE) + { + channel->canSend(); + } + } +#endif + + mIsAlreadyPostWakeup = false; + mIsInBlock = true; + + processAsyncFunctors(); + processAfterLoopFunctors(); + + if (static_cast(numComplete) == mEventEntries.size()) + { + reallocEventSize(mEventEntries.size() + 128); + } + + mTimer->schedule(); + } + + // loop指定毫秒数,但如果定时器不为空,则loop时间为当前最近定时器的剩余时间和milliseconds的较小值 + void loopCompareNearTimer(int64_t milliseconds) + { + tryInitThreadID(); + +#ifndef NDEBUG + assert(isInLoopThread()); +#endif + if (!isInLoopThread()) + { + throw BrynetCommonException("only loop in IO thread"); + } + + if (!mTimer->isEmpty()) + { + auto nearTimeout = std::chrono::duration_cast(mTimer->nearLeftTime()); + milliseconds = std::min(milliseconds, nearTimeout.count()); + } + + loop(milliseconds); + } + + // 返回true表示实际发生了wakeup所需的操作(此返回值不代表接口本身操作成功与否,因为此函数永远成功) + bool wakeup() + { + if (!isInLoopThread() && mIsInBlock && !mIsAlreadyPostWakeup.exchange(true)) + { + return mWakeupChannel->wakeup(); + } + + return false; + } + + void runAsyncFunctor(UserFunctor&& f) + { + if (isInLoopThread()) + { + f(); + } + else + { + pushAsyncFunctor(std::move(f)); + wakeup(); + } + } + void runFunctorAfterLoop(UserFunctor&& f) + { + assert(isInLoopThread()); + if (!isInLoopThread()) + { + throw BrynetCommonException("only push after functor in io thread"); + } + + mAfterLoopFunctors.emplace_back(std::move(f)); + } + brynet::base::Timer::WeakPtr runAfter(std::chrono::nanoseconds timeout, UserFunctor&& callback) + { + auto timer = std::make_shared( + std::chrono::steady_clock::now(), + std::chrono::nanoseconds(timeout), + std::move(callback)); + + if (isInLoopThread()) + { + mTimer->addTimer(timer); + } + else + { + auto timerMgr = mTimer; + runAsyncFunctor([timerMgr, timer]() { + timerMgr->addTimer(timer); + }); + } + + return timer; + } + + inline bool isInLoopThread() const + { + return mSelfThreadID == current_thread::tid(); + } + + private: + void reallocEventSize(size_t size) + { + mEventEntries.resize(size); + } + + void processAfterLoopFunctors() + { + mCopyAfterLoopFunctors.swap(mAfterLoopFunctors); + for (const auto& x : mCopyAfterLoopFunctors) + { + x(); + } + mCopyAfterLoopFunctors.clear(); + } + void processAsyncFunctors() + { + swapAsyncFunctors(); + + for (const auto& x : mCopyAsyncFunctors) + { + x(); + } + mCopyAsyncFunctors.clear(); + } + void swapAsyncFunctors() + { + std::lock_guard lck(mAsyncFunctorsMutex); + assert(mCopyAsyncFunctors.empty()); + mCopyAsyncFunctors.swap(mAsyncFunctors); + } + void pushAsyncFunctor(UserFunctor&& f) + { + std::lock_guard lck(mAsyncFunctorsMutex); + mAsyncFunctors.emplace_back(std::move(f)); + } + +#ifdef BRYNET_PLATFORM_LINUX + int getEpollHandle() const + { + return mEpollFd; + } +#elif defined BRYNET_PLATFORM_DARWIN + int getKqueueHandle() const + { + return mKqueueFd; + } +#endif + bool linkChannel(BrynetSocketFD fd, const Channel* ptr) BRYNET_NOEXCEPT + { +#ifdef BRYNET_PLATFORM_WINDOWS + return CreateIoCompletionPort((HANDLE)fd, mIOCP, (ULONG_PTR)ptr, 0) != nullptr; +#elif defined BRYNET_PLATFORM_LINUX + struct epoll_event ev = { 0, { nullptr } }; + ev.events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLRDHUP; + ev.data.ptr = (void*)ptr; + return epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &ev) == 0; +#elif defined BRYNET_PLATFORM_DARWIN + struct kevent ev[2]; + memset(&ev, 0, sizeof(ev)); + int n = 0; + EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | EV_CLEAR, NOTE_TRIGGER, 0, (void*)ptr); + EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, NOTE_TRIGGER, 0, (void*)ptr); + + struct timespec now = { 0, 0 }; + return kevent(mKqueueFd, ev, n, NULL, 0, &now) == 0; +#endif + } + TcpConnectionPtr getTcpConnection(BrynetSocketFD fd) + { + auto it = mTcpConnections.find(fd); + if (it != mTcpConnections.end()) + { + return (*it).second; + } + return nullptr; + } + void addTcpConnection(BrynetSocketFD fd, TcpConnectionPtr tcpConnection) + { + mTcpConnections[fd] = std::move(tcpConnection); + } + void removeTcpConnection(BrynetSocketFD fd) + { + mTcpConnections.erase(fd); + } + void tryInitThreadID() + { + std::call_once(mOnceInitThreadID, [this]() { + mSelfThreadID = current_thread::tid(); + }); + } + + private: + +#ifdef BRYNET_PLATFORM_WINDOWS + std::vector mEventEntries; + + typedef BOOL(WINAPI *sGetQueuedCompletionStatusEx) (HANDLE, LPOVERLAPPED_ENTRY, ULONG, PULONG, DWORD, BOOL); + sGetQueuedCompletionStatusEx mPGetQueuedCompletionStatusEx; + HANDLE mIOCP; +#elif defined BRYNET_PLATFORM_LINUX + std::vector mEventEntries; + int mEpollFd; +#elif defined BRYNET_PLATFORM_DARWIN + std::vector mEventEntries; + int mKqueueFd; +#endif + std::unique_ptr mWakeupChannel; + + std::atomic_bool mIsInBlock; + std::atomic_bool mIsAlreadyPostWakeup; + + std::mutex mAsyncFunctorsMutex; + std::vector mAsyncFunctors; + std::vector mCopyAsyncFunctors; + + std::vector mAfterLoopFunctors; + std::vector mCopyAfterLoopFunctors; + + std::once_flag mOnceInitThreadID; + current_thread::THREAD_ID_TYPE mSelfThreadID; + + brynet::base::TimerMgr::Ptr mTimer; + std::unordered_map mTcpConnections; + + friend class TcpConnection; + }; + +} } \ No newline at end of file diff --git a/libs/brynet/net/Exception.hpp b/libs/brynet/net/Exception.hpp new file mode 100644 index 0000000..2fa103b --- /dev/null +++ b/libs/brynet/net/Exception.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +namespace brynet { namespace net { + + class ConnectException : public std::runtime_error + { + public: + explicit ConnectException(const std::string& message) + : + std::runtime_error(message) + { + } + + explicit ConnectException(const char* message) + : + std::runtime_error(message) + { + } + }; + + class BrynetCommonException : public std::runtime_error + { + public: + explicit BrynetCommonException(const std::string& message) + : + std::runtime_error(message) + { + } + + explicit BrynetCommonException(const char* message) + : + std::runtime_error(message) + { + } + }; + +} } \ No newline at end of file diff --git a/libs/brynet/net/ListenThread.hpp b/libs/brynet/net/ListenThread.hpp new file mode 100644 index 0000000..3b9ef90 --- /dev/null +++ b/libs/brynet/net/ListenThread.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace brynet { namespace net { + + class ListenThread : public detail::ListenThreadDetail, + public std::enable_shared_from_this + { + public: + using Ptr = std::shared_ptr; + using AccepCallback = std::function;; + using TcpSocketProcessCallback = std::function; + + void startListen() + { + detail::ListenThreadDetail::startListen(); + } + + void stopListen() + { + detail::ListenThreadDetail::stopListen(); + } + + public: + static Ptr Create(bool isIPV6, + const std::string& ip, + int port, + const AccepCallback& callback, + const std::vector & processCallbacks = {}) + { + class make_shared_enabler : public ListenThread + { + public: + make_shared_enabler(bool isIPV6, + const std::string& ip, + int port, + const AccepCallback& callback, + const std::vector& processCallbacks) + : + ListenThread(isIPV6, ip, port, callback, processCallbacks) + {} + }; + return std::make_shared(isIPV6, ip, port, callback, processCallbacks); + } + + protected: + ListenThread(bool isIPV6, + const std::string& ip, + int port, + const AccepCallback& callback, + const std::vector& processCallbacks) + : + detail::ListenThreadDetail(isIPV6, ip, port, callback, processCallbacks) + {} + }; + +} } diff --git a/libs/brynet/net/Poller.hpp b/libs/brynet/net/Poller.hpp new file mode 100644 index 0000000..820a941 --- /dev/null +++ b/libs/brynet/net/Poller.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN +#include +#endif + +namespace brynet { namespace base { + + #ifdef BRYNET_PLATFORM_WINDOWS + const static int CHECK_READ_FLAG = (POLLIN | POLLRDNORM | POLLRDBAND); + const static int CHECK_WRITE_FLAG = (POLLOUT | POLLWRNORM); + const static int CHECK_ERROR_FLAG = (POLLERR | POLLHUP); + #elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + const static int CHECK_READ_FLAG = (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI); + const static int CHECK_WRITE_FLAG = (POLLOUT | POLLWRNORM | POLLWRBAND); + const static int CHECK_ERROR_FLAG = (POLLERR | POLLHUP); + #endif + + enum CheckType + { + ReadCheck = 0x1, + WriteCheck = 0x2, + ErrorCheck = 0x4, + }; + + struct poller_s + { + struct pollfd* pollFds; + int nfds; + int limitSize; + }; + + static void upstep_pollfd(struct poller_s* self, int upSize) + { + if (upSize <= 0) + { + return; + } + + struct pollfd* newPollfds = (struct pollfd*)malloc( + sizeof(struct pollfd) * (self->limitSize + upSize)); + if (newPollfds == nullptr) + { + return; + } + + if (self->pollFds != nullptr) + { + memcpy(newPollfds, self->pollFds, sizeof(struct pollfd) * self->nfds); + free(self->pollFds); + self->pollFds = nullptr; + } + self->pollFds = newPollfds; + self->limitSize += upSize; + } + + static struct pollfd* find_pollfd(struct poller_s* self, BrynetSocketFD fd) + { + for (int i = 0; i < self->nfds; i++) + { + if (self->pollFds[i].fd == fd) + { + return self->pollFds + i; + } + } + + return nullptr; + } + + static void try_remove_pollfd(struct poller_s* self, BrynetSocketFD fd) + { + int pos = -1; + for (int i = 0; i < self->nfds; i++) + { + if (self->pollFds[i].fd == fd) + { + pos = i; + break; + } + } + + if (pos != -1) + { + memmove(self->pollFds + pos, + self->pollFds + pos + 1, + sizeof(struct pollfd) * (self->nfds - pos - 1)); + self->nfds--; + assert(self->nfds >= 0); + } + } + + static struct poller_s* poller_new(void) + { + struct poller_s* ret = (struct poller_s*)malloc(sizeof(struct poller_s)); + if (ret != nullptr) + { + ret->pollFds = NULL; + ret->limitSize = 0; + ret->nfds = 0; + upstep_pollfd(ret, 1024); + } + + return ret; + } + + static void poller_delete(struct poller_s* self) + { + free(self->pollFds); + self->pollFds = nullptr; + self->nfds = 0; + self->limitSize = 0; + + free(self); + self = nullptr; + } + + static void poller_add(struct poller_s* self, BrynetSocketFD fd, int type) + { + if (self->limitSize == self->nfds) + { + upstep_pollfd(self, 128); + } + + if (self->limitSize <= self->nfds) + { + return; + } + + struct pollfd* pf = find_pollfd(self, fd); + if (pf == nullptr) + { + /*real add*/ + pf = self->pollFds + self->nfds; + pf->events = 0; + pf->fd = fd; + + self->nfds++; + } + + if (type & ReadCheck) + { + pf->events |= CHECK_READ_FLAG; + } + + if (type & WriteCheck) + { + pf->events |= CHECK_WRITE_FLAG; + } + + if (type & ErrorCheck) + { + //pf->events |= CHECK_ERROR_FLAG; TODO::on windows, not supports + } + } + + static void poller_del(struct poller_s* self, BrynetSocketFD fd, int type) + { + struct pollfd* pf = find_pollfd(self, fd); + if (pf == nullptr) + { + return; + } + + if (type & ReadCheck) + { + pf->events &= ~CHECK_READ_FLAG; + } + + if (type & WriteCheck) + { + pf->events &= ~CHECK_WRITE_FLAG; + } + + if (type & ErrorCheck) + { + pf->events &= ~CHECK_ERROR_FLAG; + } + + if (pf->events == 0) + { + try_remove_pollfd(self, fd); + } + } + + static void poller_remove(struct poller_s* self, BrynetSocketFD fd) + { + try_remove_pollfd(self, fd); + } + + static bool check_event(const struct pollfd* pf, enum CheckType type) + { + if (pf == nullptr) + { + return false; + } + + if ((type & ReadCheck) && + (pf->revents & CHECK_READ_FLAG)) + { + return true; + } + else if ((type & WriteCheck) && + (pf->revents & CHECK_WRITE_FLAG)) + { + return true; + } + else if ((type & ErrorCheck) && + (pf->revents & CHECK_ERROR_FLAG)) + { + return true; + } + else + { + return false; + } + } + + static void poller_visitor(struct poller_s* self, + enum CheckType type, + struct stack_s* result) + { + for (int i = 0; i < self->nfds; i++) + { + if (check_event(self->pollFds + i, type)) + { + stack_push(result, &self->pollFds[i].fd); + } + } + } + + static int poller_poll(struct poller_s* self, long overtime) + { +#ifdef BRYNET_PLATFORM_WINDOWS + int ret = WSAPoll(&self->pollFds[0], self->nfds, overtime); +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + int ret = poll(self->pollFds, self->nfds, overtime); +#endif + + if (ret == BRYNET_SOCKET_ERROR) + { + ret = (BRYNET_ERRNO != BRYNET_EINTR) ? -1 : 0; + } + + return ret; + } + + static bool poller_check(struct poller_s* self, BrynetSocketFD fd, enum CheckType type) + { + const struct pollfd* pf = find_pollfd(self, fd); + if (pf == NULL) + { + return false; + } + return check_event(pf, type); + } + +} } diff --git a/libs/brynet/net/PromiseReceive.hpp b/libs/brynet/net/PromiseReceive.hpp new file mode 100644 index 0000000..2f372f3 --- /dev/null +++ b/libs/brynet/net/PromiseReceive.hpp @@ -0,0 +1,156 @@ +#pragma once + +#include + +namespace brynet { namespace net { + + /* binary search in memory */ + void memsearch(const char *hay, size_t haysize, const char *needle, size_t needlesize, size_t& result, bool& isOK) + { + size_t haypos, needlepos; + haysize -= needlesize; + + for (haypos = 0; haypos <= haysize; haypos++) + { + for (needlepos = 0; needlepos < needlesize; needlepos++) + { + if (hay[haypos + needlepos] != needle[needlepos]) + { + // Next character in haystack. + break; + } + } + if (needlepos == needlesize) + { + result = haypos; + isOK = true; + return; + } + } + + isOK = false; + } + + class PromiseReceive; + + std::shared_ptr setupPromiseReceive(const TcpConnection::Ptr& session); + + class PromiseReceive : public std::enable_shared_from_this + { + public: + using Ptr = std::shared_ptr; + using Handle = std::function; + + PromiseReceive::Ptr receive(size_t len, Handle handle) + { + return receive(std::make_shared(len), std::move(handle)); + } + + PromiseReceive::Ptr receive(std::shared_ptr len, Handle handle) + { + return helpReceive(std::move(len), "", std::move(handle)); + } + + PromiseReceive::Ptr receiveUntil(std::string str, Handle handle) + { + if (str.empty()) + { + throw std::runtime_error("str is empty"); + } + + return helpReceive(nullptr, std::move(str), std::move(handle)); + } + + private: + PromiseReceive::Ptr helpReceive(std::shared_ptr len, std::string str, Handle handle) + { + auto pr = std::make_shared(); + pr->len = std::move(len); + pr->str = std::move(str); + pr->handle = std::move(handle); + mPendingReceives.push_back(std::move(pr)); + + return shared_from_this(); + } + + size_t process(const char* buffer, const size_t len) + { + size_t procLen = 0; + + while (!mPendingReceives.empty() && len >= procLen) + { + auto pendingReceive = mPendingReceives.front(); + if (pendingReceive->len != nullptr) + { + const auto tryReceiveLen = *pendingReceive->len; + if ((len - procLen) < tryReceiveLen) + { + break; + } + + mPendingReceives.pop_front(); + procLen += tryReceiveLen; + if (pendingReceive->handle(buffer + procLen - tryReceiveLen, tryReceiveLen) && tryReceiveLen > 0) + { + mPendingReceives.push_front(pendingReceive); + } + } + else if (!pendingReceive->str.empty()) + { + size_t pos = 0; + bool isOK = false; + auto data = buffer + procLen; + memsearch(buffer + procLen, + len - procLen, + pendingReceive->str.c_str(), + pendingReceive->str.size(), + pos, + isOK); + + if (!isOK) + { + break; + } + + mPendingReceives.pop_front(); + procLen += (pos + pendingReceive->str.size()); + if (pendingReceive->handle(data, pos)) + { + mPendingReceives.push_front(pendingReceive); + } + } + else + { + break; + } + } + + return procLen; + } + + private: + struct PendingReceive + { + std::shared_ptr len; + std::string str; + Handle handle; + }; + + std::deque> mPendingReceives; + + friend std::shared_ptr setupPromiseReceive(const TcpConnection::Ptr& session); + }; + + std::shared_ptr setupPromiseReceive(const TcpConnection::Ptr& session) + { + auto promiseReceive = std::make_shared(); + session->setDataCallback([promiseReceive](brynet::base::BasePacketReader& reader) { + auto procLen = promiseReceive->process(reader.begin(), reader.size()); + reader.addPos(procLen); + reader.savePos(); + }); + + return promiseReceive; + } + +} } diff --git a/libs/brynet/net/SSLHelper.hpp b/libs/brynet/net/SSLHelper.hpp new file mode 100644 index 0000000..fb2598d --- /dev/null +++ b/libs/brynet/net/SSLHelper.hpp @@ -0,0 +1,174 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BRYNET_USE_OPENSSL + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif + +#endif + +namespace brynet { namespace net { + +#ifdef BRYNET_USE_OPENSSL + +#ifndef CRYPTO_THREADID_set_callback + static void cryptoSetThreadIDCallback(CRYPTO_THREADID* id) + { +#ifdef BRYNET_PLATFORM_WINDOWS + CRYPTO_THREADID_set_numeric(id, + static_cast(GetCurrentThreadId())); +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + CRYPTO_THREADID_set_numeric(id, + static_cast(pthread_self())); +#endif + } +#endif + +#ifndef CRYPTO_set_locking_callback + static std::unordered_map> cryptoLocks; + static void cryptoLockingCallback(int mode, + int type, + const char* file, int line) + { + (void)file; + (void)line; + if (mode & CRYPTO_LOCK) + { + cryptoLocks[type]->lock(); + } + else if (mode & CRYPTO_UNLOCK) + { + cryptoLocks[type]->unlock(); + } + } +#endif + + static std::once_flag initCryptoThreadSafeSupportOnceFlag; + static void InitCryptoThreadSafeSupport() + { +#ifndef CRYPTO_THREADID_set_callback + CRYPTO_THREADID_set_callback(cryptoSetThreadIDCallback); +#endif + +#ifndef CRYPTO_set_locking_callback + for (int i = 0; i < CRYPTO_num_locks(); i++) + { + cryptoLocks[i] = std::make_shared(); + } + CRYPTO_set_locking_callback(cryptoLockingCallback); +#endif + } +#endif + + class SSLHelper : public brynet::base::NonCopyable, + public std::enable_shared_from_this + { + public: + using Ptr = std::shared_ptr; + +#ifdef BRYNET_USE_OPENSSL + bool initSSL(const std::string& certificate, + const std::string& privatekey) + { + std::call_once(initCryptoThreadSafeSupportOnceFlag, + InitCryptoThreadSafeSupport); + + if (mOpenSSLCTX != nullptr) + { + return false; + } + if (certificate.empty() || privatekey.empty()) + { + return false; + } + + mOpenSSLCTX = SSL_CTX_new(SSLv23_method()); + SSL_CTX_set_client_CA_list(mOpenSSLCTX, + SSL_load_client_CA_file(certificate.c_str())); + SSL_CTX_set_verify_depth(mOpenSSLCTX, 10); + + if (SSL_CTX_use_certificate_chain_file(mOpenSSLCTX, + certificate.c_str()) <= 0) + { + SSL_CTX_free(mOpenSSLCTX); + mOpenSSLCTX = nullptr; + return false; + } + + if (SSL_CTX_use_PrivateKey_file(mOpenSSLCTX, + privatekey.c_str(), + SSL_FILETYPE_PEM) <= 0) + { + SSL_CTX_free(mOpenSSLCTX); + mOpenSSLCTX = nullptr; + return false; + } + + if (!SSL_CTX_check_private_key(mOpenSSLCTX)) + { + SSL_CTX_free(mOpenSSLCTX); + mOpenSSLCTX = nullptr; + return false; + } + + return true; + } + + void destroySSL() + { + if (mOpenSSLCTX != nullptr) + { + SSL_CTX_free(mOpenSSLCTX); + mOpenSSLCTX = nullptr; + } + } + + SSL_CTX* getOpenSSLCTX() + { + return mOpenSSLCTX; + } +#endif + static Ptr Create() + { + class make_shared_enabler : public SSLHelper {}; + return std::make_shared(); + } + + protected: + SSLHelper() BRYNET_NOEXCEPT + { +#ifdef BRYNET_USE_OPENSSL + mOpenSSLCTX = nullptr; +#endif + } + + virtual ~SSLHelper() BRYNET_NOEXCEPT + { +#ifdef BRYNET_USE_OPENSSL + destroySSL(); +#endif + } + + private: +#ifdef BRYNET_USE_OPENSSL + SSL_CTX* mOpenSSLCTX; +#endif + }; + +} } diff --git a/libs/brynet/net/Socket.hpp b/libs/brynet/net/Socket.hpp new file mode 100644 index 0000000..738f507 --- /dev/null +++ b/libs/brynet/net/Socket.hpp @@ -0,0 +1,230 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace brynet { namespace net { + + class TcpConnection; + + class UniqueFd final : public brynet::base::NonCopyable + { + public: + explicit UniqueFd(BrynetSocketFD fd) + : + mFD(fd) + {} + + ~UniqueFd() + { + brynet::net::base::SocketClose(mFD); + } + + UniqueFd(const UniqueFd& other) = delete; + UniqueFd& operator=(const UniqueFd& other) = delete; + + BrynetSocketFD getFD() const + { + return mFD; + } + + private: + BrynetSocketFD mFD; + }; + + class TcpSocket : public brynet::base::NonCopyable + { + private: + class TcpSocketDeleter + { + public: + void operator()(TcpSocket* ptr) const + { + delete ptr; + } + }; + public: + using Ptr = std::unique_ptr; + + public: + static Ptr Create(BrynetSocketFD fd, bool serverSide) + { + class make_unique_enabler : public TcpSocket + { + public: + make_unique_enabler(BrynetSocketFD fd, bool serverSide) + : + TcpSocket(fd, serverSide) + {} + }; + + return Ptr(new make_unique_enabler(fd, serverSide)); + } + + public: + void setNodelay() const + { + brynet::net::base::SocketNodelay(mFD); + } + + bool setNonblock() const + { + return brynet::net::base::SocketNonblock(mFD); + } + + void setSendSize(int sdSize) const + { + brynet::net::base::SocketSetSendSize(mFD, sdSize); + } + + void setRecvSize(int rdSize) const + { + brynet::net::base::SocketSetRecvSize(mFD, rdSize); + } + + std::string getRemoteIP() const + { + return brynet::net::base::GetIPOfSocket(mFD); + } + + bool isServerSide() const + { + return mServerSide; + } + + protected: + TcpSocket(BrynetSocketFD fd, bool serverSide) + : + mFD(fd), + mServerSide(serverSide) + { + } + + virtual ~TcpSocket() + { + brynet::net::base::SocketClose(mFD); + } + + BrynetSocketFD getFD() const + { + return mFD; + } + + private: + const BrynetSocketFD mFD; + const bool mServerSide; + + friend class TcpConnection; + }; + + class EintrError : public std::exception + { + }; + + class AcceptError : public std::runtime_error + { + public: + explicit AcceptError(int errorCode) + : + std::runtime_error(std::to_string(errorCode)), + mErrorCode(errorCode) + {} + + int getErrorCode() const + { + return mErrorCode; + } + + private: + int mErrorCode; + }; + + class ListenSocket : public brynet::base::NonCopyable + { + private: + class ListenSocketDeleter + { + public: + void operator()(ListenSocket* ptr) const + { + delete ptr; + } + }; + public: + using Ptr = std::unique_ptr; + + public: + TcpSocket::Ptr accept() + { + const auto clientFD = brynet::net::base::Accept(mFD, nullptr, nullptr); + if (clientFD == BRYNET_INVALID_SOCKET) + { +#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + if (BRYNET_ERRNO == EMFILE) + { + // Thanks libev and muduo. + // Read the section named "The special problem of + // accept()ing when you can't" in libev's doc. + // By Marc Lehmann, author of libev. + mIdle.reset(); + TcpSocket::Create(brynet::net::base::Accept(mFD, nullptr, nullptr), true); + mIdle = brynet::net::TcpSocket::Create(::open("/dev/null", O_RDONLY | O_CLOEXEC), true); + } +#endif + if (BRYNET_ERRNO == EINTR) + { + throw EintrError(); + } + else + { + throw AcceptError(BRYNET_ERRNO); + } + } + + return TcpSocket::Create(clientFD, true); + } + + public: + static Ptr Create(BrynetSocketFD fd) + { + class make_unique_enabler : public ListenSocket + { + public: + explicit make_unique_enabler(BrynetSocketFD fd) + : ListenSocket(fd) + {} + }; + + return Ptr(new make_unique_enabler(fd)); + } + + protected: + explicit ListenSocket(BrynetSocketFD fd) + : + mFD(fd) + { +#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + mIdle = brynet::net::TcpSocket::Create(::open("/dev/null", O_RDONLY | O_CLOEXEC), true); +#endif + } + + virtual ~ListenSocket() + { + brynet::net::base::SocketClose(mFD); + } + + private: + const BrynetSocketFD mFD; +#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + brynet::net::TcpSocket::Ptr mIdle; +#endif + + friend class TcpConnection; + }; + +} } \ No newline at end of file diff --git a/libs/brynet/net/SocketLibFunction.hpp b/libs/brynet/net/SocketLibFunction.hpp new file mode 100644 index 0000000..97df382 --- /dev/null +++ b/libs/brynet/net/SocketLibFunction.hpp @@ -0,0 +1,329 @@ +#pragma once + +#include +#include +#include + +namespace brynet { namespace net { namespace base { + + static bool InitSocket() + { + bool ret = true; +#ifdef BRYNET_PLATFORM_WINDOWS + static WSADATA g_WSAData; + static bool WinSockIsInit = false; + if (WinSockIsInit) + { + return true; + } + if (WSAStartup(MAKEWORD(2, 2), &g_WSAData) == 0) + { + WinSockIsInit = true; + } + else + { + ret = false; + } +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + signal(SIGPIPE, SIG_IGN); +#endif + + return ret; + } + + static void DestroySocket() + { +#ifdef BRYNET_PLATFORM_WINDOWS + WSACleanup(); +#endif + } + + static int SocketNodelay(BrynetSocketFD fd) + { + const int flag = 1; + return ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&flag, sizeof(flag)); + } + + static bool SocketBlock(BrynetSocketFD fd) + { + int err; + unsigned long ul = false; +#ifdef BRYNET_PLATFORM_WINDOWS + err = ioctlsocket(fd, FIONBIO, &ul); +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + err = ioctl(fd, FIONBIO, &ul); +#endif + + return err != BRYNET_SOCKET_ERROR; + } + + static bool SocketNonblock(BrynetSocketFD fd) + { + int err; + unsigned long ul = true; +#ifdef BRYNET_PLATFORM_WINDOWS + err = ioctlsocket(fd, FIONBIO, &ul); +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + err = ioctl(fd, FIONBIO, &ul); +#endif + + return err != BRYNET_SOCKET_ERROR; + } + + static int SocketSetSendSize(BrynetSocketFD fd, int sd_size) + { + return ::setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&sd_size, sizeof(sd_size)); + } + + static int SocketSetRecvSize(BrynetSocketFD fd, int rd_size) + { + return ::setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&rd_size, sizeof(rd_size)); + } + + static BrynetSocketFD SocketCreate(int af, int type, int protocol) + { + return ::socket(af, type, protocol); + } + + static void SocketClose(BrynetSocketFD fd) + { +#ifdef BRYNET_PLATFORM_WINDOWS + ::closesocket(fd); +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + ::close(fd); +#endif + } + + static BrynetSocketFD Connect(bool isIPV6, const std::string& server_ip, int port) + { + InitSocket(); + + struct sockaddr_in ip4Addr = sockaddr_in(); + struct sockaddr_in6 ip6Addr = sockaddr_in6(); + struct sockaddr_in* paddr = &ip4Addr; + int addrLen = sizeof(ip4Addr); + + BrynetSocketFD clientfd = isIPV6 ? + SocketCreate(AF_INET6, SOCK_STREAM, 0) : + SocketCreate(AF_INET, SOCK_STREAM, 0); + + if (clientfd == BRYNET_INVALID_SOCKET) + { + return clientfd; + } + + bool ptonResult = false; + if (isIPV6) + { + ip6Addr.sin6_family = AF_INET6; + ip6Addr.sin6_port = htons(port); + ptonResult = inet_pton(AF_INET6, + server_ip.c_str(), + &ip6Addr.sin6_addr) > 0; + paddr = (struct sockaddr_in*) & ip6Addr; + addrLen = sizeof(ip6Addr); + } + else + { + ip4Addr.sin_family = AF_INET; + ip4Addr.sin_port = htons(port); + ptonResult = inet_pton(AF_INET, + server_ip.c_str(), + &ip4Addr.sin_addr) > 0; + } + + if (!ptonResult) + { + SocketClose(clientfd); + return BRYNET_INVALID_SOCKET; + } + + while (::connect(clientfd, (struct sockaddr*)paddr, addrLen) < 0) + { + if (EINTR == BRYNET_ERRNO) + { + continue; + } + + SocketClose(clientfd); + return BRYNET_INVALID_SOCKET; + } + + return clientfd; + } + + static BrynetSocketFD Listen(bool isIPV6, const char* ip, int port, int back_num) + { + InitSocket(); + + struct sockaddr_in ip4Addr = sockaddr_in(); + struct sockaddr_in6 ip6Addr = sockaddr_in6(); + struct sockaddr_in* paddr = &ip4Addr; + int addrLen = sizeof(ip4Addr); + + const auto socketfd = isIPV6 ? + socket(AF_INET6, SOCK_STREAM, 0) : + socket(AF_INET, SOCK_STREAM, 0); + if (socketfd == BRYNET_INVALID_SOCKET) + { + return BRYNET_INVALID_SOCKET; + } + + bool ptonResult = false; + if (isIPV6) + { + ip6Addr.sin6_family = AF_INET6; + ip6Addr.sin6_port = htons(port); + ptonResult = inet_pton(AF_INET6, ip, &ip6Addr.sin6_addr) > 0; + paddr = (struct sockaddr_in*) & ip6Addr; + addrLen = sizeof(ip6Addr); + } + else + { + ip4Addr.sin_family = AF_INET; + ip4Addr.sin_port = htons(port); + ip4Addr.sin_addr.s_addr = INADDR_ANY; + ptonResult = inet_pton(AF_INET, ip, &ip4Addr.sin_addr) > 0; + } + + const int reuseaddr_value = 1; + if (!ptonResult || + ::setsockopt(socketfd, + SOL_SOCKET, + SO_REUSEADDR, + (const char*)&reuseaddr_value, + sizeof(int)) < 0) + { + SocketClose(socketfd); + return BRYNET_INVALID_SOCKET; + } + + const int bindRet = ::bind(socketfd, (struct sockaddr*)paddr, addrLen); + if (bindRet == BRYNET_SOCKET_ERROR || + listen(socketfd, back_num) == BRYNET_SOCKET_ERROR) + { + SocketClose(socketfd); + return BRYNET_INVALID_SOCKET; + } + + return socketfd; + } + + static std::string getIPString(const struct sockaddr* sa) + { +#ifdef BRYNET_PLATFORM_WINDOWS + using PAddrType = PVOID; +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + using PAddrType = const void*; +#endif + char tmp[INET6_ADDRSTRLEN] = { 0 }; + switch (sa->sa_family) + { + case AF_INET: + inet_ntop(AF_INET, (PAddrType)(&(((const struct sockaddr_in*)sa)->sin_addr)), + tmp, sizeof(tmp)); + break; + case AF_INET6: + inet_ntop(AF_INET6, (PAddrType)(&(((const struct sockaddr_in6*)sa)->sin6_addr)), + tmp, sizeof(tmp)); + break; + default: + return "Unknown AF"; + } + + return tmp; + } + + static std::string GetIPOfSocket(BrynetSocketFD fd) + { +#ifdef BRYNET_PLATFORM_WINDOWS + struct sockaddr name = sockaddr(); + int namelen = sizeof(name); + if (::getpeername(fd, (struct sockaddr*) & name, &namelen) == 0) + { + return getIPString(&name); + } +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + struct sockaddr_in name = sockaddr_in(); + socklen_t namelen = sizeof(name); + if (::getpeername(fd, (struct sockaddr*) & name, &namelen) == 0) + { + return getIPString((const struct sockaddr*) & name); + } +#endif + + return ""; + } + + static int SocketSend(BrynetSocketFD fd, const char* buffer, int len) + { + int transnum = ::send(fd, buffer, len, 0); + if (transnum < 0 && BRYNET_EWOULDBLOCK == BRYNET_ERRNO) + { + transnum = 0; + } + + /* send error if transnum < 0 */ + return transnum; + } + + static BrynetSocketFD Accept(BrynetSocketFD listenSocket, struct sockaddr* addr, socklen_t* addrLen) + { + return ::accept(listenSocket, addr, addrLen); + } + + static struct sockaddr_in6 getPeerAddr(BrynetSocketFD sockfd) + { + struct sockaddr_in6 peeraddr = sockaddr_in6(); + auto addrlen = static_cast(sizeof peeraddr); + if (::getpeername(sockfd, (struct sockaddr*)(&peeraddr), &addrlen) < 0) + { + return peeraddr; + } + return peeraddr; + } + + static struct sockaddr_in6 getLocalAddr(BrynetSocketFD sockfd) + { + struct sockaddr_in6 localaddr = sockaddr_in6(); + auto addrlen = static_cast(sizeof localaddr); + if (::getsockname(sockfd, (struct sockaddr*)(&localaddr), &addrlen) < 0) + { + return localaddr; + } + return localaddr; + } + + static bool IsSelfConnect(BrynetSocketFD fd) + { + struct sockaddr_in6 localaddr = getLocalAddr(fd); + struct sockaddr_in6 peeraddr = getPeerAddr(fd); + + if (localaddr.sin6_family == AF_INET) + { + const struct sockaddr_in* laddr4 = reinterpret_cast(&localaddr); + const struct sockaddr_in* raddr4 = reinterpret_cast(&peeraddr); + return laddr4->sin_port == raddr4->sin_port + && laddr4->sin_addr.s_addr == raddr4->sin_addr.s_addr; + } + else if (localaddr.sin6_family == AF_INET6) + { +#ifdef BRYNET_PLATFORM_WINDOWS + return localaddr.sin6_port == peeraddr.sin6_port + && memcmp(&localaddr.sin6_addr.u.Byte, + &peeraddr.sin6_addr.u.Byte, + sizeof localaddr.sin6_addr.u.Byte) == 0; +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + return localaddr.sin6_port == peeraddr.sin6_port + && memcmp(&localaddr.sin6_addr.s6_addr, + &peeraddr.sin6_addr.s6_addr, + sizeof localaddr.sin6_addr.s6_addr) == 0; +#endif + } + else + { + return false; + } + } + +} } } diff --git a/libs/brynet/net/SocketLibTypes.hpp b/libs/brynet/net/SocketLibTypes.hpp new file mode 100644 index 0000000..0a881de --- /dev/null +++ b/libs/brynet/net/SocketLibTypes.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include + +#ifdef BRYNET_PLATFORM_WINDOWS +#include +#include +#include +#include +#include + +#elif defined BRYNET_PLATFORM_LINUX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#elif defined BRYNET_PLATFORM_DARWIN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else +#error "Unsupported OS, please commit an issuse." +#endif + +#ifdef BRYNET_PLATFORM_WINDOWS +typedef SOCKET BrynetSocketFD; +#define BRYNET_ERRNO WSAGetLastError() +#define BRYNET_ENOTSOCK WSAENOTSOCK +#define BRYNET_EWOULDBLOCK WSAEWOULDBLOCK +#define BRYNET_EINTR WSAEINTR +#define BRYNET_ECONNABORTED WSAECONNABORTED +#define BRYNET_SOCKET_ERROR SOCKET_ERROR +#define BRYNET_INVALID_SOCKET INVALID_SOCKET + +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN +#define BRYNET_ERRNO errno +#define BRYNET_ENOTSOCK EBADF +#define BRYNET_EWOULDBLOCK EAGAIN +#define BRYNET_EINTR EINTR +#define BRYNET_ECONNABORTED ECONNABORTED +typedef int BrynetSocketFD; +#define BRYNET_SOCKET_ERROR (-1) +#define BRYNET_INVALID_SOCKET (-1) +#endif diff --git a/libs/brynet/net/TcpConnection.hpp b/libs/brynet/net/TcpConnection.hpp new file mode 100644 index 0000000..8bed5c2 --- /dev/null +++ b/libs/brynet/net/TcpConnection.hpp @@ -0,0 +1,1196 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BRYNET_USE_OPENSSL + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif + +#endif + +namespace brynet { namespace net { + + class SendableMsg + { + public: + using Ptr = std::shared_ptr; + + virtual ~SendableMsg() = default; + + virtual const void * data() = 0; + virtual size_t size() = 0; + }; + + class TcpConnection : public Channel, + public brynet::base::NonCopyable, + public std::enable_shared_from_this + { + public: + using Ptr = std::shared_ptr; + + using EnterCallback = std::function; + using DataCallback = std::function; + using DisconnectedCallback = std::function; + using PacketSendedCallback = std::function; + + public: + Ptr static Create(TcpSocket::Ptr socket, + size_t maxRecvBufferSize, + EnterCallback&& enterCallback, + const EventLoop::Ptr& eventLoop, + const SSLHelper::Ptr& sslHelper = nullptr) + { + class make_shared_enabler : public TcpConnection + { + public: + make_shared_enabler(TcpSocket::Ptr socket, + size_t maxRecvBufferSize, + EnterCallback&& enterCallback, + EventLoop::Ptr eventLoop) + : + TcpConnection(std::move(socket), + maxRecvBufferSize, + std::move(enterCallback), + std::move(eventLoop)) + {} + }; + + const auto isServerSide = socket->isServerSide(); + auto session = std::make_shared( + std::move(socket), + maxRecvBufferSize, + std::move(enterCallback), + eventLoop); + (void)isServerSide; +#ifdef BRYNET_USE_OPENSSL + if(sslHelper != nullptr) + { + if (isServerSide) + { + if (sslHelper->getOpenSSLCTX() == nullptr || + !session->initAcceptSSL(sslHelper->getOpenSSLCTX())) + { + throw std::runtime_error("init ssl failed"); + } + } + else + { + if (!session->initConnectSSL()) + { + throw std::runtime_error("init ssl failed"); + } + } + } +#else + if (sslHelper != nullptr) + { + throw std::runtime_error("not enable ssl"); + } +#endif + + eventLoop->runAsyncFunctor([session]() + { + session->onEnterEventLoop(); + }); + return session; + } + + const EventLoop::Ptr& getEventLoop() const + { + return mEventLoop; + } + + //TODO::如果所属EventLoop已经没有工作,则可能导致内存无限大,因为所投递的请求都没有得到处理 + void send( + const SendableMsg::Ptr& msg, + PacketSendedCallback&& callback = nullptr + ) + { + auto sharedThis = shared_from_this(); + mEventLoop->runAsyncFunctor([sharedThis, msg, callback]() mutable + { + const auto len = msg->size(); + sharedThis->mSendList.emplace_back(PendingPacket{ + std::move(msg), + len, + std::move(callback) }); + sharedThis->runAfterFlush(); + }); + } + + void send( + const char* buffer, + size_t len, + PacketSendedCallback&& callback = nullptr) + { + send(std::make_shared(buffer, len), std::move(callback)); + } + + void send( + const std::shared_ptr& packet, + PacketSendedCallback&& callback = nullptr) + { + class StringSendMsg : public SendableMsg + { + public: + explicit StringSendMsg(std::shared_ptr&& msg) + : + mMsg(std::move(msg)) + {} + explicit StringSendMsg(const std::shared_ptr& msg) + : + mMsg(msg) + {} + + const void* data() override + { + return static_cast(mMsg->data()); + } + + size_t size() override + { + return mMsg->size(); + } + + private: + std::shared_ptr mMsg; + }; + + auto sharedThis = shared_from_this(); + mEventLoop->runAsyncFunctor([sharedThis, packet, callback]() mutable + { + const auto len = packet->size(); + sharedThis->mSendList.emplace_back(PendingPacket{ + std::make_shared(std::move(packet)), + len, + std::move(callback) }); + sharedThis->runAfterFlush(); + }); + } + + // setDataCallback(std::function) + template + void setDataCallback(Callback&& cb) + { + verifyArgType(cb, &Callback::operator()); + + auto sharedThis = shared_from_this(); + mEventLoop->runAsyncFunctor([sharedThis, cb]() mutable + { + sharedThis->mDataCallback = cb; + sharedThis->processRecvMessage(); + }); + } + + void setDisConnectCallback(DisconnectedCallback&& cb) + { + auto sharedThis = shared_from_this(); + mEventLoop->runAsyncFunctor([sharedThis, cb]() mutable + { + sharedThis->mDisConnectCallback = std::move(cb); + }); + } + + /* if checkTime is zero, will cancel check heartbeat */ + void setHeartBeat(std::chrono::nanoseconds checkTime) + { + auto sharedThis = shared_from_this(); + mEventLoop->runAsyncFunctor([sharedThis, checkTime]() + { + if (sharedThis->mTimer.lock() != nullptr) + { + sharedThis->mTimer.lock()->cancel(); + sharedThis->mTimer.reset(); + } + + sharedThis->mCheckTime = checkTime; + sharedThis->startPingCheckTimer(); + }); + } + + void postDisConnect() + { + auto sharedThis = shared_from_this(); + mEventLoop->runAsyncFunctor([sharedThis]() + { + sharedThis->procCloseInLoop(); + }); + } + + void postShutdown() + { + auto sharedThis = shared_from_this(); + mEventLoop->runAsyncFunctor([sharedThis]() + { + sharedThis->mEventLoop->runFunctorAfterLoop([sharedThis]() + { + sharedThis->procShutdownInLoop(); + }); + }); + } + + const std::string& getIP() const + { + return mIP; + } + + protected: + TcpConnection(TcpSocket::Ptr socket, + size_t maxRecvBufferSize, + EnterCallback&& enterCallback, + EventLoop::Ptr eventLoop) BRYNET_NOEXCEPT + : +#ifdef BRYNET_PLATFORM_WINDOWS + mOvlRecv(port::Win::OverlappedType::OverlappedRecv), + mOvlSend(port::Win::OverlappedType::OverlappedSend), + mPostClose(false), +#endif + mIP(socket->getRemoteIP()), + mSocket(std::move(socket)), + mEventLoop(std::move(eventLoop)), + mAlreadyClose(false), + mMaxRecvBufferSize(maxRecvBufferSize), + mEnterCallback(std::move(enterCallback)) + { + mRecvData = false; + mCheckTime = std::chrono::steady_clock::duration::zero(); + mIsPostFlush = false; + + mCanWrite = true; + +#ifdef BRYNET_PLATFORM_WINDOWS + mPostRecvCheck = false; + mPostWriteCheck = false; +#endif + growRecvBuffer(); + +#ifdef BRYNET_USE_OPENSSL + mSSLCtx = nullptr; + mSSL = nullptr; + mIsHandsharked = false; +#endif + } + + ~TcpConnection() BRYNET_NOEXCEPT override + { +#ifdef BRYNET_USE_OPENSSL + if (mSSL != nullptr) + { + SSL_free(mSSL); + mSSL = nullptr; + } + if (mSSLCtx != nullptr) + { + SSL_CTX_free(mSSLCtx); + mSSLCtx = nullptr; + } +#endif + + if (mTimer.lock()) + { + mTimer.lock()->cancel(); + } + } + + private: + void growRecvBuffer() + { + if (mRecvBuffer == nullptr) + { + mRecvBuffer.reset(brynet::base::buffer_new(std::min(16 * 1024, mMaxRecvBufferSize))); + mRecvBuffOriginSize = buffer_getsize(mRecvBuffer.get()); + } + else + { + if (buffer_getsize(mRecvBuffer.get()) >= mMaxRecvBufferSize) + { + return; + } + + mCurrentTanhXDiff += 0.2; + const auto newTanh = std::tanh(mCurrentTanhXDiff); + const auto maxSizeDiff = mMaxRecvBufferSize - mRecvBuffOriginSize; + const auto NewSize = mRecvBuffOriginSize + (maxSizeDiff * newTanh); + + assert(NewSize <= mMaxRecvBufferSize); + std::unique_ptr newBuffer(brynet::base::buffer_new(NewSize)); + buffer_write(newBuffer.get(), + buffer_getreadptr(mRecvBuffer.get()), + buffer_getreadvalidcount(mRecvBuffer.get())); + mRecvBuffer = std::move(newBuffer); + } + } + + /* must called in network thread */ + bool onEnterEventLoop() + { + assert(mEventLoop->isInLoopThread()); + if (!mEventLoop->isInLoopThread()) + { + return false; + } + + if (!brynet::net::base::SocketNonblock(mSocket->getFD()) || + !mEventLoop->linkChannel(mSocket->getFD(), this)) + { + return false; + } + + const auto findRet = mEventLoop->getTcpConnection(mSocket->getFD()); + (void)findRet; + assert(findRet == nullptr); + +#ifdef BRYNET_USE_OPENSSL + if (mSSL != nullptr) + { + mEventLoop->addTcpConnection(mSocket->getFD(), shared_from_this()); + processSSLHandshake(); + return true; + } +#endif + + if (!checkRead()) + { + return false; + } + + mEventLoop->addTcpConnection(mSocket->getFD(), shared_from_this()); + causeEnterCallback(); + + return true; + } + +#ifdef BRYNET_USE_OPENSSL + bool initAcceptSSL(SSL_CTX* ctx) + { + if (mSSL != nullptr) + { + return false; + } + + mSSL = SSL_new(ctx); + if (SSL_set_fd(mSSL, mSocket->getFD()) != 1) + { + ERR_print_errors_fp(stdout); + ::fflush(stdout); + return false; + } + + return true; + } + bool initConnectSSL() + { + if (mSSLCtx != nullptr) + { + return false; + } + + mSSLCtx = SSL_CTX_new(SSLv23_client_method()); + mSSL = SSL_new(mSSLCtx); + + if (SSL_set_fd(mSSL, mSocket->getFD()) != 1) + { + ERR_print_errors_fp(stdout); + ::fflush(stdout); + return false; + } + + return true; + } +#endif + + void pingCheck() + { + mTimer.reset(); + if (mRecvData) + { + mRecvData = false; + startPingCheckTimer(); + } + else + { + procCloseInLoop(); + } + } + + void startPingCheckTimer() + { + if (!mTimer.lock() && + mCheckTime != std::chrono::steady_clock::duration::zero()) + { + std::weak_ptr weakedThis = shared_from_this(); + mTimer = mEventLoop->runAfter(mCheckTime, [weakedThis]() { + auto sharedThis = weakedThis.lock(); + if (sharedThis != nullptr) + { + sharedThis->pingCheck(); + } + }); + } + } + + void canRecv() override + { +#ifdef BRYNET_PLATFORM_WINDOWS + mPostRecvCheck = false; + if (mPostClose) + { + if (!mPostWriteCheck) + { + onClose(); + } + return; + } +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + if (mAlreadyClose) + { + return; + } +#endif + +#ifdef BRYNET_USE_OPENSSL + if (mSSL != nullptr + && !mIsHandsharked + && (!processSSLHandshake() || !mIsHandsharked)) + { + return; + } +#endif + + recv(); + } + + void canSend() override + { +#ifdef BRYNET_PLATFORM_WINDOWS + mPostWriteCheck = false; + if (mPostClose) + { + if (!mPostRecvCheck) + { + onClose(); + } + return; + } +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + if (mAlreadyClose) + { + return; + } +#endif + mCanWrite = true; + +#ifdef BRYNET_USE_OPENSSL + if (mSSL != nullptr + && !mIsHandsharked + && (!processSSLHandshake() || !mIsHandsharked)) + { + return; + } +#endif + + runAfterFlush(); + } + + bool checkRead() + { + bool check_ret = true; +#ifdef BRYNET_PLATFORM_WINDOWS + static CHAR temp[] = { 0 }; + static WSABUF in_buf = { 0, temp }; + + if (mPostRecvCheck) + { + return check_ret; + } + + DWORD dwBytes = 0; + DWORD flag = 0; + const int ret = WSARecv(mSocket->getFD(), + &in_buf, + 1, + &dwBytes, + &flag, + &(mOvlRecv.base), + 0); + if (ret == BRYNET_SOCKET_ERROR) + { + check_ret = (BRYNET_ERRNO == WSA_IO_PENDING); + } + + if (check_ret) + { + mPostRecvCheck = true; + } +#endif + + return check_ret; + } + + bool checkWrite() + { + bool check_ret = true; +#ifdef BRYNET_PLATFORM_WINDOWS + static WSABUF wsendbuf[1] = { {NULL, 0} }; + + if (mPostWriteCheck) + { + return check_ret; + } + + DWORD send_len = 0; + const int ret = WSASend(mSocket->getFD(), + wsendbuf, + 1, + &send_len, + 0, + &(mOvlSend.base), + 0); + if (ret == BRYNET_SOCKET_ERROR) + { + check_ret = (BRYNET_ERRNO == WSA_IO_PENDING); + } + + if (check_ret) + { + mPostWriteCheck = true; + } +#endif + return check_ret; + } + + + void recv() + { + bool must_close = false; +#ifdef BRYNET_USE_OPENSSL + const bool notInSSL = (mSSL == nullptr); +#else + const bool notInSSL = false; +#endif + + while (true) + { + if (buffer_getwritevalidcount(mRecvBuffer.get()) == 0 + || buffer_getreadvalidcount(mRecvBuffer.get()) == 0) + { + buffer_adjustto_head(mRecvBuffer.get()); + } + + const auto tryRecvLen = buffer_getwritevalidcount(mRecvBuffer.get()); + if (tryRecvLen == 0) + { +#ifdef BRYNET_PLATFORM_WINDOWS + checkRead(); +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + //force recheck IN-OUT Event + recheckEvent(); +#endif + break; + } + + int retlen = 0; +#ifdef BRYNET_USE_OPENSSL + if (mSSL != nullptr) + { + retlen = SSL_read(mSSL, + buffer_getwriteptr(mRecvBuffer.get()), tryRecvLen); + } + else + { + retlen = ::recv(mSocket->getFD(), + buffer_getwriteptr(mRecvBuffer.get()), tryRecvLen, 0); + } +#else + retlen = ::recv(mSocket->getFD(), + buffer_getwriteptr(mRecvBuffer.get()), + static_cast(tryRecvLen), + 0); +#endif + + if (retlen == 0) + { + must_close = true; + break; + } + else if (retlen < 0) + { +#ifdef BRYNET_USE_OPENSSL + if ((mSSL != nullptr && + SSL_get_error(mSSL, retlen) == SSL_ERROR_WANT_READ) || + (BRYNET_ERRNO == BRYNET_EWOULDBLOCK)) + { + must_close = !checkRead(); + } + else + { + must_close = true; + } +#else + if (BRYNET_ERRNO != BRYNET_EWOULDBLOCK) + { + must_close = true; + } + else + { + must_close = !checkRead(); + } +#endif + break; + } + + mRecvData = true; + buffer_addwritepos(mRecvBuffer.get(), static_cast(retlen)); + if (buffer_getreadvalidcount(mRecvBuffer.get()) + == buffer_getsize(mRecvBuffer.get())) + { + growRecvBuffer(); + } + + if (notInSSL && retlen < static_cast(tryRecvLen)) + { + must_close = !checkRead(); + break; + } + } + + processRecvMessage(); + + if (must_close) + { + procCloseInLoop(); + } + } + + void flush() + { +#ifdef BRYNET_PLATFORM_WINDOWS + normalFlush(); +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN +#ifdef BRYNET_USE_OPENSSL + if (mSSL != nullptr) + { + normalFlush(); + } + else + { + quickFlush(); + } +#else + quickFlush(); +#endif +#endif + } + void normalFlush() + { +#ifdef BRYNET_PLATFORM_WINDOWS + static __declspec(thread) char* threadLocalSendBuf = nullptr; +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + static __thread char* threadLocalSendBuf = nullptr; +#endif + static const int SENDBUF_SIZE = 1024 * 32; + if (threadLocalSendBuf == nullptr) + { + threadLocalSendBuf = static_cast(malloc(SENDBUF_SIZE)); + } + +#ifdef BRYNET_USE_OPENSSL + const bool notInSSL = (mSSL == nullptr); +#else + const bool notInSSL = false; +#endif + + bool must_close = false; + + while (!mSendList.empty() && mCanWrite) + { + auto sendptr = threadLocalSendBuf; + size_t wait_send_size = 0; + + for (auto it = mSendList.begin(); it != mSendList.end(); ++it) + { + auto& packet = *it; + auto packetLeftBuf = (char*)(packet.data->data()) + packet.data->size() - packet.left; + const auto packetLeftLen = packet.left; + + if ((wait_send_size + packetLeftLen) > SENDBUF_SIZE) + { + if (it == mSendList.begin()) + { + sendptr = packetLeftBuf; + wait_send_size = packetLeftLen; + } + break; + } + + memcpy(static_cast(sendptr + wait_send_size), static_cast(packetLeftBuf), packetLeftLen); + wait_send_size += packetLeftLen; + } + + if (wait_send_size == 0) + { + break; + } + + int send_retlen = 0; +#ifdef BRYNET_USE_OPENSSL + if (mSSL != nullptr) + { + send_retlen = SSL_write(mSSL, sendptr, wait_send_size); + } + else + { + send_retlen = ::send(mSocket->getFD(), sendptr, wait_send_size, 0); + } +#else + send_retlen = ::send(mSocket->getFD(), sendptr, static_cast(wait_send_size), 0); +#endif + if (send_retlen <= 0) + { + +#ifdef BRYNET_USE_OPENSSL + if ((mSSL != nullptr && SSL_get_error(mSSL, send_retlen) == SSL_ERROR_WANT_WRITE) || + (BRYNET_ERRNO == BRYNET_EWOULDBLOCK)) + { + mCanWrite = false; + must_close = !checkWrite(); + } + else + { + must_close = true; + } +#else + if (BRYNET_ERRNO == BRYNET_EWOULDBLOCK) + { + mCanWrite = false; + must_close = !checkWrite(); + } + else + { + must_close = true; + } +#endif + break; + } + + auto tmp_len = static_cast(send_retlen); + for (auto it = mSendList.begin(); it != mSendList.end();) + { + auto& packet = *it; + if (packet.left > tmp_len) + { + packet.left -= tmp_len; + break; + } + + tmp_len -= packet.left; + if (packet.mCompleteCallback != nullptr) + { + (packet.mCompleteCallback)(); + } + it = mSendList.erase(it); + } + + if (notInSSL && static_cast(send_retlen) != wait_send_size) + { + mCanWrite = false; + must_close = !checkWrite(); + break; + } + } + + if (must_close) + { + procCloseInLoop(); + } + } +#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + void quickFlush() + { +#ifndef MAX_IOVEC + constexpr size_t MAX_IOVEC = 1024; +#endif + + struct iovec iov[MAX_IOVEC]; + bool must_close = false; + + while (!mSendList.empty() && mCanWrite) + { + size_t num = 0; + size_t ready_send_len = 0; + for (const auto& p : mSendList) + { + iov[num].iov_base = (void*)(static_cast(p.data->data()) + p.data->size() - p.left); + iov[num].iov_len = p.left; + ready_send_len += p.left; + + num++; + if (num >= MAX_IOVEC) + { + break; + } + } + + if (num == 0) + { + break; + } + + const int send_len = writev(mSocket->getFD(), iov, static_cast(num)); + if (send_len <= 0) + { + if (BRYNET_ERRNO == BRYNET_EWOULDBLOCK) + { + mCanWrite = false; + must_close = !checkWrite(); + } + else + { + must_close = true; + } + break; + } + + auto tmp_len = static_cast(send_len); + for (auto it = mSendList.begin(); it != mSendList.end();) + { + PendingPacket& b = *it; + if (b.left > tmp_len) + { + b.left -= tmp_len; + break; + } + + tmp_len -= b.left; + if (b.mCompleteCallback != nullptr) + { + b.mCompleteCallback(); + } + it = mSendList.erase(it); + } + + if (static_cast(send_len) != ready_send_len) + { + mCanWrite = false; + must_close = !checkWrite(); + break; + } + } + + if (must_close) + { + procCloseInLoop(); + } + } +#endif + + void onClose() override + { + if (mAlreadyClose) + { + return; + } + mAlreadyClose = true; + +#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + unregisterPollerEvent(); +#endif + + assert(mEnterCallback == nullptr); + auto callBack = mDisConnectCallback; + auto sharedThis = shared_from_this(); + auto eventLoop = mEventLoop; + std::shared_ptr socket = std::move(const_cast(mSocket)); + mEventLoop->runFunctorAfterLoop([callBack, + sharedThis, + eventLoop, + socket]() { + if (callBack != nullptr) + { + callBack(sharedThis); + } + auto tmp = eventLoop->getTcpConnection(socket->getFD()); + assert(tmp == sharedThis); + if (tmp == sharedThis) + { + eventLoop->removeTcpConnection(socket->getFD()); + } + }); + + mCanWrite = false; + mDisConnectCallback = nullptr; + mDataCallback = nullptr; + mRecvBuffer = nullptr; + } + + void procCloseInLoop() + { + mCanWrite = false; +#ifdef BRYNET_PLATFORM_WINDOWS + if (mPostWriteCheck || mPostRecvCheck) + { + if (mPostClose) + { + return; + } + mPostClose = true; + //windows下立即关闭socket可能导致fd被另外的TcpConnection重用,而导致此对象在IOCP返回相关完成结果时内存已经释放 + if (mPostRecvCheck) + { + CancelIoEx(HANDLE(mSocket->getFD()), &mOvlRecv.base); + } + if (mPostWriteCheck) + { + CancelIoEx(HANDLE(mSocket->getFD()), &mOvlSend.base); + } + } + else + { + onClose(); + } +#elif defined BRYNET_PLATFORM_LINUX + onClose(); +#elif defined BRYNET_PLATFORM_DARWIN + onClose(); +#endif + } + + void procShutdownInLoop() + { + mCanWrite = false; + if (mSocket != nullptr) + { +#ifdef BRYNET_PLATFORM_WINDOWS + shutdown(mSocket->getFD(), SD_SEND); +#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + shutdown(mSocket->getFD(), SHUT_WR); +#endif + } + } + + void runAfterFlush() + { + if (!mIsPostFlush && !mSendList.empty() && mCanWrite) + { + auto sharedThis = shared_from_this(); + mEventLoop->runFunctorAfterLoop([sharedThis]() { + sharedThis->mIsPostFlush = false; + sharedThis->flush(); + }); + + mIsPostFlush = true; + } + } +#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN + void recheckEvent() + { +#ifdef BRYNET_PLATFORM_LINUX + struct epoll_event ev = { 0, { nullptr } }; + ev.events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLRDHUP; + ev.data.ptr = (Channel*)(this); + epoll_ctl(mEventLoop->getEpollHandle(), EPOLL_CTL_MOD, mSocket->getFD(), &ev); +#elif defined BRYNET_PLATFORM_DARWIN + struct kevent ev[2]; + memset(&ev, 0, sizeof(ev)); + int n = 0; + EV_SET(&ev[n++], mSocket->getFD(), EVFILT_READ, EV_ENABLE, 0, 0, (Channel*)(this)); + EV_SET(&ev[n++], mSocket->getFD(), EVFILT_WRITE, EV_ENABLE, 0, 0, (Channel*)(this)); + + struct timespec now = { 0, 0 }; + kevent(mEventLoop->getKqueueHandle(), ev, n, NULL, 0, &now); +#endif + } + void unregisterPollerEvent() + { +#ifdef BRYNET_PLATFORM_LINUX + struct epoll_event ev = { 0, { nullptr } }; + epoll_ctl(mEventLoop->getEpollHandle(), EPOLL_CTL_DEL, mSocket->getFD(), &ev); +#elif defined BRYNET_PLATFORM_DARWIN + struct kevent ev[2]; + memset(&ev, 0, sizeof(ev)); + int n = 0; + EV_SET(&ev[n++], mSocket->getFD(), EVFILT_READ, EV_DELETE, 0, 0, NULL); + EV_SET(&ev[n++], mSocket->getFD(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + + struct timespec now = { 0, 0 }; + kevent(mEventLoop->getKqueueHandle(), ev, n, NULL, 0, &now); +#endif + } +#endif +#ifdef BRYNET_USE_OPENSSL + bool processSSLHandshake() + { + if (mIsHandsharked) + { + return true; + } + + bool mustClose = false; + int ret = 0; + + if (mSSLCtx != nullptr) + { + ret = SSL_connect(mSSL); + } + else + { + ret = SSL_accept(mSSL); + } + + if (ret == 1) + { + mIsHandsharked = true; + if (checkRead()) + { + causeEnterCallback(); + } + else + { + mustClose = true; + } + } + else if (ret == 0) + { + mustClose = true; + } + else if (ret < 0) + { + int err = SSL_get_error(mSSL, ret); + if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) + { + if (!checkRead()) + { + mustClose = true; + } + } + else + { + mustClose = true; + } + } + + if (mustClose) + { + causeEnterCallback(); + procCloseInLoop(); + return false; + } + return true; + } +#endif + void causeEnterCallback() + { + assert(mEventLoop->isInLoopThread()); + if (mEventLoop->isInLoopThread() && mEnterCallback != nullptr) + { + auto tmp = mEnterCallback; + mEnterCallback = nullptr; + tmp(shared_from_this()); + } + } + + void processRecvMessage() + { + if (mDataCallback != nullptr && buffer_getreadvalidcount(mRecvBuffer.get()) > 0) + { + auto reader = brynet::base::BasePacketReader(buffer_getreadptr(mRecvBuffer.get()), + buffer_getreadvalidcount(mRecvBuffer.get()), false); + mDataCallback(reader); + const auto consumedLen = reader.savedPos(); + assert(consumedLen <= reader.size()); + if (consumedLen <= reader.size()) + { + buffer_addreadpos(mRecvBuffer.get(), consumedLen); + } + } + } + + template + static void verifyArgType(const CallbackType&, void(CallbackType::*)(Arg&) const) + { + static_assert(std::is_reference::value, "arg must be reference type"); + static_assert(!std::is_const::type>::value, "arg can't be const type"); + } + + private: + +#ifdef BRYNET_PLATFORM_WINDOWS + struct port::Win::OverlappedExt mOvlRecv; + struct port::Win::OverlappedExt mOvlSend; + + bool mPostRecvCheck; + bool mPostWriteCheck; + bool mPostClose; +#endif + const std::string mIP; + const TcpSocket::Ptr mSocket; + const EventLoop::Ptr mEventLoop; + bool mCanWrite; + bool mAlreadyClose; + + class BufferDeleter + { + public: + void operator()(struct brynet::base::buffer_s* ptr) const + { + brynet::base::buffer_delete(ptr); + } + }; + std::unique_ptr mRecvBuffer; + double mCurrentTanhXDiff = 0; + size_t mRecvBuffOriginSize = 0; + const size_t mMaxRecvBufferSize; + + struct PendingPacket + { + SendableMsg::Ptr data; + size_t left; + PacketSendedCallback mCompleteCallback; + }; + + using PacketListType = std::deque; + PacketListType mSendList; + + EnterCallback mEnterCallback; + DataCallback mDataCallback; + DisconnectedCallback mDisConnectCallback; + + bool mIsPostFlush; + +#ifdef BRYNET_USE_OPENSSL + SSL_CTX* mSSLCtx; + SSL* mSSL; + bool mIsHandsharked; +#endif + bool mRecvData; + std::chrono::nanoseconds mCheckTime{}; + brynet::base::Timer::WeakPtr mTimer; + }; + +} } diff --git a/libs/brynet/net/TcpService.hpp b/libs/brynet/net/TcpService.hpp new file mode 100644 index 0000000..6da5d00 --- /dev/null +++ b/libs/brynet/net/TcpService.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include + +namespace brynet { namespace net { + + class AddSocketOption + { + private: + using AddSocketOptionFunc = detail::AddSocketOptionFunc; + using AddSocketOptionInfo = detail::AddSocketOptionInfo; + + public: + static AddSocketOptionFunc AddEnterCallback( + TcpConnection::EnterCallback callback) + { + return [callback](AddSocketOptionInfo& option) { + option.enterCallback.push_back(callback); + }; + } +#ifdef BRYNET_USE_OPENSSL + static AddSocketOptionFunc WithClientSideSSL() + { + return [](AddSocketOptionInfo& option) { + option.useSSL = true; + }; + } + static AddSocketOptionFunc WithServerSideSSL(SSLHelper::Ptr sslHelper) + { + return [sslHelper](AddSocketOptionInfo& option) { + option.sslHelper = sslHelper; + option.useSSL = true; + }; + } +#endif + static AddSocketOptionFunc WithMaxRecvBufferSize(size_t size) + { + return [size](AddSocketOptionInfo& option) { + option.maxRecvBufferSize = size; + }; + } + static AddSocketOptionFunc WithForceSameThreadLoop(bool same) + { + return [same](AddSocketOptionInfo& option) { + option.forceSameThreadLoop = same; + }; + } + }; + + class TcpService : public detail::TcpServiceDetail, + public std::enable_shared_from_this + { + public: + using Ptr = std::shared_ptr; + using FrameCallback = detail::TcpServiceDetail::FrameCallback; + + public: + static Ptr Create() + { + struct make_shared_enabler : public TcpService {}; + return std::make_shared(); + } + + void startWorkerThread(size_t threadNum, + FrameCallback callback = nullptr) + { + detail::TcpServiceDetail::startWorkerThread(threadNum, callback); + } + + void stopWorkerThread() + { + detail::TcpServiceDetail::stopWorkerThread(); + } + + template + bool addTcpConnection(TcpSocket::Ptr socket, + const Options& ... options) + { + return detail::TcpServiceDetail::addTcpConnection(std::move(socket), options...); + } + + EventLoop::Ptr getRandomEventLoop() + { + return detail::TcpServiceDetail::getRandomEventLoop(); + } + + private: + TcpService() = default; + }; + +} } \ No newline at end of file diff --git a/libs/brynet/net/detail/AddSocketOptionInfo.hpp b/libs/brynet/net/detail/AddSocketOptionInfo.hpp new file mode 100644 index 0000000..5a5aa32 --- /dev/null +++ b/libs/brynet/net/detail/AddSocketOptionInfo.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace brynet { namespace net { namespace detail { + + class AddSocketOptionInfo final + { + public: + AddSocketOptionInfo() + { + useSSL = false; + forceSameThreadLoop = false; + maxRecvBufferSize = 128; + } + + std::vector enterCallback; + SSLHelper::Ptr sslHelper; + bool useSSL; + bool forceSameThreadLoop; + size_t maxRecvBufferSize; + }; + + using AddSocketOptionFunc = std::function; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/detail/ConnectorDetail.hpp b/libs/brynet/net/detail/ConnectorDetail.hpp new file mode 100644 index 0000000..93e0f81 --- /dev/null +++ b/libs/brynet/net/detail/ConnectorDetail.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BRYNET_HAVE_LANG_CXX17 +#include +#else +#include +#endif + +namespace brynet { namespace net { namespace detail { + + class AsyncConnectorDetail : public brynet::base::NonCopyable + { + protected: + void startWorkerThread() + { +#ifdef BRYNET_HAVE_LANG_CXX17 + std::lock_guard lck(mThreadGuard); +#else + std::lock_guard lck(mThreadGuard); +#endif + + if (mThread != nullptr) + { + return; + } + + mIsRun = std::make_shared(true); + mWorkInfo = std::make_shared(); + mEventLoop = std::make_shared(); + + auto eventLoop = mEventLoop; + auto workerInfo = mWorkInfo; + auto isRun = mIsRun; + + mThread = std::make_shared([eventLoop, workerInfo, isRun]() { + while (*isRun) + { + detail::RunOnceCheckConnect(eventLoop, workerInfo); + } + + workerInfo->causeAllFailed(); + }); + } + + void stopWorkerThread() + { +#ifdef BRYNET_HAVE_LANG_CXX17 + std::lock_guard lck(mThreadGuard); +#else + std::lock_guard lck(mThreadGuard); +#endif + + if (mThread == nullptr) + { + return; + } + + mEventLoop->runAsyncFunctor([this]() { + *mIsRun = false; + }); + + try + { + if (mThread->joinable()) + { + mThread->join(); + } + } + catch (std::system_error & e) + { + (void)e; + } + + mEventLoop = nullptr; + mWorkInfo = nullptr; + mIsRun = nullptr; + mThread = nullptr; + } + + void asyncConnect(const std::vector& options) + { +#ifdef BRYNET_HAVE_LANG_CXX17 + std::shared_lock lck(mThreadGuard); +#else + std::lock_guard lck(mThreadGuard); +#endif + detail::ConnectOptionsInfo option; + for (const auto& func : options) + { + func(option); + } + + if (option.completedCallback == nullptr && option.faledCallback == nullptr) + { + throw ConnectException("all callback is nullptr"); + } + if (option.ip.empty()) + { + throw ConnectException("addr is empty"); + } + + if (!(*mIsRun)) + { + throw ConnectException("work thread already stop"); + } + + auto workInfo = mWorkInfo; + auto address = detail::AsyncConnectAddr(std::move(option.ip), + option.port, + option.timeout, + std::move(option.completedCallback), + std::move(option.faledCallback), + std::move(option.processCallbacks)); + mEventLoop->runAsyncFunctor([workInfo, address]() { + workInfo->processConnect(address); + }); + } + + protected: + AsyncConnectorDetail() + { + mIsRun = std::make_shared(false); + } + + virtual ~AsyncConnectorDetail() + { + stopWorkerThread(); + } + + private: + std::shared_ptr mEventLoop; + + std::shared_ptr mWorkInfo; + std::shared_ptr mThread; +#ifdef BRYNET_HAVE_LANG_CXX17 + std::shared_mutex mThreadGuard; +#else + std::mutex mThreadGuard; +#endif + std::shared_ptr mIsRun; + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/detail/ConnectorWorkInfo.hpp b/libs/brynet/net/detail/ConnectorWorkInfo.hpp new file mode 100644 index 0000000..c0a8538 --- /dev/null +++ b/libs/brynet/net/detail/ConnectorWorkInfo.hpp @@ -0,0 +1,368 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef BRYNET_HAVE_LANG_CXX17 +#include +#else +#include +#endif + +namespace brynet { namespace net { namespace detail { + + class ConnectOptionsInfo; + using ConnectOptionFunc = std::function; + + class AsyncConnectAddr final + { + public: + using CompletedCallback = std::function; + using ProcessTcpSocketCallback = std::function; + using FailedCallback = std::function; + + public: + AsyncConnectAddr(std::string&& ip, + int port, + std::chrono::nanoseconds timeout, + CompletedCallback&& successCB, + FailedCallback&& failedCB, + std::vector&& processCallbacks) + : + mIP(std::move(ip)), + mPort(port), + mTimeout(timeout), + mSuccessCB(std::move(successCB)), + mFailedCB(std::move(failedCB)), + mProcessCallbacks(std::move(processCallbacks)) + { + } + + const std::string& getIP() const + { + return mIP; + } + + int getPort() const + { + return mPort; + } + + const CompletedCallback& getSuccessCB() const + { + return mSuccessCB; + } + + const FailedCallback& getFailedCB() const + { + return mFailedCB; + } + + const std::vector& getProcessCallbacks() const + { + return mProcessCallbacks; + } + + std::chrono::nanoseconds getTimeout() const + { + return mTimeout; + } + + private: + const std::string mIP; + const int mPort; + const std::chrono::nanoseconds mTimeout; + const CompletedCallback mSuccessCB; + const FailedCallback mFailedCB; + const std::vector mProcessCallbacks; + }; + + class ConnectorWorkInfo final : public brynet::base::NonCopyable + { + public: + using Ptr = std::shared_ptr; + + ConnectorWorkInfo() BRYNET_NOEXCEPT + { + mPoller.reset(brynet::base::poller_new()); + mPollResult.reset(brynet::base::stack_new(1024, sizeof(BrynetSocketFD))); + } + + void checkConnectStatus(int millsecond) + { + if (poller_poll(mPoller.get(), millsecond) <= 0) + { + return; + } + + std::set totalFds; + std::set successFds; + + poller_visitor(mPoller.get(), brynet::base::WriteCheck, mPollResult.get()); + while (true) + { + auto p = stack_popfront(mPollResult.get()); + if (p == nullptr) + { + break; + } + + const auto fd = *(BrynetSocketFD*)p; + totalFds.insert(fd); + if (isConnectSuccess(fd, false) && + !brynet::net::base::IsSelfConnect(fd)) + { + successFds.insert(fd); + } + } + + for (auto fd : totalFds) + { + poller_remove(mPoller.get(), fd); + + const auto it = mConnectingInfos.find(fd); + if (it == mConnectingInfos.end()) + { + continue; + } + + auto socket = TcpSocket::Create(fd, false); + const auto& connectingInfo = it->second; + if (successFds.find(fd) != successFds.end()) + { + for (const auto& process : connectingInfo.processCallbacks) + { + process(*socket); + } + if (connectingInfo.successCB != nullptr) + { + connectingInfo.successCB(std::move(socket)); + } + } + else + { + if (connectingInfo.failedCB != nullptr) + { + connectingInfo.failedCB(); + } + } + + mConnectingInfos.erase(it); + } + } + + bool isConnectSuccess(BrynetSocketFD clientfd, bool willCheckWrite) const + { + if (willCheckWrite && !poller_check(mPoller.get(), clientfd, brynet::base::WriteCheck)) + { + return false; + } + + int error = BRYNET_SOCKET_ERROR; + int len = sizeof(error); + if (getsockopt(clientfd, + SOL_SOCKET, + SO_ERROR, + (char*)&error, + (socklen_t*)&len) == BRYNET_SOCKET_ERROR) + { + return false; + } + + return error == 0; + } + + void checkTimeout() + { + for (auto it = mConnectingInfos.begin(); it != mConnectingInfos.end();) + { + const auto now = std::chrono::steady_clock::now(); + if ((now - it->second.startConnectTime) < it->second.timeout) + { + ++it; + continue; + } + + auto fd = it->first; + auto cb = it->second.failedCB; + + poller_remove(mPoller.get(), fd); + mConnectingInfos.erase(it++); + + brynet::net::base::SocketClose(fd); + if (cb != nullptr) + { + //TODO::don't modify mConnectingInfos in cb + cb(); + } + } + } + + void processConnect(const AsyncConnectAddr& addr) + { + struct sockaddr_in server_addr = sockaddr_in(); + BrynetSocketFD clientfd = BRYNET_INVALID_SOCKET; + +#ifdef BRYNET_PLATFORM_WINDOWS + const int ExpectedError = WSAEWOULDBLOCK; +#else + const int ExpectedError = EINPROGRESS; +#endif + int n = 0; + + brynet::net::base::InitSocket(); + + clientfd = brynet::net::base::SocketCreate(AF_INET, SOCK_STREAM, 0); + if (clientfd == BRYNET_INVALID_SOCKET) + { + goto FAILED; + } + + brynet::net::base::SocketNonblock(clientfd); + server_addr.sin_family = AF_INET; + inet_pton(AF_INET, addr.getIP().c_str(), &server_addr.sin_addr.s_addr); + server_addr.sin_port = static_cast(htons(addr.getPort())); + + n = connect(clientfd, (struct sockaddr*) & server_addr, sizeof(struct sockaddr)); + if (n == 0) + { + if (brynet::net::base::IsSelfConnect(clientfd)) + { + goto FAILED; + } + } + else if (BRYNET_ERRNO != ExpectedError) + { + goto FAILED; + } + else + { + ConnectingInfo ci; + ci.startConnectTime = std::chrono::steady_clock::now(); + ci.successCB = addr.getSuccessCB(); + ci.failedCB = addr.getFailedCB(); + ci.timeout = addr.getTimeout(); + ci.processCallbacks = addr.getProcessCallbacks(); + + mConnectingInfos[clientfd] = ci; + poller_add(mPoller.get(), clientfd, brynet::base::WriteCheck); + + return; + } + + if (addr.getSuccessCB() != nullptr) + { + auto tcpSocket = TcpSocket::Create(clientfd, false); + for (const auto& process : addr.getProcessCallbacks()) + { + process(*tcpSocket); + } + addr.getSuccessCB()(std::move(tcpSocket)); + } + return; + + FAILED: + if (clientfd != BRYNET_INVALID_SOCKET) + { + brynet::net::base::SocketClose(clientfd); + clientfd = BRYNET_INVALID_SOCKET; + (void)clientfd; + } + if (addr.getFailedCB() != nullptr) + { + addr.getFailedCB()(); + } + } + + void causeAllFailed() + { + auto copyMap = mConnectingInfos; + mConnectingInfos.clear(); + + for (const auto& v : copyMap) + { + auto fd = v.first; + auto cb = v.second.failedCB; + + poller_remove(mPoller.get(), fd); + brynet::net::base::SocketClose(fd); + if (cb != nullptr) + { + cb(); + } + } + } + + private: + class ConnectingInfo + { + public: + ConnectingInfo() + { + timeout = std::chrono::nanoseconds::zero(); + } + + std::chrono::steady_clock::time_point startConnectTime; + std::chrono::nanoseconds timeout; + AsyncConnectAddr::CompletedCallback successCB; + AsyncConnectAddr::FailedCallback failedCB; + std::vector processCallbacks; + }; + + std::map mConnectingInfos; + + class PollerDeleter + { + public: + void operator()(struct brynet::base::poller_s* ptr) const + { + brynet::base::poller_delete(ptr); + } + }; + class StackDeleter + { + public: + void operator()(struct brynet::base::stack_s* ptr) const + { + brynet::base::stack_delete(ptr); + } + }; + + std::unique_ptr mPoller; + std::unique_ptr mPollResult; + }; + + static void RunOnceCheckConnect( + const std::shared_ptr& eventLoop, + const std::shared_ptr& workerInfo) + { + eventLoop->loop(std::chrono::milliseconds(10).count()); + workerInfo->checkConnectStatus(0); + workerInfo->checkTimeout(); + } + + class ConnectOptionsInfo final + { + public: + ConnectOptionsInfo() + : + port(0), + timeout(std::chrono::seconds(10)) + { + } + + std::string ip; + int port; + std::chrono::nanoseconds timeout; + std::vector processCallbacks; + AsyncConnectAddr::CompletedCallback completedCallback; + AsyncConnectAddr::FailedCallback faledCallback; + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/detail/IOLoopData.hpp b/libs/brynet/net/detail/IOLoopData.hpp new file mode 100644 index 0000000..888f312 --- /dev/null +++ b/libs/brynet/net/detail/IOLoopData.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace brynet { namespace net { namespace detail { + + class TcpServiceDetail; + + class IOLoopData : public brynet::base::NonCopyable, + public std::enable_shared_from_this + { + public: + using Ptr = std::shared_ptr; + + static Ptr Create(EventLoop::Ptr eventLoop, + std::shared_ptr ioThread) + { + class make_shared_enabler : public IOLoopData + { + public: + make_shared_enabler(EventLoop::Ptr eventLoop, + std::shared_ptr ioThread) + : + IOLoopData(std::move(eventLoop), std::move(ioThread)) + {} + }; + + return std::make_shared(std::move(eventLoop), + std::move(ioThread)); + } + + const EventLoop::Ptr& getEventLoop() const + { + return mEventLoop; + } + + protected: + const std::shared_ptr& getIOThread() const + { + return mIOThread; + } + + IOLoopData(EventLoop::Ptr eventLoop, + std::shared_ptr ioThread) + : + mEventLoop(std::move(eventLoop)), + mIOThread(std::move(ioThread)) + {} + virtual ~IOLoopData() = default; + + const EventLoop::Ptr mEventLoop; + + private: + std::shared_ptr mIOThread; + + friend class TcpServiceDetail; + }; + + using IOLoopDataPtr = std::shared_ptr; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/detail/ListenThreadDetail.hpp b/libs/brynet/net/detail/ListenThreadDetail.hpp new file mode 100644 index 0000000..634fc80 --- /dev/null +++ b/libs/brynet/net/detail/ListenThreadDetail.hpp @@ -0,0 +1,169 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace brynet { namespace net { namespace detail { + + class ListenThreadDetail : public brynet::base::NonCopyable + { + protected: + using AccepCallback = std::function; + using TcpSocketProcessCallback = std::function; + + void startListen() + { + std::lock_guard lck(mListenThreadGuard); + + if (mListenThread != nullptr) + { + return; + } + + const auto fd = brynet::net::base::Listen(mIsIPV6, mIP.c_str(), mPort, 512); + if (fd == BRYNET_INVALID_SOCKET) + { + throw BrynetCommonException( + std::string("listen error of:") + std::to_string(BRYNET_ERRNO)); + } + + mRunListen = std::make_shared(true); + + auto listenSocket = std::shared_ptr(ListenSocket::Create(fd)); + auto isRunListen = mRunListen; + auto callback = mCallback; + auto processCallbacks = mProcessCallbacks; + mListenThread = std::make_shared( + [isRunListen, listenSocket, callback, processCallbacks]() mutable { + while (*isRunListen) + { + auto clientSocket = runOnceListen(listenSocket); + if (clientSocket == nullptr) + { + continue; + } + + if (*isRunListen) + { + for (const auto& process : processCallbacks) + { + process(*clientSocket); + } + callback(std::move(clientSocket)); + } + } + }); + } + + void stopListen() + { + std::lock_guard lck(mListenThreadGuard); + + if (mListenThread == nullptr) + { + return; + } + + *mRunListen = false; + auto selfIP = mIP; + if (selfIP == "0.0.0.0") + { + selfIP = "127.0.0.1"; + } + + auto connector = AsyncConnector::Create(); + connector->startWorkerThread(); + + wrapper::SocketConnectBuilder connectBuilder; + connectBuilder + .configureConnector(connector) + .configureConnectOptions({ + ConnectOption::WithTimeout(std::chrono::seconds(2)), + ConnectOption::WithAddr(selfIP, mPort) + }) + .syncConnect(); + + try + { + if (mListenThread->joinable()) + { + mListenThread->join(); + } + } + catch (std::system_error & e) + { + (void)e; + } + mListenThread = nullptr; + } + + protected: + ListenThreadDetail(bool isIPV6, + const std::string& ip, + int port, + const AccepCallback& callback, + const std::vector& processCallbacks) + : + mIsIPV6(isIPV6), + mIP(ip), + mPort(port), + mCallback(callback), + mProcessCallbacks(processCallbacks) + { + if (mCallback == nullptr) + { + throw BrynetCommonException("accept callback is nullptr"); + } + mRunListen = std::make_shared(false); + } + + virtual ~ListenThreadDetail() BRYNET_NOEXCEPT + { + stopListen(); + } + + private: + static brynet::net::TcpSocket::Ptr runOnceListen(const std::shared_ptr& listenSocket) + { + try + { + return listenSocket->accept(); + } + catch (const EintrError & e) + { + std::cerr << "accept eintr execption:" << e.what() << std::endl; + } + catch (const AcceptError & e) + { + std::cerr << "accept execption:" << e.what() << std::endl; + } + + return nullptr; + } + + private: + const bool mIsIPV6; + const std::string mIP; + const int mPort; + const AccepCallback mCallback; + const std::vector mProcessCallbacks; + + std::shared_ptr mRunListen; + std::shared_ptr mListenThread; + std::mutex mListenThreadGuard; + }; + +} } } diff --git a/libs/brynet/net/detail/TCPServiceDetail.hpp b/libs/brynet/net/detail/TCPServiceDetail.hpp new file mode 100644 index 0000000..d64c422 --- /dev/null +++ b/libs/brynet/net/detail/TCPServiceDetail.hpp @@ -0,0 +1,198 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace brynet { namespace net { namespace detail { + + class TcpServiceDetail : public brynet::base::NonCopyable + { + protected: + using FrameCallback = std::function; + const static unsigned int sDefaultLoopTimeOutMS = 100; + + void startWorkerThread(size_t threadNum, + FrameCallback callback = nullptr) + { + std::lock_guard lck(mServiceGuard); + std::lock_guard lock(mIOLoopGuard); + + if (!mIOLoopDatas.empty()) + { + return; + } + + mRunIOLoop = std::make_shared(true); + + mIOLoopDatas.resize(threadNum); + for (auto& v : mIOLoopDatas) + { + auto eventLoop = std::make_shared(); + auto runIoLoop = mRunIOLoop; + v = IOLoopData::Create(eventLoop, + std::make_shared( + [callback, runIoLoop, eventLoop]() { + while (*runIoLoop) + { + eventLoop->loopCompareNearTimer(sDefaultLoopTimeOutMS); + if (callback != nullptr) + { + callback(eventLoop); + } + } + })); + } + } + + void stopWorkerThread() + { + std::lock_guard lck(mServiceGuard); + std::lock_guard lock(mIOLoopGuard); + + *mRunIOLoop = false; + + for (const auto& v : mIOLoopDatas) + { + v->getEventLoop()->wakeup(); + try + { + if (v->getIOThread()->joinable()) + { + v->getIOThread()->join(); + } + } + catch (std::system_error & e) + { + (void)e; + } + } + mIOLoopDatas.clear(); + } + + template + bool addTcpConnection(TcpSocket::Ptr socket, + const Options& ... options) + { + return _addTcpConnection(std::move(socket), { options... }); + } + + EventLoop::Ptr getRandomEventLoop() + { + std::lock_guard lock(mIOLoopGuard); + + const auto ioLoopSize = mIOLoopDatas.size(); + if (ioLoopSize == 0) + { + return nullptr; + } + else if (ioLoopSize == 1) + { + return mIOLoopDatas.front()->getEventLoop(); + } + else + { + return mIOLoopDatas[mRandom() % ioLoopSize]->getEventLoop(); + } + } + + TcpServiceDetail() BRYNET_NOEXCEPT + : + mRandom(static_cast( + std::chrono::system_clock::now().time_since_epoch().count())) + { + mRunIOLoop = std::make_shared(false); + } + + virtual ~TcpServiceDetail() BRYNET_NOEXCEPT + { + stopWorkerThread(); + } + + bool _addTcpConnection(TcpSocket::Ptr socket, + const std::vector& optionFuncs) + { + AddSocketOptionInfo options; + for (const auto& v : optionFuncs) + { + if (v != nullptr) + { + v(options); + } + } + + if (options.maxRecvBufferSize <= 0) + { + throw BrynetCommonException("buffer size is zero"); + } + + EventLoop::Ptr eventLoop; + if (options.forceSameThreadLoop) + { + eventLoop = getSameThreadEventLoop(); + } + else + { + eventLoop = getRandomEventLoop(); + } + if (eventLoop == nullptr) + { + return false; + } + + auto wrapperEnterCallback = [options](const TcpConnection::Ptr& tcpConnection) { + for (const auto& callback : options.enterCallback) + { + callback(tcpConnection); + } + }; + + if (options.useSSL && options.sslHelper == nullptr) + { + options.sslHelper = SSLHelper::Create(); + } + + TcpConnection::Create(std::move(socket), + options.maxRecvBufferSize, + wrapperEnterCallback, + eventLoop, + options.sslHelper); + + return true; + } + + EventLoop::Ptr getSameThreadEventLoop() + { + std::lock_guard lock(mIOLoopGuard); + for (const auto& v : mIOLoopDatas) + { + if (v->getEventLoop()->isInLoopThread()) + { + return v->getEventLoop(); + } + } + return nullptr; + } + + private: + std::vector mIOLoopDatas; + mutable std::mutex mIOLoopGuard; + std::shared_ptr mRunIOLoop; + + std::mutex mServiceGuard; + std::mt19937 mRandom; + }; + +} } } diff --git a/libs/brynet/net/detail/WakeupChannel.hpp b/libs/brynet/net/detail/WakeupChannel.hpp new file mode 100644 index 0000000..83e3c16 --- /dev/null +++ b/libs/brynet/net/detail/WakeupChannel.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef BRYNET_PLATFORM_WINDOWS +#include +#endif + +#include +#include + +namespace brynet { namespace net { namespace detail { + +#ifdef BRYNET_PLATFORM_WINDOWS + class WakeupChannel final : public Channel, public brynet::base::NonCopyable + { + public: + explicit WakeupChannel(HANDLE iocp) + : + mIOCP(iocp), + mWakeupOvl(port::Win::OverlappedType::OverlappedRecv) + { + } + + bool wakeup() BRYNET_NOEXCEPT + { + return PostQueuedCompletionStatus(mIOCP, + 0, + reinterpret_cast(this), + &mWakeupOvl.base); + } + + private: + void canRecv() BRYNET_NOEXCEPT override + { + ; + } + + void canSend() BRYNET_NOEXCEPT override + { + ; + } + + void onClose() BRYNET_NOEXCEPT override + { + ; + } + + HANDLE mIOCP; + port::Win::OverlappedExt mWakeupOvl; + }; +#elif defined BRYNET_PLATFORM_LINUX + class WakeupChannel final : public Channel, public brynet::base::NonCopyable + { + public: + explicit WakeupChannel(BrynetSocketFD fd) : mUniqueFd(fd) + { + } + + bool wakeup() + { + uint64_t one = 1; + return write(mUniqueFd.getFD(), &one, sizeof one) > 0; + } + + private: + void canRecv() override + { + char temp[1024 * 10]; + while (true) + { + auto n = read(mUniqueFd.getFD(), temp, sizeof(temp)); + if (n == -1 || static_cast(n) < sizeof(temp)) + { + break; + } + } + } + + void canSend() override + { + } + + void onClose() override + { + } + + private: + UniqueFd mUniqueFd; + }; + +#elif defined BRYNET_PLATFORM_DARWIN + class WakeupChannel final : public Channel, public brynet::base::NonCopyable + { + public: + explicit WakeupChannel(int kqueuefd, int ident) + : + mKqueueFd(kqueuefd), + mUserEvent(ident) + { + } + + bool wakeup() + { + struct kevent ev; + EV_SET(&ev, mUserEvent, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); + + struct timespec timeout = { 0, 0 }; + return kevent(mKqueueFd, &ev, 1, NULL, 0, &timeout) == 0; + } + + private: + void canRecv() override + { + } + + void canSend() override + { + } + + void onClose() override + { + } + + private: + int mKqueueFd; + int mUserEvent; + }; +#endif + +} } } \ No newline at end of file diff --git a/libs/brynet/net/http/HttpFormat.hpp b/libs/brynet/net/http/HttpFormat.hpp new file mode 100644 index 0000000..e6f4dcd --- /dev/null +++ b/libs/brynet/net/http/HttpFormat.hpp @@ -0,0 +1,222 @@ +#pragma once + +#include +#include +#include +#include + +namespace brynet { namespace net { namespace http { + + class HttpQueryParameter final + { + public: + void add(const std::string& k, const std::string& v) + { + if (!mParameter.empty()) + { + mParameter += "&"; + } + + mParameter += k; + mParameter += "="; + mParameter += v; + } + + const std::string& getResult() const + { + return mParameter; + } + + private: + std::string mParameter; + }; + + class HttpRequest final + { + public: + + enum class HTTP_METHOD + { + HTTP_METHOD_HEAD, + HTTP_METHOD_GET, + HTTP_METHOD_POST, + HTTP_METHOD_PUT, + HTTP_METHOD_DELETE, + HTTP_METHOD_MAX + }; + + HttpRequest() + { + setMethod(HTTP_METHOD::HTTP_METHOD_GET); + } + + void setMethod(HTTP_METHOD protocol) + { + mMethod = protocol; + assert(mMethod > HTTP_METHOD::HTTP_METHOD_HEAD && + mMethod < HTTP_METHOD::HTTP_METHOD_MAX); + } + + void setHost(const std::string& host) + { + addHeadValue("Host", host); + } + + void setUrl(const std::string& url) + { + mUrl = url; + } + + void setCookie(const std::string& v) + { + addHeadValue("Cookie", v); + } + + void setContentType(const std::string& v) + { + addHeadValue("Content-Type", v); + } + + void setQuery(const std::string& query) + { + mQuery = query; + } + + void setBody(const std::string& body) + { + mBody = body; + addHeadValue("Content-Length", std::to_string(body.size())); + } + + void addHeadValue(const std::string& field, + const std::string& value) + { + mHeadField[field] = value; + } + + std::string getResult() const + { + const auto MethodMax = static_cast(HTTP_METHOD::HTTP_METHOD_MAX); + const static std::array HttpMethodString = + { "HEAD", "GET", "POST", "PUT", "DELETE" }; + + std::string ret; + if (mMethod >= HTTP_METHOD::HTTP_METHOD_HEAD && + mMethod < HTTP_METHOD::HTTP_METHOD_MAX) + { + ret += HttpMethodString[static_cast(mMethod)]; + } + + ret += " "; + ret += mUrl; + if (!mQuery.empty()) + { + ret += "?"; + ret += mQuery; + } + + ret += " HTTP/1.1\r\n"; + + for (auto& v : mHeadField) + { + ret += v.first; + ret += ": "; + ret += v.second; + ret += "\r\n"; + } + + ret += "\r\n"; + + if (!mBody.empty()) + { + ret += mBody; + } + + return ret; + } + + private: + std::string mUrl; + std::string mQuery; + std::string mBody; + HTTP_METHOD mMethod; + std::map mHeadField; + }; + + class HttpResponse final + { + public: + enum class HTTP_RESPONSE_STATUS + { + NONE, + OK = 200, + }; + + HttpResponse() : mStatus(HTTP_RESPONSE_STATUS::OK) + { + } + + void setStatus(HTTP_RESPONSE_STATUS status) + { + mStatus = status; + } + + void setContentType(const std::string& v) + { + addHeadValue("Content-Type", v); + } + + void addHeadValue(const std::string& field, + const std::string& value) + { + mHeadField[field] = value; + } + + void setBody(const std::string& body) + { + mBody = body; + addHeadValue("Content-Length", std::to_string(body.size())); + } + + std::string getResult() const + { + std::string ret = "HTTP/1.1 "; + + ret += std::to_string(static_cast(mStatus)); + switch (mStatus) + { + case HTTP_RESPONSE_STATUS::OK: + ret += " OK"; + break; + default: + ret += "UNKNOWN"; + break; + } + + ret += "\r\n"; + + for (auto& v : mHeadField) + { + ret += v.first; + ret += ": "; + ret += v.second; + ret += "\r\n"; + } + + ret += "\r\n"; + + if (!mBody.empty()) + { + ret += mBody; + } + + return ret; + } + + private: + HTTP_RESPONSE_STATUS mStatus; + std::map mHeadField; + std::string mBody; + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/http/HttpParser.hpp b/libs/brynet/net/http/HttpParser.hpp new file mode 100644 index 0000000..9b79680 --- /dev/null +++ b/libs/brynet/net/http/HttpParser.hpp @@ -0,0 +1,318 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "http_parser.h" +#include + +namespace brynet { namespace net { namespace http { + + class HttpService; + + class HTTPParser + { + public: + using Ptr = std::shared_ptr; + + explicit HTTPParser(http_parser_type parserType) + : + mParserType(parserType) + { + mLastWasValue = true; + + mIsWebSocket = false; + mIsKeepAlive = false; + mISCompleted = false; + mStatusCode = 0; + mWSFrameType = WebSocketFormat::WebSocketFrameType::ERROR_FRAME; + mSettings.on_status = sStatusHandle; + mSettings.on_body = sBodyHandle; + mSettings.on_url = sUrlHandle; + mSettings.on_header_field = sHeadField; + mSettings.on_header_value = sHeadValue; + mSettings.on_headers_complete = sHeadComplete; + mSettings.on_message_begin = sMessageBegin; + mSettings.on_message_complete = sMessageEnd; + mSettings.on_chunk_header = sChunkHeader; + mSettings.on_chunk_complete = sChunkComplete; + mParser.data = this; + + http_parser_init(&mParser, mParserType); + } + + virtual ~HTTPParser() = default; + + bool isWebSocket() const + { + return mIsWebSocket; + } + + bool isKeepAlive() const + { + return mIsKeepAlive; + } + + int method() const + { + // mMethod's value defined in http_method, such as HTTP_GET、HTTP_POST. + // if mMethod is -1, it's invalid. + return mMethod; + } + + const std::string& getPath() const + { + return mPath; + } + + const std::string& getQuery() const + { + return mQuery; + } + + const std::string& getStatus() const + { + return mStatus; + } + + int getStatusCode() const + { + return mStatusCode; + } + + bool hasEntry(const std::string& key, + const std::string& value) const + { + const auto it = mHeadValues.find(key); + return it != mHeadValues.end() && value == it->second; + } + + bool hasKey(const std::string& key) const + { + return mHeadValues.find(key) != mHeadValues.end(); + } + + const std::string& getValue(const std::string& key) const + { + const static std::string emptystr(""); + + auto it = mHeadValues.find(key); + if (it != mHeadValues.end()) + { + return (*it).second; + } + else + { + return emptystr; + } + } + + const std::string& getBody() const + { + return mBody; + } + + std::string& getWSCacheFrame() + { + return mWSCacheFrame; + } + + std::string& getWSParseString() + { + return mWSParsePayload; + } + + WebSocketFormat::WebSocketFrameType getWSFrameType() const + { + return mWSFrameType; + } + + void cacheWSFrameType(WebSocketFormat::WebSocketFrameType frameType) + { + mWSFrameType = frameType; + } + + private: + void clearParse() + { + mMethod = -1; + mISCompleted = false; + mLastWasValue = true; + mUrl.clear(); + mQuery.clear(); + mBody.clear(); + mStatus.clear(); + mCurrentField.clear(); + mCurrentValue.clear(); + mHeadValues.clear(); + mPath.clear(); + } + + size_t tryParse(const char* buffer, size_t len) + { + const size_t nparsed = http_parser_execute(&mParser, &mSettings, buffer, len); + if (mISCompleted) + { + mIsWebSocket = mParser.upgrade; + mIsKeepAlive = hasEntry("Connection", "Keep-Alive"); + mMethod = mParser.method; + http_parser_init(&mParser, mParserType); + } + + return nparsed; + } + + bool isCompleted() const + { + return mISCompleted; + } + + private: + static int sChunkHeader(http_parser* hp) + { + (void)hp; + return 0; + } + + static int sChunkComplete(http_parser* hp) + { + (void)hp; + return 0; + } + + static int sMessageBegin(http_parser* hp) + { + HTTPParser* httpParser = (HTTPParser*)hp->data; + httpParser->clearParse(); + + return 0; + } + + static int sMessageEnd(http_parser* hp) + { + HTTPParser* httpParser = (HTTPParser*)hp->data; + httpParser->mISCompleted = true; + return 0; + } + + static int sHeadComplete(http_parser* hp) + { + HTTPParser* httpParser = (HTTPParser*)hp->data; + + if (httpParser->mUrl.empty()) + { + return 0; + } + + struct http_parser_url u; + + const int result = http_parser_parse_url(httpParser->mUrl.data(), + httpParser->mUrl.size(), + 0, + &u); + if (result != 0) + { + return -1; + } + + if (!(u.field_set & (1 << UF_PATH))) + { + fprintf(stderr, + "\n\n*** failed to parse PATH in URL %s ***\n\n", + httpParser->mUrl.c_str()); + return -1; + } + + httpParser->mPath = std::string( + httpParser->mUrl.data() + u.field_data[UF_PATH].off, + u.field_data[UF_PATH].len); + if (u.field_set & (1 << UF_QUERY)) + { + httpParser->mQuery = std::string( + httpParser->mUrl.data() + u.field_data[UF_QUERY].off, + u.field_data[UF_QUERY].len); + } + + return 0; + } + + static int sUrlHandle(http_parser* hp, const char* url, size_t length) + { + HTTPParser* httpParser = (HTTPParser*)hp->data; + httpParser->mUrl.append(url, length); + + return 0; + } + + static int sHeadValue(http_parser* hp, const char* at, size_t length) + { + HTTPParser* httpParser = (HTTPParser*)hp->data; + auto& value = httpParser->mHeadValues[httpParser->mCurrentField]; + value.append(at, length); + httpParser->mLastWasValue = true; + return 0; + } + + static int sHeadField(http_parser* hp, const char* at, size_t length) + { + HTTPParser* httpParser = (HTTPParser*)hp->data; + if (httpParser->mLastWasValue) + { + httpParser->mCurrentField.clear(); + } + httpParser->mCurrentField.append(at, length); + httpParser->mLastWasValue = false; + + return 0; + } + + static int sStatusHandle(http_parser* hp, const char* at, size_t length) + { + HTTPParser* httpParser = (HTTPParser*)hp->data; + httpParser->mStatus.append(at, length); + httpParser->mStatusCode = hp->status_code; + return 0; + } + + static int sBodyHandle(http_parser* hp, const char* at, size_t length) + { + HTTPParser* httpParser = (HTTPParser*)hp->data; + httpParser->mBody.append(at, length); + return 0; + } + + private: + const http_parser_type mParserType; + http_parser mParser; + http_parser_settings mSettings; + + int mMethod = -1; + bool mIsWebSocket; + bool mIsKeepAlive; + bool mISCompleted; + + bool mLastWasValue; + std::string mCurrentField; + std::string mCurrentValue; + + std::string mPath; + std::string mQuery; + std::string mStatus; + std::map mHeadValues; + int mStatusCode; + + std::string mUrl; + std::string mBody; + + std::string mWSCacheFrame; + std::string mWSParsePayload; + WebSocketFormat::WebSocketFrameType mWSFrameType; + + private: + friend class HttpService; + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/http/HttpService.hpp b/libs/brynet/net/http/HttpService.hpp new file mode 100644 index 0000000..fb824ea --- /dev/null +++ b/libs/brynet/net/http/HttpService.hpp @@ -0,0 +1,342 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace brynet { namespace net { namespace http { + + class HttpService; + class HttpSessionHandlers; + + class HttpSession : public brynet::base::NonCopyable + { + public: + using Ptr = std::shared_ptr; + + using EnterCallback = std::function ; + using HttpParserCallback = std::function ; + using WsCallback = std::function < void( const HttpSession::Ptr&, + WebSocketFormat::WebSocketFrameType opcode, + const std::string& payload)>; + + using ClosedCallback = std::function ; + using WsConnectedCallback = std::function ; + + public: + template + void send(PacketType&& packet, + TcpConnection::PacketSendedCallback&& callback = nullptr) + { + mSession->send(std::forward>(packet), + std::move(callback)); + } + void send(const char* packet, + size_t len, + TcpConnection::PacketSendedCallback&& callback = nullptr) + { + mSession->send(packet, len, std::move(callback)); + } + + void postShutdown() const + { + mSession->postShutdown(); + } + + void postClose() const + { + mSession->postDisConnect(); + } + + protected: + explicit HttpSession(TcpConnection::Ptr session) + { + mSession = std::move(session); + } + + virtual ~HttpSession() = default; + + static Ptr Create(TcpConnection::Ptr session) + { + class make_shared_enabler : public HttpSession + { + public: + explicit make_shared_enabler(TcpConnection::Ptr session) + : + HttpSession(std::move(session)) + {} + }; + return std::make_shared(std::move(session)); + } + + const TcpConnection::Ptr& getSession() const + { + return mSession; + } + + const HttpParserCallback& getHttpCallback() const + { + return mHttpRequestCallback; + } + + const ClosedCallback& getCloseCallback() const + { + return mCloseCallback; + } + + const WsCallback& getWSCallback() const + { + return mWSCallback; + } + + const WsConnectedCallback& getWSConnectedCallback() const + { + return mWSConnectedCallback; + } + + private: + void setHttpCallback(HttpParserCallback&& callback) + { + mHttpRequestCallback = std::move(callback); + } + + void setClosedCallback(ClosedCallback&& callback) + { + mCloseCallback = std::move(callback); + } + + void setWSCallback(WsCallback&& callback) + { + mWSCallback = std::move(callback); + } + + void setWSConnected(WsConnectedCallback&& callback) + { + mWSConnectedCallback = std::move(callback); + } + + private: + TcpConnection::Ptr mSession; + HttpParserCallback mHttpRequestCallback; + WsCallback mWSCallback; + ClosedCallback mCloseCallback; + WsConnectedCallback mWSConnectedCallback; + + friend class HttpService; + }; + + class HttpSessionHandlers + { + public: + void setHttpCallback(HttpSession::HttpParserCallback&& callback) + { + mHttpRequestCallback = std::move(callback); + } + + void setClosedCallback(HttpSession::ClosedCallback&& callback) + { + mCloseCallback = std::move(callback); + } + + void setWSCallback(HttpSession::WsCallback&& callback) + { + mWSCallback = std::move(callback); + } + + void setWSConnected(HttpSession::WsConnectedCallback&& callback) + { + mWSConnectedCallback = std::move(callback); + } + + private: + HttpSession::HttpParserCallback mHttpRequestCallback; + HttpSession::WsCallback mWSCallback; + HttpSession::ClosedCallback mCloseCallback; + HttpSession::WsConnectedCallback mWSConnectedCallback; + + friend class HttpService; + }; + + class HttpService + { + public: + static void setup(const TcpConnection::Ptr& session, + const HttpSession::EnterCallback& enterCallback) + { + auto httpSession = HttpSession::Create(session); + if (enterCallback != nullptr) + { + HttpSessionHandlers handlers; + enterCallback(httpSession, handlers); + httpSession->setHttpCallback(std::move(handlers.mHttpRequestCallback)); + httpSession->setClosedCallback(std::move(handlers.mCloseCallback)); + httpSession->setWSCallback(std::move(handlers.mWSCallback)); + httpSession->setWSConnected(std::move(handlers.mWSConnectedCallback)); + } + HttpService::handle(httpSession); + } + + private: + static void handle(const HttpSession::Ptr& httpSession) + { + /*TODO::keep alive and timeout close */ + auto& session = httpSession->getSession(); + + session->setDisConnectCallback([httpSession](const TcpConnection::Ptr&) { + const auto& tmp = httpSession->getCloseCallback(); + if (tmp != nullptr) + { + tmp(httpSession); + } + }); + + auto httpParser = std::make_shared(HTTP_BOTH); + session->setDataCallback([httpSession, httpParser]( + brynet::base::BasePacketReader& reader) { + size_t retLen = 0; + + if (httpParser->isWebSocket()) + { + retLen = HttpService::ProcessWebSocket( reader.begin(), + reader.size(), + httpParser, + httpSession); + } + else + { + retLen = HttpService::ProcessHttp( reader.begin(), + reader.size(), + httpParser, + httpSession); + } + + reader.addPos(retLen); + reader.savePos(); + }); + } + + static size_t ProcessWebSocket(const char* buffer, + size_t len, + const HTTPParser::Ptr& httpParser, + const HttpSession::Ptr& httpSession) + { + size_t leftLen = len; + + const auto& wsCallback = httpSession->getWSCallback(); + auto& cacheFrame = httpParser->getWSCacheFrame(); + auto& parseString = httpParser->getWSParseString(); + + while (leftLen > 0) + { + parseString.clear(); + + auto opcode = WebSocketFormat::WebSocketFrameType::ERROR_FRAME; + size_t frameSize = 0; + bool isFin = false; + + if (!WebSocketFormat::wsFrameExtractBuffer(buffer, + leftLen, + parseString, + opcode, + frameSize, + isFin)) + { + // 如果没有解析出完整的ws frame则退出函数 + break; + } + + // 如果当前fram的fin为false或者opcode为延续包 + // 则将当前frame的payload添加到cache + if (!isFin || + opcode == WebSocketFormat::WebSocketFrameType::CONTINUATION_FRAME) + { + cacheFrame += parseString; + parseString.clear(); + } + // 如果当前fram的fin为false,并且opcode不为延续包 + // 则表示收到分段payload的第一个段(frame),需要缓存当前frame的opcode + if (!isFin && + opcode != WebSocketFormat::WebSocketFrameType::CONTINUATION_FRAME) + { + httpParser->cacheWSFrameType(opcode); + } + + leftLen -= frameSize; + buffer += frameSize; + + if (!isFin) + { + continue; + } + + // 如果fin为true,并且opcode为延续包 + // 则表示分段payload全部接受完毕 + // 因此需要获取之前第一次收到分段frame的opcode作为整个payload的类型 + if (opcode == WebSocketFormat::WebSocketFrameType::CONTINUATION_FRAME) + { + if (!cacheFrame.empty()) + { + parseString = std::move(cacheFrame); + cacheFrame.clear(); + } + opcode = httpParser->getWSFrameType(); + } + + if (wsCallback != nullptr) + { + wsCallback(httpSession, opcode, parseString); + } + } + + return (len - leftLen); + } + + static size_t ProcessHttp(const char* buffer, + size_t len, + const HTTPParser::Ptr& httpParser, + const HttpSession::Ptr& httpSession) + { + size_t retlen = len; + if (!httpParser->isCompleted()) + { + retlen = httpParser->tryParse(buffer, len); + if (!httpParser->isCompleted()) + { + return retlen; + } + } + + if (httpParser->isWebSocket()) + { + if (httpParser->hasKey("Sec-WebSocket-Key")) + { + auto response = WebSocketFormat::wsHandshake( + httpParser->getValue("Sec-WebSocket-Key")); + httpSession->send(response.c_str(), + response.size()); + } + + const auto& wsConnectedCallback = httpSession->getWSConnectedCallback(); + if (wsConnectedCallback != nullptr) + { + wsConnectedCallback(httpSession, *httpParser); + } + } + else + { + const auto& httpCallback = httpSession->getHttpCallback(); + if (httpCallback != nullptr) + { + httpCallback(*httpParser, httpSession); + } + } + + return retlen; + } + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/http/WebSocketFormat.hpp b/libs/brynet/net/http/WebSocketFormat.hpp new file mode 100644 index 0000000..17a3015 --- /dev/null +++ b/libs/brynet/net/http/WebSocketFormat.hpp @@ -0,0 +1,239 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace brynet { namespace net { namespace http { + + class WebSocketFormat + { + public: + enum class WebSocketFrameType { + ERROR_FRAME = 0xff, + CONTINUATION_FRAME = 0x00, + TEXT_FRAME = 0x01, + BINARY_FRAME = 0x02, + CLOSE_FRAME = 0x08, + PING_FRAME = 0x09, + PONG_FRAME = 0x0A + }; + + static std::string wsHandshake(std::string secKey) + { + secKey.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + + CSHA1 s1; + s1.Update((unsigned char*)secKey.c_str(), static_cast(secKey.size())); + s1.Final(); + unsigned char puDest[20]; + s1.GetHash(puDest); + + std::string base64Str = brynet::base::crypto::base64_encode((const unsigned char*)puDest, 20); + + std::string response = "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: "; + + response += base64Str; + response += "\r\n\r\n"; + + return response; + } + + static bool wsFrameBuild(const char* payload, + size_t payloadLen, + std::string& frame, + WebSocketFrameType frame_type = WebSocketFrameType::TEXT_FRAME, + bool isFin = true, + bool masking = false) + { + const auto unixTime = std::chrono::system_clock::now(). + time_since_epoch(). + count(); + static std::mt19937 random(static_cast(unixTime)); + + static_assert(std::is_same::value, ""); + + const uint8_t head = static_cast(frame_type) | (isFin ? 0x80 : 0x00); + + frame.clear(); + frame.push_back(static_cast(head)); + if (payloadLen <= 125) + { + // mask << 7 | payloadLen, mask = 0 + frame.push_back(static_cast(payloadLen)); + } + else if (payloadLen <= 0xFFFF) + { + // 126 + 16bit len + frame.push_back(126); + frame.push_back((payloadLen & 0xFF00) >> 8); + frame.push_back(payloadLen & 0x00FF); + } + else + { + // 127 + 64bit len + frame.push_back(127); + // assume payload len is less than u_int32_max + frame.push_back(0x00); + frame.push_back(0x00); + frame.push_back(0x00); + frame.push_back(0x00); + frame.push_back(static_cast((payloadLen & 0xFF000000) >> 24)); + frame.push_back(static_cast((payloadLen & 0x00FF0000) >> 16)); + frame.push_back(static_cast((payloadLen & 0x0000FF00) >> 8)); + frame.push_back(static_cast(payloadLen & 0x000000FF)); + } + + if (masking) + { + frame[1] = ((uint8_t)frame[1]) | 0x80; + uint8_t mask[4]; + for (auto& m : mask) + { + m = static_cast(random()); + frame.push_back(m); + } + + frame.reserve(frame.size() + payloadLen); + + for (size_t i = 0; i < payloadLen; i++) + { + frame.push_back(static_cast(payload[i]) ^ mask[i % 4]); + } + } + else + { + frame.append(payload, payloadLen); + } + + return true; + } + + static bool wsFrameBuild(const std::string& payload, + std::string& frame, + WebSocketFrameType frame_type = WebSocketFrameType::TEXT_FRAME, + bool isFin = true, + bool masking = false) + { + return wsFrameBuild(payload.c_str(), + payload.size(), + frame, + frame_type, + isFin, + masking); + } + + static bool wsFrameExtractBuffer(const char* inbuffer, + const size_t bufferSize, + std::string& payload, + WebSocketFrameType& outopcode, + size_t& frameSize, + bool& outfin) + { + const auto buffer = (const unsigned char*)inbuffer; + + if (bufferSize < 2) + { + return false; + } + + outfin = (buffer[0] & 0x80) != 0; + outopcode = (WebSocketFrameType)(buffer[0] & 0x0F); + const bool isMasking = (buffer[1] & 0x80) != 0; + uint32_t payloadlen = buffer[1] & 0x7F; + + uint32_t pos = 2; + if (payloadlen == 126) + { + if (bufferSize < 4) + { + return false; + } + + payloadlen = (buffer[2] << 8) + buffer[3]; + pos = 4; + } + else if (payloadlen == 127) + { + if (bufferSize < 10) + { + return false; + } + + if (buffer[2] != 0 || + buffer[3] != 0 || + buffer[4] != 0 || + buffer[5] != 0) + { + return false; + } + + if ((buffer[6] & 0x80) != 0) + { + return false; + } + + payloadlen = (buffer[6] << 24) + + (buffer[7] << 16) + + (buffer[8] << 8) + + buffer[9]; + pos = 10; + } + + uint8_t mask[4]; + if (isMasking) + { + if (bufferSize < (pos + 4)) + { + return false; + } + + mask[0] = buffer[pos++]; + mask[1] = buffer[pos++]; + mask[2] = buffer[pos++]; + mask[3] = buffer[pos++]; + } + + if (bufferSize < (pos + payloadlen)) + { + return false; + } + + if (isMasking) + { + payload.reserve(payloadlen); + for (size_t j = 0; j < payloadlen; j++) + payload.push_back(buffer[pos+j] ^ mask[j % 4]); + } + else + { + payload.append((const char*)(buffer + pos), payloadlen); + } + + frameSize = payloadlen + pos; + + return true; + } + + static bool wsFrameExtractString(const std::string& buffer, + std::string& payload, + WebSocketFrameType& opcode, + size_t& frameSize, bool& isFin) + { + return wsFrameExtractBuffer(buffer.c_str(), + buffer.size(), + payload, + opcode, + frameSize, + isFin); + } + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/http/http_parser.h b/libs/brynet/net/http/http_parser.h new file mode 100644 index 0000000..1c03d36 --- /dev/null +++ b/libs/brynet/net/http/http_parser.h @@ -0,0 +1,3206 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef http_parser_h +#define http_parser_h + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Also update SONAME in the Makefile whenever you change these. */ +#define HTTP_PARSER_VERSION_MAJOR 2 +#define HTTP_PARSER_VERSION_MINOR 9 +#define HTTP_PARSER_VERSION_PATCH 2 + +#include +#if defined(_WIN32) && !defined(__MINGW32__) && \ + (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) +#include +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef HTTP_PARSER_STRICT +# define HTTP_PARSER_STRICT 1 +#endif + +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef HTTP_MAX_HEADER_SIZE +# define HTTP_MAX_HEADER_SIZE (80*1024) +#endif + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; + + +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * Returning `2` from on_headers_complete will tell parser that it should not + * expect neither a body nor any futher responses on this connection. This is + * useful for handling responses to a CONNECT request which may not contain + * `Upgrade` or `Connection: upgrade` headers. + * + * http_data_cb does not return data chunks. It will be called arbitrarily + * many times for each string. E.G. you might get 10 callbacks for "on_url" + * each providing just a few characters more data. + */ +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); +typedef int (*http_cb) (http_parser*); + + +/* Status Codes */ +#define HTTP_STATUS_MAP(XX) \ + XX(100, CONTINUE, Continue) \ + XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \ + XX(102, PROCESSING, Processing) \ + XX(200, OK, OK) \ + XX(201, CREATED, Created) \ + XX(202, ACCEPTED, Accepted) \ + XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \ + XX(204, NO_CONTENT, No Content) \ + XX(205, RESET_CONTENT, Reset Content) \ + XX(206, PARTIAL_CONTENT, Partial Content) \ + XX(207, MULTI_STATUS, Multi-Status) \ + XX(208, ALREADY_REPORTED, Already Reported) \ + XX(226, IM_USED, IM Used) \ + XX(300, MULTIPLE_CHOICES, Multiple Choices) \ + XX(301, MOVED_PERMANENTLY, Moved Permanently) \ + XX(302, FOUND, Found) \ + XX(303, SEE_OTHER, See Other) \ + XX(304, NOT_MODIFIED, Not Modified) \ + XX(305, USE_PROXY, Use Proxy) \ + XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \ + XX(308, PERMANENT_REDIRECT, Permanent Redirect) \ + XX(400, BAD_REQUEST, Bad Request) \ + XX(401, UNAUTHORIZED, Unauthorized) \ + XX(402, PAYMENT_REQUIRED, Payment Required) \ + XX(403, FORBIDDEN, Forbidden) \ + XX(404, NOT_FOUND, Not Found) \ + XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \ + XX(406, NOT_ACCEPTABLE, Not Acceptable) \ + XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \ + XX(408, REQUEST_TIMEOUT, Request Timeout) \ + XX(409, CONFLICT, Conflict) \ + XX(410, GONE, Gone) \ + XX(411, LENGTH_REQUIRED, Length Required) \ + XX(412, PRECONDITION_FAILED, Precondition Failed) \ + XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \ + XX(414, URI_TOO_LONG, URI Too Long) \ + XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \ + XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \ + XX(417, EXPECTATION_FAILED, Expectation Failed) \ + XX(421, MISDIRECTED_REQUEST, Misdirected Request) \ + XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \ + XX(423, LOCKED, Locked) \ + XX(424, FAILED_DEPENDENCY, Failed Dependency) \ + XX(426, UPGRADE_REQUIRED, Upgrade Required) \ + XX(428, PRECONDITION_REQUIRED, Precondition Required) \ + XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ + XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ + XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ + XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ + XX(501, NOT_IMPLEMENTED, Not Implemented) \ + XX(502, BAD_GATEWAY, Bad Gateway) \ + XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \ + XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \ + XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \ + XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \ + XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \ + XX(508, LOOP_DETECTED, Loop Detected) \ + XX(510, NOT_EXTENDED, Not Extended) \ + XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \ + +enum http_status + { +#define XX(num, name, string) HTTP_STATUS_##name = num, + HTTP_STATUS_MAP(XX) +#undef XX + }; + + +/* Request Methods */ +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + /* pathological */ \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + /* WebDAV */ \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + /* subversion */ \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + /* upnp */ \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + /* CalDAV */ \ + XX(30, MKCALENDAR, MKCALENDAR) \ + /* RFC-2068, section 19.6.1.2 */ \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + /* icecast */ \ + XX(33, SOURCE, SOURCE) \ + +enum http_method + { +#define XX(num, name, string) HTTP_##name = num, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; + + +/* Flag values for http_parser.flags field */ +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_CONNECTION_UPGRADE = 1 << 3 + , F_TRAILING = 1 << 4 + , F_UPGRADE = 1 << 5 + , F_SKIPBODY = 1 << 6 + , F_CONTENTLENGTH = 1 << 7 + }; + + +/* Map for errno-related constants + * + * The provided argument should be a macro that takes 2 arguments. + */ +#define HTTP_ERRNO_MAP(XX) \ + /* No error */ \ + XX(OK, "success") \ + \ + /* Callback-related errors */ \ + XX(CB_message_begin, "the on_message_begin callback failed") \ + XX(CB_url, "the on_url callback failed") \ + XX(CB_header_field, "the on_header_field callback failed") \ + XX(CB_header_value, "the on_header_value callback failed") \ + XX(CB_headers_complete, "the on_headers_complete callback failed") \ + XX(CB_body, "the on_body callback failed") \ + XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ + XX(CB_chunk_header, "the on_chunk_header callback failed") \ + XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ + \ + /* Parsing-related errors */ \ + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + XX(INVALID_VERSION, "invalid HTTP version") \ + XX(INVALID_STATUS, "invalid HTTP status code") \ + XX(INVALID_METHOD, "invalid HTTP method") \ + XX(INVALID_URL, "invalid URL") \ + XX(INVALID_HOST, "invalid host") \ + XX(INVALID_PORT, "invalid port") \ + XX(INVALID_PATH, "invalid path") \ + XX(INVALID_QUERY_STRING, "invalid query string") \ + XX(INVALID_FRAGMENT, "invalid fragment") \ + XX(LF_EXPECTED, "LF character expected") \ + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + XX(UNEXPECTED_CONTENT_LENGTH, \ + "unexpected content-length header") \ + XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + XX(INVALID_CONSTANT, "invalid constant string") \ + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ + XX(STRICT, "strict mode assertion failed") \ + XX(PAUSED, "parser is paused") \ + XX(UNKNOWN, "an unknown error occurred") + + +/* Define HPE_* values for each errno value above */ +#define HTTP_ERRNO_GEN(n, s) HPE_##n, +enum http_errno { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) +}; +#undef HTTP_ERRNO_GEN + + +/* Get an http_errno value from an http_parser */ +#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) + + +struct http_parser { + /** PRIVATE **/ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 7; /* enum state from http_parser.c */ + unsigned int header_state : 7; /* enum header_state from http_parser.c */ + unsigned int index : 7; /* index into current matcher */ + unsigned int lenient_http_headers : 1; + + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; + + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + unsigned int upgrade : 1; + + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; + + +struct http_parser_settings { + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_status; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; + /* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + */ + http_cb on_chunk_header; + http_cb on_chunk_complete; +}; + + +enum http_parser_url_fields + { UF_SCHEMA = 0 + , UF_HOST = 1 + , UF_PORT = 2 + , UF_PATH = 3 + , UF_QUERY = 4 + , UF_FRAGMENT = 5 + , UF_USERINFO = 6 + , UF_MAX = 7 + }; + + +/* Result structure for http_parser_parse_url(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +struct http_parser_url { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; +}; + + +/* Returns the library version. Bits 16-23 contain the major version number, + * bits 8-15 the minor version number and bits 0-7 the patch level. + * Usage example: + * + * unsigned long version = http_parser_version(); + * unsigned major = (version >> 16) & 255; + * unsigned minor = (version >> 8) & 255; + * unsigned patch = version & 255; + * printf("http_parser v%u.%u.%u\n", major, minor, patch); + */ +static unsigned long http_parser_version(void); + +static void http_parser_init(http_parser *parser, enum http_parser_type type); + + +/* Initialize http_parser_settings members to 0 + */ +static void http_parser_settings_init(http_parser_settings *settings); + + +/* Executes the parser. Returns number of parsed bytes. Sets + * `parser->http_errno` on error. */ +static size_t http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len); + + +/* If http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns 0, then this should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +static int http_should_keep_alive(const http_parser *parser); + +/* Returns a string version of the HTTP method. */ +static const char *http_method_str(enum http_method m); + +/* Returns a string version of the HTTP status code. */ +static const char *http_status_str(enum http_status s); + +/* Return a string name of the given error */ +static const char *http_errno_name(enum http_errno err); + +/* Return a string description of the given error */ +static const char *http_errno_description(enum http_errno err); + +/* Initialize all http_parser_url members to 0 */ +static void http_parser_url_init(struct http_parser_url *u); + +/* Parse a URL; return nonzero on failure */ +static int http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, + struct http_parser_url *u); + +/* Pause or un-pause the parser; a nonzero value pauses */ +static void http_parser_pause(http_parser *parser, int paused); + +/* Checks if this is the final chunk of the body. */ +static int http_body_is_final(const http_parser *parser); + +/* Change the maximum header size provided at compile time. */ +static void http_parser_set_max_header_size(uint32_t size); + +static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE; + +#ifndef ULLONG_MAX +#define ULLONG_MAX ((uint64_t)-1) /* 2^64-1 */ +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#ifndef BIT_AT +#define BIT_AT(a, i) \ + (!!((unsigned int)(a)[(unsigned int)(i) >> 3] & \ + (1 << ((unsigned int)(i)&7)))) +#endif + +#ifndef ELEM_AT +#define ELEM_AT(a, i, v) ((unsigned int)(i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) +#endif + +#define SET_ERRNO(e) \ + do \ + { \ + parser->nread = nread; \ + parser->http_errno = (e); \ + } while (0) + +#define CURRENT_STATE() p_state +#define UPDATE_STATE(V) p_state = (enum state)(V); +#define RETURN(V) \ + do \ + { \ + parser->nread = nread; \ + parser->state = CURRENT_STATE(); \ + return (V); \ + } while (0); +#define REEXECUTE() \ + goto reexecute; + +#ifdef __GNUC__ +#define LIKELY(X) __builtin_expect(!!(X), 1) +#define UNLIKELY(X) __builtin_expect(!!(X), 0) +#else +#define LIKELY(X) (X) +#define UNLIKELY(X) (X) +#endif + +/* Run the notify callback FOR, returning ER if it fails */ +#define CALLBACK_NOTIFY_(FOR, ER) \ + do \ + { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (LIKELY(settings->on_##FOR)) \ + { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser))) \ + { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) \ + { \ + return (ER); \ + } \ + } \ + } while (0) + +/* Run the notify callback FOR and consume the current byte */ +#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) + +/* Run the notify callback FOR and don't consume the current byte */ +#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) + +/* Run data callback FOR with LEN bytes, returning ER if it fails */ +#define CALLBACK_DATA_(FOR, LEN, ER) \ + do \ + { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) \ + { \ + if (LIKELY(settings->on_##FOR)) \ + { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != \ + settings->on_##FOR(parser, FOR##_mark, (LEN)))) \ + { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) \ + { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ + } while (0) + +/* Run the data callback FOR and consume the current byte */ +#define CALLBACK_DATA(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) + +/* Run the data callback FOR and don't consume the current byte */ +#define CALLBACK_DATA_NOADVANCE(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) + +/* Set the mark FOR; non-destructive if mark is already set */ +#define MARK(FOR) \ + do \ + { \ + if (!FOR##_mark) \ + { \ + FOR##_mark = p; \ + } \ + } while (0) + +/* Don't allow the total size of the HTTP headers (including the status + * line) to exceed max_header_size. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we + * make the web a little safer. max_header_size is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ +#define COUNT_HEADER_SIZE(V) \ + do \ + { \ + nread += (uint32_t)(V); \ + if (UNLIKELY(nread > max_header_size)) \ + { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ + } while (0) + +#define PROXY_CONNECTION "proxy-connection" +#define CONNECTION "connection" +#define CONTENT_LENGTH "content-length" +#define TRANSFER_ENCODING "transfer-encoding" +#define UPGRADE "upgrade" +#define CHUNKED "chunked" +#define KEEP_ALIVE "keep-alive" +#define CLOSE "close" + + static const char *method_strings[] = + { +#define XX(num, name, string) #string, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + /* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ + static const char tokens[256] = { + /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + ' ', '!', 0, '#', '$', '%', '&', '\'', + /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', 0, + /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', + /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, + /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', + /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', 0, '~', 0}; + + static const int8_t unhex[256] = + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +#if HTTP_PARSER_STRICT +#define T(v) 0 +#else +#define T(v) v +#endif + + static const uint8_t normal_url_char[32] = { + /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, + /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, + /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, + /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, + }; + +#undef T + + enum state + { + s_dead = 1 /* important that this is > 0 */ + + , + s_start_req_or_res, + s_res_or_resp_H, + s_start_res, + s_res_H, + s_res_HT, + s_res_HTT, + s_res_HTTP, + s_res_http_major, + s_res_http_dot, + s_res_http_minor, + s_res_http_end, + s_res_first_status_code, + s_res_status_code, + s_res_status_start, + s_res_status, + s_res_line_almost_done + + , + s_start_req + + , + s_req_method, + s_req_spaces_before_url, + s_req_schema, + s_req_schema_slash, + s_req_schema_slash_slash, + s_req_server_start, + s_req_server, + s_req_server_with_at, + s_req_path, + s_req_query_string_start, + s_req_query_string, + s_req_fragment_start, + s_req_fragment, + s_req_http_start, + s_req_http_H, + s_req_http_HT, + s_req_http_HTT, + s_req_http_HTTP, + s_req_http_I, + s_req_http_IC, + s_req_http_major, + s_req_http_dot, + s_req_http_minor, + s_req_http_end, + s_req_line_almost_done + + , + s_header_field_start, + s_header_field, + s_header_value_discard_ws, + s_header_value_discard_ws_almost_done, + s_header_value_discard_lws, + s_header_value_start, + s_header_value, + s_header_value_lws + + , + s_header_almost_done + + , + s_chunk_size_start, + s_chunk_size, + s_chunk_parameters, + s_chunk_size_almost_done + + , + s_headers_almost_done, + s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + , + s_chunk_data, + s_chunk_data_almost_done, + s_chunk_data_done + + , + s_body_identity, + s_body_identity_eof + + , + s_message_done + }; + +#define PARSING_HEADER(state) (state <= s_headers_done) + + enum header_states + { + h_general = 0, + h_C, + h_CO, + h_CON + + , + h_matching_connection, + h_matching_proxy_connection, + h_matching_content_length, + h_matching_transfer_encoding, + h_matching_upgrade + + , + h_connection, + h_content_length, + h_content_length_num, + h_content_length_ws, + h_transfer_encoding, + h_upgrade + + , + h_matching_transfer_encoding_chunked, + h_matching_connection_token_start, + h_matching_connection_keep_alive, + h_matching_connection_close, + h_matching_connection_upgrade, + h_matching_connection_token + + , + h_transfer_encoding_chunked, + h_connection_keep_alive, + h_connection_close, + h_connection_upgrade + }; + + enum http_host_state + { + s_http_host_dead = 1, + s_http_userinfo_start, + s_http_userinfo, + s_http_host_start, + s_http_host_v6_start, + s_http_host, + s_http_host_v6, + s_http_host_v6_end, + s_http_host_v6_zone_start, + s_http_host_v6_zone, + s_http_host_port_start, + s_http_host_port + }; + +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) +#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ + (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ + (c) == ')') +#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ + (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ + (c) == '$' || (c) == ',') + +#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c]) + +#if HTTP_PARSER_STRICT +#define TOKEN(c) STRICT_TOKEN(c) +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define TOKEN(c) tokens[(unsigned char)c] +#define IS_URL_CHAR(c) \ + (BIT_AT(normal_url_char, (unsigned char)c) || ((c)&0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + +/** + * Verify that a char is a valid visible (printable) US-ASCII + * character or %x80-FF + **/ +#define IS_HEADER_CHAR(ch) \ + (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) + +#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) + +#if HTTP_PARSER_STRICT +#define STRICT_CHECK(cond) \ + do \ + { \ + if (cond) \ + { \ + SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ + } while (0) +#define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) +#else +#define STRICT_CHECK(cond) +#define NEW_MESSAGE() start_state +#endif + +/* Map errno values to strings for human-readable output */ +#define HTTP_STRERROR_GEN(n, s) {"HPE_" #n, s}, + static struct + { + const char *name; + const char *description; + } http_strerror_tab[] = { + HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)}; +#undef HTTP_STRERROR_GEN + + static int http_message_needs_eof(const http_parser *parser); + + /* Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ + static enum state + parse_url_char(enum state s, const char ch) + { + if (ch == ' ' || ch == '\r' || ch == '\n') + { + return s_dead; + } + +#if HTTP_PARSER_STRICT + if (ch == '\t' || ch == '\f') + { + return s_dead; + } +#endif + + switch (s) + { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + + if (ch == '/' || ch == '*') + { + return s_req_path; + } + + if (IS_ALPHA(ch)) + { + return s_req_schema; + } + + break; + + case s_req_schema: + if (IS_ALPHA(ch)) + { + return s; + } + + if (ch == ':') + { + return s_req_schema_slash; + } + + break; + + case s_req_schema_slash: + if (ch == '/') + { + return s_req_schema_slash_slash; + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') + { + return s_req_server_start; + } + + break; + + case s_req_server_with_at: + if (ch == '@') + { + return s_dead; + } + + /* fall through */ + case s_req_server_start: + case s_req_server: + if (ch == '/') + { + return s_req_path; + } + + if (ch == '?') + { + return s_req_query_string_start; + } + + if (ch == '@') + { + return s_req_server_with_at; + } + + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') + { + return s_req_server; + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) + { + return s; + } + + switch (ch) + { + case '?': + return s_req_query_string_start; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) + { + return s_req_query_string; + } + + switch (ch) + { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) + { + return s_req_fragment; + } + + switch (ch) + { + case '?': + return s_req_fragment; + + case '#': + return s; + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) + { + return s; + } + + switch (ch) + { + case '?': + case '#': + return s; + } + + break; + + default: + break; + } + + /* We should never fall out of the switch above unless there's an error */ + return s_dead; + } + + static size_t http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len) + { + char c, ch; + int8_t unhex_val; + const char *p = data; + const char *header_field_mark = 0; + const char *header_value_mark = 0; + const char *url_mark = 0; + const char *body_mark = 0; + const char *status_mark = 0; + enum state p_state = (enum state)parser->state; + const unsigned int lenient = parser->lenient_http_headers; + uint32_t nread = parser->nread; + + /* We're in an error state. Don't bother doing anything. */ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) + { + return 0; + } + + if (len == 0) + { + switch (CURRENT_STATE()) + { + case s_body_identity_eof: + /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if + * we got paused. + */ + CALLBACK_NOTIFY_NOADVANCE(message_complete); + return 0; + + case s_dead: + case s_start_req_or_res: + case s_start_res: + case s_start_req: + return 0; + + default: + SET_ERRNO(HPE_INVALID_EOF_STATE); + return 1; + } + } + + if (CURRENT_STATE() == s_header_field) + header_field_mark = data; + if (CURRENT_STATE() == s_header_value) + header_value_mark = data; + switch (CURRENT_STATE()) + { + case s_req_path: + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_server: + case s_req_server_with_at: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + url_mark = data; + break; + case s_res_status: + status_mark = data; + break; + default: + break; + } + + for (p = data; p != data + len; p++) + { + ch = *p; + + if (PARSING_HEADER(CURRENT_STATE())) + COUNT_HEADER_SIZE(1); + + reexecute: + switch (CURRENT_STATE()) + { + + case s_dead: + /* this state is used after a 'Connection: close' message + * the parser will error out if it reads another message + */ + if (LIKELY(ch == CR || ch == LF)) + break; + + SET_ERRNO(HPE_CLOSED_CONNECTION); + goto error; + + case s_start_req_or_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') + { + UPDATE_STATE(s_res_or_resp_H); + + CALLBACK_NOTIFY(message_begin); + } + else + { + parser->type = HTTP_REQUEST; + UPDATE_STATE(s_start_req); + REEXECUTE(); + } + + break; + } + + case s_res_or_resp_H: + if (ch == 'T') + { + parser->type = HTTP_RESPONSE; + UPDATE_STATE(s_res_HT); + } + else + { + if (UNLIKELY(ch != 'E')) + { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + parser->type = HTTP_REQUEST; + parser->method = HTTP_HEAD; + parser->index = 2; + UPDATE_STATE(s_req_method); + } + break; + + case s_start_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') + { + UPDATE_STATE(s_res_H); + } + else + { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + CALLBACK_NOTIFY(message_begin); + break; + } + + case s_res_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HT); + break; + + case s_res_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HTT); + break; + + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_res_HTTP); + break; + + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_res_http_major); + break; + + case s_res_http_major: + if (UNLIKELY(!IS_NUM(ch))) + { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_res_http_dot); + break; + + case s_res_http_dot: + { + if (UNLIKELY(ch != '.')) + { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_res_http_minor); + break; + } + + case s_res_http_minor: + if (UNLIKELY(!IS_NUM(ch))) + { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_res_http_end); + break; + + case s_res_http_end: + { + if (UNLIKELY(ch != ' ')) + { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_res_first_status_code); + break; + } + + case s_res_first_status_code: + { + if (!IS_NUM(ch)) + { + if (ch == ' ') + { + break; + } + + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + parser->status_code = ch - '0'; + UPDATE_STATE(s_res_status_code); + break; + } + + case s_res_status_code: + { + if (!IS_NUM(ch)) + { + switch (ch) + { + case ' ': + UPDATE_STATE(s_res_status_start); + break; + case CR: + case LF: + UPDATE_STATE(s_res_status_start); + REEXECUTE(); + break; + default: + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + break; + } + + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (UNLIKELY(parser->status_code > 999)) + { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + + break; + } + + case s_res_status_start: + { + MARK(status); + UPDATE_STATE(s_res_status); + parser->index = 0; + + if (ch == CR || ch == LF) + REEXECUTE(); + + break; + } + + case s_res_status: + if (ch == CR) + { + UPDATE_STATE(s_res_line_almost_done); + CALLBACK_DATA(status); + break; + } + + if (ch == LF) + { + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA(status); + break; + } + + break; + + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_field_start); + break; + + case s_start_req: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (UNLIKELY(!IS_ALPHA(ch))) + { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + parser->method = (enum http_method)0; + parser->index = 1; + switch (ch) + { + case 'A': + parser->method = HTTP_ACL; + break; + case 'B': + parser->method = HTTP_BIND; + break; + case 'C': + parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ + break; + case 'D': + parser->method = HTTP_DELETE; + break; + case 'G': + parser->method = HTTP_GET; + break; + case 'H': + parser->method = HTTP_HEAD; + break; + case 'L': + parser->method = HTTP_LOCK; /* or LINK */ + break; + case 'M': + parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ + break; + case 'N': + parser->method = HTTP_NOTIFY; + break; + case 'O': + parser->method = HTTP_OPTIONS; + break; + case 'P': + parser->method = HTTP_POST; + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ + break; + case 'R': + parser->method = HTTP_REPORT; /* or REBIND */ + break; + case 'S': + parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ + break; + case 'T': + parser->method = HTTP_TRACE; + break; + case 'U': + parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ + break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + UPDATE_STATE(s_req_method); + + CALLBACK_NOTIFY(message_begin); + + break; + } + + case s_req_method: + { + const char *matcher; + if (UNLIKELY(ch == '\0')) + { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[parser->index] == '\0') + { + UPDATE_STATE(s_req_spaces_before_url); + } + else if (ch == matcher[parser->index]) + { + ; /* nada */ + } + else if ((ch >= 'A' && ch <= 'Z') || ch == '-') + { + + switch (parser->method << 16 | parser->index << 8 | ch) + { +#define XX(meth, pos, ch, new_meth) \ + case (HTTP_##meth << 16 | pos << 8 | ch): \ + parser->method = HTTP_##new_meth; \ + break; + + XX(POST, 1, 'U', PUT) + XX(POST, 1, 'A', PATCH) + XX(POST, 1, 'R', PROPFIND) + XX(PUT, 2, 'R', PURGE) + XX(CONNECT, 1, 'H', CHECKOUT) + XX(CONNECT, 2, 'P', COPY) + XX(MKCOL, 1, 'O', MOVE) + XX(MKCOL, 1, 'E', MERGE) + XX(MKCOL, 1, '-', MSEARCH) + XX(MKCOL, 2, 'A', MKACTIVITY) + XX(MKCOL, 3, 'A', MKCALENDAR) + XX(SUBSCRIBE, 1, 'E', SEARCH) + XX(SUBSCRIBE, 1, 'O', SOURCE) + XX(REPORT, 2, 'B', REBIND) + XX(PROPFIND, 4, 'P', PROPPATCH) + XX(LOCK, 1, 'I', LINK) + XX(UNLOCK, 2, 'S', UNSUBSCRIBE) + XX(UNLOCK, 2, 'B', UNBIND) + XX(UNLOCK, 3, 'I', UNLINK) +#undef XX + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } + else + { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + ++parser->index; + break; + } + + case s_req_spaces_before_url: + { + if (ch == ' ') + break; + + MARK(url); + if (parser->method == HTTP_CONNECT) + { + UPDATE_STATE(s_req_server_start); + } + + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) + { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + + break; + } + + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + { + switch (ch) + { + /* No whitespace allowed here */ + case ' ': + case CR: + case LF: + SET_ERRNO(HPE_INVALID_URL); + goto error; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) + { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + + break; + } + + case s_req_server: + case s_req_server_with_at: + case s_req_path: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + { + switch (ch) + { + case ' ': + UPDATE_STATE(s_req_http_start); + CALLBACK_DATA(url); + break; + case CR: + case LF: + parser->http_major = 0; + parser->http_minor = 9; + UPDATE_STATE((ch == CR) ? s_req_line_almost_done : s_header_field_start); + CALLBACK_DATA(url); + break; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) + { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; + } + + case s_req_http_start: + switch (ch) + { + case ' ': + break; + case 'H': + UPDATE_STATE(s_req_http_H); + break; + case 'I': + if (parser->method == HTTP_SOURCE) + { + UPDATE_STATE(s_req_http_I); + break; + } + /* fall through */ + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + break; + + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HT); + break; + + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HTT); + break; + + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_req_http_HTTP); + break; + + case s_req_http_I: + STRICT_CHECK(ch != 'C'); + UPDATE_STATE(s_req_http_IC); + break; + + case s_req_http_IC: + STRICT_CHECK(ch != 'E'); + UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */ + break; + + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_req_http_major); + break; + + case s_req_http_major: + if (UNLIKELY(!IS_NUM(ch))) + { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_dot); + break; + + case s_req_http_dot: + { + if (UNLIKELY(ch != '.')) + { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_req_http_minor); + break; + } + + case s_req_http_minor: + if (UNLIKELY(!IS_NUM(ch))) + { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_req_http_end); + break; + + case s_req_http_end: + { + if (ch == CR) + { + UPDATE_STATE(s_req_line_almost_done); + break; + } + + if (ch == LF) + { + UPDATE_STATE(s_header_field_start); + break; + } + + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + break; + } + + /* end of request line */ + case s_req_line_almost_done: + { + if (UNLIKELY(ch != LF)) + { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_field_start); + break; + } + + case s_header_field_start: + { + if (ch == CR) + { + UPDATE_STATE(s_headers_almost_done); + break; + } + + if (ch == LF) + { + /* they might be just sending \n instead of \r\n so this would be + * the second \n to denote the end of headers*/ + UPDATE_STATE(s_headers_almost_done); + REEXECUTE(); + } + + c = TOKEN(ch); + + if (UNLIKELY(!c)) + { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + MARK(header_field); + + parser->index = 0; + UPDATE_STATE(s_header_field); + + switch (c) + { + case 'c': + parser->header_state = h_C; + break; + + case 'p': + parser->header_state = h_matching_proxy_connection; + break; + + case 't': + parser->header_state = h_matching_transfer_encoding; + break; + + case 'u': + parser->header_state = h_matching_upgrade; + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_field: + { + const char *start = p; + for (; p != data + len; p++) + { + ch = *p; + c = TOKEN(ch); + + if (!c) + break; + + switch (parser->header_state) + { + case h_general: + { + size_t left = data + len - p; + const char *pe = p + MIN(left, max_header_size); + while (p + 1 < pe && TOKEN(p[1])) + { + p++; + } + break; + } + + case h_C: + parser->index++; + parser->header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + parser->index++; + parser->header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + parser->index++; + switch (c) + { + case 'n': + parser->header_state = h_matching_connection; + break; + case 't': + parser->header_state = h_matching_content_length; + break; + default: + parser->header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + parser->index++; + if (parser->index > sizeof(CONNECTION) - 1 || c != CONNECTION[parser->index]) + { + parser->header_state = h_general; + } + else if (parser->index == sizeof(CONNECTION) - 2) + { + parser->header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + parser->index++; + if (parser->index > sizeof(PROXY_CONNECTION) - 1 || c != PROXY_CONNECTION[parser->index]) + { + parser->header_state = h_general; + } + else if (parser->index == sizeof(PROXY_CONNECTION) - 2) + { + parser->header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + parser->index++; + if (parser->index > sizeof(CONTENT_LENGTH) - 1 || c != CONTENT_LENGTH[parser->index]) + { + parser->header_state = h_general; + } + else if (parser->index == sizeof(CONTENT_LENGTH) - 2) + { + parser->header_state = h_content_length; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + parser->index++; + if (parser->index > sizeof(TRANSFER_ENCODING) - 1 || c != TRANSFER_ENCODING[parser->index]) + { + parser->header_state = h_general; + } + else if (parser->index == sizeof(TRANSFER_ENCODING) - 2) + { + parser->header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || c != UPGRADE[parser->index]) + { + parser->header_state = h_general; + } + else if (parser->index == sizeof(UPGRADE) - 2) + { + parser->header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') + parser->header_state = h_general; + break; + + default: + assert(0 && "Unknown header_state"); + break; + } + } + + if (p == data + len) + { + --p; + COUNT_HEADER_SIZE(p - start); + break; + } + + COUNT_HEADER_SIZE(p - start); + + if (ch == ':') + { + UPDATE_STATE(s_header_value_discard_ws); + CALLBACK_DATA(header_field); + break; + } + + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + case s_header_value_discard_ws: + if (ch == ' ' || ch == '\t') + break; + + if (ch == CR) + { + UPDATE_STATE(s_header_value_discard_ws_almost_done); + break; + } + + if (ch == LF) + { + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + /* fall through */ + + case s_header_value_start: + { + MARK(header_value); + + UPDATE_STATE(s_header_value); + parser->index = 0; + + c = LOWER(ch); + + switch (parser->header_state) + { + case h_upgrade: + parser->flags |= F_UPGRADE; + parser->header_state = h_general; + break; + + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) + { + parser->header_state = h_matching_transfer_encoding_chunked; + } + else + { + parser->header_state = h_general; + } + break; + + case h_content_length: + if (UNLIKELY(!IS_NUM(ch))) + { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + if (parser->flags & F_CONTENTLENGTH) + { + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; + } + + parser->flags |= F_CONTENTLENGTH; + parser->content_length = ch - '0'; + parser->header_state = h_content_length_num; + break; + + /* when obsolete line folding is encountered for content length + * continue to the s_header_value state */ + case h_content_length_ws: + break; + + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') + { + parser->header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } + else if (c == 'c') + { + parser->header_state = h_matching_connection_close; + } + else if (c == 'u') + { + parser->header_state = h_matching_connection_upgrade; + } + else + { + parser->header_state = h_matching_connection_token; + } + break; + + /* Multi-value `Connection` header */ + case h_matching_connection_token_start: + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_value: + { + const char *start = p; + enum header_states h_state = (enum header_states)parser->header_state; + for (; p != data + len; p++) + { + ch = *p; + if (ch == CR) + { + UPDATE_STATE(s_header_almost_done); + parser->header_state = h_state; + CALLBACK_DATA(header_value); + break; + } + + if (ch == LF) + { + UPDATE_STATE(s_header_almost_done); + COUNT_HEADER_SIZE(p - start); + parser->header_state = h_state; + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + + if (!lenient && !IS_HEADER_CHAR(ch)) + { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + c = LOWER(ch); + + switch (h_state) + { + case h_general: + { + size_t left = data + len - p; + const char *pe = p + MIN(left, max_header_size); + + for (; p != pe; p++) + { + ch = *p; + if (ch == CR || ch == LF) + { + --p; + break; + } + if (!lenient && !IS_HEADER_CHAR(ch)) + { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + } + if (p == data + len) + --p; + break; + } + + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; + + case h_content_length: + if (ch == ' ') + break; + h_state = h_content_length_num; + /* fall through */ + + case h_content_length_num: + { + uint64_t t; + + if (ch == ' ') + { + h_state = h_content_length_ws; + break; + } + + if (UNLIKELY(!IS_NUM(ch))) + { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + t = parser->content_length; + t *= 10; + t += ch - '0'; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) + { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + parser->content_length = t; + break; + } + + case h_content_length_ws: + if (ch == ' ') + break; + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED) - 1 || c != CHUNKED[parser->index]) + { + h_state = h_general; + } + else if (parser->index == sizeof(CHUNKED) - 2) + { + h_state = h_transfer_encoding_chunked; + } + break; + + case h_matching_connection_token_start: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') + { + h_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } + else if (c == 'c') + { + h_state = h_matching_connection_close; + } + else if (c == 'u') + { + h_state = h_matching_connection_upgrade; + } + else if (STRICT_TOKEN(c)) + { + h_state = h_matching_connection_token; + } + else if (c == ' ' || c == '\t') + { + /* Skip lws */ + } + else + { + h_state = h_general; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE) - 1 || c != KEEP_ALIVE[parser->index]) + { + h_state = h_matching_connection_token; + } + else if (parser->index == sizeof(KEEP_ALIVE) - 2) + { + h_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE) - 1 || c != CLOSE[parser->index]) + { + h_state = h_matching_connection_token; + } + else if (parser->index == sizeof(CLOSE) - 2) + { + h_state = h_connection_close; + } + break; + + /* looking for 'Connection: upgrade' */ + case h_matching_connection_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || + c != UPGRADE[parser->index]) + { + h_state = h_matching_connection_token; + } + else if (parser->index == sizeof(UPGRADE) - 2) + { + h_state = h_connection_upgrade; + } + break; + + case h_matching_connection_token: + if (ch == ',') + { + h_state = h_matching_connection_token_start; + parser->index = 0; + } + break; + + case h_transfer_encoding_chunked: + if (ch != ' ') + h_state = h_general; + break; + + case h_connection_keep_alive: + case h_connection_close: + case h_connection_upgrade: + if (ch == ',') + { + if (h_state == h_connection_keep_alive) + { + parser->flags |= F_CONNECTION_KEEP_ALIVE; + } + else if (h_state == h_connection_close) + { + parser->flags |= F_CONNECTION_CLOSE; + } + else if (h_state == h_connection_upgrade) + { + parser->flags |= F_CONNECTION_UPGRADE; + } + h_state = h_matching_connection_token_start; + parser->index = 0; + } + else if (ch != ' ') + { + h_state = h_matching_connection_token; + } + break; + + default: + UPDATE_STATE(s_header_value); + h_state = h_general; + break; + } + } + parser->header_state = h_state; + + if (p == data + len) + --p; + + COUNT_HEADER_SIZE(p - start); + break; + } + + case s_header_almost_done: + { + if (UNLIKELY(ch != LF)) + { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_value_lws); + break; + } + + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') + { + if (parser->header_state == h_content_length_num) + { + /* treat obsolete line folding as space */ + parser->header_state = h_content_length_ws; + } + UPDATE_STATE(s_header_value_start); + REEXECUTE(); + } + + /* finished the header */ + switch (parser->header_state) + { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + default: + break; + } + + UPDATE_STATE(s_header_field_start); + REEXECUTE(); + } + + case s_header_value_discard_ws_almost_done: + { + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + case s_header_value_discard_lws: + { + if (ch == ' ' || ch == '\t') + { + UPDATE_STATE(s_header_value_discard_ws); + break; + } + else + { + switch (parser->header_state) + { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_content_length: + /* do not allow empty content length */ + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + break; + default: + break; + } + + /* header value was empty */ + MARK(header_value); + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + } + + case s_headers_almost_done: + { + STRICT_CHECK(ch != LF); + + if (parser->flags & F_TRAILING) + { + /* End of a chunked request */ + UPDATE_STATE(s_message_done); + CALLBACK_NOTIFY_NOADVANCE(chunk_complete); + REEXECUTE(); + } + + /* Cannot use chunked encoding and a content-length header together + per the HTTP specification. */ + if ((parser->flags & F_CHUNKED) && + (parser->flags & F_CONTENTLENGTH)) + { + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; + } + + UPDATE_STATE(s_headers_done); + + /* Set this here so that on_headers_complete() callbacks can see it */ + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) + { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } + else + { + parser->upgrade = (parser->method == HTTP_CONNECT); + } + + /* Here we call the headers_complete callback. This is somewhat + * different than other callbacks because if the user returns 1, we + * will interpret that as saying that this message has no body. This + * is needed for the annoying case of recieving a response to a HEAD + * request. + * + * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so + * we have to simulate it by handling a change in errno below. + */ + if (settings->on_headers_complete) + { + switch (settings->on_headers_complete(parser)) + { + case 0: + break; + + case 2: + parser->upgrade = 1; + + /* fall through */ + case 1: + parser->flags |= F_SKIPBODY; + break; + + default: + SET_ERRNO(HPE_CB_headers_complete); + RETURN(p - data); /* Error */ + } + } + + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) + { + RETURN(p - data); + } + + REEXECUTE(); + } + + case s_headers_done: + { + int hasBody; + STRICT_CHECK(ch != LF); + + parser->nread = 0; + nread = 0; + + hasBody = parser->flags & F_CHUNKED || + (parser->content_length > 0 && parser->content_length != ULLONG_MAX); + if (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) + { + /* Exit, the rest of the message is in a different protocol. */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + RETURN((p - data) + 1); + } + + if (parser->flags & F_SKIPBODY) + { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } + else if (parser->flags & F_CHUNKED) + { + /* chunked encoding - ignore Content-Length header */ + UPDATE_STATE(s_chunk_size_start); + } + else + { + if (parser->content_length == 0) + { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } + else if (parser->content_length != ULLONG_MAX) + { + /* Content-Length header given and non-zero */ + UPDATE_STATE(s_body_identity); + } + else + { + if (!http_message_needs_eof(parser)) + { + /* Assume content-length 0 - read the next */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } + else + { + /* Read body until EOF */ + UPDATE_STATE(s_body_identity_eof); + } + } + } + + break; + } + + case s_body_identity: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t)((data + len) - p)); + + assert(parser->content_length != 0 && parser->content_length != ULLONG_MAX); + + /* The difference between advancing content_length and p is because + * the latter will automaticaly advance on the next loop iteration. + * Further, if content_length ends up at 0, we want to see the last + * byte again for our message complete callback. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) + { + UPDATE_STATE(s_message_done); + + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. + * + * The alternative to doing this is to wait for the next byte to + * trigger the data callback, just as in every other case. The + * problem with this is that this makes it difficult for the test + * harness to distinguish between complete-on-EOF and + * complete-on-length. It's not clear that this distinction is + * important for applications, but let's keep it for now. + */ + CALLBACK_DATA_(body, p - body_mark + 1, p - data); + REEXECUTE(); + } + + break; + } + + /* read until EOF */ + case s_body_identity_eof: + MARK(body); + p = data + len - 1; + + break; + + case s_message_done: + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + if (parser->upgrade) + { + /* Exit, the rest of the message is in a different protocol. */ + RETURN((p - data) + 1); + } + break; + + case s_chunk_size_start: + { + assert(nread == 1); + assert(parser->flags & F_CHUNKED); + + unhex_val = unhex[(unsigned char)ch]; + if (UNLIKELY(unhex_val == -1)) + { + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + parser->content_length = unhex_val; + UPDATE_STATE(s_chunk_size); + break; + } + + case s_chunk_size: + { + uint64_t t; + + assert(parser->flags & F_CHUNKED); + + if (ch == CR) + { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + + unhex_val = unhex[(unsigned char)ch]; + + if (unhex_val == -1) + { + if (ch == ';' || ch == ' ') + { + UPDATE_STATE(s_chunk_parameters); + break; + } + + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + t = parser->content_length; + t *= 16; + t += unhex_val; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) + { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = t; + break; + } + + case s_chunk_parameters: + { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) + { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + break; + } + + case s_chunk_size_almost_done: + { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + + parser->nread = 0; + nread = 0; + + if (parser->content_length == 0) + { + parser->flags |= F_TRAILING; + UPDATE_STATE(s_header_field_start); + } + else + { + UPDATE_STATE(s_chunk_data); + } + CALLBACK_NOTIFY(chunk_header); + break; + } + + case s_chunk_data: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t)((data + len) - p)); + + assert(parser->flags & F_CHUNKED); + assert(parser->content_length != 0 && parser->content_length != ULLONG_MAX); + + /* See the explanation in s_body_identity for why the content + * length and data pointers are managed this way. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) + { + UPDATE_STATE(s_chunk_data_almost_done); + } + + break; + } + + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + assert(parser->content_length == 0); + STRICT_CHECK(ch != CR); + UPDATE_STATE(s_chunk_data_done); + CALLBACK_DATA(body); + break; + + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + parser->nread = 0; + nread = 0; + UPDATE_STATE(s_chunk_size_start); + CALLBACK_NOTIFY(chunk_complete); + break; + + default: + assert(0 && "unhandled state"); + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); + goto error; + } + } + + /* Run callbacks for any marks that we have leftover after we ran out of + * bytes. There should be at most one of these set, so it's OK to invoke + * them in series (unset marks will not result in callbacks). + * + * We use the NOADVANCE() variety of callbacks here because 'p' has already + * overflowed 'data' and this allows us to correct for the off-by-one that + * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' + * value that's in-bounds). + */ + + assert(((header_field_mark ? 1 : 0) + + (header_value_mark ? 1 : 0) + + (url_mark ? 1 : 0) + + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); + + CALLBACK_DATA_NOADVANCE(header_field); + CALLBACK_DATA_NOADVANCE(header_value); + CALLBACK_DATA_NOADVANCE(url); + CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); + + RETURN(len); + + error: + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) + { + SET_ERRNO(HPE_UNKNOWN); + } + + RETURN(p - data); + } + + /* Does the parser need to see an EOF to find the end of the message? */ + static int http_message_needs_eof(const http_parser *parser) + { + if (parser->type == HTTP_REQUEST) + { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + parser->flags & F_SKIPBODY) + { /* response to a HEAD request */ + return 0; + } + + if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) + { + return 0; + } + + return 1; + } + + static int http_should_keep_alive(const http_parser *parser) + { + if (parser->http_major > 0 && parser->http_minor > 0) + { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) + { + return 0; + } + } + else + { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) + { + return 0; + } + } + + return !http_message_needs_eof(parser); + } + + static const char * + http_method_str(enum http_method m) + { + return ELEM_AT(method_strings, m, ""); + } + + static const char * + http_status_str(enum http_status s) + { + switch (s) + { +#define XX(num, name, string) \ + case HTTP_STATUS_##name: \ + return #string; + HTTP_STATUS_MAP(XX) +#undef XX + default: + return ""; + } + } + + static void + http_parser_init(http_parser *parser, enum http_parser_type t) + { + void *data = parser->data; /* preserve application data */ + memset(parser, 0, sizeof(*parser)); + parser->data = data; + parser->type = t; + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->http_errno = HPE_OK; + } + + static void + http_parser_settings_init(http_parser_settings *settings) + { + memset(settings, 0, sizeof(*settings)); + } + + static const char * + http_errno_name(enum http_errno err) + { + assert(((size_t)err) < ARRAY_SIZE(http_strerror_tab)); + return http_strerror_tab[err].name; + } + + static const char * + http_errno_description(enum http_errno err) + { + assert(((size_t)err) < ARRAY_SIZE(http_strerror_tab)); + return http_strerror_tab[err].description; + } + + static enum http_host_state + http_parse_host_char(enum http_host_state s, const char ch) + { + switch (s) + { + case s_http_userinfo: + case s_http_userinfo_start: + if (ch == '@') + { + return s_http_host_start; + } + + if (IS_USERINFO_CHAR(ch)) + { + return s_http_userinfo; + } + break; + + case s_http_host_start: + if (ch == '[') + { + return s_http_host_v6_start; + } + + if (IS_HOST_CHAR(ch)) + { + return s_http_host; + } + + break; + + case s_http_host: + if (IS_HOST_CHAR(ch)) + { + return s_http_host; + } + + /* fall through */ + case s_http_host_v6_end: + if (ch == ':') + { + return s_http_host_port_start; + } + + break; + + case s_http_host_v6: + if (ch == ']') + { + return s_http_host_v6_end; + } + + /* fall through */ + case s_http_host_v6_start: + if (IS_HEX(ch) || ch == ':' || ch == '.') + { + return s_http_host_v6; + } + + if (s == s_http_host_v6 && ch == '%') + { + return s_http_host_v6_zone_start; + } + break; + + case s_http_host_v6_zone: + if (ch == ']') + { + return s_http_host_v6_end; + } + + /* fall through */ + case s_http_host_v6_zone_start: + /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ + if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || + ch == '~') + { + return s_http_host_v6_zone; + } + break; + + case s_http_host_port: + case s_http_host_port_start: + if (IS_NUM(ch)) + { + return s_http_host_port; + } + + break; + + default: + break; + } + return s_http_host_dead; + } + + static int + http_parse_host(const char *buf, struct http_parser_url *u, int found_at) + { + enum http_host_state s; + + const char *p; + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + + assert(u->field_set & (1 << UF_HOST)); + + u->field_data[UF_HOST].len = 0; + + s = found_at ? s_http_userinfo_start : s_http_host_start; + + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) + { + enum http_host_state new_s = http_parse_host_char(s, *p); + + if (new_s == s_http_host_dead) + { + return 1; + } + + switch (new_s) + { + case s_http_host: + if (s != s_http_host) + { + u->field_data[UF_HOST].off = (uint16_t)(p - buf); + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6: + if (s != s_http_host_v6) + { + u->field_data[UF_HOST].off = (uint16_t)(p - buf); + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + u->field_data[UF_HOST].len++; + break; + + case s_http_host_port: + if (s != s_http_host_port) + { + u->field_data[UF_PORT].off = (uint16_t)(p - buf); + u->field_data[UF_PORT].len = 0; + u->field_set |= (1 << UF_PORT); + } + u->field_data[UF_PORT].len++; + break; + + case s_http_userinfo: + if (s != s_http_userinfo) + { + u->field_data[UF_USERINFO].off = (uint16_t)(p - buf); + u->field_data[UF_USERINFO].len = 0; + u->field_set |= (1 << UF_USERINFO); + } + u->field_data[UF_USERINFO].len++; + break; + + default: + break; + } + s = new_s; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) + { + case s_http_host_start: + case s_http_host_v6_start: + case s_http_host_v6: + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + case s_http_host_port_start: + case s_http_userinfo: + case s_http_userinfo_start: + return 1; + default: + break; + } + + return 0; + } + + static void + http_parser_url_init(struct http_parser_url *u) + { + memset(u, 0, sizeof(*u)); + } + + static int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, + struct http_parser_url *u) + { + enum state s; + const char *p; + enum http_parser_url_fields uf, old_uf; + int found_at = 0; + + if (buflen == 0) + { + return 1; + } + + u->port = u->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) + { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) + { + case s_dead: + return 1; + + /* Skip delimeters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = UF_SCHEMA; + break; + + case s_req_server_with_at: + found_at = 1; + + /* fall through */ + case s_req_server: + uf = UF_HOST; + break; + + case s_req_path: + uf = UF_PATH; + break; + + case s_req_query_string: + uf = UF_QUERY; + break; + + case s_req_fragment: + uf = UF_FRAGMENT; + break; + + default: + assert(!"Unexpected state"); + return 1; + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) + { + u->field_data[uf].len++; + continue; + } + + u->field_data[uf].off = (uint16_t)(p - buf); + u->field_data[uf].len = 1; + + u->field_set |= (1 << uf); + old_uf = uf; + } + + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((u->field_set & (1 << UF_SCHEMA)) && + (u->field_set & (1 << UF_HOST)) == 0) + { + return 1; + } + + if (u->field_set & (1 << UF_HOST)) + { + if (http_parse_host(buf, u, found_at) != 0) + { + return 1; + } + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST) | (1 << UF_PORT))) + { + return 1; + } + + if (u->field_set & (1 << UF_PORT)) + { + uint16_t off; + uint16_t len; + const char *p; + const char *end; + unsigned long v; + + off = u->field_data[UF_PORT].off; + len = u->field_data[UF_PORT].len; + end = buf + off + len; + + /* NOTE: The characters are already validated and are in the [0-9] range */ + assert(off + len <= buflen && "Port number overflow"); + v = 0; + for (p = buf + off; p < end; p++) + { + v *= 10; + v += *p - '0'; + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) + { + return 1; + } + } + + u->port = (uint16_t)v; + } + + return 0; + } + + static void + http_parser_pause(http_parser *parser, int paused) + { + /* Users should only be pausing/unpausing a parser that is not in an error + * state. In non-debug builds, there's not much that we can do about this + * other than ignore it. + */ + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) + { + uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */ + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); + } + else + { + assert(0 && "Attempting to pause parser in error state"); + } + } + + static int http_body_is_final(const struct http_parser *parser) + { + return parser->state == s_message_done; + } + + static unsigned long + http_parser_version(void) + { + return HTTP_PARSER_VERSION_MAJOR * 0x10000 | + HTTP_PARSER_VERSION_MINOR * 0x00100 | + HTTP_PARSER_VERSION_PATCH * 0x00001; + } + + static void + http_parser_set_max_header_size(uint32_t size) + { + max_header_size = size; + } + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libs/brynet/net/port/Win.hpp b/libs/brynet/net/port/Win.hpp new file mode 100644 index 0000000..07e0bd7 --- /dev/null +++ b/libs/brynet/net/port/Win.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace brynet { namespace net { namespace port { + +#ifdef BRYNET_PLATFORM_WINDOWS + class Win + { + public: + enum class OverlappedType + { + OverlappedNone = 0, + OverlappedRecv, + OverlappedSend, + }; + + struct OverlappedExt + { + OVERLAPPED base; + const OverlappedType OP; + + OverlappedExt(OverlappedType op) BRYNET_NOEXCEPT : OP(op) + { + memset(&base, 0, sizeof(base)); + } + }; + }; +#endif + +} } } \ No newline at end of file diff --git a/libs/brynet/net/wrapper/ConnectionBuilder.hpp b/libs/brynet/net/wrapper/ConnectionBuilder.hpp new file mode 100644 index 0000000..7cdc8ee --- /dev/null +++ b/libs/brynet/net/wrapper/ConnectionBuilder.hpp @@ -0,0 +1,191 @@ +#pragma once + +#include +#include +#include +#include + +namespace brynet { namespace net { namespace wrapper { + + template + class BaseSocketConnectBuilder + { + protected: + using AddSocketOptionFunc = detail::AddSocketOptionFunc; + using ConnectOptionFunc = detail::ConnectOptionFunc; + + public: + virtual ~BaseSocketConnectBuilder() = default; + + Derived& configureConnector(AsyncConnector::Ptr connector) + { + mConnector = std::move(connector); + return static_cast(*this); + } + + Derived& configureConnectOptions( + std::vector options) + { + mConnectOptions = std::move(options); + return static_cast(*this); + } + + void asyncConnect() const + { + asyncConnect(mConnectOptions); + } + + TcpSocket::Ptr syncConnect() const + { + return syncConnect(mConnectOptions); + } + + protected: + void asyncConnect(std::vector connectOptions) const + { + if (mConnector == nullptr) + { + throw BrynetCommonException("connector is nullptr"); + } + if (connectOptions.empty()) + { + throw BrynetCommonException("options is empty"); + } + + mConnector->asyncConnect(connectOptions); + } + + TcpSocket::Ptr syncConnect(std::vector connectOptions) const + { + auto timeout = ConnectOption::ExtractTimeout(connectOptions); + + auto socketPromise = std::make_shared>(); + connectOptions.push_back(ConnectOption::WithCompletedCallback( + [socketPromise](TcpSocket::Ptr socket) { + socketPromise->set_value(std::move(socket)); + })); + connectOptions.push_back(ConnectOption::WithFailedCallback([socketPromise]() { + socketPromise->set_value(nullptr); + })); + + asyncConnect(connectOptions); + + auto future = socketPromise->get_future(); + if (future.wait_for(timeout) != std::future_status::ready) + { + return nullptr; + } + + return future.get(); + } + + std::vector getConnectOptions() const + { + return mConnectOptions; + } + + private: + AsyncConnector::Ptr mConnector; + std::vector mConnectOptions; + }; + + class SocketConnectBuilder : public BaseSocketConnectBuilder + { + }; + + template + class BaseConnectionBuilder : public BaseSocketConnectBuilder + { + protected: + using AddSocketOptionFunc = detail::AddSocketOptionFunc; + using ConnectOptionFunc = detail::ConnectOptionFunc; + + public: + Derived& configureService(TcpService::Ptr service) + { + mTcpService = std::move(service); + return static_cast(*this); + } + + Derived& configureConnectionOptions(std::vector options) + { + mConnectionOptions = std::move(options); + return static_cast(*this); + } + + void asyncConnect() const + { + asyncConnect(BaseSocketConnectBuilder::getConnectOptions(), + mConnectionOptions); + } + + TcpConnection::Ptr syncConnect() const + { + return syncConnect(BaseSocketConnectBuilder::getConnectOptions(), + mConnectionOptions); + } + + protected: + void asyncConnect(std::vector connectOptions, + std::vector connectionOptions) const + { + if (mTcpService == nullptr) + { + throw BrynetCommonException("tcp serviceis nullptr"); + } + if (connectionOptions.empty()) + { + throw BrynetCommonException("options is empty"); + } + + auto service = mTcpService; + auto enterCallback = [service, connectionOptions](TcpSocket::Ptr socket) mutable { + service->addTcpConnection(std::move(socket), connectionOptions); + }; + connectOptions.push_back(ConnectOption::WithCompletedCallback(enterCallback)); + + BaseSocketConnectBuilder::asyncConnect(connectOptions); + } + + TcpConnection::Ptr syncConnect(std::vector connectOptions, + std::vector connectionOptions) const + { + auto timeout = ConnectOption::ExtractTimeout(connectOptions); + auto sessionPromise = std::make_shared>(); + + connectOptions.push_back(ConnectOption::WithFailedCallback( + [sessionPromise]() { + sessionPromise->set_value(nullptr); + })); + + connectionOptions.push_back(AddSocketOption::AddEnterCallback( + [sessionPromise](const TcpConnection::Ptr& session) { + sessionPromise->set_value(session); + })); + + asyncConnect(connectOptions, connectionOptions); + + auto future = sessionPromise->get_future(); + if (future.wait_for(timeout) != std::future_status::ready) + { + return nullptr; + } + + return future.get(); + } + + std::vector getConnectionOptions() const + { + return mConnectionOptions; + } + + private: + TcpService::Ptr mTcpService; + std::vector mConnectionOptions; + }; + + class ConnectionBuilder : public BaseConnectionBuilder + { + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/wrapper/HttpConnectionBuilder.hpp b/libs/brynet/net/wrapper/HttpConnectionBuilder.hpp new file mode 100644 index 0000000..7f4467d --- /dev/null +++ b/libs/brynet/net/wrapper/HttpConnectionBuilder.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +namespace brynet { namespace net { namespace wrapper { + + class HttpConnectionBuilder : public BaseConnectionBuilder + { + public: + HttpConnectionBuilder& configureEnterCallback( + http::HttpSession::EnterCallback&& callback) + { + mHttpEnterCallback = std::move(callback); + return *this; + } + + void asyncConnect() const + { + if (mHttpEnterCallback == nullptr) + { + throw BrynetCommonException("not setting http enter callback"); + } + + auto connectionOptions = + BaseConnectionBuilder::getConnectionOptions(); + auto callback = mHttpEnterCallback; + + connectionOptions.push_back( + AddSocketOption::AddEnterCallback( + [callback](const TcpConnection::Ptr& session) { + http::HttpService::setup(session, callback); + })); + + BaseConnectionBuilder::asyncConnect( + BaseConnectionBuilder::getConnectOptions(), + connectionOptions); + } + + private: + http::HttpSession::EnterCallback mHttpEnterCallback; + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/wrapper/HttpServiceBuilder.hpp b/libs/brynet/net/wrapper/HttpServiceBuilder.hpp new file mode 100644 index 0000000..692d412 --- /dev/null +++ b/libs/brynet/net/wrapper/HttpServiceBuilder.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +namespace brynet { namespace net { namespace wrapper { + + class HttpListenerBuilder : public BaseListenerBuilder + { + public: + HttpListenerBuilder& configureEnterCallback(http::HttpSession::EnterCallback&& callback) + { + mHttpEnterCallback = std::move(callback); + return *this; + } + + void asyncRun() + { + if (mHttpEnterCallback == nullptr) + { + throw BrynetCommonException("not setting http enter callback"); + } + + auto connectionOptions = + BaseListenerBuilder::getConnectionOptions(); + auto callback = mHttpEnterCallback; + connectionOptions.push_back( + AddSocketOption::AddEnterCallback( + [callback](const TcpConnection::Ptr& session) { + http::HttpService::setup(session, callback); + })); + BaseListenerBuilder::asyncRun(connectionOptions); + } + + private: + http::HttpSession::EnterCallback mHttpEnterCallback; + }; + +} } } \ No newline at end of file diff --git a/libs/brynet/net/wrapper/ServiceBuilder.hpp b/libs/brynet/net/wrapper/ServiceBuilder.hpp new file mode 100644 index 0000000..ce340ee --- /dev/null +++ b/libs/brynet/net/wrapper/ServiceBuilder.hpp @@ -0,0 +1,167 @@ +#pragma once + +#include +#include +#include + +namespace brynet { namespace net { namespace wrapper { + + class ListenConfig final + { + public: + ListenConfig() + { + mSetting = false; + mIsIpV6 = false; + mPort = 0; + } + + void setAddr(bool ipV6, std::string ip, int port) + { + mIsIpV6 = ipV6; + mListenAddr = ip; + mPort = port; + mSetting = true; + } + + std::string ip() const + { + return mListenAddr; + } + + int port() const + { + return mPort; + } + + bool useIpV6() const + { + return mIsIpV6; + } + + bool hasSetting() const + { + return mSetting; + } + + private: + std::string mListenAddr; + int mPort; + bool mIsIpV6; + bool mSetting; + }; + + class BuildListenConfig + { + public: + explicit BuildListenConfig(ListenConfig* config) + : + mConfig(config) + { + } + + void setAddr(bool ipV6, std::string ip, int port) + { + mConfig->setAddr(ipV6, ip, port); + } + + private: + ListenConfig* mConfig; + }; + + template + class BaseListenerBuilder + { + protected: + using AddSocketOptionFunc = detail::AddSocketOptionFunc; + using ConnectOptionFunc = detail::ConnectOptionFunc; + + public: + virtual ~BaseListenerBuilder() = default; + + Derived& configureService(TcpService::Ptr service) + { + mTcpService = std::move(service); + return static_cast(*this); + } + + Derived& configureSocketOptions(std::vector options) + { + mSocketOptions = std::move(options); + return static_cast(*this); + } + + Derived& configureConnectionOptions(std::vector options) + { + mConnectionOptions = std::move(options); + return static_cast(*this); + } + + template + Derived& configureListen(const BuilderFunc& builder) + { + BuildListenConfig buildConfig(&mListenConfig); + builder(buildConfig); + return static_cast(*this); + } + + void asyncRun() + { + asyncRun(getConnectionOptions()); + } + + void stop() + { + if (mListenThread) + { + mListenThread->stopListen(); + } + } + + std::vector getConnectionOptions() const + { + return mConnectionOptions; + } + + protected: + void asyncRun(std::vector connectionOptions) + { + if (mTcpService == nullptr) + { + throw BrynetCommonException("tcp service is nullptr"); + } + if (connectionOptions.empty()) + { + throw BrynetCommonException("options is empty"); + } + if (!mListenConfig.hasSetting()) + { + throw BrynetCommonException("not config listen addr"); + } + + auto service = mTcpService; + mListenThread = ListenThread::Create(mListenConfig.useIpV6(), + mListenConfig.ip(), + mListenConfig.port(), + [service, connectionOptions](brynet::net::TcpSocket::Ptr socket) { + service->addTcpConnection(std::move(socket), connectionOptions); + }, + mSocketOptions); + mListenThread->startListen(); + } + + private: + TcpService::Ptr mTcpService; + std::vector mSocketOptions; + ListenConfig mListenConfig; + ListenThread::Ptr mListenThread; + + private: + std::vector mConnectionOptions; + }; + + class ListenerBuilder : public BaseListenerBuilder + { + }; + +} } } \ No newline at end of file diff --git a/libs/rapidjson/allocators.h b/libs/rapidjson/allocators.h new file mode 100644 index 0000000..44ec529 --- /dev/null +++ b/libs/rapidjson/allocators.h @@ -0,0 +1,284 @@ +// 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. +// +// 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 +*/ + + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// 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 RAPIDJSON_MALLOC(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + RAPIDJSON_FREE(originalPtr); + return NULL; + } + return RAPIDJSON_REALLOC(originalPtr, newSize); + } + static void Free(void *ptr) { RAPIDJSON_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) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + 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. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! 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. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; + } + + static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< 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/libs/rapidjson/cursorstreamwrapper.h b/libs/rapidjson/cursorstreamwrapper.h new file mode 100644 index 0000000..fd6513d --- /dev/null +++ b/libs/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// 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. +// +// 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_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/libs/rapidjson/document.h b/libs/rapidjson/document.h new file mode 100644 index 0000000..6ed8516 --- /dev/null +++ b/libs/rapidjson/document.h @@ -0,0 +1,2732 @@ +// 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. +// +// 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 "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include +#ifdef __cpp_lib_three_way_comparison +#include +#endif + +RAPIDJSON_DIAG_PUSH +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif // __GNUC__ + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // 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; + +/*! \def RAPIDJSON_DEFAULT_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default allocator. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_ALLOCATOR +#define RAPIDJSON_DEFAULT_ALLOCATOR MemoryPoolAllocator +#endif + +/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default stack allocator for Document. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR CrtAllocator +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultObjectCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY +// number of objects that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultArrayCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY +// number of array elements that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 +#endif + +//! 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 +class GenericMember { +public: + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT + : name(std::move(rhs.name)), + value(std::move(rhs.value)) + { + } + + //! Move assignment in C++11 + GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { + return *this = static_cast(rhs); + } +#endif + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. + */ + GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + name = rhs.name; + value = rhs.value; + } + return *this; + } + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } + +private: + //! Copy constructor is not permitted. + GenericMember(const GenericMember& rhs); +}; + +/////////////////////////////////////////////////////////////////////////////// +// 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 { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + + //! Pointer to (const) GenericMember + typedef pointer Pointer; + //! Reference to (const) GenericMember + typedef reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef 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 + //@{ + template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } + template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } + template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } + template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } + template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } + template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } + +#ifdef __cpp_lib_three_way_comparison + template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } +#endif + //@} + + //! @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 +class GenericMemberIterator; + +//! non-const GenericMemberIterator +template +class GenericMemberIterator { + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +class 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(NotNullStrLen(str)) {} + + //! 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(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + //! 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: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; +}; + +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + +//! 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); +} + +//! 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 + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// 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. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.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_() { + static const uint16_t defaultFlags[] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.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(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + \see CopyFrom() + */ + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: { + SizeType count = rhs.data_.o.size; + Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); + } + data_.f.flags = kObjectFlag; + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } + + //! 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_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { 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_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(GetStringPointer())); + 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 { + if (RAPIDJSON_LIKELY(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 + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator, copyConstStrings); + 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 Complexity is quadratic in Object's member number and linear for the rest (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(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.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; } + + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + + //! 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(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object 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& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + if (newCapacity > data_.o.capacity) { + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member)))); + data_.o.capacity = newCapacity; + } + return *this; + } + + //! 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(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(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()); + + ObjectData& o = data_.o; + if (o.size >= o.capacity) + MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator); + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + 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(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy + --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(GetMembersPointer() != 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(static_cast(&*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; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@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()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~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 GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + 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) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), 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); + GetElementsPointer()[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()); + GetElementsPointer()[--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(GetElementsPointer() != 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(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + 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; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + + //! 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 ((data_.f.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) { return SetString(StringRef(s, length), allocator); } + + //! 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(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } + +#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(StringRef(s), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! 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.data_.f.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 = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.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 = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // 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 = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; + static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 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 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / 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 ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + 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) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(static_cast(m), members, count * sizeof(Member)); + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(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 = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(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_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.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_; +}; + +//! 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 +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; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! 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); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occurred 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 RawNumber(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 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; + + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/libs/rapidjson/encodedstream.h b/libs/rapidjson/encodedstream.h new file mode 100644 index 0000000..cf046b8 --- /dev/null +++ b/libs/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// 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. +// +// 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" +#include "memorystream.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_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! 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_) { + int 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/libs/rapidjson/encodings.h b/libs/rapidjson/encodings.h new file mode 100644 index 0000000..50ad18b --- /dev/null +++ b/libs/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// 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. +// +// 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" + +#if defined(_MSC_VER) && !defined(__clang__) +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 RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + Ch c; + RAPIDJSON_COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_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(static_cast((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, static_cast((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(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 + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE 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 + static RAPIDJSON_FORCEINLINE 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) && !defined(__clang__)) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/libs/rapidjson/error/en.h b/libs/rapidjson/error/en.h new file mode 100644 index 0000000..37a62eb --- /dev/null +++ b/libs/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. +// +// 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 be followed 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/libs/rapidjson/error/error.h b/libs/rapidjson/error/error.h new file mode 100644 index 0000000..71f6ec4 --- /dev/null +++ b/libs/rapidjson/error/error.h @@ -0,0 +1,161 @@ +// 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. +// +// 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 { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +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_; } + + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } + //! 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_; } + + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return 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/libs/rapidjson/filereadstream.h b/libs/rapidjson/filereadstream.h new file mode 100644 index 0000000..f8bb43c --- /dev/null +++ b/libs/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. +// +// 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 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = std::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/libs/rapidjson/filewritestream.h b/libs/rapidjson/filewritestream.h new file mode 100644 index 0000000..5d89588 --- /dev/null +++ b/libs/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. +// +// 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 output using fwrite(). +/*! + \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 = std::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/libs/rapidjson/fwd.h b/libs/rapidjson/fwd.h new file mode 100644 index 0000000..d62f77f --- /dev/null +++ b/libs/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// 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. +// +// 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; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +class 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/libs/rapidjson/internal/biginteger.h b/libs/rapidjson/internal/biginteger.h new file mode 100644 index 0000000..1245578 --- /dev/null +++ b/libs/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. +// +// 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(__INTEL_COMPILER) && 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_ + offset, digits_, 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/libs/rapidjson/internal/clzll.h b/libs/rapidjson/internal/clzll.h new file mode 100644 index 0000000..8fc5118 --- /dev/null +++ b/libs/rapidjson/internal/clzll.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. +// +// 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_CLZLL_H_ +#define RAPIDJSON_CLZLL_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(UNDER_CE) +#include +#if defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#else +#pragma intrinsic(_BitScanReverse) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline uint32_t clzll(uint64_t x) { + // Passing 0 to __builtin_clzll is UB in GCC and results in an + // infinite loop in the software implementation. + RAPIDJSON_ASSERT(x != 0); + +#if defined(_MSC_VER) && !defined(UNDER_CE) + unsigned long r = 0; +#if defined(_WIN64) + _BitScanReverse64(&r, x); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); +#endif // _WIN64 + + return 63 - r; +#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) + // __builtin_clzll wrapper + return static_cast(__builtin_clzll(x)); +#else + // naive version + uint32_t r = 0; + while (!(x & (static_cast(1) << 63))) { + x <<= 1; + ++r; + } + + return r; +#endif // _MSC_VER +} + +#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CLZLL_H_ diff --git a/libs/rapidjson/internal/diyfp.h b/libs/rapidjson/internal/diyfp.h new file mode 100644 index 0000000..a40797e --- /dev/null +++ b/libs/rapidjson/internal/diyfp.h @@ -0,0 +1,257 @@ +// 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. +// +// 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" +#include "clzll.h" +#include + +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) +#include +#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() : f(), e() {} + + 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 { + int s = static_cast(clzll(f)); + return DiyFp(f << s, e - s); + } + + 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; + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + 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 + }; + RAPIDJSON_ASSERT(index < 87); + 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) { + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 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/libs/rapidjson/internal/dtoa.h b/libs/rapidjson/internal/dtoa.h new file mode 100644 index 0000000..621402f --- /dev/null +++ b/libs/rapidjson/internal/dtoa.h @@ -0,0 +1,245 @@ +// 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. +// +// 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++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#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 int 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); + int 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; + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); + 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, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && 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] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + 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'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + 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, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + 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, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/libs/rapidjson/internal/ieee754.h b/libs/rapidjson/internal/ieee754.h new file mode 100644 index 0000000..68c9e96 --- /dev/null +++ b/libs/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// 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. +// +// 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 IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + 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 int EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return 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/libs/rapidjson/internal/itoa.h b/libs/rapidjson/internal/itoa.h new file mode 100644 index 0000000..9fe8c93 --- /dev/null +++ b/libs/rapidjson/internal/itoa.h @@ -0,0 +1,308 @@ +// 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. +// +// 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) { + RAPIDJSON_ASSERT(buffer != 0); + + 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) { + RAPIDJSON_ASSERT(buffer != 0); + 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) { + RAPIDJSON_ASSERT(buffer != 0); + 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]; + + *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) { + RAPIDJSON_ASSERT(buffer != 0); + 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/libs/rapidjson/internal/meta.h b/libs/rapidjson/internal/meta.h new file mode 100644 index 0000000..27092dc --- /dev/null +++ b/libs/rapidjson/internal/meta.h @@ -0,0 +1,186 @@ +// 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. +// +// 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) && !defined(__clang__) +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(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/libs/rapidjson/internal/pow10.h b/libs/rapidjson/internal/pow10.h new file mode 100644 index 0000000..eae1a43 --- /dev/null +++ b/libs/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. +// +// 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/libs/rapidjson/internal/regex.h b/libs/rapidjson/internal/regex.h new file mode 100644 index 0000000..6446c40 --- /dev/null +++ b/libs/rapidjson/internal/regex.h @@ -0,0 +1,739 @@ +// 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. +// +// 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) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#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 { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +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_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! 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 Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +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; + }; + + 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) { + 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)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + 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 + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + 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 + } + } + + 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: + RAPIDJSON_ASSERT(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; + + 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: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + 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(); // Copy constructor to prevent invalidation + 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; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + 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 + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + 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 + } + } + + Allocator* ownAllocator_; + Allocator* allocator_; + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.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 = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (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 = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/libs/rapidjson/internal/stack.h b/libs/rapidjson/internal/stack.h new file mode 100644 index 0000000..73abd70 --- /dev/null +++ b/libs/rapidjson/internal/stack.h @@ -0,0 +1,232 @@ +// 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. +// +// 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" +#include + +#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_); // NOLINT (+clang-analyzer-unix.Malloc) + 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(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) + 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_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); + 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/libs/rapidjson/internal/strfunc.h b/libs/rapidjson/internal/strfunc.h new file mode 100644 index 0000000..baecb6c --- /dev/null +++ b/libs/rapidjson/internal/strfunc.h @@ -0,0 +1,69 @@ +// 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. +// +// 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" +#include + +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) { + RAPIDJSON_ASSERT(s != 0); + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + 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/libs/rapidjson/internal/strtod.h b/libs/rapidjson/internal/strtod.h new file mode 100644 index 0000000..d61a67a --- /dev/null +++ b/libs/rapidjson/internal/strtod.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. +// +// 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" +#include +#include + +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, int dLen, int dExp, double* result) { + uint64_t significand = 0; + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; 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 < dLen && decimals[i] >= '5') // Rounding + significand++; + + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + dExp += remaining; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // 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 int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + int scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + 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, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); + 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 = 0.0; + if (StrtodFast(d, p, &result)) + return result; + + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + + // Trim leading zeros + while (dLen > 0 && *decimals == '0') { + dLen--; + decimals++; + } + + // Trim trailing zeros + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; + } + + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) + return 0.0; + + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, dLen, dExp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/libs/rapidjson/internal/swap.h b/libs/rapidjson/internal/swap.h new file mode 100644 index 0000000..2cf92f9 --- /dev/null +++ b/libs/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. +// +// 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/libs/rapidjson/istreamwrapper.h b/libs/rapidjson/istreamwrapper.h new file mode 100644 index 0000000..01437ec --- /dev/null +++ b/libs/rapidjson/istreamwrapper.h @@ -0,0 +1,128 @@ +// 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. +// +// 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_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); + } + + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + 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 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + BasicIStreamWrapper(); + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/libs/rapidjson/memorybuffer.h b/libs/rapidjson/memorybuffer.h new file mode 100644 index 0000000..ffbc41e --- /dev/null +++ b/libs/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. +// +// 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/libs/rapidjson/memorystream.h b/libs/rapidjson/memorystream.h new file mode 100644 index 0000000..77af6c9 --- /dev/null +++ b/libs/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. +// +// 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 RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(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/libs/rapidjson/msinttypes/inttypes.h b/libs/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000..1811128 --- /dev/null +++ b/libs/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/libs/rapidjson/msinttypes/stdint.h b/libs/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000..3d4477b --- /dev/null +++ b/libs/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/libs/rapidjson/ostreamwrapper.h b/libs/rapidjson/ostreamwrapper.h new file mode 100644 index 0000000..11ed4d3 --- /dev/null +++ b/libs/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// 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. +// +// 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_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // 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: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/libs/rapidjson/pointer.h b/libs/rapidjson/pointer.h new file mode 100644 index 0000000..90e5903 --- /dev/null +++ b/libs/rapidjson/pointer.h @@ -0,0 +1,1415 @@ +// 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. +// +// 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) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#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_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : 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; + } + + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + 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.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //@} + + //!@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, internal::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] = static_cast(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); } + + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + + //@} + + //!@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(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + m = v->MemberEnd(); + v = &(--m)->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. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) 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(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + 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, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@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; + ValueType& 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; + ValueType& 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; + ValueType& 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(GenericValue(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_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + 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 = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(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(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(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, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +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 + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/libs/rapidjson/prettywriter.h b/libs/rapidjson/prettywriter.h new file mode 100644 index 0000000..fe45df1 --- /dev/null +++ b/libs/rapidjson/prettywriter.h @@ -0,0 +1,277 @@ +// 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. +// +// 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 + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of output 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, unsigned writeFlags = kWriteDefaultFlags> +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. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif + + //! 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; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::EndValue(Base::WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kStringType); + return Base::EndValue(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); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndObject()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::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 && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndArray()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::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)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(Base::WriteRawValue(json, length)); + } + +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 + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + 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_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/libs/rapidjson/rapidjson.h b/libs/rapidjson/rapidjson.h new file mode 100644 index 0000000..78aa89a --- /dev/null +++ b/libs/rapidjson/rapidjson.h @@ -0,0 +1,692 @@ +// 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. +// +// 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 overridden 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 + +// token concatenation +#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 +//!@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 1 +#define RAPIDJSON_PATCH_VERSION 0 +#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 +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#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 endianness 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 endianness 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) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianness 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(__x86_64__) && defined(__ILP32__)) || 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. The default is 8 bytes. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#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_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2/Neon optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. + + To enable these optimizations, three different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, 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_NEON) || 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 + +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation 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 + +#if defined(__GNUC__) || defined(__clang__) +#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_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// 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)) + +#if defined(__has_builtin) +#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) +#else +#define RAPIDJSON_HAS_BUILTIN(x) 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// 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__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) + +#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 >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#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 +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +/////////////////////////////////////////////////////////////////////////////// +// C++17 features + +#if defined(__has_cpp_attribute) +# if __has_cpp_attribute(fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] +# else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +# endif +#else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +#endif + +//!@endcond + +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT_ASSERT(x) +#else +#include +#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// malloc/realloc/free + +#ifndef RAPIDJSON_MALLOC +///! customization point for global \c malloc +#define RAPIDJSON_MALLOC(size) std::malloc(size) +#endif +#ifndef RAPIDJSON_REALLOC +///! customization point for global \c realloc +#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) +#endif +#ifndef RAPIDJSON_FREE +///! customization point for global \c free +#define RAPIDJSON_FREE(ptr) std::free(ptr) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(TypeName) new TypeName +#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/libs/rapidjson/reader.h b/libs/rapidjson/reader.h new file mode 100644 index 0000000..09ace4e --- /dev/null +++ b/libs/rapidjson/reader.h @@ -0,0 +1,2244 @@ +// 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. +// +// 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 "encodedstream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#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. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. + 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); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + 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(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + 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); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#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_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + +#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 + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*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 <= end - 16; p += 16) { + const __m128i s = _mm_loadu_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 + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon 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; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON + +#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_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#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_(), state_(IterativeParsingStartState) {} + + //! 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); + } + + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \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 + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred 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(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + 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 (Consume(is, '}')) { + 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(!Consume(is, ':'))) + 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.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // 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 (Consume(is, ']')) { + 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; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + 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, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + 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 + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe + is.Take(); + os.Put('\''); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + // high surrogate, check if followed by valid low surrogate + if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + // single low surrogate + else + { + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + } + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + 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()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + 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] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + 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, 0x1F) == 0x1F + 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 + if (length != 0) { + 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] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + 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, 0x1F) == 0x1F + 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] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + 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, 0x1F) == 0x1F + 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; + } +#elif defined(RAPIDJSON_NEON) + // 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 + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(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 + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(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 + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + uint32_t lz = internal::clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + 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) : Base(reader, is), stackStream(reader.stack_) {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // 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++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + 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')) { + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + 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 (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { + 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, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + 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); + + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(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, startOffset); + } + + // 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 { + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingMemberValueState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { + +//!@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) const { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // 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 + }, + // 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 + }, + // 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 + }, + // 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 + }, + // 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 + }, + // 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 + }, + // ElementDelimiter + { + 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 + }, + // MemberDelimiter + { + 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 + }, + // 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 + }, + }; // 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: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // 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: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // 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 IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + + 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_; + IterativeParsingState state_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/libs/rapidjson/schema.h b/libs/rapidjson/schema.h new file mode 100644 index 0000000..fc39d06 --- /dev/null +++ b/libs/rapidjson/schema.h @@ -0,0 +1,2496 @@ +// 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 "stringbuffer.h" +#include // abs, floor + +#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 + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#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 FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue() = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void Disallowed() = 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 RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + 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 IValidationErrorHandler ErrorHandlerType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) : + factory(f), + error_handler(eh), + schema(s), + valueSchema(), + invalidKeyword(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + 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 (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; + 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 arrayElementIndex; + bool* propertyExist; + 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; + typedef IValidationErrorHandler ErrorHandler; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + notValidatorIndex_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false), + defaultValueLength_(0) + { + 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[256u + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + + if (schemaDocument) { + 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 = typeless_; + } + } + } + + 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; + hasRequired_ = true; + } + } + + 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_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + + } + + ~Schema() { + AllocatorType::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(); + AllocatorType::Free(pattern_); + } +#endif + } + + const SValue& GetURI() const { + return uri_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + + 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 = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } + } + else + context.valueSchema = typeless_; + + 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) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + } + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + 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; + context.error_handler.DisallowedValue(); + 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()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); + 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; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); + 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) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } else + oneValid = true; + } + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + } + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + 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))) { + DisallowedType(context, GetNumberString()); + 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))) { + DisallowedType(context, GetStringString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + } + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 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; + context.valueSchema = typeless_; + } + } + + SizeType index = 0; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + context.error_handler.DisallowedProperty(str, len); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + } + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) { + context.error_handler.StartMissingProperties(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + } + + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + } + + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + } + + if (hasDependencies_) { + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; + if (context.propertyExist[sourceIndex]) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); + } + } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); + 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_) { + context.error_handler.TooFewItems(elementCount, minItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + } + + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(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, static_cast(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') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + +#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; + }; + + 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(), allocator_); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); + try { + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + AllocatorType::Free(r); + } + } + 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)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + } + else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + 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) { + context.error_handler.NotMultipleOf(i, multipleOf_); + 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)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + } + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + 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()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); + 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) { + context.error_handler.NotMultipleOf(d, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + return true; + } + + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + + 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_; + SValue uri_; + PointerType pointer_; + const SchemaType* typeless_; + 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 minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + 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_; + + SizeType defaultValueLength_; +}; + +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() = static_cast(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; + typedef GenericValue URIType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. + \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. + */ + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 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_); + } + } + else if (refEntry->schema) + *refEntry->schema = typeless_; + + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + RAPIDJSON_DELETE(ownAllocator_); + } + + const URIType& GetURI() const { return uri_; } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + 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 = typeless_; + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + 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)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + new (schemaMap_.template Push()) SchemaEntry(source, const_cast(sc), false, allocator_); + 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(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref + URIType uri_; +}; + +//! 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 internal::IValidationErrorHandler +{ +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; + typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; + + //! 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()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + 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()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), + 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(); + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); + valid_ = true; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { return valid_; } + + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + + //! Gets the JSON pointer pointed to the invalid schema. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); + } + + //! 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 { + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + } + + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(SchemaType::GetMaxLengthString(), + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(SchemaType::GetMinLengthString(), + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetPatternString()); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetAdditionalItemsString(), true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMinItemsString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMaxItemsString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(SchemaType::GetUniqueItemsString(), true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMaxPropertiesString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMinPropertiesString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetRequiredString()); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + missingDependents_, GetStateAllocator()); + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetDependenciesString()); + return true; + } + + void DisallowedValue() { + currentError_.SetObject(); + AddCurrentError(SchemaType::GetEnumString()); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetTypeString()); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) { + MergeError(static_cast(subvalidators[i])->GetError()); + } + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(SchemaType::GetNotString()); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + +#undef RAPIDJSON_STRING_ + +#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_ || 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 RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + 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_ || 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_ || 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_ || 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, documentStack_.template Bottom(), documentStack_.GetSize(), +#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 FreeState(void* p) { + StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, + const char* basePath, size_t basePathSize, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); + } + + 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; + RAPIDJSON_ASSERT(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) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); + 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, *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(); + } + + void AddErrorLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + sb.Clear(); + memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()), + CurrentSchema().GetURI().GetString(), + CurrentSchema().GetURI().GetStringLength() * sizeof(Ch)); + GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) { + AddErrorLocation(currentError_, parent); + AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(keyword); + } + + void AddErrorArray(const typename SchemaType::ValueType& keyword, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(keyword); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + 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) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; + 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; + typedef GenericValue ValueType; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + error_.SetObject(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); + } + + 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_; } + const ValueType& GetError() const { return error_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + StackAllocator allocator_; + ValueType error_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/libs/rapidjson/stream.h b/libs/rapidjson/stream.h new file mode 100644 index 0000000..1fd7091 --- /dev/null +++ b/libs/rapidjson/stream.h @@ -0,0 +1,223 @@ +// 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. +// +// 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); +} + +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +/////////////////////////////////////////////////////////////////////////////// +// 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/libs/rapidjson/stringbuffer.h b/libs/rapidjson/stringbuffer.h new file mode 100644 index 0000000..82ad3ca --- /dev/null +++ b/libs/rapidjson/stringbuffer.h @@ -0,0 +1,121 @@ +// 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. +// +// 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); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(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(); + } + + //! Get the size of string in bytes in the string buffer. + size_t GetSize() const { return stack_.GetSize(); } + + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + + 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/libs/rapidjson/writer.h b/libs/rapidjson/writer.h new file mode 100644 index 0000000..8b38921 --- /dev/null +++ b/libs/rapidjson/writer.h @@ -0,0 +1,710 @@ +// 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. +// +// 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/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! 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, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! 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)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + + //! 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(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(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 EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kStringType); + return EndValue(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); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + 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); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + + static const size_t kDefaultLevelDepth = 32; + +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 + }; + + 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) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + 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 OutputStream::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 (ScanWriteUnescapedString(is, 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(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + 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; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + 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) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + 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 != endAligned; 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, 0x1F) == 0x1F + 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 len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f9a2e56 --- /dev/null +++ b/main.cpp @@ -0,0 +1,80 @@ +#include +#include +#include + +#include +#include +#include +#include + +using namespace brynet; +using namespace brynet::net; + +std::atomic_llong TotalRecvSize = ATOMIC_VAR_INIT(0); +std::atomic_llong total_client_num = ATOMIC_VAR_INIT(0); +std::atomic_llong total_packet_num = ATOMIC_VAR_INIT(0); + +int main(int argc, char **argv) +{ + if (argc != 3) + { + fprintf(stderr, "Usage: \n"); + exit(-1); + } + + auto service = TcpService::Create(); + service->startWorkerThread(atoi(argv[2])); + + auto enterCallback = [](const TcpConnection::Ptr& session) { + total_client_num++; + + session->setDataCallback([session](const char* buffer, size_t len) { + session->send(buffer, len); + TotalRecvSize += len; + total_packet_num++; + + std::cout << "------------------------------------- PACKET -------------------------------------" << std::endl; + std::cout << buffer << std::endl; + std::cout << "------------------------------------------------------------------------------------" << std::endl << std::endl; + + return len; + }); + + session->setDisConnectCallback([](const TcpConnection::Ptr& session) { + (void)session; + total_client_num--; + }); + }; + + wrapper::ListenerBuilder listener; + listener.configureService(service) + .configureSocketOptions({ + [](TcpSocket& socket) { + socket.setNodelay(); + } + }) + .configureConnectionOptions({ + brynet::net::AddSocketOption::WithMaxRecvBufferSize(1024 * 1024), + brynet::net::AddSocketOption::AddEnterCallback(enterCallback) + }) + .configureListen([=](wrapper::BuildListenConfig config) { + config.setAddr(false, "0.0.0.0", atoi(argv[1])); + }) + .asyncRun(); + + EventLoop mainLoop; + while (true) + { + mainLoop.loop(1000); + + total_packet_num = 0; + TotalRecvSize = 0; + + if (brynet::base::app_kbhit()) + { + break; + } + } + + return 0; +} diff --git a/server b/server new file mode 100755 index 0000000000000000000000000000000000000000..c8b9aaca698298de541528d14ebc6a349eb5940a GIT binary patch literal 1145344 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

Z&shP?h>_=q2dOBO#OWrD8Y^%5*wvR6 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%0DpW>e*>9qxW)STkL&)!p&z2`XscH_8Y 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�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 i`f2ycmzTV`o=X7CQ5=!U2 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

lX;e!<3%WYrkN6tme` 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 literal 0 HcmV?d00001