mirror of
https://github.com/Relintai/rcpp_framework.git
synced 2025-04-20 01:43:12 +02:00
Initial commit.
This commit is contained in:
commit
f9ce8319e9
616
SConstruct
Normal file
616
SConstruct
Normal file
@ -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=<string>")
|
||||
|
||||
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=<string>")
|
||||
|
||||
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)
|
1
compile_linux.sh
Executable file
1
compile_linux.sh
Executable file
@ -0,0 +1 @@
|
||||
g++ -o3 cout_server.cpp -o server -Ilibs -lpthread
|
80
cout_server.cpp
Normal file
80
cout_server.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#include <brynet/net/EventLoop.hpp>
|
||||
#include <brynet/net/TcpService.hpp>
|
||||
#include <brynet/net/wrapper/ServiceBuilder.hpp>
|
||||
#include <brynet/base/AppStatus.hpp>
|
||||
|
||||
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: <listen port> <net work thread num>\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;
|
||||
}
|
2
libs/HEADS
Normal file
2
libs/HEADS
Normal file
@ -0,0 +1,2 @@
|
||||
RapidJSON 0ccdbf364c577803e2a751f5aededce935314313
|
||||
brynet 5ddb2cb2b46e5e7730327217932d0d3daf8e5b2a
|
3
libs/brynet/Version.hpp
Normal file
3
libs/brynet/Version.hpp
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define BRYNET_VERSION 1008000
|
30
libs/brynet/base/Any.hpp
Normal file
30
libs/brynet/base/Any.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/base/CPP_VERSION.hpp>
|
||||
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
#include <any>
|
||||
#else
|
||||
#include <cstdint>
|
||||
#endif
|
||||
|
||||
namespace brynet { namespace base {
|
||||
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
using BrynetAny = std::any;
|
||||
|
||||
template<typename T>
|
||||
auto cast(const BrynetAny& ud)
|
||||
{
|
||||
return std::any_cast<T>(&ud);
|
||||
}
|
||||
#else
|
||||
using BrynetAny = int64_t;
|
||||
template<typename T>
|
||||
const T* cast(const BrynetAny& ud)
|
||||
{
|
||||
return static_cast<const T*>(&ud);
|
||||
}
|
||||
#endif
|
||||
|
||||
} }
|
47
libs/brynet/base/AppStatus.hpp
Normal file
47
libs/brynet/base/AppStatus.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdbool>
|
||||
#include <cstdio>
|
||||
#include <signal.h>
|
||||
|
||||
#include <brynet/base/Platform.hpp>
|
||||
|
||||
#ifdef BRYNET_PLATFORM_WINDOWS
|
||||
#include <conio.h>
|
||||
#else
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#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
|
||||
}
|
||||
|
||||
} }
|
129
libs/brynet/base/Array.hpp
Normal file
129
libs/brynet/base/Array.hpp
Normal file
@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdbool>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} }
|
189
libs/brynet/base/Buffer.hpp
Normal file
189
libs/brynet/base/Buffer.hpp
Normal file
@ -0,0 +1,189 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdbool>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} }
|
16
libs/brynet/base/CPP_VERSION.hpp
Normal file
16
libs/brynet/base/CPP_VERSION.hpp
Normal file
@ -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
|
9
libs/brynet/base/Noexcept.hpp
Normal file
9
libs/brynet/base/Noexcept.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/base/CPP_VERSION.hpp>
|
||||
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
#define BRYNET_NOEXCEPT noexcept
|
||||
#else
|
||||
#define BRYNET_NOEXCEPT
|
||||
#endif
|
16
libs/brynet/base/NonCopyable.hpp
Normal file
16
libs/brynet/base/NonCopyable.hpp
Normal file
@ -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;
|
||||
};
|
||||
|
||||
} }
|
435
libs/brynet/base/Packet.hpp
Normal file
435
libs/brynet/base/Packet.hpp
Normal file
@ -0,0 +1,435 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <cstdbool>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/base/endian/Endian.hpp>
|
||||
|
||||
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<typename T>
|
||||
BasePacketWriter & operator << (const T& v)
|
||||
{
|
||||
static_assert(!std::is_pointer<T>::value, "T must is't a pointer");
|
||||
static_assert(std::is_class <T>::value, "T must a class or struct type");
|
||||
static_assert(std::is_pod <T>::value, "T must a pod type");
|
||||
writeBuffer((const char*)&v, sizeof(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Arg1, typename... Args>
|
||||
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<typename T>
|
||||
void read(T& value)
|
||||
{
|
||||
static_assert(std::is_same<T, typename std::remove_pointer<T>::type>::value,
|
||||
"T must a normal type");
|
||||
static_assert(std::is_pod<T>::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<size_t SIZE>
|
||||
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>;
|
||||
|
||||
} }
|
9
libs/brynet/base/Platform.hpp
Normal file
9
libs/brynet/base/Platform.hpp
Normal file
@ -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
|
175
libs/brynet/base/Stack.hpp
Normal file
175
libs/brynet/base/Stack.hpp
Normal file
@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdbool>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
#include <brynet/base/Array.hpp>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* stack的stack_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;
|
||||
}
|
||||
|
||||
} }
|
167
libs/brynet/base/Timer.hpp
Normal file
167
libs/brynet/base/Timer.hpp
Normal file
@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
|
||||
#include <brynet/base/Noexcept.hpp>
|
||||
|
||||
namespace brynet { namespace base {
|
||||
|
||||
class TimerMgr;
|
||||
|
||||
class Timer final
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Timer>;
|
||||
using WeakPtr = std::weak_ptr<Timer>;
|
||||
using Callback = std::function<void(void)>;
|
||||
|
||||
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<TimerMgr>;
|
||||
|
||||
template<typename F, typename ...TArgs>
|
||||
Timer::WeakPtr addTimer(
|
||||
std::chrono::nanoseconds timeout,
|
||||
F&& callback,
|
||||
TArgs&& ...args)
|
||||
{
|
||||
auto timer = std::make_shared<Timer>(
|
||||
std::chrono::steady_clock::now(),
|
||||
std::chrono::nanoseconds(timeout),
|
||||
std::bind(std::forward<F>(callback), std::forward<TArgs>(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<Timer::Ptr, std::vector<Timer::Ptr>, CompareTimer> mTimers;
|
||||
};
|
||||
|
||||
} }
|
65
libs/brynet/base/WaitGroup.hpp
Normal file
65
libs/brynet/base/WaitGroup.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
|
||||
namespace brynet { namespace base {
|
||||
|
||||
class WaitGroup : public NonCopyable
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<WaitGroup> Ptr;
|
||||
|
||||
static Ptr Create()
|
||||
{
|
||||
struct make_shared_enabler : public WaitGroup {};
|
||||
return std::make_shared<make_shared_enabler>();
|
||||
}
|
||||
|
||||
public:
|
||||
void add(int i = 1)
|
||||
{
|
||||
mCounter += i;
|
||||
}
|
||||
|
||||
void done()
|
||||
{
|
||||
mCounter--;
|
||||
mCond.notify_all();
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
mCond.wait(l, [&] { return mCounter <= 0; });
|
||||
}
|
||||
|
||||
template<class Rep, class Period>
|
||||
void wait(const std::chrono::duration<Rep, Period>& timeout)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
mCond.wait_for(l, timeout, [&] {
|
||||
return mCounter <= 0;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
WaitGroup()
|
||||
:
|
||||
mCounter(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~WaitGroup() = default;
|
||||
|
||||
private:
|
||||
std::mutex mMutex;
|
||||
std::atomic<int> mCounter;
|
||||
std::condition_variable mCond;
|
||||
};
|
||||
} }
|
105
libs/brynet/base/crypto/Base64.hpp
Normal file
105
libs/brynet/base/crypto/Base64.hpp
Normal file
@ -0,0 +1,105 @@
|
||||
#ifndef _BRYNET_BASE_BASE64_H
|
||||
#define _BRYNET_BASE_BASE64_H
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
496
libs/brynet/base/crypto/SHA1.hpp
Normal file
496
libs/brynet/base/crypto/SHA1.hpp
Normal file
@ -0,0 +1,496 @@
|
||||
/*
|
||||
100% free public domain implementation of the SHA-1 algorithm
|
||||
by Dominik Reichl <dominik.reichl@t-online.de>
|
||||
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 <memory.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef SHA1_UTILITY_FUNCTIONS
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#ifdef SHA1_STL_FUNCTIONS
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
#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 <tchar.h>
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
#include <tchar.h>
|
||||
#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<UINT_32>(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<UINT_8>((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<UINT_8>((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<TCHAR>& 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
|
140
libs/brynet/base/endian/Endian.hpp
Normal file
140
libs/brynet/base/endian/Endian.hpp
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdbool>
|
||||
#include <cstring>
|
||||
|
||||
#include <brynet/net/SocketLibTypes.hpp>
|
||||
|
||||
#ifdef BRYNET_PLATFORM_LINUX
|
||||
#include <endian.h>
|
||||
#elif defined BRYNET_PLATFORM_DARWIN
|
||||
#include <sys/_endian.h>
|
||||
#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
|
||||
|
||||
} } }
|
89
libs/brynet/net/AsyncConnector.hpp
Normal file
89
libs/brynet/net/AsyncConnector.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/detail/ConnectorDetail.hpp>
|
||||
|
||||
namespace brynet { namespace net {
|
||||
|
||||
class ConnectOption final
|
||||
{
|
||||
public:
|
||||
using CompletedCallback = std::function<void(TcpSocket::Ptr)>;
|
||||
using ProcessTcpSocketCallback = std::function<void(TcpSocket&)>;
|
||||
using FailedCallback = std::function<void()>;
|
||||
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<ConnectOptionFunc>& 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<AsyncConnector>
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<AsyncConnector>;
|
||||
|
||||
void startWorkerThread()
|
||||
{
|
||||
detail::AsyncConnectorDetail::startWorkerThread();
|
||||
}
|
||||
|
||||
void stopWorkerThread()
|
||||
{
|
||||
detail::AsyncConnectorDetail::stopWorkerThread();
|
||||
}
|
||||
|
||||
void asyncConnect(const std::vector<detail::ConnectOptionFunc>& options)
|
||||
{
|
||||
detail::AsyncConnectorDetail::asyncConnect(options);
|
||||
}
|
||||
|
||||
static Ptr Create()
|
||||
{
|
||||
class make_shared_enabler : public AsyncConnector {};
|
||||
return std::make_shared<make_shared_enabler>();
|
||||
}
|
||||
|
||||
private:
|
||||
AsyncConnector() = default;
|
||||
};
|
||||
|
||||
} }
|
20
libs/brynet/net/Channel.hpp
Normal file
20
libs/brynet/net/Channel.hpp
Normal file
@ -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;
|
||||
};
|
||||
|
||||
} }
|
55
libs/brynet/net/CurrentThread.hpp
Normal file
55
libs/brynet/net/CurrentThread.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/base/Platform.hpp>
|
||||
|
||||
#ifdef BRYNET_PLATFORM_WINDOWS
|
||||
#include <winsock2.h>
|
||||
#include <Windows.h>
|
||||
#elif defined BRYNET_PLATFORM_LINUX
|
||||
#include <unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/unistd.h>
|
||||
#elif defined BRYNET_PLATFORM_DARWIN
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#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<pid_t>(::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;
|
||||
}
|
||||
|
||||
} } }
|
444
libs/brynet/net/EventLoop.hpp
Normal file
444
libs/brynet/net/EventLoop.hpp
Normal file
@ -0,0 +1,444 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
#include <brynet/base/Timer.hpp>
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/base/Noexcept.hpp>
|
||||
#include <brynet/net/SocketLibFunction.hpp>
|
||||
#include <brynet/net/CurrentThread.hpp>
|
||||
|
||||
#include <brynet/net/Channel.hpp>
|
||||
#include <brynet/net/Socket.hpp>
|
||||
#include <brynet/net/Exception.hpp>
|
||||
#include <brynet/net/detail/WakeupChannel.hpp>
|
||||
|
||||
namespace brynet { namespace net {
|
||||
|
||||
class Channel;
|
||||
class TcpConnection;
|
||||
using TcpConnectionPtr = std::shared_ptr<TcpConnection>;
|
||||
|
||||
class EventLoop : public brynet::base::NonCopyable
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<EventLoop>;
|
||||
using UserFunctor = std::function<void(void)>;
|
||||
|
||||
public:
|
||||
EventLoop()
|
||||
BRYNET_NOEXCEPT
|
||||
:
|
||||
#ifdef BRYNET_PLATFORM_WINDOWS
|
||||
mIOCP(CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)),
|
||||
mWakeupChannel(std::make_unique<detail::WakeupChannel>(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<sGetQueuedCompletionStatusEx>(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<brynet::base::TimerMgr>();
|
||||
}
|
||||
|
||||
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<ULONG>(mEventEntries.size()),
|
||||
&numComplete,
|
||||
static_cast<DWORD>(milliseconds),
|
||||
false))
|
||||
{
|
||||
numComplete = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& e : mEventEntries)
|
||||
{
|
||||
const auto timeout = (numComplete == 0) ? static_cast<DWORD>(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<const port::Win::OverlappedExt*>(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<size_t>(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<std::chrono::milliseconds>(mTimer->nearLeftTime());
|
||||
milliseconds = std::min<int64_t>(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<brynet::base::Timer>(
|
||||
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<std::mutex> lck(mAsyncFunctorsMutex);
|
||||
assert(mCopyAsyncFunctors.empty());
|
||||
mCopyAsyncFunctors.swap(mAsyncFunctors);
|
||||
}
|
||||
void pushAsyncFunctor(UserFunctor&& f)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<OVERLAPPED_ENTRY> mEventEntries;
|
||||
|
||||
typedef BOOL(WINAPI *sGetQueuedCompletionStatusEx) (HANDLE, LPOVERLAPPED_ENTRY, ULONG, PULONG, DWORD, BOOL);
|
||||
sGetQueuedCompletionStatusEx mPGetQueuedCompletionStatusEx;
|
||||
HANDLE mIOCP;
|
||||
#elif defined BRYNET_PLATFORM_LINUX
|
||||
std::vector<epoll_event> mEventEntries;
|
||||
int mEpollFd;
|
||||
#elif defined BRYNET_PLATFORM_DARWIN
|
||||
std::vector<struct kevent> mEventEntries;
|
||||
int mKqueueFd;
|
||||
#endif
|
||||
std::unique_ptr<detail::WakeupChannel> mWakeupChannel;
|
||||
|
||||
std::atomic_bool mIsInBlock;
|
||||
std::atomic_bool mIsAlreadyPostWakeup;
|
||||
|
||||
std::mutex mAsyncFunctorsMutex;
|
||||
std::vector<UserFunctor> mAsyncFunctors;
|
||||
std::vector<UserFunctor> mCopyAsyncFunctors;
|
||||
|
||||
std::vector<UserFunctor> mAfterLoopFunctors;
|
||||
std::vector<UserFunctor> mCopyAfterLoopFunctors;
|
||||
|
||||
std::once_flag mOnceInitThreadID;
|
||||
current_thread::THREAD_ID_TYPE mSelfThreadID;
|
||||
|
||||
brynet::base::TimerMgr::Ptr mTimer;
|
||||
std::unordered_map<BrynetSocketFD, TcpConnectionPtr> mTcpConnections;
|
||||
|
||||
friend class TcpConnection;
|
||||
};
|
||||
|
||||
} }
|
41
libs/brynet/net/Exception.hpp
Normal file
41
libs/brynet/net/Exception.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} }
|
58
libs/brynet/net/ListenThread.hpp
Normal file
58
libs/brynet/net/ListenThread.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/detail/ListenThreadDetail.hpp>
|
||||
|
||||
namespace brynet { namespace net {
|
||||
|
||||
class ListenThread : public detail::ListenThreadDetail,
|
||||
public std::enable_shared_from_this<ListenThread>
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<ListenThread>;
|
||||
using AccepCallback = std::function<void(TcpSocket::Ptr)>;;
|
||||
using TcpSocketProcessCallback = std::function<void(TcpSocket&)>;
|
||||
|
||||
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<TcpSocketProcessCallback> & 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<TcpSocketProcessCallback>& processCallbacks)
|
||||
:
|
||||
ListenThread(isIPV6, ip, port, callback, processCallbacks)
|
||||
{}
|
||||
};
|
||||
return std::make_shared<make_shared_enabler>(isIPV6, ip, port, callback, processCallbacks);
|
||||
}
|
||||
|
||||
protected:
|
||||
ListenThread(bool isIPV6,
|
||||
const std::string& ip,
|
||||
int port,
|
||||
const AccepCallback& callback,
|
||||
const std::vector<TcpSocketProcessCallback>& processCallbacks)
|
||||
:
|
||||
detail::ListenThreadDetail(isIPV6, ip, port, callback, processCallbacks)
|
||||
{}
|
||||
};
|
||||
|
||||
} }
|
266
libs/brynet/net/Poller.hpp
Normal file
266
libs/brynet/net/Poller.hpp
Normal file
@ -0,0 +1,266 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstdbool>
|
||||
#include <string>
|
||||
|
||||
#include <brynet/net/SocketLibTypes.hpp>
|
||||
#include <brynet/base/Stack.hpp>
|
||||
|
||||
#if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
|
||||
#include <poll.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
} }
|
156
libs/brynet/net/PromiseReceive.hpp
Normal file
156
libs/brynet/net/PromiseReceive.hpp
Normal file
@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/TcpService.hpp>
|
||||
|
||||
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<PromiseReceive> setupPromiseReceive(const TcpConnection::Ptr& session);
|
||||
|
||||
class PromiseReceive : public std::enable_shared_from_this<PromiseReceive>
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<PromiseReceive>;
|
||||
using Handle = std::function<bool(const char* buffer, size_t len)>;
|
||||
|
||||
PromiseReceive::Ptr receive(size_t len, Handle handle)
|
||||
{
|
||||
return receive(std::make_shared<size_t>(len), std::move(handle));
|
||||
}
|
||||
|
||||
PromiseReceive::Ptr receive(std::shared_ptr<size_t> 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<size_t> len, std::string str, Handle handle)
|
||||
{
|
||||
auto pr = std::make_shared<PendingReceive>();
|
||||
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<size_t> len;
|
||||
std::string str;
|
||||
Handle handle;
|
||||
};
|
||||
|
||||
std::deque<std::shared_ptr<PendingReceive>> mPendingReceives;
|
||||
|
||||
friend std::shared_ptr<PromiseReceive> setupPromiseReceive(const TcpConnection::Ptr& session);
|
||||
};
|
||||
|
||||
std::shared_ptr<PromiseReceive> setupPromiseReceive(const TcpConnection::Ptr& session)
|
||||
{
|
||||
auto promiseReceive = std::make_shared<PromiseReceive>();
|
||||
session->setDataCallback([promiseReceive](brynet::base::BasePacketReader& reader) {
|
||||
auto procLen = promiseReceive->process(reader.begin(), reader.size());
|
||||
reader.addPos(procLen);
|
||||
reader.savePos();
|
||||
});
|
||||
|
||||
return promiseReceive;
|
||||
}
|
||||
|
||||
} }
|
174
libs/brynet/net/SSLHelper.hpp
Normal file
174
libs/brynet/net/SSLHelper.hpp
Normal file
@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/base/Platform.hpp>
|
||||
#include <brynet/base/Noexcept.hpp>
|
||||
|
||||
#ifdef BRYNET_USE_OPENSSL
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#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<unsigned long>(GetCurrentThreadId()));
|
||||
#elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN
|
||||
CRYPTO_THREADID_set_numeric(id,
|
||||
static_cast<unsigned long>(pthread_self()));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CRYPTO_set_locking_callback
|
||||
static std::unordered_map<int, std::shared_ptr<std::mutex>> 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<std::mutex>();
|
||||
}
|
||||
CRYPTO_set_locking_callback(cryptoLockingCallback);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
class SSLHelper : public brynet::base::NonCopyable,
|
||||
public std::enable_shared_from_this<SSLHelper>
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<SSLHelper>;
|
||||
|
||||
#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<make_shared_enabler>();
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
} }
|
230
libs/brynet/net/Socket.hpp
Normal file
230
libs/brynet/net/Socket.hpp
Normal file
@ -0,0 +1,230 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/net/SocketLibFunction.hpp>
|
||||
|
||||
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<TcpSocket, TcpSocketDeleter>;
|
||||
|
||||
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<ListenSocket, ListenSocketDeleter>;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} }
|
329
libs/brynet/net/SocketLibFunction.hpp
Normal file
329
libs/brynet/net/SocketLibFunction.hpp
Normal file
@ -0,0 +1,329 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <brynet/net/SocketLibTypes.hpp>
|
||||
|
||||
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<socklen_t>(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<socklen_t>(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<struct sockaddr_in*>(&localaddr);
|
||||
const struct sockaddr_in* raddr4 = reinterpret_cast<struct sockaddr_in*>(&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;
|
||||
}
|
||||
}
|
||||
|
||||
} } }
|
71
libs/brynet/net/SocketLibTypes.hpp
Normal file
71
libs/brynet/net/SocketLibTypes.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/base/Platform.hpp>
|
||||
|
||||
#ifdef BRYNET_PLATFORM_WINDOWS
|
||||
#include <winsock2.h>
|
||||
#include <WinError.h>
|
||||
#include <winsock.h>
|
||||
#include <Ws2tcpip.h>
|
||||
#include <errno.h>
|
||||
|
||||
#elif defined BRYNET_PLATFORM_LINUX
|
||||
#include <signal.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#elif defined BRYNET_PLATFORM_DARWIN
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/event.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#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
|
1196
libs/brynet/net/TcpConnection.hpp
Normal file
1196
libs/brynet/net/TcpConnection.hpp
Normal file
File diff suppressed because it is too large
Load Diff
91
libs/brynet/net/TcpService.hpp
Normal file
91
libs/brynet/net/TcpService.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/detail/TCPServiceDetail.hpp>
|
||||
|
||||
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<TcpService>
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<TcpService>;
|
||||
using FrameCallback = detail::TcpServiceDetail::FrameCallback;
|
||||
|
||||
public:
|
||||
static Ptr Create()
|
||||
{
|
||||
struct make_shared_enabler : public TcpService {};
|
||||
return std::make_shared<make_shared_enabler>();
|
||||
}
|
||||
|
||||
void startWorkerThread(size_t threadNum,
|
||||
FrameCallback callback = nullptr)
|
||||
{
|
||||
detail::TcpServiceDetail::startWorkerThread(threadNum, callback);
|
||||
}
|
||||
|
||||
void stopWorkerThread()
|
||||
{
|
||||
detail::TcpServiceDetail::stopWorkerThread();
|
||||
}
|
||||
|
||||
template<typename... Options>
|
||||
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;
|
||||
};
|
||||
|
||||
} }
|
27
libs/brynet/net/detail/AddSocketOptionInfo.hpp
Normal file
27
libs/brynet/net/detail/AddSocketOptionInfo.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/TcpConnection.hpp>
|
||||
#include <brynet/net/SSLHelper.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace detail {
|
||||
|
||||
class AddSocketOptionInfo final
|
||||
{
|
||||
public:
|
||||
AddSocketOptionInfo()
|
||||
{
|
||||
useSSL = false;
|
||||
forceSameThreadLoop = false;
|
||||
maxRecvBufferSize = 128;
|
||||
}
|
||||
|
||||
std::vector<TcpConnection::EnterCallback> enterCallback;
|
||||
SSLHelper::Ptr sslHelper;
|
||||
bool useSSL;
|
||||
bool forceSameThreadLoop;
|
||||
size_t maxRecvBufferSize;
|
||||
};
|
||||
|
||||
using AddSocketOptionFunc = std::function<void(AddSocketOptionInfo& option)>;
|
||||
|
||||
} } }
|
161
libs/brynet/net/detail/ConnectorDetail.hpp
Normal file
161
libs/brynet/net/detail/ConnectorDetail.hpp
Normal file
@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/base/CPP_VERSION.hpp>
|
||||
#include <brynet/base/Any.hpp>
|
||||
#include <brynet/base/Noexcept.hpp>
|
||||
#include <brynet/net/SocketLibFunction.hpp>
|
||||
#include <brynet/net/Poller.hpp>
|
||||
#include <brynet/net/Exception.hpp>
|
||||
#include <brynet/net/EventLoop.hpp>
|
||||
#include <brynet/net/Socket.hpp>
|
||||
#include <brynet/net/detail/ConnectorWorkInfo.hpp>
|
||||
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
#include <shared_mutex>
|
||||
#else
|
||||
#include <mutex>
|
||||
#endif
|
||||
|
||||
namespace brynet { namespace net { namespace detail {
|
||||
|
||||
class AsyncConnectorDetail : public brynet::base::NonCopyable
|
||||
{
|
||||
protected:
|
||||
void startWorkerThread()
|
||||
{
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
std::lock_guard<std::shared_mutex> lck(mThreadGuard);
|
||||
#else
|
||||
std::lock_guard<std::mutex> lck(mThreadGuard);
|
||||
#endif
|
||||
|
||||
if (mThread != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mIsRun = std::make_shared<bool>(true);
|
||||
mWorkInfo = std::make_shared<detail::ConnectorWorkInfo>();
|
||||
mEventLoop = std::make_shared<EventLoop>();
|
||||
|
||||
auto eventLoop = mEventLoop;
|
||||
auto workerInfo = mWorkInfo;
|
||||
auto isRun = mIsRun;
|
||||
|
||||
mThread = std::make_shared<std::thread>([eventLoop, workerInfo, isRun]() {
|
||||
while (*isRun)
|
||||
{
|
||||
detail::RunOnceCheckConnect(eventLoop, workerInfo);
|
||||
}
|
||||
|
||||
workerInfo->causeAllFailed();
|
||||
});
|
||||
}
|
||||
|
||||
void stopWorkerThread()
|
||||
{
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
std::lock_guard<std::shared_mutex> lck(mThreadGuard);
|
||||
#else
|
||||
std::lock_guard<std::mutex> 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<detail::ConnectOptionFunc>& options)
|
||||
{
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
std::shared_lock<std::shared_mutex> lck(mThreadGuard);
|
||||
#else
|
||||
std::lock_guard<std::mutex> 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<bool>(false);
|
||||
}
|
||||
|
||||
virtual ~AsyncConnectorDetail()
|
||||
{
|
||||
stopWorkerThread();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<EventLoop> mEventLoop;
|
||||
|
||||
std::shared_ptr<detail::ConnectorWorkInfo> mWorkInfo;
|
||||
std::shared_ptr<std::thread> mThread;
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
std::shared_mutex mThreadGuard;
|
||||
#else
|
||||
std::mutex mThreadGuard;
|
||||
#endif
|
||||
std::shared_ptr<bool> mIsRun;
|
||||
};
|
||||
|
||||
} } }
|
368
libs/brynet/net/detail/ConnectorWorkInfo.hpp
Normal file
368
libs/brynet/net/detail/ConnectorWorkInfo.hpp
Normal file
@ -0,0 +1,368 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/base/CPP_VERSION.hpp>
|
||||
#include <brynet/net/SocketLibTypes.hpp>
|
||||
#include <brynet/net/Socket.hpp>
|
||||
#include <brynet/net/Poller.hpp>
|
||||
|
||||
#ifdef BRYNET_HAVE_LANG_CXX17
|
||||
#include <shared_mutex>
|
||||
#else
|
||||
#include <mutex>
|
||||
#endif
|
||||
|
||||
namespace brynet { namespace net { namespace detail {
|
||||
|
||||
class ConnectOptionsInfo;
|
||||
using ConnectOptionFunc = std::function<void(ConnectOptionsInfo & option)>;
|
||||
|
||||
class AsyncConnectAddr final
|
||||
{
|
||||
public:
|
||||
using CompletedCallback = std::function<void(TcpSocket::Ptr)>;
|
||||
using ProcessTcpSocketCallback = std::function<void(TcpSocket&)>;
|
||||
using FailedCallback = std::function<void()>;
|
||||
|
||||
public:
|
||||
AsyncConnectAddr(std::string&& ip,
|
||||
int port,
|
||||
std::chrono::nanoseconds timeout,
|
||||
CompletedCallback&& successCB,
|
||||
FailedCallback&& failedCB,
|
||||
std::vector<ProcessTcpSocketCallback>&& 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<ProcessTcpSocketCallback>& 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<ProcessTcpSocketCallback> mProcessCallbacks;
|
||||
};
|
||||
|
||||
class ConnectorWorkInfo final : public brynet::base::NonCopyable
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<ConnectorWorkInfo>;
|
||||
|
||||
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<BrynetSocketFD> totalFds;
|
||||
std::set<BrynetSocketFD> 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<decltype(server_addr.sin_port)>(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<AsyncConnectAddr::ProcessTcpSocketCallback> processCallbacks;
|
||||
};
|
||||
|
||||
std::map<BrynetSocketFD, ConnectingInfo> 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<struct brynet::base::poller_s, PollerDeleter> mPoller;
|
||||
std::unique_ptr<struct brynet::base::stack_s, StackDeleter> mPollResult;
|
||||
};
|
||||
|
||||
static void RunOnceCheckConnect(
|
||||
const std::shared_ptr<brynet::net::EventLoop>& eventLoop,
|
||||
const std::shared_ptr<ConnectorWorkInfo>& 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<AsyncConnectAddr::ProcessTcpSocketCallback> processCallbacks;
|
||||
AsyncConnectAddr::CompletedCallback completedCallback;
|
||||
AsyncConnectAddr::FailedCallback faledCallback;
|
||||
};
|
||||
|
||||
} } }
|
67
libs/brynet/net/detail/IOLoopData.hpp
Normal file
67
libs/brynet/net/detail/IOLoopData.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/net/EventLoop.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace detail {
|
||||
|
||||
class TcpServiceDetail;
|
||||
|
||||
class IOLoopData : public brynet::base::NonCopyable,
|
||||
public std::enable_shared_from_this<IOLoopData>
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<IOLoopData>;
|
||||
|
||||
static Ptr Create(EventLoop::Ptr eventLoop,
|
||||
std::shared_ptr<std::thread> ioThread)
|
||||
{
|
||||
class make_shared_enabler : public IOLoopData
|
||||
{
|
||||
public:
|
||||
make_shared_enabler(EventLoop::Ptr eventLoop,
|
||||
std::shared_ptr<std::thread> ioThread)
|
||||
:
|
||||
IOLoopData(std::move(eventLoop), std::move(ioThread))
|
||||
{}
|
||||
};
|
||||
|
||||
return std::make_shared<make_shared_enabler>(std::move(eventLoop),
|
||||
std::move(ioThread));
|
||||
}
|
||||
|
||||
const EventLoop::Ptr& getEventLoop() const
|
||||
{
|
||||
return mEventLoop;
|
||||
}
|
||||
|
||||
protected:
|
||||
const std::shared_ptr<std::thread>& getIOThread() const
|
||||
{
|
||||
return mIOThread;
|
||||
}
|
||||
|
||||
IOLoopData(EventLoop::Ptr eventLoop,
|
||||
std::shared_ptr<std::thread> ioThread)
|
||||
:
|
||||
mEventLoop(std::move(eventLoop)),
|
||||
mIOThread(std::move(ioThread))
|
||||
{}
|
||||
virtual ~IOLoopData() = default;
|
||||
|
||||
const EventLoop::Ptr mEventLoop;
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::thread> mIOThread;
|
||||
|
||||
friend class TcpServiceDetail;
|
||||
};
|
||||
|
||||
using IOLoopDataPtr = std::shared_ptr<IOLoopData>;
|
||||
|
||||
} } }
|
169
libs/brynet/net/detail/ListenThreadDetail.hpp
Normal file
169
libs/brynet/net/detail/ListenThreadDetail.hpp
Normal file
@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/base/Noexcept.hpp>
|
||||
#include <brynet/net/SocketLibFunction.hpp>
|
||||
#include <brynet/net/AsyncConnector.hpp>
|
||||
#include <brynet/net/wrapper/ConnectionBuilder.hpp>
|
||||
#include <brynet/net/Socket.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace detail {
|
||||
|
||||
class ListenThreadDetail : public brynet::base::NonCopyable
|
||||
{
|
||||
protected:
|
||||
using AccepCallback = std::function<void(TcpSocket::Ptr)>;
|
||||
using TcpSocketProcessCallback = std::function<void(TcpSocket&)>;
|
||||
|
||||
void startListen()
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<bool>(true);
|
||||
|
||||
auto listenSocket = std::shared_ptr<ListenSocket>(ListenSocket::Create(fd));
|
||||
auto isRunListen = mRunListen;
|
||||
auto callback = mCallback;
|
||||
auto processCallbacks = mProcessCallbacks;
|
||||
mListenThread = std::make_shared<std::thread>(
|
||||
[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<std::mutex> 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<TcpSocketProcessCallback>& processCallbacks)
|
||||
:
|
||||
mIsIPV6(isIPV6),
|
||||
mIP(ip),
|
||||
mPort(port),
|
||||
mCallback(callback),
|
||||
mProcessCallbacks(processCallbacks)
|
||||
{
|
||||
if (mCallback == nullptr)
|
||||
{
|
||||
throw BrynetCommonException("accept callback is nullptr");
|
||||
}
|
||||
mRunListen = std::make_shared<bool>(false);
|
||||
}
|
||||
|
||||
virtual ~ListenThreadDetail() BRYNET_NOEXCEPT
|
||||
{
|
||||
stopListen();
|
||||
}
|
||||
|
||||
private:
|
||||
static brynet::net::TcpSocket::Ptr runOnceListen(const std::shared_ptr<ListenSocket>& 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<TcpSocketProcessCallback> mProcessCallbacks;
|
||||
|
||||
std::shared_ptr<bool> mRunListen;
|
||||
std::shared_ptr<std::thread> mListenThread;
|
||||
std::mutex mListenThreadGuard;
|
||||
};
|
||||
|
||||
} } }
|
198
libs/brynet/net/detail/TCPServiceDetail.hpp
Normal file
198
libs/brynet/net/detail/TCPServiceDetail.hpp
Normal file
@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/base/Noexcept.hpp>
|
||||
#include <brynet/net/TcpConnection.hpp>
|
||||
#include <brynet/net/SSLHelper.hpp>
|
||||
#include <brynet/net/Socket.hpp>
|
||||
#include <brynet/net/detail/IOLoopData.hpp>
|
||||
#include <brynet/net/detail/AddSocketOptionInfo.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace detail {
|
||||
|
||||
class TcpServiceDetail : public brynet::base::NonCopyable
|
||||
{
|
||||
protected:
|
||||
using FrameCallback = std::function<void(const EventLoop::Ptr&)>;
|
||||
const static unsigned int sDefaultLoopTimeOutMS = 100;
|
||||
|
||||
void startWorkerThread(size_t threadNum,
|
||||
FrameCallback callback = nullptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(mServiceGuard);
|
||||
std::lock_guard<std::mutex> lock(mIOLoopGuard);
|
||||
|
||||
if (!mIOLoopDatas.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mRunIOLoop = std::make_shared<bool>(true);
|
||||
|
||||
mIOLoopDatas.resize(threadNum);
|
||||
for (auto& v : mIOLoopDatas)
|
||||
{
|
||||
auto eventLoop = std::make_shared<EventLoop>();
|
||||
auto runIoLoop = mRunIOLoop;
|
||||
v = IOLoopData::Create(eventLoop,
|
||||
std::make_shared<std::thread>(
|
||||
[callback, runIoLoop, eventLoop]() {
|
||||
while (*runIoLoop)
|
||||
{
|
||||
eventLoop->loopCompareNearTimer(sDefaultLoopTimeOutMS);
|
||||
if (callback != nullptr)
|
||||
{
|
||||
callback(eventLoop);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void stopWorkerThread()
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(mServiceGuard);
|
||||
std::lock_guard<std::mutex> 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<typename... Options>
|
||||
bool addTcpConnection(TcpSocket::Ptr socket,
|
||||
const Options& ... options)
|
||||
{
|
||||
return _addTcpConnection(std::move(socket), { options... });
|
||||
}
|
||||
|
||||
EventLoop::Ptr getRandomEventLoop()
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<unsigned int>(
|
||||
std::chrono::system_clock::now().time_since_epoch().count()))
|
||||
{
|
||||
mRunIOLoop = std::make_shared<bool>(false);
|
||||
}
|
||||
|
||||
virtual ~TcpServiceDetail() BRYNET_NOEXCEPT
|
||||
{
|
||||
stopWorkerThread();
|
||||
}
|
||||
|
||||
bool _addTcpConnection(TcpSocket::Ptr socket,
|
||||
const std::vector<AddSocketOptionFunc>& 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<std::mutex> lock(mIOLoopGuard);
|
||||
for (const auto& v : mIOLoopDatas)
|
||||
{
|
||||
if (v->getEventLoop()->isInLoopThread())
|
||||
{
|
||||
return v->getEventLoop();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<IOLoopDataPtr> mIOLoopDatas;
|
||||
mutable std::mutex mIOLoopGuard;
|
||||
std::shared_ptr<bool> mRunIOLoop;
|
||||
|
||||
std::mutex mServiceGuard;
|
||||
std::mt19937 mRandom;
|
||||
};
|
||||
|
||||
} } }
|
139
libs/brynet/net/detail/WakeupChannel.hpp
Normal file
139
libs/brynet/net/detail/WakeupChannel.hpp
Normal file
@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/net/SocketLibFunction.hpp>
|
||||
#ifdef BRYNET_PLATFORM_WINDOWS
|
||||
#include <brynet/net/port/Win.hpp>
|
||||
#endif
|
||||
|
||||
#include <brynet/net/Channel.hpp>
|
||||
#include <brynet/net/Socket.hpp>
|
||||
|
||||
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<ULONG_PTR>(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<size_t>(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
|
||||
|
||||
} } }
|
222
libs/brynet/net/http/HttpFormat.hpp
Normal file
222
libs/brynet/net/http/HttpFormat.hpp
Normal file
@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
|
||||
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<size_t>(HTTP_METHOD::HTTP_METHOD_MAX);
|
||||
const static std::array<std::string, MethodMax> 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<size_t>(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<std::string, std::string> 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<int>(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<std::string, std::string> mHeadField;
|
||||
std::string mBody;
|
||||
};
|
||||
|
||||
} } }
|
318
libs/brynet/net/http/HttpParser.hpp
Normal file
318
libs/brynet/net/http/HttpParser.hpp
Normal file
@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "http_parser.h"
|
||||
#include <brynet/net/http/WebSocketFormat.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace http {
|
||||
|
||||
class HttpService;
|
||||
|
||||
class HTTPParser
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HTTPParser>;
|
||||
|
||||
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<std::string, std::string> mHeadValues;
|
||||
int mStatusCode;
|
||||
|
||||
std::string mUrl;
|
||||
std::string mBody;
|
||||
|
||||
std::string mWSCacheFrame;
|
||||
std::string mWSParsePayload;
|
||||
WebSocketFormat::WebSocketFrameType mWSFrameType;
|
||||
|
||||
private:
|
||||
friend class HttpService;
|
||||
};
|
||||
|
||||
} } }
|
342
libs/brynet/net/http/HttpService.hpp
Normal file
342
libs/brynet/net/http/HttpService.hpp
Normal file
@ -0,0 +1,342 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <brynet/base/NonCopyable.hpp>
|
||||
#include <brynet/base/Any.hpp>
|
||||
#include <brynet/net/TcpService.hpp>
|
||||
#include <brynet/net/http/HttpParser.hpp>
|
||||
#include <brynet/net/http/WebSocketFormat.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace http {
|
||||
|
||||
class HttpService;
|
||||
class HttpSessionHandlers;
|
||||
|
||||
class HttpSession : public brynet::base::NonCopyable
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HttpSession>;
|
||||
|
||||
using EnterCallback = std::function <void(const HttpSession::Ptr&, HttpSessionHandlers&)>;
|
||||
using HttpParserCallback = std::function <void(const HTTPParser&, const HttpSession::Ptr&)>;
|
||||
using WsCallback = std::function < void( const HttpSession::Ptr&,
|
||||
WebSocketFormat::WebSocketFrameType opcode,
|
||||
const std::string& payload)>;
|
||||
|
||||
using ClosedCallback = std::function <void(const HttpSession::Ptr&)>;
|
||||
using WsConnectedCallback = std::function <void(const HttpSession::Ptr&, const HTTPParser&)>;
|
||||
|
||||
public:
|
||||
template<typename PacketType>
|
||||
void send(PacketType&& packet,
|
||||
TcpConnection::PacketSendedCallback&& callback = nullptr)
|
||||
{
|
||||
mSession->send(std::forward<std::shared_ptr<std::string>>(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<make_shared_enabler>(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<HTTPParser>(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;
|
||||
}
|
||||
};
|
||||
|
||||
} } }
|
239
libs/brynet/net/http/WebSocketFormat.hpp
Normal file
239
libs/brynet/net/http/WebSocketFormat.hpp
Normal file
@ -0,0 +1,239 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
#include <brynet/base/crypto/Base64.hpp>
|
||||
#include <brynet/base/crypto/SHA1.hpp>
|
||||
|
||||
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<unsigned int>(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<unsigned int>(unixTime));
|
||||
|
||||
static_assert(std::is_same<std::string::value_type, char>::value, "");
|
||||
|
||||
const uint8_t head = static_cast<uint8_t>(frame_type) | (isFin ? 0x80 : 0x00);
|
||||
|
||||
frame.clear();
|
||||
frame.push_back(static_cast<char>(head));
|
||||
if (payloadLen <= 125)
|
||||
{
|
||||
// mask << 7 | payloadLen, mask = 0
|
||||
frame.push_back(static_cast<uint8_t>(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<char>((payloadLen & 0xFF000000) >> 24));
|
||||
frame.push_back(static_cast<char>((payloadLen & 0x00FF0000) >> 16));
|
||||
frame.push_back(static_cast<char>((payloadLen & 0x0000FF00) >> 8));
|
||||
frame.push_back(static_cast<char>(payloadLen & 0x000000FF));
|
||||
}
|
||||
|
||||
if (masking)
|
||||
{
|
||||
frame[1] = ((uint8_t)frame[1]) | 0x80;
|
||||
uint8_t mask[4];
|
||||
for (auto& m : mask)
|
||||
{
|
||||
m = static_cast<uint8_t>(random());
|
||||
frame.push_back(m);
|
||||
}
|
||||
|
||||
frame.reserve(frame.size() + payloadLen);
|
||||
|
||||
for (size_t i = 0; i < payloadLen; i++)
|
||||
{
|
||||
frame.push_back(static_cast<uint8_t>(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);
|
||||
}
|
||||
};
|
||||
|
||||
} } }
|
3206
libs/brynet/net/http/http_parser.h
Normal file
3206
libs/brynet/net/http/http_parser.h
Normal file
File diff suppressed because it is too large
Load Diff
31
libs/brynet/net/port/Win.hpp
Normal file
31
libs/brynet/net/port/Win.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/SocketLibTypes.hpp>
|
||||
|
||||
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
|
||||
|
||||
} } }
|
191
libs/brynet/net/wrapper/ConnectionBuilder.hpp
Normal file
191
libs/brynet/net/wrapper/ConnectionBuilder.hpp
Normal file
@ -0,0 +1,191 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <brynet/net/TcpService.hpp>
|
||||
#include <brynet/net/AsyncConnector.hpp>
|
||||
#include <brynet/net/Exception.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace wrapper {
|
||||
|
||||
template<typename Derived>
|
||||
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<Derived&>(*this);
|
||||
}
|
||||
|
||||
Derived& configureConnectOptions(
|
||||
std::vector<ConnectOptionFunc> options)
|
||||
{
|
||||
mConnectOptions = std::move(options);
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
void asyncConnect() const
|
||||
{
|
||||
asyncConnect(mConnectOptions);
|
||||
}
|
||||
|
||||
TcpSocket::Ptr syncConnect() const
|
||||
{
|
||||
return syncConnect(mConnectOptions);
|
||||
}
|
||||
|
||||
protected:
|
||||
void asyncConnect(std::vector<ConnectOptionFunc> 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<ConnectOptionFunc> connectOptions) const
|
||||
{
|
||||
auto timeout = ConnectOption::ExtractTimeout(connectOptions);
|
||||
|
||||
auto socketPromise = std::make_shared<std::promise<TcpSocket::Ptr>>();
|
||||
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<ConnectOptionFunc> getConnectOptions() const
|
||||
{
|
||||
return mConnectOptions;
|
||||
}
|
||||
|
||||
private:
|
||||
AsyncConnector::Ptr mConnector;
|
||||
std::vector<ConnectOptionFunc> mConnectOptions;
|
||||
};
|
||||
|
||||
class SocketConnectBuilder : public BaseSocketConnectBuilder<SocketConnectBuilder>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
class BaseConnectionBuilder : public BaseSocketConnectBuilder<Derived>
|
||||
{
|
||||
protected:
|
||||
using AddSocketOptionFunc = detail::AddSocketOptionFunc;
|
||||
using ConnectOptionFunc = detail::ConnectOptionFunc;
|
||||
|
||||
public:
|
||||
Derived& configureService(TcpService::Ptr service)
|
||||
{
|
||||
mTcpService = std::move(service);
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
Derived& configureConnectionOptions(std::vector<AddSocketOptionFunc> options)
|
||||
{
|
||||
mConnectionOptions = std::move(options);
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
void asyncConnect() const
|
||||
{
|
||||
asyncConnect(BaseSocketConnectBuilder<Derived>::getConnectOptions(),
|
||||
mConnectionOptions);
|
||||
}
|
||||
|
||||
TcpConnection::Ptr syncConnect() const
|
||||
{
|
||||
return syncConnect(BaseSocketConnectBuilder<Derived>::getConnectOptions(),
|
||||
mConnectionOptions);
|
||||
}
|
||||
|
||||
protected:
|
||||
void asyncConnect(std::vector<ConnectOptionFunc> connectOptions,
|
||||
std::vector<AddSocketOptionFunc> 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<Derived>::asyncConnect(connectOptions);
|
||||
}
|
||||
|
||||
TcpConnection::Ptr syncConnect(std::vector<ConnectOptionFunc> connectOptions,
|
||||
std::vector<AddSocketOptionFunc> connectionOptions) const
|
||||
{
|
||||
auto timeout = ConnectOption::ExtractTimeout(connectOptions);
|
||||
auto sessionPromise = std::make_shared<std::promise<TcpConnection::Ptr>>();
|
||||
|
||||
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<AddSocketOptionFunc> getConnectionOptions() const
|
||||
{
|
||||
return mConnectionOptions;
|
||||
}
|
||||
|
||||
private:
|
||||
TcpService::Ptr mTcpService;
|
||||
std::vector<AddSocketOptionFunc> mConnectionOptions;
|
||||
};
|
||||
|
||||
class ConnectionBuilder : public BaseConnectionBuilder<ConnectionBuilder>
|
||||
{
|
||||
};
|
||||
|
||||
} } }
|
44
libs/brynet/net/wrapper/HttpConnectionBuilder.hpp
Normal file
44
libs/brynet/net/wrapper/HttpConnectionBuilder.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/http/HttpService.hpp>
|
||||
#include <brynet/net/wrapper/ConnectionBuilder.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace wrapper {
|
||||
|
||||
class HttpConnectionBuilder : public BaseConnectionBuilder<HttpConnectionBuilder>
|
||||
{
|
||||
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<HttpConnectionBuilder>::getConnectionOptions();
|
||||
auto callback = mHttpEnterCallback;
|
||||
|
||||
connectionOptions.push_back(
|
||||
AddSocketOption::AddEnterCallback(
|
||||
[callback](const TcpConnection::Ptr& session) {
|
||||
http::HttpService::setup(session, callback);
|
||||
}));
|
||||
|
||||
BaseConnectionBuilder<HttpConnectionBuilder>::asyncConnect(
|
||||
BaseConnectionBuilder<HttpConnectionBuilder>::getConnectOptions(),
|
||||
connectionOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
http::HttpSession::EnterCallback mHttpEnterCallback;
|
||||
};
|
||||
|
||||
} } }
|
39
libs/brynet/net/wrapper/HttpServiceBuilder.hpp
Normal file
39
libs/brynet/net/wrapper/HttpServiceBuilder.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/http/HttpService.hpp>
|
||||
#include <brynet/net/wrapper/ServiceBuilder.hpp>
|
||||
|
||||
namespace brynet { namespace net { namespace wrapper {
|
||||
|
||||
class HttpListenerBuilder : public BaseListenerBuilder<HttpListenerBuilder>
|
||||
{
|
||||
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<HttpListenerBuilder>::getConnectionOptions();
|
||||
auto callback = mHttpEnterCallback;
|
||||
connectionOptions.push_back(
|
||||
AddSocketOption::AddEnterCallback(
|
||||
[callback](const TcpConnection::Ptr& session) {
|
||||
http::HttpService::setup(session, callback);
|
||||
}));
|
||||
BaseListenerBuilder<HttpListenerBuilder>::asyncRun(connectionOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
http::HttpSession::EnterCallback mHttpEnterCallback;
|
||||
};
|
||||
|
||||
} } }
|
167
libs/brynet/net/wrapper/ServiceBuilder.hpp
Normal file
167
libs/brynet/net/wrapper/ServiceBuilder.hpp
Normal file
@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
#include <brynet/net/TcpService.hpp>
|
||||
#include <brynet/net/ListenThread.hpp>
|
||||
#include <brynet/net/Exception.hpp>
|
||||
|
||||
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<typename Derived>
|
||||
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<Derived&>(*this);
|
||||
}
|
||||
|
||||
Derived& configureSocketOptions(std::vector<ListenThread::TcpSocketProcessCallback> options)
|
||||
{
|
||||
mSocketOptions = std::move(options);
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
Derived& configureConnectionOptions(std::vector<AddSocketOptionFunc> options)
|
||||
{
|
||||
mConnectionOptions = std::move(options);
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
template<typename BuilderFunc>
|
||||
Derived& configureListen(const BuilderFunc& builder)
|
||||
{
|
||||
BuildListenConfig buildConfig(&mListenConfig);
|
||||
builder(buildConfig);
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
void asyncRun()
|
||||
{
|
||||
asyncRun(getConnectionOptions());
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (mListenThread)
|
||||
{
|
||||
mListenThread->stopListen();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<AddSocketOptionFunc> getConnectionOptions() const
|
||||
{
|
||||
return mConnectionOptions;
|
||||
}
|
||||
|
||||
protected:
|
||||
void asyncRun(std::vector<AddSocketOptionFunc> 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<ListenThread::TcpSocketProcessCallback> mSocketOptions;
|
||||
ListenConfig mListenConfig;
|
||||
ListenThread::Ptr mListenThread;
|
||||
|
||||
private:
|
||||
std::vector<AddSocketOptionFunc> mConnectionOptions;
|
||||
};
|
||||
|
||||
class ListenerBuilder : public BaseListenerBuilder<ListenerBuilder>
|
||||
{
|
||||
};
|
||||
|
||||
} } }
|
284
libs/rapidjson/allocators.h
Normal file
284
libs/rapidjson/allocators.h
Normal file
@ -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 <typename BaseAllocator = CrtAllocator>
|
||||
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<ChunkHeader*>(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<char *>(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<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
|
||||
size_t increment = static_cast<size_t>(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<ChunkHeader*>(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_
|
78
libs/rapidjson/cursorstreamwrapper.h
Normal file
78
libs/rapidjson/cursorstreamwrapper.h
Normal file
@ -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 <typename InputStream, typename Encoding = UTF8<> >
|
||||
class CursorStreamWrapper : public GenericStreamWrapper<InputStream, Encoding> {
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
CursorStreamWrapper(InputStream& is):
|
||||
GenericStreamWrapper<InputStream, Encoding>(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_
|
2732
libs/rapidjson/document.h
Normal file
2732
libs/rapidjson/document.h
Normal file
File diff suppressed because it is too large
Load Diff
299
libs/rapidjson/encodedstream.h
Normal file
299
libs/rapidjson/encodedstream.h
Normal file
@ -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 <typename Encoding, typename InputByteStream>
|
||||
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<UTF8<>, MemoryStream> {
|
||||
public:
|
||||
typedef UTF8<>::Ch Ch;
|
||||
|
||||
EncodedInputStream(MemoryStream& is) : is_(is) {
|
||||
if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
|
||||
if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
|
||||
if (static_cast<unsigned char>(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 <typename Encoding, typename OutputByteStream>
|
||||
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<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::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 <typename CharType, typename InputByteStream>
|
||||
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<const unsigned char *>(is_->Peek4());
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
unsigned bom = static_cast<unsigned>(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 <typename CharType, typename OutputByteStream>
|
||||
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_
|
716
libs/rapidjson/encodings.h
Normal file
716
libs/rapidjson/encodings.h
Normal file
@ -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<typename OutputStream>
|
||||
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 <typename InputStream>
|
||||
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 <typename InputStream, typename OutputStream>
|
||||
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 <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is);
|
||||
|
||||
//! Take a character from input byte stream.
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is);
|
||||
|
||||
//! Put BOM to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os);
|
||||
|
||||
//! Put a character to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
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<typename CharType = char>
|
||||
struct UTF8 {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
|
||||
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
||||
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
|
||||
typename InputStream::Ch c = is.Take();
|
||||
if (!(c & 0x80)) {
|
||||
*codepoint = static_cast<unsigned char>(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char type = GetRange(static_cast<unsigned char>(c));
|
||||
if (type >= 32) {
|
||||
*codepoint = 0;
|
||||
} else {
|
||||
*codepoint = (0xFFu >> type) & static_cast<unsigned char>(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 <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
#define RAPIDJSON_COPY() os.Put(c = is.Take())
|
||||
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(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<unsigned char>(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 <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
typename InputByteStream::Ch c = Take(is);
|
||||
if (static_cast<unsigned char>(c) != 0xEFu) return c;
|
||||
c = is.Take();
|
||||
if (static_cast<unsigned char>(c) != 0xBBu) return c;
|
||||
c = is.Take();
|
||||
if (static_cast<unsigned char>(c) != 0xBFu) return c;
|
||||
c = is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return static_cast<Ch>(is.Take());
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(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<typename CharType = wchar_t>
|
||||
struct UTF16 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
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<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename OutputStream>
|
||||
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<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
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<unsigned>(c);
|
||||
return true;
|
||||
}
|
||||
else if (c <= 0xDBFF) {
|
||||
*codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
|
||||
c = is.Take();
|
||||
*codepoint |= (static_cast<unsigned>(c) & 0x3FF);
|
||||
*codepoint += 0x10000;
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
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<typename OutputStream::Ch>(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<typename CharType = wchar_t>
|
||||
struct UTF16LE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<uint8_t>(is.Take());
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 big endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16BE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(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<typename CharType = unsigned>
|
||||
struct UTF32 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(codepoint);
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
PutUnsafe(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
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 <typename InputStream, typename OutputStream>
|
||||
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<typename CharType = unsigned>
|
||||
struct UTF32LE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<uint8_t>(is.Take());
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 big endian encoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32BE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(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<typename CharType = char>
|
||||
struct ASCII {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 0 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
uint8_t c = static_cast<uint8_t>(is.Take());
|
||||
*codepoint = c;
|
||||
return c <= 0X7F;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
uint8_t c = static_cast<uint8_t>(is.Take());
|
||||
os.Put(static_cast<typename OutputStream::Ch>(c));
|
||||
return c <= 0x7F;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
uint8_t c = static_cast<uint8_t>(Take(is));
|
||||
return static_cast<Ch>(c);
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return static_cast<Ch>(is.Take());
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
(void)os;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(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<typename CharType>
|
||||
struct AutoUTF {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
template<typename OutputStream>
|
||||
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<typename OutputStream>
|
||||
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 <typename InputStream>
|
||||
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 <typename InputStream, typename OutputStream>
|
||||
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<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder {
|
||||
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
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<typename InputStream, typename OutputStream>
|
||||
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<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Transcode(is, os); // Since source/target encoding is different, must transcode.
|
||||
}
|
||||
};
|
||||
|
||||
// Forward declaration.
|
||||
template<typename Stream>
|
||||
inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
|
||||
|
||||
//! Specialization of Transcoder with same source and target encoding.
|
||||
template<typename Encoding>
|
||||
struct Transcoder<Encoding, Encoding> {
|
||||
template<typename InputStream, typename OutputStream>
|
||||
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<typename InputStream, typename OutputStream>
|
||||
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<typename InputStream, typename OutputStream>
|
||||
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_
|
74
libs/rapidjson/error/en.h
Normal file
74
libs/rapidjson/error/en.h
Normal file
@ -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_
|
161
libs/rapidjson/error/error.h
Normal file
161
libs/rapidjson/error/error.h
Normal file
@ -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_
|
99
libs/rapidjson/filereadstream.h
Normal file
99
libs/rapidjson/filereadstream.h
Normal file
@ -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 <cstdio>
|
||||
|
||||
#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<size_t>(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_
|
104
libs/rapidjson/filewritestream.h
Normal file
104
libs/rapidjson/filewritestream.h
Normal file
@ -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 <cstdio>
|
||||
|
||||
#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<size_t>(bufferEnd_ - current_);
|
||||
while (n > avail) {
|
||||
std::memset(current_, c, avail);
|
||||
current_ += avail;
|
||||
Flush();
|
||||
n -= avail;
|
||||
avail = static_cast<size_t>(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<size_t>(current_ - buffer_), fp_);
|
||||
if (result < static_cast<size_t>(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_
|
151
libs/rapidjson/fwd.h
Normal file
151
libs/rapidjson/fwd.h
Normal file
@ -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<typename CharType> struct UTF8;
|
||||
template<typename CharType> struct UTF16;
|
||||
template<typename CharType> struct UTF16BE;
|
||||
template<typename CharType> struct UTF16LE;
|
||||
template<typename CharType> struct UTF32;
|
||||
template<typename CharType> struct UTF32BE;
|
||||
template<typename CharType> struct UTF32LE;
|
||||
template<typename CharType> struct ASCII;
|
||||
template<typename CharType> struct AutoUTF;
|
||||
|
||||
template<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder;
|
||||
|
||||
// allocators.h
|
||||
|
||||
class CrtAllocator;
|
||||
|
||||
template <typename BaseAllocator>
|
||||
class MemoryPoolAllocator;
|
||||
|
||||
// stream.h
|
||||
|
||||
template <typename Encoding>
|
||||
struct GenericStringStream;
|
||||
|
||||
typedef GenericStringStream<UTF8<char> > StringStream;
|
||||
|
||||
template <typename Encoding>
|
||||
struct GenericInsituStringStream;
|
||||
|
||||
typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
|
||||
|
||||
// stringbuffer.h
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericStringBuffer;
|
||||
|
||||
typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
|
||||
|
||||
// filereadstream.h
|
||||
|
||||
class FileReadStream;
|
||||
|
||||
// filewritestream.h
|
||||
|
||||
class FileWriteStream;
|
||||
|
||||
// memorybuffer.h
|
||||
|
||||
template <typename Allocator>
|
||||
struct GenericMemoryBuffer;
|
||||
|
||||
typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
|
||||
|
||||
// memorystream.h
|
||||
|
||||
struct MemoryStream;
|
||||
|
||||
// reader.h
|
||||
|
||||
template<typename Encoding, typename Derived>
|
||||
struct BaseReaderHandler;
|
||||
|
||||
template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
|
||||
class GenericReader;
|
||||
|
||||
typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
|
||||
|
||||
// writer.h
|
||||
|
||||
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
|
||||
class Writer;
|
||||
|
||||
// prettywriter.h
|
||||
|
||||
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
|
||||
class PrettyWriter;
|
||||
|
||||
// document.h
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericMember;
|
||||
|
||||
template <bool Const, typename Encoding, typename Allocator>
|
||||
class GenericMemberIterator;
|
||||
|
||||
template<typename CharType>
|
||||
struct GenericStringRef;
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericValue;
|
||||
|
||||
typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
|
||||
|
||||
template <typename Encoding, typename Allocator, typename StackAllocator>
|
||||
class GenericDocument;
|
||||
|
||||
typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
|
||||
|
||||
// pointer.h
|
||||
|
||||
template <typename ValueType, typename Allocator>
|
||||
class GenericPointer;
|
||||
|
||||
typedef GenericPointer<Value, CrtAllocator> Pointer;
|
||||
|
||||
// schema.h
|
||||
|
||||
template <typename SchemaDocumentType>
|
||||
class IGenericRemoteSchemaDocumentProvider;
|
||||
|
||||
template <typename ValueT, typename Allocator>
|
||||
class GenericSchemaDocument;
|
||||
|
||||
typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
|
||||
typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
|
||||
|
||||
template <
|
||||
typename SchemaDocumentType,
|
||||
typename OutputHandler,
|
||||
typename StateAllocator>
|
||||
class GenericSchemaValidator;
|
||||
|
||||
typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSONFWD_H_
|
290
libs/rapidjson/internal/biginteger.h
Normal file
290
libs/rapidjson/internal/biginteger.h
Normal file
@ -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 <intrin.h> // 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<uint32_t>(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<unsigned>(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<unsigned>(*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<uint128>(a) * static_cast<uint128>(b);
|
||||
p += k;
|
||||
*outHigh = static_cast<uint64_t>(p >> 64);
|
||||
return static_cast<uint64_t>(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<uint64_t>(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_
|
71
libs/rapidjson/internal/clzll.h
Normal file
71
libs/rapidjson/internal/clzll.h
Normal file
@ -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 <intrin.h>
|
||||
#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<uint32_t>(x >> 32)))
|
||||
return 63 - (r + 32);
|
||||
|
||||
// Scan the low 32 bits.
|
||||
_BitScanReverse(&r, static_cast<uint32_t>(x & 0xFFFFFFFF));
|
||||
#endif // _WIN64
|
||||
|
||||
return 63 - r;
|
||||
#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll)
|
||||
// __builtin_clzll wrapper
|
||||
return static_cast<uint32_t>(__builtin_clzll(x));
|
||||
#else
|
||||
// naive version
|
||||
uint32_t r = 0;
|
||||
while (!(x & (static_cast<uint64_t>(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_
|
257
libs/rapidjson/internal/diyfp.h
Normal file
257
libs/rapidjson/internal/diyfp.h
Normal file
@ -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 <limits>
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER)
|
||||
#include <intrin.h>
|
||||
#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<int>((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<uint128>(f) * static_cast<uint128>(rhs.f);
|
||||
uint64_t h = static_cast<uint64_t>(p >> 64);
|
||||
uint64_t l = static_cast<uint64_t>(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<int>(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<double>::infinity();
|
||||
}
|
||||
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
||||
static_cast<uint64_t>(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<int>(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<int>(dk);
|
||||
if (dk - k > 0.0)
|
||||
k++;
|
||||
|
||||
unsigned index = static_cast<unsigned>((k >> 3) + 1);
|
||||
*K = -(-348 + static_cast<int>(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<unsigned>(exp + 348) / 8u;
|
||||
*outExp = -348 + static_cast<int>(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_
|
245
libs/rapidjson/internal/dtoa.h
Normal file
245
libs/rapidjson/internal/dtoa.h
Normal file
@ -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<uint32_t>(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<char>('0' + static_cast<char>(d));
|
||||
kappa--;
|
||||
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
||||
if (tmp <= delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// kappa = 0
|
||||
for (;;) {
|
||||
p2 *= 10;
|
||||
delta *= 10;
|
||||
char d = static_cast<char>(p2 >> -one.e);
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('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<char>('0' + static_cast<char>(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<char>('0' + static_cast<char>(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<size_t>(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<size_t>(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<size_t>(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_
|
78
libs/rapidjson/internal/ieee754.h
Normal file
78
libs/rapidjson/internal/ieee754.h
Normal file
@ -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<int>(((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_
|
308
libs/rapidjson/internal/itoa.h
Normal file
308
libs/rapidjson/internal/itoa.h
Normal file
@ -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<char>('0' + static_cast<char>(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<uint32_t>(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<uint32_t>(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<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(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<uint32_t>(value / kTen16); // 1 to 1844
|
||||
value %= kTen16;
|
||||
|
||||
if (a < 10)
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(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<char>('0' + static_cast<char>(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<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(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<uint64_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u64toa(u, buffer);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ITOA_
|
186
libs/rapidjson/internal/meta.h
Normal file
186
libs/rapidjson/internal/meta.h
Normal file
@ -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 <type_traits>
|
||||
#endif
|
||||
|
||||
//@cond RAPIDJSON_INTERNAL
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
|
||||
template <typename T> struct Void { typedef void Type; };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BoolType, TrueType, FalseType
|
||||
//
|
||||
template <bool Cond> struct BoolType {
|
||||
static const bool Value = Cond;
|
||||
typedef BoolType Type;
|
||||
};
|
||||
typedef BoolType<true> TrueType;
|
||||
typedef BoolType<false> FalseType;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
|
||||
//
|
||||
|
||||
template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
|
||||
template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
|
||||
template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
|
||||
template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
|
||||
|
||||
template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
|
||||
template <> struct AndExprCond<true, true> : TrueType {};
|
||||
template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
|
||||
template <> struct OrExprCond<false, false> : FalseType {};
|
||||
|
||||
template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
|
||||
template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
|
||||
template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
|
||||
template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AddConst, MaybeAddConst, RemoveConst
|
||||
template <typename T> struct AddConst { typedef const T Type; };
|
||||
template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
|
||||
template <typename T> struct RemoveConst { typedef T Type; };
|
||||
template <typename T> struct RemoveConst<const T> { typedef T Type; };
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsSame, IsConst, IsMoreConst, IsPointer
|
||||
//
|
||||
template <typename T, typename U> struct IsSame : FalseType {};
|
||||
template <typename T> struct IsSame<T, T> : TrueType {};
|
||||
|
||||
template <typename T> struct IsConst : FalseType {};
|
||||
template <typename T> struct IsConst<const T> : TrueType {};
|
||||
|
||||
template <typename CT, typename T>
|
||||
struct IsMoreConst
|
||||
: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
|
||||
BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
|
||||
|
||||
template <typename T> struct IsPointer : FalseType {};
|
||||
template <typename T> struct IsPointer<T*> : TrueType {};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsBaseOf
|
||||
//
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: BoolType< ::std::is_base_of<B,D>::value> {};
|
||||
|
||||
#else // simplified version adopted from Boost
|
||||
|
||||
template<typename B, typename D> struct IsBaseOfImpl {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
|
||||
|
||||
typedef char (&Yes)[1];
|
||||
typedef char (&No) [2];
|
||||
|
||||
template <typename T>
|
||||
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 <typename B, typename D> struct IsBaseOf
|
||||
: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
|
||||
|
||||
#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EnableIf / DisableIf
|
||||
//
|
||||
template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
|
||||
template <typename T> struct EnableIfCond<false, T> { /* empty */ };
|
||||
|
||||
template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
|
||||
template <typename T> struct DisableIfCond<true, T> { /* empty */ };
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct EnableIf : EnableIfCond<Condition::Value, T> {};
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct DisableIf : DisableIfCond<Condition::Value, T> {};
|
||||
|
||||
// SFINAE helpers
|
||||
struct SfinaeTag {};
|
||||
template <typename T> struct RemoveSfinaeTag;
|
||||
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { 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 \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_DISABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::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_
|
55
libs/rapidjson/internal/pow10.h
Normal file
55
libs/rapidjson/internal/pow10.h
Normal file
@ -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_
|
739
libs/rapidjson/internal/regex.h
Normal file
739
libs/rapidjson/internal/regex.h
Normal file
@ -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 <typename SourceStream, typename Encoding>
|
||||
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 <typename Encoding, typename Allocator>
|
||||
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 <typename Encoding, typename Allocator = CrtAllocator>
|
||||
class GenericRegex {
|
||||
public:
|
||||
typedef Encoding EncodingType;
|
||||
typedef typename Encoding::Ch Ch;
|
||||
template <typename, typename> 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<Encoding> ss(source);
|
||||
DecodedStream<GenericStringStream<Encoding>, 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<State>()[index];
|
||||
}
|
||||
|
||||
const State& GetState(SizeType index) const {
|
||||
RAPIDJSON_ASSERT(index < stateCount_);
|
||||
return states_.template Bottom<State>()[index];
|
||||
}
|
||||
|
||||
Range& GetRange(SizeType index) {
|
||||
RAPIDJSON_ASSERT(index < rangeCount_);
|
||||
return ranges_.template Bottom<Range>()[index];
|
||||
}
|
||||
|
||||
const Range& GetRange(SizeType index) const {
|
||||
RAPIDJSON_ASSERT(index < rangeCount_);
|
||||
return ranges_.template Bottom<Range>()[index];
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
void Parse(DecodedStream<InputStream, Encoding>& ds) {
|
||||
Stack<Allocator> operandStack(allocator_, 256); // Frag
|
||||
Stack<Allocator> operatorStack(allocator_, 256); // Operator
|
||||
Stack<Allocator> atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis)
|
||||
|
||||
*atomCountStack.template Push<unsigned>() = 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<Operator>() < kAlternation)
|
||||
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||
return;
|
||||
*operatorStack.template Push<Operator>() = kAlternation;
|
||||
*atomCountStack.template Top<unsigned>() = 0;
|
||||
break;
|
||||
|
||||
case '(':
|
||||
*operatorStack.template Push<Operator>() = kLeftParenthesis;
|
||||
*atomCountStack.template Push<unsigned>() = 0;
|
||||
break;
|
||||
|
||||
case ')':
|
||||
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
|
||||
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||
return;
|
||||
if (operatorStack.Empty())
|
||||
return;
|
||||
operatorStack.template Pop<Operator>(1);
|
||||
atomCountStack.template Pop<unsigned>(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>() = 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<Operator>(1)))
|
||||
return;
|
||||
|
||||
// Link the operand to matching state.
|
||||
if (operandStack.GetSize() == sizeof(Frag)) {
|
||||
Frag* e = operandStack.template Pop<Frag>(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<State>();
|
||||
s->out = out;
|
||||
s->out1 = out1;
|
||||
s->codepoint = codepoint;
|
||||
s->rangeStart = kRegexInvalidRange;
|
||||
return stateCount_++;
|
||||
}
|
||||
|
||||
void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
|
||||
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
|
||||
*operandStack.template Push<Frag>() = Frag(s, s, s);
|
||||
}
|
||||
|
||||
void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
|
||||
if (*atomCountStack.template Top<unsigned>())
|
||||
*operatorStack.template Push<Operator>() = kConcatenation;
|
||||
(*atomCountStack.template Top<unsigned>())++;
|
||||
}
|
||||
|
||||
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<Allocator>& operandStack, Operator op) {
|
||||
switch (op) {
|
||||
case kConcatenation:
|
||||
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
|
||||
{
|
||||
Frag e2 = *operandStack.template Pop<Frag>(1);
|
||||
Frag e1 = *operandStack.template Pop<Frag>(1);
|
||||
Patch(e1.out, e2.start);
|
||||
*operandStack.template Push<Frag>() = 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<Frag>(1);
|
||||
Frag e1 = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(e1.start, e2.start, 0);
|
||||
*operandStack.template Push<Frag>() = 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<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
*operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case kZeroOrMore:
|
||||
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||
Frag e = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
Patch(e.out, s);
|
||||
*operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case kOneOrMore:
|
||||
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||
Frag e = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
Patch(e.out, s);
|
||||
*operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
// syntax error (e.g. unclosed kLeftParenthesis)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EvalQuantifier(Stack<Allocator>& 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<Allocator>& operandStack) {
|
||||
const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
|
||||
SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
|
||||
State* s = states_.template Push<State>(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>() = Frag(src.start + count, src.out + count, src.minIndex + count);
|
||||
stateCount_ += count;
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool ParseUnsigned(DecodedStream<InputStream, Encoding>& 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 <typename InputStream>
|
||||
bool ParseRange(DecodedStream<InputStream, Encoding>& 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<Range>();
|
||||
r->start = r->end = codepoint;
|
||||
r->next = kRegexInvalidRange;
|
||||
return rangeCount_++;
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool CharacterEscape(DecodedStream<InputStream, Encoding>& 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<Allocator> states_;
|
||||
Stack<Allocator> ranges_;
|
||||
SizeType root_;
|
||||
SizeType stateCount_;
|
||||
SizeType rangeCount_;
|
||||
|
||||
static const unsigned kInfinityQuantifier = ~0u;
|
||||
|
||||
// For SearchWithAnchoring()
|
||||
bool anchorBegin_;
|
||||
bool anchorEnd_;
|
||||
};
|
||||
|
||||
template <typename RegexType, typename Allocator = CrtAllocator>
|
||||
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<unsigned*>(allocator_->Malloc(GetStateSetSize()));
|
||||
state0_.template Reserve<SizeType>(regex_.stateCount_);
|
||||
state1_.template Reserve<SizeType>(regex_.stateCount_);
|
||||
}
|
||||
|
||||
~GenericRegexSearch() {
|
||||
Allocator::Free(stateSet_);
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool Match(InputStream& is) {
|
||||
return SearchWithAnchoring(is, true, true);
|
||||
}
|
||||
|
||||
bool Match(const Ch* s) {
|
||||
GenericStringStream<Encoding> is(s);
|
||||
return Match(is);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool Search(InputStream& is) {
|
||||
return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_);
|
||||
}
|
||||
|
||||
bool Search(const Ch* s) {
|
||||
GenericStringStream<Encoding> is(s);
|
||||
return Search(is);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef typename RegexType::State State;
|
||||
typedef typename RegexType::Range Range;
|
||||
|
||||
template <typename InputStream>
|
||||
bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) {
|
||||
DecodedStream<InputStream, Encoding> ds(is);
|
||||
|
||||
state0_.Clear();
|
||||
Stack<Allocator> *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<SizeType>(); s != current->template End<SizeType>(); ++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<Allocator>& 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<SizeType>() = 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<Allocator> state0_;
|
||||
Stack<Allocator> state1_;
|
||||
uint32_t* stateSet_;
|
||||
};
|
||||
|
||||
typedef GenericRegex<UTF8<> > Regex;
|
||||
typedef GenericRegexSearch<Regex> 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_
|
232
libs/rapidjson/internal/stack.h
Normal file
232
libs/rapidjson/internal/stack.h
Normal file
@ -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 <cstddef>
|
||||
|
||||
#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 <typename Allocator>
|
||||
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<typename T>
|
||||
RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
|
||||
// Expand the stack if needed
|
||||
if (RAPIDJSON_UNLIKELY(static_cast<std::ptrdiff_t>(sizeof(T) * count) > (stackEnd_ - stackTop_)))
|
||||
Expand<T>(count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
|
||||
Reserve<T>(count);
|
||||
return PushUnsafe<T>(count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
|
||||
RAPIDJSON_ASSERT(stackTop_);
|
||||
RAPIDJSON_ASSERT(static_cast<std::ptrdiff_t>(sizeof(T) * count) <= (stackEnd_ - stackTop_));
|
||||
T* ret = reinterpret_cast<T*>(stackTop_);
|
||||
stackTop_ += sizeof(T) * count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Pop(size_t count) {
|
||||
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
|
||||
stackTop_ -= count * sizeof(T);
|
||||
return reinterpret_cast<T*>(stackTop_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Top() {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* Top() const {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* End() { return reinterpret_cast<T*>(stackTop_); }
|
||||
|
||||
template<typename T>
|
||||
const T* End() const { return reinterpret_cast<T*>(stackTop_); }
|
||||
|
||||
template<typename T>
|
||||
T* Bottom() { return reinterpret_cast<T*>(stack_); }
|
||||
|
||||
template<typename T>
|
||||
const T* Bottom() const { return reinterpret_cast<T*>(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<size_t>(stackTop_ - stack_); }
|
||||
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
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<char*>(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_
|
69
libs/rapidjson/internal/strfunc.h
Normal file
69
libs/rapidjson/internal/strfunc.h
Normal file
@ -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 <cwchar>
|
||||
|
||||
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 <typename Ch>
|
||||
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<typename Encoding>
|
||||
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
|
||||
RAPIDJSON_ASSERT(s != 0);
|
||||
RAPIDJSON_ASSERT(outCount != 0);
|
||||
GenericStringStream<Encoding> 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_
|
290
libs/rapidjson/internal/strtod.h
Normal file
290
libs/rapidjson/internal/strtod.h
Normal file
@ -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 <climits>
|
||||
#include <limits>
|
||||
|
||||
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 <typename T>
|
||||
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<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
|
||||
|
||||
BigInteger bS(bInt);
|
||||
bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
|
||||
|
||||
BigInteger hS(1);
|
||||
hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(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<unsigned>(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<unsigned>(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<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
|
||||
}
|
||||
|
||||
inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) {
|
||||
RAPIDJSON_ASSERT(dLen >= 0);
|
||||
const BigInteger dInt(decimals, static_cast<unsigned>(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<int>(length);
|
||||
|
||||
RAPIDJSON_ASSERT(length >= decimalPosition);
|
||||
RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX);
|
||||
int dExpAdjust = static_cast<int>(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<double>::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_
|
46
libs/rapidjson/internal/swap.h
Normal file
46
libs/rapidjson/internal/swap.h
Normal file
@ -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++ <algorithm> 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 <typename T>
|
||||
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_
|
128
libs/rapidjson/istreamwrapper.h
Normal file
128
libs/rapidjson/istreamwrapper.h
Normal file
@ -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 <iosfwd>
|
||||
#include <ios>
|
||||
|
||||
#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 <typename StreamType>
|
||||
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<size_t>(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<std::streamsize>(bufferSize_))) {
|
||||
readCount_ = static_cast<size_t>(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<std::istream> IStreamWrapper;
|
||||
typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ISTREAMWRAPPER_H_
|
70
libs/rapidjson/memorybuffer.h
Normal file
70
libs/rapidjson/memorybuffer.h
Normal file
@ -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 <typename Allocator = CrtAllocator>
|
||||
struct GenericMemoryBuffer {
|
||||
typedef char Ch; // byte
|
||||
|
||||
GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() { stack_.ShrinkToFit(); }
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetBuffer() const {
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> 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<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
71
libs/rapidjson/memorystream.h
Normal file
71
libs/rapidjson/memorystream.h
Normal file
@ -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<size_t>(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_
|
316
libs/rapidjson/msinttypes/inttypes.h
Normal file
316
libs/rapidjson/msinttypes/inttypes.h
Normal file
@ -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 <inttypes.h>
|
||||
#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_ ]
|
300
libs/rapidjson/msinttypes/stdint.h
Normal file
300
libs/rapidjson/msinttypes/stdint.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#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 <boost/cstdint.hpp>.
|
||||
// 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 <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we have to wrap <wchar.h> 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 <wchar.h>
|
||||
#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 <wchar.h>
|
||||
#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 <boost/cstdint.hpp>.
|
||||
// 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_ ]
|
81
libs/rapidjson/ostreamwrapper.h
Normal file
81
libs/rapidjson/ostreamwrapper.h
Normal file
@ -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 <iosfwd>
|
||||
|
||||
#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 <typename StreamType>
|
||||
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<std::ostream> OStreamWrapper;
|
||||
typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_OSTREAMWRAPPER_H_
|
1415
libs/rapidjson/pointer.h
Normal file
1415
libs/rapidjson/pointer.h
Normal file
File diff suppressed because it is too large
Load Diff
277
libs/rapidjson/prettywriter.h
Normal file
277
libs/rapidjson/prettywriter.h
Normal file
@ -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 OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
|
||||
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
|
||||
public:
|
||||
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> 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<PrettyWriter>(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<Ch>& str) {
|
||||
return String(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StartObject() {
|
||||
PrettyPrefix(kObjectType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) 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<Ch>& 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<typename Base::Level>()->inArray); // currently inside an Array, not Object
|
||||
RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top<typename Base::Level>()->valueCount % 2); // Object has a Key without a Value
|
||||
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(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>()) 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<typename Base::Level>()->inArray);
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(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<typename Base::Level>();
|
||||
|
||||
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<typename OutputStream::Ch>(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_
|
692
libs/rapidjson/rapidjson.h
Normal file
692
libs/rapidjson/rapidjson.h
Normal file
@ -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 <cstdlib> // malloc(), realloc(), free(), size_t
|
||||
#include <cstring> // 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 "<major>.<minor>.<patch>" 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 <string>
|
||||
#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 <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#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 <endian.h>
|
||||
# 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<size_t>(7u)) & ~static_cast<size_t>(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<uint64_t>(high32) << 32) | static_cast<uint64_t>(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<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
|
||||
#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(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 <cassert>
|
||||
#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 <bool x> struct STATIC_ASSERTION_FAILURE;
|
||||
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
|
||||
template <size_t x> 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<bool(x) >)> \
|
||||
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 <cassert>
|
||||
#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_
|
2244
libs/rapidjson/reader.h
Normal file
2244
libs/rapidjson/reader.h
Normal file
File diff suppressed because it is too large
Load Diff
2496
libs/rapidjson/schema.h
Normal file
2496
libs/rapidjson/schema.h
Normal file
File diff suppressed because it is too large
Load Diff
223
libs/rapidjson/stream.h
Normal file
223
libs/rapidjson/stream.h
Normal file
@ -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<typename Stream>
|
||||
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<StringStream>.
|
||||
*/
|
||||
enum { copyOptimization = 0 };
|
||||
};
|
||||
|
||||
//! Reserve n characters for writing to a stream.
|
||||
template<typename Stream>
|
||||
inline void PutReserve(Stream& stream, size_t count) {
|
||||
(void)stream;
|
||||
(void)count;
|
||||
}
|
||||
|
||||
//! Write character to a stream, presuming buffer is reserved.
|
||||
template<typename Stream>
|
||||
inline void PutUnsafe(Stream& stream, typename Stream::Ch c) {
|
||||
stream.Put(c);
|
||||
}
|
||||
|
||||
//! Put N copies of a character to a stream.
|
||||
template<typename Stream, typename Ch>
|
||||
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 <typename InputStream, typename Encoding = UTF8<> >
|
||||
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 <typename Encoding>
|
||||
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<size_t>(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 <typename Encoding>
|
||||
struct StreamTraits<GenericStringStream<Encoding> > {
|
||||
enum { copyOptimization = 1 };
|
||||
};
|
||||
|
||||
//! String stream with UTF8 encoding.
|
||||
typedef GenericStringStream<UTF8<> > StringStream;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// InsituStringStream
|
||||
|
||||
//! A read-write string stream.
|
||||
/*! This string stream is particularly designed for in-situ parsing.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding>
|
||||
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<size_t>(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<size_t>(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 <typename Encoding>
|
||||
struct StreamTraits<GenericInsituStringStream<Encoding> > {
|
||||
enum { copyOptimization = 1 };
|
||||
};
|
||||
|
||||
//! Insitu string stream with UTF8 encoding.
|
||||
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STREAM_H_
|
121
libs/rapidjson/stringbuffer.h
Normal file
121
libs/rapidjson/stringbuffer.h
Normal file
@ -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 <utility> // 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 <typename Encoding, typename Allocator = CrtAllocator>
|
||||
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<Ch>() = c; }
|
||||
void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.ShrinkToFit();
|
||||
stack_.template Pop<Ch>(1);
|
||||
}
|
||||
|
||||
void Reserve(size_t count) { stack_.template Reserve<Ch>(count); }
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetString() const {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.template Pop<Ch>(1);
|
||||
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
//! 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<Allocator> stack_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
GenericStringBuffer(const GenericStringBuffer&);
|
||||
GenericStringBuffer& operator=(const GenericStringBuffer&);
|
||||
};
|
||||
|
||||
//! String buffer with UTF8 encoding
|
||||
typedef GenericStringBuffer<UTF8<> > StringBuffer;
|
||||
|
||||
template<typename Encoding, typename Allocator>
|
||||
inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) {
|
||||
stream.Reserve(count);
|
||||
}
|
||||
|
||||
template<typename Encoding, typename Allocator>
|
||||
inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) {
|
||||
stream.PutUnsafe(c);
|
||||
}
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
|
||||
std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_STRINGBUFFER_H_
|
710
libs/rapidjson/writer.h
Normal file
710
libs/rapidjson/writer.h
Normal file
@ -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 <new> // placement new
|
||||
|
||||
#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_BitScanForward)
|
||||
#endif
|
||||
#ifdef RAPIDJSON_SSE42
|
||||
#include <nmmintrin.h>
|
||||
#elif defined(RAPIDJSON_SSE2)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(RAPIDJSON_NEON)
|
||||
#include <arm_neon.h>
|
||||
#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 OutputStream, typename SourceEncoding = UTF8<>, 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<OutputStream> 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<Ch>& str) {
|
||||
return String(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StartObject() {
|
||||
Prefix(kObjectType);
|
||||
new (level_stack_.template Push<Level>()) 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<Ch>& 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<Level>()->inArray); // currently inside an Array, not Object
|
||||
RAPIDJSON_ASSERT(0 == level_stack_.template Top<Level>()->valueCount % 2); // Object has a Key without a Value
|
||||
level_stack_.template Pop<Level>(1);
|
||||
return EndValue(WriteEndObject());
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
Prefix(kArrayType);
|
||||
new (level_stack_.template Push<Level>()) Level(true);
|
||||
return WriteStartArray();
|
||||
}
|
||||
|
||||
bool EndArray(SizeType elementCount = 0) {
|
||||
(void)elementCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(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<size_t>(end - buffer));
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteUint(unsigned u) {
|
||||
char buffer[10];
|
||||
const char* end = internal::u32toa(u, buffer);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteInt64(int64_t i64) {
|
||||
char buffer[21];
|
||||
const char* end = internal::i64toa(i64, buffer);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteUint64(uint64_t u64) {
|
||||
char buffer[20];
|
||||
char* end = internal::u64toa(u64, buffer);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*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<size_t>(end - buffer));
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*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<SourceEncoding> is(str);
|
||||
while (ScanWriteUnescapedString(is, length)) {
|
||||
const Ch c = is.Peek();
|
||||
if (!TargetEncoding::supportUnicode && static_cast<unsigned>(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<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) {
|
||||
is.Take();
|
||||
PutUnsafe(*os_, '\\');
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(escape[static_cast<unsigned char>(c)]));
|
||||
if (escape[static_cast<unsigned char>(c)] == 'u') {
|
||||
PutUnsafe(*os_, '0');
|
||||
PutUnsafe(*os_, '0');
|
||||
PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]);
|
||||
PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]);
|
||||
}
|
||||
}
|
||||
else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
|
||||
Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
|
||||
Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
|
||||
return false;
|
||||
}
|
||||
PutUnsafe(*os_, '\"');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& 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<SourceEncoding> is(json);
|
||||
while (RAPIDJSON_LIKELY(is.Tell() < length)) {
|
||||
RAPIDJSON_ASSERT(is.Peek() != '\0');
|
||||
if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
|
||||
Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
|
||||
Transcoder<SourceEncoding, TargetEncoding>::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<Level>();
|
||||
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<StackAllocator> 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<StringBuffer>::WriteInt(int i) {
|
||||
char *buffer = os_->Push(11);
|
||||
const char* end = internal::i32toa(i, buffer);
|
||||
os_->Pop(static_cast<size_t>(11 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
|
||||
char *buffer = os_->Push(10);
|
||||
const char* end = internal::u32toa(u, buffer);
|
||||
os_->Pop(static_cast<size_t>(10 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
|
||||
char *buffer = os_->Push(21);
|
||||
const char* end = internal::i64toa(i64, buffer);
|
||||
os_->Pop(static_cast<size_t>(21 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
|
||||
char *buffer = os_->Push(20);
|
||||
const char* end = internal::u64toa(u, buffer);
|
||||
os_->Pop(static_cast<size_t>(20 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::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<size_t>(25 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::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<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
|
||||
const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~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<const __m128i *>(&dquote[0]));
|
||||
const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
|
||||
const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
|
||||
|
||||
for (; p != endAligned; p += 16) {
|
||||
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(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<unsigned short>(_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<SizeType>(__builtin_ffs(r) - 1);
|
||||
#endif
|
||||
char* q = reinterpret_cast<char*>(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<StringBuffer>::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<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
|
||||
const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~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<const uint8_t *>(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<char*>(os_->PushUnsafe(len));
|
||||
for (size_t i = 0; i < len; i++)
|
||||
q[i] = p[i];
|
||||
|
||||
p += len;
|
||||
break;
|
||||
}
|
||||
vst1q_u8(reinterpret_cast<uint8_t *>(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_
|
80
main.cpp
Normal file
80
main.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#include <brynet/net/EventLoop.hpp>
|
||||
#include <brynet/net/TcpService.hpp>
|
||||
#include <brynet/net/wrapper/ServiceBuilder.hpp>
|
||||
#include <brynet/base/AppStatus.hpp>
|
||||
|
||||
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: <listen port> <net work thread num>\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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user