diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..01985bde --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,147 @@ +# Copyright 2016, 2017 Peter Dimov +# Copyright (C) 2017 - 2019 James E. King III +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) + +# +# Generic Appveyor build script for boostorg repositories +# See: https://github.com/boostorg/boost-ci/ +# +# Instructions for customizing this script for your library: +# +# 1. Customize the compilers and language levels you want. +# 2. If you have more than include/, src/, test/, example/, examples/, +# benchmark/ or tools/ directories, set the environment variable DEPINST. +# For example if your build uses code in "bench/" and "fog/" directories: +# - DEPINST: --include bench --include fog +# 3. Enable pull request builds in your boostorg/ account. +# +# That's it - the script will do everything else for you. +# + +version: 1.0.{build}-{branch} + +shallow_clone: true + +branches: + only: + - master + - develop + - /bugfix\/.*/ + - /feature\/.*/ + - /fix\/.*/ + - /pr\/.*/ + +matrix: + # Adding MAYFAIL to any matrix job allows it to fail but the build stays green: + allow_failures: + - MAYFAIL: true + +environment: + global: + # see: http://www.boost.org/build/doc/html/bbv2/overview/invocation.html#bbv2.overview.invocation.properties + # to use the default for a given environment, comment it out; recommend you build debug and release however: + # on Windows it is important to exercise all the possibilities, especially shared vs static, however most + # libraries that care about this exercise it in their Jamfiles... + # B2_ADDRESS_MODEL: address-model=64,32 + # B2_LINK: link=shared,static + # B2_THREADING: threading=multi,single + B2_VARIANT: variant=release + + matrix: + - FLAVOR: Visual Studio 2019 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + B2_ADDRESS_MODEL: address-model=64 + B2_CXXFLAGS: cxxflags=-permissive- + B2_CXXSTD: latest # 2a + B2_TOOLSET: msvc-14.2 + + - FLAVOR: Visual Studio 2017 C++2a Strict + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + B2_ADDRESS_MODEL: address-model=64 + B2_CXXFLAGS: cxxflags=-permissive- + B2_CXXSTD: latest # 2a + B2_TOOLSET: msvc-14.1 + + - FLAVOR: Visual Studio 2017 C++17 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + B2_ADDRESS_MODEL: address-model=64 + B2_CXXSTD: 17 + B2_TOOLSET: msvc-14.1 + B2_VARIANT: variant=debug + + - FLAVOR: Visual Studio 2017 C++14 (Default) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + B2_ADDRESS_MODEL: address-model=64,32 + B2_TOOLSET: msvc-14.1 + + - FLAVOR: clang-cl + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + B2_ADDRESS_MODEL: address-model=64 + B2_CXXSTD: 11 + B2_TOOLSET: clang-win + + - FLAVOR: Visual Studio 2015 C++14 (Default) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + B2_ADDRESS_MODEL: address-model=64,32 + B2_TOOLSET: msvc-14.0 + B2_VARIANT: variant=debug + + - FLAVOR: Visual Studio 2010, 2012, 2013 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + B2_TOOLSET: msvc-10.0,msvc-11.0,msvc-12.0 + + - FLAVOR: cygwin (32-bit) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + ADDPATH: C:\cygwin\bin; + B2_ADDRESS_MODEL: address-model=32 + B2_CXXSTD: 03,11 + # https://github.com/boostorg/test/issues/144 + B2_DEFINES: define=_POSIX_C_SOURCE=200112L + B2_THREADING: threadapi=pthread + B2_TOOLSET: gcc + B2_VARIANT: variant=debug + + - FLAVOR: cygwin (64-bit) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + ADDPATH: C:\cygwin64\bin; + B2_ADDRESS_MODEL: address-model=64 + B2_CXXSTD: 11,17 + # https://github.com/boostorg/test/issues/144 + B2_DEFINES: define=_POSIX_C_SOURCE=200112L define=__USE_ISOC99 + B2_THREADING: threadapi=pthread + B2_TOOLSET: gcc + + - FLAVOR: mingw32 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + ARCH: i686 + B2_ADDRESS_MODEL: address-model=32 + B2_CXXSTD: 03,11 + SCRIPT: ci\appveyor\mingw.bat + B2_VARIANT: variant=debug + + - FLAVOR: mingw64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + ARCH: x86_64 + B2_ADDRESS_MODEL: address-model=64 + B2_CXXSTD: 11,17 + B2_DEFINES: define=__USE_ISOC99 + SCRIPT: ci\appveyor\mingw.bat + +install: + - set SELF=%APPVEYOR_PROJECT_NAME:-=_% + - git clone https://github.com/boostorg/boost-ci.git C:\boost-ci + - xcopy /s /e /q /i C:\boost-ci\ci .\ci + - ci\appveyor\install.bat + +build: off + +test_script: + - set SELF=%APPVEYOR_PROJECT_NAME:-=_% + - PATH=%ADDPATH%%PATH% + # The definition of B2_TOOLCXX omits B2_CXXSTD= if it was not defined above + - IF NOT DEFINED B2_CXXSTD (SET B2_TOOLCXX=toolset=%B2_TOOLSET%) ELSE (SET B2_TOOLCXX=toolset=%B2_TOOLSET% cxxstd=%B2_CXXSTD%) + # Echo the complete build command to the build log + - IF NOT DEFINED SCRIPT (ECHO b2 libs/%SELF:\=/% %B2_TOOLCXX% %B2_CXXFLAGS% %B2_DEFINES% %B2_THREADING% %B2_ADDRESS_MODEL% %B2_LINK% %B2_THREADING% %B2_VARIANT% -j3) + # Now go build... + - IF DEFINED SCRIPT (call libs\%SELF%\%SCRIPT%) ELSE (b2 libs/%SELF:\=/% %B2_TOOLCXX% %B2_CXXFLAGS% %B2_DEFINES% %B2_THREADING% %B2_ADDRESS_MODEL% %B2_LINK% %B2_THREADING% %B2_VARIANT% -j3) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml new file mode 100644 index 00000000..d958e52e --- /dev/null +++ b/.azure-pipelines.yml @@ -0,0 +1,311 @@ +# Copyright 2015-2019 Rene Rivera. +# Copyright 2019 Mateusz Loskot +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) + +# +# Generic Azure Pipelines build script for boostorg repositories +# See: https://github.com/boostorg/boost-ci/ +# +# Instructions for customizing this script for your library: +# +# 1. Customize the compilers and language levels you want. +# 2. If you have more than include/, src/, test/, example/, examples/, +# benchmark/ or tools/ directories, set the environment variable DEPINST. +# For example if your build uses code in "bench/" and "fog/" directories: +# - DEPINST: --include bench --include fog +# 3. Enable pull request builds in your boostorg/ account. +# +# That's it - the script will do everything else for you. + +trigger: + branches: + include: + - develop + - master + - bugfix/* + - feature/* + - fix/* + - pr/* + +pr: + branches: + include: + - develop + +variables: + - name: B2_VARIANT + value: variant=release,debug + +stages: + +- stage: Test + jobs: + + - job: 'Linux' + pool: + vmImage: 'ubuntu-16.04' + strategy: + matrix: + GCC 8: + B2_TOOLSET: gcc + B2_CXXSTD: 14,17,2a + CXX: g++-8 + PACKAGES: g++-8 + GCC 7: + B2_TOOLSET: gcc + B2_CXXSTD: 11,14,17 + CXX: g++-7 + PACKAGES: g++-7 + GCC 6: + B2_TOOLSET: gcc + B2_CXXSTD: 11,14 + CXX: g++-6 + PACKAGES: g++-6 + GCC 5: + B2_TOOLSET: gcc + B2_CXXSTD: 11 + CXX: g++-5 + PACKAGES: g++-5 + GCC 4.9: + B2_TOOLSET: gcc + B2_CXXSTD: 03,11 + CXX: g++-4.9 + PACKAGES: g++-4.9 + GCC 4.8: + B2_TOOLSET: gcc + B2_CXXSTD: 03,11 + CXX: g++-4.8 + PACKAGES: g++-4.8 + Clang 8: + B2_TOOLSET: clang + B2_CXXSTD: 14,17,2a + CXX: clang++-8 + PACKAGES: clang-8 + LLVM_REPO: llvm-toolchain-xenial-8 + Clang 7: + B2_TOOLSET: clang + B2_CXXSTD: 14,17,2a + CXX: clang++-7 + PACKAGES: clang-7 + LLVM_REPO: llvm-toolchain-xenial-7 + Clang 6: + B2_TOOLSET: clang + B2_CXXSTD: 03,11,14,17,2a + B2_CXXFLAGS: -stdlib=libc++ + CXX: clang++-6.0 + PACKAGES: clang-6.0 + LLVM_REPO: llvm-toolchain-xenial-6.0 + Clang 6: + B2_TOOLSET: clang + B2_CXXSTD: 14,17,2a + CXX: clang++-6.0 + PACKAGES: clang-6.0 + LLVM_REPO: llvm-toolchain-xenial-6.0 + Clang 5: + B2_TOOLSET: clang + B2_CXXSTD: 11,14,17 + PACKAGES: clang-5.0 + CXX: clang++-5.0 + LLVM_REPO: llvm-toolchain-xenial-5.0 + Clang 4: + B2_TOOLSET: clang + B2_CXXSTD: 11,14,17 + CXX: clang++-4.0 + PACKAGES: clang-4.0 + LLVM_REPO: llvm-toolchain-xenial-4.0 + Clang 3.9: + B2_TOOLSET: clang + B2_CXXSTD: 03,11,14 + CXX: clang++-3.9 + PACKAGES: clang-3.9 + Clang 3.8: + B2_TOOLSET: clang + CXX: clang++-3.8 + B2_CXXSTD: 03,11,14 + PACKAGES: clang-3.8 + Clang 3.7: + B2_TOOLSET: clang + B2_CXXSTD: 03,11 + CXX: clang++-3.7 + PACKAGES: clang-3.7 + Clang 3.6: + B2_TOOLSET: clang + B2_CXXSTD: 03,11 + CXX: clang++-3.6 + PACKAGES: clang-3.6 + Clang 3.5: + B2_TOOLSET: clang + B2_CXXSTD: 03,11 + CXX: clang++-3.5 + PACKAGES: clang-3.5 + steps: + - bash: | + set -e + uname -a + sudo -E apt-add-repository -y "ppa:ubuntu-toolchain-r/test" + if test -n "${LLVM_REPO}" ; then + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo -E apt-add-repository "deb http://apt.llvm.org/xenial/ ${LLVM_REPO} main" + fi + sudo -E apt-get update + sudo -E apt-get -yq --no-install-suggests --no-install-recommends install ${PACKAGES} + + git clone --branch master https://github.com/boostorg/boost-ci.git boost-ci + cp -pr boost-ci/ci boost-ci/.codecov.yml . + rm -rf boost-ci + source ci/azure-pipelines/install.sh + + # AzP requires to run special task in order to export + # SELF and BOOST_ROOT as job-scoped variable from a script. + # NOTE: Disable set -x is necessary, see the troubleshooting guide + # on "Variables having ' (single quote) appended": + # https://docs.microsoft.com/en-us/azure/devops/pipelines/troubleshooting + set +x + echo "##vso[task.setvariable variable=SELF]"$SELF + echo "##vso[task.setvariable variable=BOOST_ROOT]"$BOOST_ROOT + set -x + displayName: 'Install' + - bash: | + set -e + echo "SELF=$SELF" + echo "BOOST_ROOT=$BOOST_ROOT" + + cd $BOOST_ROOT/libs/$SELF + ci/azure-pipelines/build.sh --debug-configuration + displayName: 'Build' + + - job: 'Windows' + strategy: + matrix: + VS 2019 C++2a Strict: + B2_TOOLSET: msvc-14.2 + B2_CXXSTD: latest # 2a + B2_CXXFLAGS: cxxflags=-permissive- + B2_ADDRESS_MODEL: address-model=64 + VM_IMAGE: 'windows-2019' + VS 2017 C++2a Strict: + B2_TOOLSET: msvc-14.1 + B2_CXXSTD: latest # 2a + B2_CXXFLAGS: cxxflags=-permissive- + B2_ADDRESS_MODEL: address-model=64 + VM_IMAGE: 'vs2017-win2016' + VS 2017 C++17: + B2_TOOLSET: msvc-14.1 + B2_CXXSTD: 17 + B2_ADDRESS_MODEL: address-model=64,32 + VM_IMAGE: 'vs2017-win2016' + VS 2017 C++14: + B2_TOOLSET: msvc-14.1 + #B2_CXXSTD: 14 # default + B2_ADDRESS_MODEL: address-model=64,32 + VM_IMAGE: 'vs2017-win2016' + VS 2015 C++14: + B2_TOOLSET: msvc-14.0 + #B2_CXXSTD: 14 # default + B2_ADDRESS_MODEL: address-model=64,32 + VM_IMAGE: 'vs2015-win2012r2' + + pool: + vmImage: $(VM_IMAGE) + steps: + - script: | + set SELF=%BUILD_REPOSITORY_NAME:-=_% + for /f "tokens=2 delims=/" %%a in ("%SELF%") do set SELF=%%a + set BOOST_ROOT=%BUILD_SOURCESDIRECTORY%\boost-root + + git clone --branch master https://github.com/boostorg/boost-ci.git boost-ci + xcopy /s /e /q /i boost-ci\ci .\ci + cmd /k ci\azure-pipelines\install.bat + + echo ##vso[task.setvariable variable=SELF]%SELF% + echo ##vso[task.setvariable variable=BOOST_ROOT]%BOOST_ROOT% + displayName: 'Install' + - script: | + PATH=%ADDPATH%%PATH% + REM The definition of B2_TOOLCXX omits B2_CXXSTD= if it was not defined above + IF NOT DEFINED B2_CXXSTD (SET B2_TOOLCXX=toolset=%B2_TOOLSET%) ELSE (SET B2_TOOLCXX=toolset=%B2_TOOLSET% cxxstd=%B2_CXXSTD%) + cd %BOOST_ROOT% + ECHO b2 libs/%SELF:\=/% %B2_TOOLCXX% %B2_CXXFLAGS% %B2_DEFINES% %B2_THREADING% %B2_ADDRESS_MODEL% %B2_LINK% %B2_THREADING% %B2_VARIANT% -j3 + b2 libs/%SELF:\=/% %B2_TOOLCXX% %B2_CXXFLAGS% %B2_DEFINES% %B2_THREADING% %B2_ADDRESS_MODEL% %B2_LINK% %B2_THREADING% %B2_VARIANT% -j3 + displayName: 'Build' + + - job: 'macOS' + pool: + vmImage: 'macOS-10.13' + strategy: + matrix: + Xcode 10.1: + B2_TOOLSET: clang + B2_CXXSTD: 14,17,2a + XCODE_APP: /Applications/Xcode_10.1.app + Xcode 10.0: + B2_TOOLSET: clang + B2_CXXSTD: 14,17,2a + XCODE_APP: /Applications/Xcode_10.app + Xcode 9.4.1: + B2_TOOLSET: clang + B2_CXXSTD: 11,14,17 + XCODE_APP: /Applications/Xcode_9.4.1.app + Xcode 9.4: + B2_TOOLSET: clang + B2_CXXSTD: 11,14,17 + XCODE_APP: /Applications/Xcode_9.4.app + Xcode 9.3.1: + B2_TOOLSET: clang + B2_CXXSTD: 11,14,17 + XCODE_APP: /Applications/Xcode_9.3.1.app + Xcode 9.3: + B2_TOOLSET: clang + B2_CXXSTD: 11,14 + XCODE_APP: /Applications/Xcode_9.3.app + Xcode 9.2: + B2_TOOLSET: clang + B2_CXXSTD: 11,14 + XCODE_APP: /Applications/Xcode_9.2.app + Xcode 9.1: + B2_TOOLSET: clang + B2_CXXSTD: 03,11 + XCODE_APP: /Applications/Xcode_9.1.app + Xcode 9.0.1: + B2_TOOLSET: clang + B2_CXXSTD: 03,11 + XCODE_APP: /Applications/Xcode_9.0.1.app + Xcode 9.0: + B2_TOOLSET: clang + B2_CXXSTD: 03,11 + XCODE_APP: /Applications/Xcode_9.app + Xcode 8.3.3: + B2_TOOLSET: clang + B2_CXXSTD: 03,11 + XCODE_APP: /Applications/Xcode_8.3.3.app + steps: + - bash: | + set -e + uname -a + sudo xcode-select -switch ${XCODE_APP} + which clang++ + + git clone --branch master https://github.com/boostorg/boost-ci.git boost-ci + cp -pr boost-ci/ci boost-ci/.codecov.yml . + rm -rf boost-ci + source ci/azure-pipelines/install.sh + + # AzP requires to run special task in order to export + # SELF and BOOST_ROOT as job-scoped variable from a script. + # NOTE: Disable set -x is necessary, see the troubleshooting guide + # on "Variables having ' (single quote) appended": + # https://docs.microsoft.com/en-us/azure/devops/pipelines/troubleshooting + set +x + echo "##vso[task.setvariable variable=SELF]"$SELF + echo "##vso[task.setvariable variable=BOOST_ROOT]"$BOOST_ROOT + set -x + displayName: Install + - bash: | + set -e + echo "SELF=$SELF" + echo "BOOST_ROOT=$BOOST_ROOT" + + cd $BOOST_ROOT/libs/$SELF + ci/azure-pipelines/build.sh --debug-configuration + displayName: 'Build' diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c98f65ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +build/ +*.*# +static/log.txt +tags diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..9448b7c2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,236 @@ +# Copyright 2016 Peter Dimov +# Copyright 2017 - 2019 James E. King III +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) + +# +# Generic Travis CI build script for boostorg repositories +# See: https://github.com/boostorg/boost-ci +# +# Instructions for customizing this script for your library: +# +# 1. Customize the compilers and language levels you want in the 'jobs'. +# 2. If you have more than include/, src/, test/, example/, examples/, or +# tools/ directories, modify your Travis CI project and add the environment +# variable DEPINST. For example if your build uses code in "bench/" and +# "fog/" directories, then set DEPINST to the following: +# --include bench --include fog +# 3. If you want to enable Coverity Scan, you need to provide the environment +# variables COVERITY_SCAN_TOKEN and COVERITY_SCAN_NOTIFICATION_EMAIL in +# your github settings. +# 4. If you want to enable a big-endian build, you need to uncomment the +# big-endian build job. +# 5. Enable pull request builds in your boostorg/ account. +# +# That's it - the scripts will do everything else for you. + +dist: xenial +language: cpp + +env: + global: + # see: http://www.boost.org/build/doc/html/bbv2/overview/invocation.html#bbv2.overview.invocation.properties + # - B2_ADDRESS_MODEL=address-model=64,32 + # - B2_LINK=link=shared,static + # - B2_THREADING=threading=multi,single + - B2_VARIANT=variant=release + +install: + - git clone https://github.com/boostorg/boost-ci.git boost-ci + - cp -pr boost-ci/ci boost-ci/.codecov.yml . + - source ci/travis/install.sh + +addons: + apt: + packages: + - binutils-gold + - gdb + - libc6-dbg + - qemu-user-static + +services: + - docker + +branches: + only: + - master + - develop + - /bugfix\/.*/ + - /feature\/.*/ + - /fix\/.*/ + - /pr\/.*/ + +script: + - cd $BOOST_ROOT/libs/$SELF + - ci/travis/build.sh + +# +# Default toolsets in Ubuntu +# +# trusty xenial bionic +# 14.04 16.04 18.04 +# ------ ------ ------ +# clang 3.4 3.8 6.0 +# gcc 4.8.2 5.3.1 7.3.0 +# + +anchors: + clang-38: &clang-38 { apt: { packages: [ "clang-3.8", + "libstdc++-6-dev" ], sources: [ "llvm-toolchain-xenial-3.8", + "ubuntu-toolchain-r-test" ] } } + clang-4: &clang-4 { apt: { packages: [ "clang-4.0", + "libstdc++-6-dev" ], sources: [ "llvm-toolchain-xenial-4.0", + "ubuntu-toolchain-r-test" ] } } + clang-5: &clang-5 { apt: { packages: [ "clang-5.0", + "libstdc++-7-dev" ], sources: [ "llvm-toolchain-xenial-5.0", + "ubuntu-toolchain-r-test" ] } } + clang-6: &clang-6 { apt: { packages: [ "clang-6.0", + "libc6-dbg", + "libc++-dev", + "libstdc++-8-dev" ], sources: [ "llvm-toolchain-xenial-6.0", + "ubuntu-toolchain-r-test" ] } } + clang-7: &clang-7 { apt: { packages: [ "clang-7", + "libc6-dbg", + "libc++-dev", + "libstdc++-8-dev" ], sources: [ "llvm-toolchain-xenial-7", + "ubuntu-toolchain-r-test" ] } } + clang-8: &clang-8 { apt: { packages: [ "clang-8", + "libc6-dbg", + "libc++-dev", + "libstdc++-8-dev" ], sources: [ "llvm-toolchain-xenial-8", + "ubuntu-toolchain-r-test" ] } } + gcc-48: &gcc-48 { apt: { packages: [ "g++-4.8" ] } } + gcc-49: &gcc-49 { apt: { packages: [ "g++-4.9" ], sources: [ "ubuntu-toolchain-r-test" ] } } + gcc-5: &gcc-5 { apt: { packages: [ "g++-5" ] } } + gcc-6: &gcc-6 { apt: { packages: [ "g++-6" ], sources: [ "ubuntu-toolchain-r-test" ] } } + gcc-7: &gcc-7 { apt: { packages: [ "g++-7" ], sources: [ "ubuntu-toolchain-r-test" ] } } + gcc-8: &gcc-8 { apt: { packages: [ "g++-8" ], sources: [ "ubuntu-toolchain-r-test" ] } } + gcc-9: &gcc-9 { apt: { packages: [ "g++-9" ], sources: [ "ubuntu-toolchain-r-test" ] } } + +jobs: + allow_failures: + - env: + - COPY="all the environment settings from your job" + + include: + # coverage + - os: linux + env: + - COMMENT=codecov.io + - B2_CXXSTD=11 + - B2_TOOLSET=gcc-8 + - B2_DEFINES="define=BOOST_NO_STRESS_TEST=1" + addons: *gcc-8 + script: + - cd $BOOST_ROOT/libs/$SELF + - ci/travis/codecov.sh + + - os: linux + env: + - COMMENT=asan + - B2_VARIANT=variant=debug + - B2_TOOLSET=gcc-8 + - B2_CXXSTD=11,14 + - B2_CXXFLAGS="address-sanitizer=norecover" + - B2_DEFINES="define=BOOST_NO_STRESS_TEST=1" + addons: *gcc-8 + + - os: linux + env: + - COMMENT=tsan + - B2_VARIANT=variant=debug + - B2_TOOLSET=gcc-8 + - B2_CXXSTD=11,14 + - B2_CXXFLAGS="thread-sanitizer=norecover" + - B2_DEFINES="define=BOOST_NO_STRESS_TEST=1" + addons: *gcc-8 + + - os: linux + env: + - COMMENT=ubsan + - B2_VARIANT=variant=debug + - B2_TOOLSET=gcc-8 + - B2_CXXSTD=11,14 + - B2_CXXFLAGS="undefined-sanitizer=norecover" + - B2_DEFINES="define=BOOST_NO_STRESS_TEST=1" + - B2_LINKFLAGS="linkflags=-fuse-ld=gold" + - UBSAN_OPTIONS=print_stacktrace=1 + addons: *gcc-8 + + - os: linux + env: + - COMMENT=valgrind + - B2_TOOLSET=clang-6.0 + - B2_CXXSTD=11,14 + - B2_DEFINES="define=BOOST_NO_STRESS_TEST=1" + - B2_VARIANT=variant=debug + - B2_TESTFLAGS=testing.launcher=valgrind + - VALGRIND_OPTS=--error-exitcode=1 + addons: *clang-6 + script: + - cd $BOOST_ROOT/libs/$SELF + - ci/travis/valgrind.sh + + # libstdc++ + - { os: "linux", dist: "trusty", # xenial has libstdc++ from gcc 5.4.0 with newer ABI + env: [ "B2_TOOLSET=gcc-4.8", "B2_CXXSTD=11" ], addons: *gcc-48 } + - { os: "linux", dist: "trusty", # xenial has libstdc++ from gcc 5.4.0 with newer ABI + env: [ "B2_TOOLSET=gcc-4.9", "B2_CXXSTD=11" ], addons: *gcc-49 } + - { os: "linux", env: [ "B2_TOOLSET=gcc-5", "B2_CXXSTD=11" ], addons: *gcc-5 } + - { os: "linux", env: [ "B2_TOOLSET=gcc-6", "B2_CXXSTD=11,14" ], addons: *gcc-6 } + - { os: "linux", env: [ "B2_TOOLSET=gcc-7", "B2_CXXSTD=14,17" ], addons: *gcc-7 } + - { os: "linux", env: [ "B2_TOOLSET=gcc-8", "B2_CXXSTD=17,2a" ], addons: *gcc-8 } + - { os: "linux", env: [ "B2_TOOLSET=gcc-9", "B2_CXXSTD=17,2a" ], addons: *gcc-9 } + - { os: "linux", dist: "trusty", # xenial has libstdc++ from gcc 5.4.0 with newer ABI + env: [ "B2_TOOLSET=clang-3.8", "B2_CXXSTD=11" ], addons: *clang-38 } + - { os: "linux", env: [ "B2_TOOLSET=clang-4.0", "B2_CXXSTD=11,14" ], addons: *clang-4 } + - { os: "linux", env: [ "B2_TOOLSET=clang-5.0", "B2_CXXSTD=11,14" ], addons: *clang-5 } + - { os: "linux", env: [ "B2_TOOLSET=clang-6.0", "B2_CXXSTD=14,17" ], addons: *clang-6 } + - { os: "linux", env: [ "B2_TOOLSET=clang-7", "B2_CXXSTD=17,2a" ], addons: *clang-7 } + - { os: "linux", env: [ "B2_TOOLSET=clang-8", "B2_CXXSTD=17,2a" ], addons: *clang-8 } + + # libc++ + - { os: "linux", env: [ "B2_TOOLSET=clang-6.0", "B2_CXXSTD=11,14", + "B2_CXXFLAGS=-stdlib=libc++" ], addons: *clang-6 } + - { os: "osx" , env: [ "B2_TOOLSET=clang", "B2_CXXSTD=11,17" ] } + + # to enable Intel ICC define INTEL_ICC_SERIAL_NUMBER and the following (under development): + # - { os: "linux", env: [ "B2_TOOLSET=intel-linux", "B2_CXXSTD=11,14,17" ], addons: *gcc-7, + # script: cd $BOOST_ROOT/libs/$SELF && ci/travis/intelicc.sh } + + # uncomment to enable a big-endian build job, just note that it is 5-10 times slower + # than a regular build and travis has a 50 minute time limit per job + # - os: linux + # env: + # - COMMENT=big-endian + # - B2_CXXSTD=03 + # - B2_TOOLSET=gcc + # - B2_DEFINES="define=BOOST_NO_STRESS_TEST=1" + # - BDDE_OS=red + # - BDDE_ARCH=ppc64 + # script: + # - cd $BOOST_ROOT/libs/$SELF + # - ci/travis/bdde.sh + + # - os: linux + # env: + # - COMMENT=cppcheck + # script: + # - cd $BOOST_ROOT/libs/$SELF + # - ci/travis/cppcheck.sh + + #################### Jobs to run on pushes to master, develop ################### + + # Coverity Scan + - os: linux + if: (env(COVERITY_SCAN_NOTIFICATION_EMAIL) IS present) AND (branch IN (develop, master)) AND (type IN (cron, push)) + env: + - COMMENT="Coverity Scan" + - B2_TOOLSET=clang + script: + - cd $BOOST_ROOT/libs/$SELF + - ci/travis/coverity.sh + +notifications: + email: + false diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..1373d853 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,147 @@ +# +# Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/boostorg/fixed_string +# + +cmake_minimum_required (VERSION 3.5.1) + +if (POLICY CMP0074) + cmake_policy (SET CMP0074 NEW) +endif() + +#------------------------------------------------------------------------------- + +function (DoGroupSources curdir rootdir folder) + file (GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*) + foreach (child ${children}) + if (IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child}) + DoGroupSources (${curdir}/${child} ${rootdir} ${folder}) + elseif (${child} STREQUAL "CMakeLists.txt") + source_group("" FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child}) + else() + string (REGEX REPLACE ^${rootdir} ${folder} groupname ${curdir}) + string (REPLACE "/" "\\" groupname ${groupname}) + source_group (${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child}) + endif() + endforeach() +endfunction() + +function (GroupSources curdir folder) + DoGroupSources (${curdir} ${curdir} ${folder}) +endfunction() + +#------------------------------------------------------------------------------- +# +# JSON +# +#------------------------------------------------------------------------------- + +project (JSON VERSION 1) + +set_property (GLOBAL PROPERTY USE_FOLDERS ON) + +if (MSVC) + set (CMAKE_VERBOSE_MAKEFILE FALSE) + + add_definitions ( + -D_WIN32_WINNT=0x0601 + ) + + add_compile_options( + /permissive- # strict C++ + /W4 # enable all warnings + /MP # Multi-processor compilation + ) + + set (Boost_USE_STATIC_LIBS ON) + set (Boost_USE_STATIC_RUNTIME ON) + + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") + set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ob2 /Oi /Ot /GL /MT") + set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Oi /Ot /MT") + + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") + set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") + + # for RelWithDebInfo builds, disable incremental linking + # since CMake sets it ON by default for that build type and it + # causes warnings + # + string (REPLACE "/INCREMENTAL" "/INCREMENTAL:NO" replacement_flags + ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}) + set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO ${replacement_flags}) + +else() + set (THREADS_PREFER_PTHREAD_FLAG ON) + find_package (Threads) + + set( CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra -Wpedantic -Wno-unused-parameter") + + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wrange-loop-analysis") + endif () +endif() + +# Must come before Boost includes, otherwise the +# IDE sees the wrong file due to boost/ symlinks. +include_directories (include) + +#------------------------------------------------------------------------------- +# +# Boost +# +#------------------------------------------------------------------------------- + +get_filename_component (BOOST_ROOT ../../ ABSOLUTE) + +# VFALCO I want static but "b2 stage" builds a minimal set which excludes static +add_definitions (-DBOOST_ALL_STATIC_LINK=1) + +include_directories (${BOOST_ROOT}) + +link_directories(${BOOST_ROOT}/stage/lib) + +#------------------------------------------------------------------------------- + +if ("${VARIANT}" STREQUAL "coverage") + if (MSVC) + else() + set (CMAKE_BUILD_TYPE DEBUG) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2 --coverage") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") + endif() + +elseif ("${VARIANT}" STREQUAL "ubasan") + if (MSVC) + else() + set (CMAKE_BUILD_TYPE RELWITHDEBINFO) + set (CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -msse4.2 -funsigned-char -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=address,undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/tools/blacklist.supp") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address,undefined -fno-sanitize-recover=address,undefined") + endif() + +elseif ("${VARIANT}" STREQUAL "debug") + set (CMAKE_BUILD_TYPE DEBUG) + +elseif ("${VARIANT}" STREQUAL "release") + set (CMAKE_BUILD_TYPE RELEASE) + +endif() + +#------------------------------------------------------------------------------- +# +# Tests and examples +# + +file (GLOB_RECURSE PROJECT_FILES + ${PROJECT_SOURCE_DIR}/include/boost/json.hpp + ${PROJECT_SOURCE_DIR}/include/boost/json/*.hpp + ${PROJECT_SOURCE_DIR}/include/boost/json/*.ipp +) + +add_subdirectory (test) diff --git a/Jamfile b/Jamfile new file mode 100644 index 00000000..12fa5ca8 --- /dev/null +++ b/Jamfile @@ -0,0 +1,45 @@ +# +# Copyright (c) 2019 Vinnie Falco (vinnie dot falco at gmail dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/boostorg/json +# + +import ac ; +import os ; +import feature ; +import boost ; +import modules ; +import testing ; +import ../../config/checks/config : requires ; + +boost.use-project ; + +local defines = + [ requires + cxx11_constexpr + cxx11_decltype + cxx11_hdr_tuple + cxx11_template_aliases + cxx11_variadic_templates + ] + . + /boost//headers + BOOST_ALL_NO_LIB=1 + msvc-14.1:"/permissive-" + msvc-14.2:"/permissive-" + msvc,release:"/Ob2 /Oi /Ot" + windows:_WIN32_WINNT=0x0601 + ; + +project /boost/json + : requirements + static + $(defines) + : usage-requirements + $(defines) + ; + +build-project test ; diff --git a/LICENSE_1_0.txt b/LICENSE_1_0.txt new file mode 100644 index 00000000..36b7cd93 --- /dev/null +++ b/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 6ca955b9..1b6c44b5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ -# json -Boost.JSON +# Boost.JSON + +This is currently **NOT** an official Boost library. diff --git a/include/boost/json.hpp b/include/boost/json.hpp new file mode 100644 index 00000000..87e9c222 --- /dev/null +++ b/include/boost/json.hpp @@ -0,0 +1,30 @@ +// +// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_HPP +#define BOOST_JSON_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/boost/json/allocator.hpp b/include/boost/json/allocator.hpp new file mode 100644 index 00000000..f5fd49c3 --- /dev/null +++ b/include/boost/json/allocator.hpp @@ -0,0 +1,90 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_ALLOCATOR_HPP +#define BOOST_JSON_ALLOCATOR_HPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +template +class allocator +{ + storage_ptr sp_; + + template + friend class allocator; + +public: + using pointer = T*; + using reference = T&; + using const_pointer = T const*; + using const_reference = T const&; + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + template + struct rebind + { + using other = allocator; + }; + + allocator(allocator const&) = default; + allocator& operator=(allocator const&) = default; + + allocator(); + + allocator(allocator&&) noexcept; + + allocator& operator=(allocator&&) noexcept; + + template + allocator(allocator const& other) noexcept; + + explicit + allocator(storage_ptr sp) noexcept; + + T* + allocate(size_t n); + + void + deallocate(T* t, size_t n) noexcept; + + bool + operator==(allocator const&) const noexcept; + + bool + operator!=(allocator const&) const noexcept; + + storage_ptr + get_storage() const & noexcept + { + return sp_; + } + + storage_ptr + get_storage() && noexcept + { + return std::move(sp_); + } +}; + +} // json +} // beast +} // boost + +#include + +#endif diff --git a/include/boost/json/array.hpp b/include/boost/json/array.hpp new file mode 100644 index 00000000..19c42561 --- /dev/null +++ b/include/boost/json/array.hpp @@ -0,0 +1,440 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_ARRAY_HPP +#define BOOST_JSON_ARRAY_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class value; + +/** The native type of array values +*/ +class array +{ + class table; + + table* tab_ = nullptr; + storage_ptr sp_; + + struct cleanup_assign; + struct cleanup_insert; + +public: + using value_type = value; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value&; + using const_reference = value const&; + using pointer = value*; + using const_pointer = value const*; + using iterator = value*; + using const_iterator = value const*; + using reverse_iterator = + std::reverse_iterator; + using const_reverse_iterator = + std::reverse_iterator; + + //-------------------------------------------------------------------------- + // + // Special Members + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + ~array(); + + BOOST_BEAST_DECL + array(); + + BOOST_BEAST_DECL + explicit + array(storage_ptr store); + + BOOST_BEAST_DECL + array( + size_type count); + + BOOST_BEAST_DECL + array( + size_type count, + storage_ptr store); + + BOOST_BEAST_DECL + array( + size_type count, + value_type const& v); + + BOOST_BEAST_DECL + array( + size_type count, + value_type const& v, + storage_ptr store); + + template + array( + InputIt first, InputIt last); + + template + array( + InputIt first, InputIt last, + storage_ptr store); + + BOOST_BEAST_DECL + array(array const& other); + + BOOST_BEAST_DECL + array( + array const& other, + storage_ptr store); + + BOOST_BEAST_DECL + array(array&& other) noexcept; + + BOOST_BEAST_DECL + array( + array&& other, + storage_ptr store); + + BOOST_BEAST_DECL + array( + std::initializer_list list); + + BOOST_BEAST_DECL + array( + std::initializer_list list, + storage_ptr store); + + BOOST_BEAST_DECL + array& + operator=(array&& other); + + BOOST_BEAST_DECL + array& + operator=(array const& other); + + BOOST_BEAST_DECL + array& + operator=( + std::initializer_list list); + + BOOST_BEAST_DECL + storage_ptr const& + get_storage() const noexcept + { + return sp_; + } + + //-------------------------------------------------------------------------- + // + // Elements + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + reference + at(size_type pos); + + BOOST_BEAST_DECL + const_reference + at(size_type pos) const; + + BOOST_BEAST_DECL + reference + operator[](size_type pos); + + BOOST_BEAST_DECL + const_reference + operator[](size_type pos) const; + + reference + front() + { + return (*this)[0]; + } + + const_reference + front() const + { + return (*this)[0]; + } + + reference + back() + { + return (*this)[size() - 1]; + } + + const_reference + back() const + { + return (*this)[size() - 1]; + } + + BOOST_BEAST_DECL + value_type* + data() noexcept; + + BOOST_BEAST_DECL + value_type const* + data() const noexcept; + + //-------------------------------------------------------------------------- + // + // Iterators + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + iterator + begin() noexcept; + + BOOST_BEAST_DECL + const_iterator + begin() const noexcept; + + BOOST_BEAST_DECL + const_iterator + cbegin() noexcept + { + return begin(); + } + + BOOST_BEAST_DECL + iterator + end() noexcept; + + BOOST_BEAST_DECL + const_iterator + end() const noexcept; + + BOOST_BEAST_DECL + const_iterator + cend() noexcept + { + return end(); + } + + BOOST_BEAST_DECL + reverse_iterator + rbegin() noexcept; + + BOOST_BEAST_DECL + const_reverse_iterator + rbegin() const noexcept; + + BOOST_BEAST_DECL + const_reverse_iterator + crbegin() noexcept + { + return rbegin(); + } + + BOOST_BEAST_DECL + reverse_iterator + rend() noexcept; + + BOOST_BEAST_DECL + const_reverse_iterator + rend() const noexcept; + + BOOST_BEAST_DECL + const_reverse_iterator + crend() noexcept + { + return rend(); + } + + //-------------------------------------------------------------------------- + // + // Capacity + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + bool + empty() const noexcept; + + BOOST_BEAST_DECL + size_type + size() const noexcept; + + BOOST_BEAST_DECL + size_type + max_size() const noexcept; + + BOOST_BEAST_DECL + void + reserve(size_type new_capacity); + + BOOST_BEAST_DECL + size_type + capacity() const noexcept; + + BOOST_BEAST_DECL + void + shrink_to_fit() noexcept; + + //-------------------------------------------------------------------------- + // + // Modifiers + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + void + clear() noexcept; + + BOOST_BEAST_DECL + iterator + insert( + const_iterator before, + value_type const& v); + + BOOST_BEAST_DECL + iterator + insert( + const_iterator before, + value_type&& v); + + BOOST_BEAST_DECL + iterator + insert ( + const_iterator before, + size_type count, + value_type const& v); + + template + iterator + insert( + const_iterator before, + InputIt first, InputIt last); + + BOOST_BEAST_DECL + iterator + insert( + const_iterator before, + std::initializer_list list); + + template + iterator + emplace( + const_iterator before, + Arg&& arg); + + BOOST_BEAST_DECL + iterator + erase(const_iterator pos); + + BOOST_BEAST_DECL + iterator + erase( + const_iterator first, + const_iterator last); + + BOOST_BEAST_DECL + void + push_back(value_type const& v); + + BOOST_BEAST_DECL + void + push_back(value_type&& v); + + template + reference + emplace_back(Arg&& arg); + + BOOST_BEAST_DECL + void + pop_back(); + + BOOST_BEAST_DECL + void + resize(size_type count); + + BOOST_BEAST_DECL + void + resize( + size_type count, + value_type const& v); + + BOOST_BEAST_DECL + void + swap(array& other) noexcept; + +private: + template + using iter_cat = typename + std::iterator_traits::iterator_category; + + template + array( + InputIt first, InputIt last, + storage_ptr store, + std::input_iterator_tag); + + template + array( + InputIt first, InputIt last, + storage_ptr store, + std::forward_iterator_tag); + + template + iterator + insert( + const_iterator before, + InputIt first, InputIt last, + std::input_iterator_tag); + + template + iterator + insert( + const_iterator before, + InputIt first, InputIt last, + std::forward_iterator_tag); + + template + iterator + emplace_impl( + const_iterator before, + Arg&& arg); + + BOOST_BEAST_DECL + void + destroy( + value* first, + value* last); + + BOOST_BEAST_DECL + void + move( + value* to, + value* from, + size_type n) noexcept; +}; + +} // json +} // beast +} // boost + +// Must be included here for this file to stand alone +#include + +// headers for this file are at the bottom of value.hpp + +#endif diff --git a/include/boost/json/assign_string.hpp b/include/boost/json/assign_string.hpp new file mode 100644 index 00000000..f1829630 --- /dev/null +++ b/include/boost/json/assign_string.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_ASSIGN_STRING_HPP +#define BOOST_JSON_ASSIGN_STRING_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +template +void +from_json( + std::basic_string< + char, + std::char_traits, + Allocator>& t, + value const& v) +{ + if(! v.is_string()) + throw system_error( + error::expected_string); + auto& s= v.as_string(); + t.assign(s.data(), s.size()); +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/assign_vector.hpp b/include/boost/json/assign_vector.hpp new file mode 100644 index 00000000..4bd00d9f --- /dev/null +++ b/include/boost/json/assign_vector.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_ASSIGN_VECTOR_HPP +#define BOOST_JSON_ASSIGN_VECTOR_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +template::value>::type +#endif +> +void +from_json( + std::vector& t, + value const& v) +{ + if(! v.is_array()) + throw system_error( + error::expected_array); + auto& arr = v.as_array(); + t.resize(0); + t.resize(arr.size()); + auto it = t.begin(); + for(auto const& e : arr) + e.store(*it++); +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/basic_parser.hpp b/include/boost/json/basic_parser.hpp new file mode 100644 index 00000000..61a556b5 --- /dev/null +++ b/include/boost/json/basic_parser.hpp @@ -0,0 +1,190 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_BASIC_PARSER_HPP +#define BOOST_JSON_BASIC_PARSER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +/** A parser for serialized JSON +*/ +class basic_parser +#ifndef BOOST_BEAST_DOXYGEN + : private detail::parser_base +#endif +{ + enum class state : char; + + /// Depth to which the stack does not require dynamic allocation + static std::size_t const stack_capacity = 64; + + detail::stack< + state, stack_capacity> stack_; + number::mantissa_type n_mant_; + number::exponent_type n_exp_; + bool n_neg_; + bool n_exp_neg_; + bool is_key_; + + BOOST_BEAST_DECL + static + bool + append_digit( + number::mantissa_type* value, + char digit); + + BOOST_BEAST_DECL + static + bool + append_digit( + number::exponent_type* value, + char digit, bool neg); + +public: + /// Returns `true` if the parser has completed without error + BOOST_BEAST_DECL + bool + is_done() const noexcept; + + /** Reset the state, to parse a new document. + */ + BOOST_BEAST_DECL + void + reset(); + + template< + class ConstBufferSequence +#ifndef BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + ! std::is_convertible< + ConstBufferSequence, + net::const_buffer>::value>::type +#endif + > + std::size_t + write_some( + ConstBufferSequence const& buffers, + error_code& ec); + + BOOST_BEAST_DECL + std::size_t + write_some( + net::const_buffer buffer, + error_code& ec); + + template< + class ConstBufferSequence +#ifndef BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + ! std::is_convertible< + ConstBufferSequence, + net::const_buffer>::value>::type +#endif + > + std::size_t + write( + ConstBufferSequence const& buffers, + error_code& ec); + + BOOST_BEAST_DECL + std::size_t + write( + net::const_buffer buffer, + error_code& ec); + + BOOST_BEAST_DECL + void + write_eof(error_code& ec); + +protected: + /// Constructor (default) + BOOST_BEAST_DECL + basic_parser(); + + virtual + void + on_document_begin(error_code& ec) = 0; + + virtual + void + on_object_begin(error_code& ec) = 0; + + virtual + void + on_object_end(error_code& ec) = 0; + + virtual + void + on_array_begin(error_code& ec) = 0; + + virtual + void + on_array_end(error_code& ec) = 0; + + virtual + void + on_key_data( + string_view s, + error_code& ec) = 0; + + virtual + void + on_key_end( + string_view s, + error_code& ec) = 0; + + virtual + void + on_string_data( + string_view s, + error_code& ec) = 0; + + virtual + void + on_string_end( + string_view, + error_code& ec) = 0; + + virtual + void + on_number(number n, error_code& ec) = 0; + + virtual + void + on_bool(bool b, error_code& ec) = 0; + + virtual + void + on_null(error_code& ec) = 0; +}; + +} // json +} // beast +} // boost + +#include +#if BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/detail/basic_parser.hpp b/include/boost/json/detail/basic_parser.hpp new file mode 100644 index 00000000..31df01fb --- /dev/null +++ b/include/boost/json/detail/basic_parser.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_DETAIL_BASIC_PARSER_HPP +#define BOOST_JSON_DETAIL_BASIC_PARSER_HPP + +namespace boost { +namespace beast { +namespace json { +namespace detail { + +struct parser_base +{ + static + bool + is_ws(char c) noexcept + { + return + c == ' ' || c == '\t' || + c == '\r' || c == '\n'; + } + + static + bool + is_digit(char c) noexcept + { + return static_cast(c - '0') < 10; + } + + static + bool + is_control(char c) + { + return static_cast(c) < 32; + } +}; + +} // detail +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/detail/is_specialized.hpp b/include/boost/json/detail/is_specialized.hpp new file mode 100644 index 00000000..0253f5b3 --- /dev/null +++ b/include/boost/json/detail/is_specialized.hpp @@ -0,0 +1,39 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_DETAIL_IS_SPECIALIZED_HPP +#define BOOST_JSON_DETAIL_IS_SPECIALIZED_HPP + +#include + +namespace boost { +namespace beast { +namespace json { +namespace detail { + +struct primary_template +{ +}; + +template +using is_specialized = + std::integral_constant::value>; + +template +using remove_cr = + typename std::remove_const< + typename std::remove_reference::type>::type; + +} // detail +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/detail/stack.hpp b/include/boost/json/detail/stack.hpp new file mode 100644 index 00000000..e967f9d9 --- /dev/null +++ b/include/boost/json/detail/stack.hpp @@ -0,0 +1,190 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_DETAIL_STACK_HPP +#define BOOST_JSON_DETAIL_STACK_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { +namespace detail { + +template +class stack +{ + std::vector v_; + std::size_t n_ = 0; // includes v_.size() + typename std::aligned_storage< + N * sizeof(T), alignof(T)>::type buf_; + + T* + base() noexcept + { + return reinterpret_cast< + T*>(&buf_); + } + + T const* + base() const noexcept + { + return reinterpret_cast< + T const*>(&buf_); + } + + void + destroy() + { + for(auto p = base(), + last = base() + + (n_ < N ? n_ : N); + p != last; ++p) + { + (*p).~T(); + } + } + +public: + using value_type = T; + + stack() = default; + + ~stack() + { + destroy(); + } + + // element access + + T& + operator[](std::size_t i) noexcept + { + BOOST_ASSERT(i < n_); + if(v_.empty()) + return base()[n_ - (i + 1)]; + if(i < v_.size()) + return v_[v_.size() - (i + 1)]; + return base()[ + N - (v_.size() + i + 1)]; + } + + T const& + operator[](std::size_t i) const noexcept + { + BOOST_ASSERT(i < n_); + if(v_.empty()) + return base()[n_ - (i + 1)]; + if(i < v_.size()) + return v_[v_.size() - (i + 1)]; + return base()[ + N - (v_.size() + i + 1)]; + } + + T& + front() noexcept + { + return (*this)[0]; + } + + T const& + front() const noexcept + { + return (*this)[0]; + } + + // capacity + + bool + empty() const noexcept + { + return n_ == 0; + } + + std::size_t + size() const noexcept + { + return n_; + } + + std::size_t + max_size() const noexcept + { + return v_.max_size(); + } + + void + reserve(std::size_t n) + { + if(n <= N) + return; + v_.reserve(n - N); + } + + std::size_t + capacity() const noexcept + { + return N + v_.capacity(); + } + + // modifiers + + void + clear() noexcept + { + v_.clear(); + destroy(); + n_ = 0; + } + + void + push_front(T const& t) + { + if(n_ < N) + ::new(&base()[n_]) T(t); + else + v_.push_back(t); + ++n_; + } + + template + void + emplace_front(Args&&... args) + { + if(n_ < N) + ::new(&base()[n_]) T( + std::forward(args)...); + else + v_.emplace_back( + std::forward(args)...); + ++n_; + } + + void + pop_front() + { + BOOST_ASSERT(n_ > 0); + if(! v_.empty()) + v_.pop_back(); + else + base()[n_ - 1].~T(); + --n_; + } +}; + +} // detail +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/detail/storage_adaptor.hpp b/include/boost/json/detail/storage_adaptor.hpp new file mode 100644 index 00000000..34faa82d --- /dev/null +++ b/include/boost/json/detail/storage_adaptor.hpp @@ -0,0 +1,112 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_DETAIL_STORAGE_ADAPTOR_HPP +#define BOOST_JSON_DETAIL_STORAGE_ADAPTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { +namespace detail { + +template +using allocator_of_char = + typename beast::detail::allocator_traits< + Allocator>::template rebind_alloc; + +template +struct storage_adaptor + : boost::empty_value< + allocator_of_char> + , storage +{ + // VFALCO This is all public because msvc friend bugs + + std::atomic count_; + + explicit + storage_adaptor(Allocator const& alloc) + : boost::empty_value< + allocator_of_char>( + boost::empty_init_t{}, alloc) + , count_(1) + { + } + + ~storage_adaptor() + { + } + + void + addref() noexcept override + { + ++count_; + } + + void + release() noexcept override + { + if(--count_ > 0) + return; + delete this; + } + + void* + allocate( + std::size_t n, + std::size_t align) override + { + auto const n1 = + boost::alignment::align_up(n, align); + BOOST_ASSERT(n1 >= n); + return this->get().allocate(n1); + } + + void + deallocate( + void* p, + std::size_t n, + std::size_t) noexcept override + { + this->get().deallocate( + reinterpret_cast< + char*>(p), n); + } + + bool + is_equal( + storage const& other) const noexcept override + { + auto p = dynamic_cast< + storage_adaptor const*>(&other); + if(! p) + return false; + //return this->get() == p->get(); + // VFALCO We require pointer equality + // to prevent objects from different + // "documents" getting mixed together. + return this == p; + } +}; + +} // detail +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/detail/value.hpp b/include/boost/json/detail/value.hpp new file mode 100644 index 00000000..a98f56f3 --- /dev/null +++ b/include/boost/json/detail/value.hpp @@ -0,0 +1,150 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_DETAIL_VALUE_HPP +#define BOOST_JSON_DETAIL_VALUE_HPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class value; + +namespace detail { + +// Determine if `to_json(value&,T)` is found via ADL +template +struct has_adl_to_json : std::false_type +{ +}; + +template +struct has_adl_to_json(), + std::declval()))>> + : std::true_type +{ +}; + +// Determine if `from_json(T&,value)` is found via ADL +template +struct has_adl_from_json : std::false_type +{ +}; + +template +struct has_adl_from_json(), + std::declval()))>> + : std::true_type +{ +}; + +// Determine if `t.to_json(value&)` exists +template +struct has_mf_to_json : std::false_type +{ +}; + +template +struct has_mf_to_json().to_json( + std::declval()))>> + : std::true_type +{ +}; + +// Determine if `t.to_json(value const&)` exists +template +struct has_mf_from_json : std::false_type +{ +}; + +template +struct has_mf_from_json().from_json( + std::declval()))>> + : std::true_type +{ +}; + +template +void +call_to_json( + T const& t, + value& v, + std::true_type) +{ + t.to_json(v); +} + +template +void +call_to_json( + T const& t, + value& v, + std::false_type) +{ + to_json(t, v); +} + +template +void +call_to_json( + T const& t, + value& v) +{ + call_to_json(t, v, + has_mf_to_json{}); +} + +template +void +call_from_json( + T& t, + value const& v, + std::true_type) +{ + t.from_json(v); +} + +template +void +call_from_json( + T& t, + value const& v, + std::false_type) +{ + from_json(t, v); +} + +template +void +call_from_json( + T& t, + value const& v) +{ + call_from_json(t, v, + has_mf_from_json{}); +} + +} // detail +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/detail/varint.hpp b/include/boost/json/detail/varint.hpp new file mode 100644 index 00000000..b401fa8d --- /dev/null +++ b/include/boost/json/detail/varint.hpp @@ -0,0 +1,45 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_DETAIL_VARINT_HPP +#define BOOST_JSON_DETAIL_VARINT_HPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { +namespace detail { + +BOOST_BEAST_DECL +int +varint_size(std::uint64_t value); + +BOOST_BEAST_DECL +std::pair +varint_read(void const* src); + +BOOST_BEAST_DECL +int +varint_write( + void* dest, + std::uint64_t value); + +} // detail +} // json +} // beast +} // boost + +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/detail/varint.ipp b/include/boost/json/detail/varint.ipp new file mode 100644 index 00000000..267a5673 --- /dev/null +++ b/include/boost/json/detail/varint.ipp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_DETAIL_VARINT_IPP +#define BOOST_JSON_DETAIL_VARINT_IPP + +#include +#include + +namespace boost { +namespace beast { +namespace json { +namespace detail { + +int +varint_size(std::uint64_t value) +{ + int n = 1; + while(value > 127) + { + ++n; + value /= 128; + } + return n; +} + +std::pair +varint_read(void const* src) +{ + auto cp0 = reinterpret_cast< + unsigned char const*>(src); + auto cp = cp0; + std::size_t value = 0; + std::size_t factor = 1; + while(*cp > 127) + { + value += (*cp++ & 0x7f) * factor; + factor *= 128; + } + value += *cp++ * factor; + return {value, + static_cast(cp - cp0)}; +} + +int +varint_write( + void* dest, + std::uint64_t value) +{ + auto cp0 = reinterpret_cast< + unsigned char*>(dest); + auto cp = cp0; + while(value > 127) + { + *cp++ = static_cast< + unsigned char>(value & 0x7f); + value >>= 7; + } + *cp++ = static_cast< + unsigned char>(value); + return static_cast(cp - cp0); +} + +} // detail +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/error.hpp b/include/boost/json/error.hpp new file mode 100644 index 00000000..e178b59f --- /dev/null +++ b/include/boost/json/error.hpp @@ -0,0 +1,81 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_ERROR_HPP +#define BOOST_JSON_ERROR_HPP + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +/// Error codes returned by JSON operations +enum class error +{ + syntax = 1, + + /// Unexpected extra data encountered while parsing + extra_data, + + /// A mantissa overflowed while parsing + mantissa_overflow, + + /// The parser encountered an exponent that overflowed + exponent_overflow, + + /// The parser's maximum depth limit was reached + too_deep, + + /// Expected a value of kind object + expected_object, + + /// Expected a value of kind array + expected_array, + + /// Expected a value of kind string + expected_string, + + /// Expect a value of kind number + expected_number, + + /// Expected a value of kind boolean + expected_bool, + + /// Expected a value of kind boolean + expected_null, + + /// An integer assignment would overflow + integer_overflow, + + /// The key was not found in the object + key_not_found +}; + +/// Error conditions corresponding to JSON errors +enum class condition +{ + /// A parser-related error + parse_error = 1, + + /// An error on assignment to or from a JSON value + assign_error +}; + +} // websocket +} // beast +} // boost + +#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/impl/allocator.hpp b/include/boost/json/impl/allocator.hpp new file mode 100644 index 00000000..fda77911 --- /dev/null +++ b/include/boost/json/impl/allocator.hpp @@ -0,0 +1,99 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_ALLOCATOR_HPP +#define BOOST_JSON_IMPL_ALLOCATOR_HPP + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +template +allocator:: +allocator() + : sp_(default_storage()) +{ +} + +template +allocator:: +allocator(allocator&& other) noexcept + : allocator(other) +{ + // Postcondition: *this == other +} + +template +auto +allocator:: +operator=(allocator&& other) noexcept -> + allocator& +{ + // Postcondition: *this == other + sp_ = other.sp_; + return *this; +} + +template +template +allocator:: +allocator(allocator const& other) noexcept + : sp_(other.sp_) +{ +} + +template +allocator:: +allocator(storage_ptr sp) noexcept + : sp_(std::move(sp)) +{ +} + +template +T* +allocator:: +allocate(size_t n) +{ + return static_cast(sp_->allocate( + n * sizeof(T), alignof(T))); +} + +template +void +allocator:: +deallocate(T* p, size_t n) noexcept +{ + sp_->deallocate( + p, n * sizeof(T), alignof(T)); +} + +template +bool +allocator:: +operator==(allocator const& other) const noexcept +{ + return *sp_ == *other.sp_; +} + +template +bool +allocator:: +operator!=(allocator const& other) const noexcept +{ + return *sp_ != *other.sp_; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/array.hpp b/include/boost/json/impl/array.hpp new file mode 100644 index 00000000..e42dcbcd --- /dev/null +++ b/include/boost/json/impl/array.hpp @@ -0,0 +1,262 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_ARRAY_HPP +#define BOOST_JSON_IMPL_ARRAY_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +//------------------------------------------------------------------------------ + +class array::table +{ + union + { + std::size_t capacity_; + value unused_; // for alignment + }; + + BOOST_STATIC_ASSERT( + sizeof(value) >= sizeof(std::size_t)); + + BOOST_BEAST_DECL + ~table(); + +public: + size_type size = 0; + + explicit + table(size_type capacity) + : capacity_(capacity) + { + } + + size_type + capacity() const noexcept + { + return capacity_; + } + + value_type* + begin() noexcept + { + return reinterpret_cast< + value_type*>(this + 1); + } + + value_type* + end() noexcept + { + return begin() + size; + } + + BOOST_BEAST_DECL + static + table* + create( + size_type capacity, + storage_ptr const& sp); + + BOOST_BEAST_DECL + static + void + destroy( + table* tab, + storage_ptr const& sp); +}; + +//------------------------------------------------------------------------------ + +struct array::cleanup_assign +{ + array& self; + table* tab; + bool ok = false; + + BOOST_BEAST_DECL + explicit + cleanup_assign( + array& self); + + BOOST_BEAST_DECL + ~cleanup_assign(); +}; + +//------------------------------------------------------------------------------ + +struct array::cleanup_insert +{ + array& self; + size_type pos; + size_type n; + size_type valid = 0; + bool ok = false; + + BOOST_BEAST_DECL + cleanup_insert( + size_type pos_, + size_type n_, + array& self_); + + BOOST_BEAST_DECL + ~cleanup_insert(); +}; + +//------------------------------------------------------------------------------ + +template +array:: +array( + InputIt first, InputIt last) + : array( + first, last, + default_storage()) +{ +} + +template +array:: +array( + InputIt first, InputIt last, + storage_ptr store) + : array( + first, last, + std::move(store), + iter_cat{}) +{ +} + +template +auto +array:: +insert( + const_iterator before, + InputIt first, InputIt last) -> + iterator +{ + return insert(before, first, last, + iter_cat{}); +} + +template +auto +array:: +emplace( + const_iterator before, + Arg&& arg) -> + iterator +{ + return emplace_impl( + before, std::forward(arg)); +} + +template +auto +array:: +emplace_back(Arg&& arg) -> + reference +{ + return *emplace_impl( + end(), std::forward(arg)); +} + +//------------------------------------------------------------------------------ + +template +array:: +array( + InputIt first, InputIt last, + storage_ptr store, + std::input_iterator_tag) + : sp_(std::move(store)) +{ + while(first != last) + emplace_impl(end(), *first++); +} + +template +array:: +array( + InputIt first, InputIt last, + storage_ptr store, + std::forward_iterator_tag) + : sp_(std::move(store)) +{ + reserve(std::distance(first, last)); + while(first != last) + emplace_impl(end(), *first++); +} +template +auto +array:: +insert( + const_iterator before, + InputIt first, InputIt last, + std::input_iterator_tag) -> + iterator +{ + auto pos = before - begin(); + while(first != last) + before = insert(before, *first++) + 1; + return begin() + pos; +} + +template +auto +array:: +insert( + const_iterator before, + InputIt first, InputIt last, + std::forward_iterator_tag) -> + iterator +{ + auto count = std::distance(first, last); + auto pos = before - begin(); + reserve(size() + count); + cleanup_insert c(pos, count, *this); + while(count--) + { + ::new(&begin()[pos++]) value_type( + *first++, sp_); + ++c.valid; + } + c.ok = true; + return begin() + c.pos; +} + +template +auto +array:: +emplace_impl( + const_iterator before, + Arg&& arg) -> + iterator +{ + auto const pos = before - begin(); + reserve(size() + 1); + cleanup_insert c(pos, 1, *this); + ::new(&tab_->begin()[pos]) value_type( + std::forward(arg), sp_); + c.ok = true; + return begin() + pos; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/array.ipp b/include/boost/json/impl/array.ipp new file mode 100644 index 00000000..07d26443 --- /dev/null +++ b/include/boost/json/impl/array.ipp @@ -0,0 +1,746 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_ARRAY_IPP +#define BOOST_JSON_IMPL_ARRAY_IPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +//------------------------------------------------------------------------------ + +array:: +table:: +~table() +{ + while(size > 0) + begin()[--size].~value(); +} + +auto +array:: +table:: +create( + size_type capacity, + storage_ptr const& sp) -> + table* +{ + return ::new(sp->allocate( + sizeof(table) + + capacity * sizeof(value_type), + sizeof(value))) table(capacity); +} + +void +array:: +table:: +destroy( + table* tab, + storage_ptr const& sp) +{ + auto const capacity = tab->capacity(); + tab->~table(); + sp->deallocate(tab, + sizeof(table) + + capacity * sizeof(value_type), + sizeof(value)); +} + +//------------------------------------------------------------------------------ + +array:: +cleanup_assign:: +cleanup_assign( + array& self_) + : self(self_) + , tab(boost::exchange( + self_.tab_, nullptr)) +{ +} + +array:: +cleanup_assign:: +~cleanup_assign() +{ + if(ok) + { + if(tab) + table::destroy(tab, self.sp_); + } + else + { + if(self.tab_) + table::destroy( + self.tab_, self.sp_); + self.tab_ = tab; + } +} + +//------------------------------------------------------------------------------ + +array:: +cleanup_insert:: +cleanup_insert( + size_type pos_, + size_type n_, + array& self_) + : self(self_) + , pos(pos_) + , n(n_) +{ + self.move( + self.data() + pos + n, + self.data() + pos, + self.size() - pos); +} + +array:: +cleanup_insert:: +~cleanup_insert() +{ + if(ok) + { + self.tab_->size += n; + } + else + { + for(size_type i = n; + valid--; ++i) + self[i].~value(); + + self.move( + self.data() + pos, + self.data() + pos + n, + self.size() - pos); + } +} + +//------------------------------------------------------------------------------ +// +// Special Members +// +//------------------------------------------------------------------------------ + +array:: +~array() +{ + if(tab_) + table::destroy(tab_, sp_); +} + +array:: +array() + : sp_(default_storage()) +{ +} + +array:: +array(storage_ptr store) + : sp_(std::move(store)) +{ +} + +array:: +array( + size_type count) + : array( + count, kind::null, + default_storage()) +{ +} + +array:: +array( + size_type count, + storage_ptr store) + : array( + count, kind::null, + std::move(store)) +{ +} + +array:: +array( + size_type count, + value_type const& v) + : array( + count, v, + default_storage()) +{ +} + +array:: +array( + size_type count, + value_type const& v, + storage_ptr store) + : sp_(std::move(store)) +{ + resize(count, v); +} + +array:: +array(array const& other) + : sp_(other.get_storage()) +{ + *this = other; +} + +array:: +array( + array const& other, + storage_ptr store) + : sp_(std::move(store)) +{ + *this = other; +} + +array:: +array(array&& other) noexcept + : tab_(boost::exchange( + other.tab_, nullptr)) + , sp_(other.sp_) +{ +} + +array:: +array( + array&& other, + storage_ptr store) + : sp_(std::move(store)) +{ + *this = std::move(other); +} + +array:: +array( + std::initializer_list list) + : sp_(default_storage()) +{ + *this = list; +} + +array:: +array( + std::initializer_list list, + storage_ptr store) + : sp_(std::move(store)) +{ + *this = list; +} + +array& +array:: +operator=(array&& other) +{ + if(*sp_ == *other.sp_) + { + if(tab_) + table::destroy(tab_, sp_); + tab_ = boost::exchange( + other.tab_, nullptr); + } + else + { + *this = other; + } + return *this; +} + +array& +array:: +operator=(array const& other) +{ + cleanup_assign c(*this); + reserve(other.size()); + for(auto const& v : other) + emplace_impl(end(), v); + c.ok = true; + return *this; +} + +array& +array:: +operator=( + std::initializer_list list) +{ + cleanup_assign c(*this); + reserve(list.size()); + for(auto it = list.begin(); + it != list.end(); ++it) + emplace_impl(end(), std::move(*it)); + c.ok = true; + return *this; +} + +//------------------------------------------------------------------------------ +// +// Elements +// +//------------------------------------------------------------------------------ + +auto +array:: +at(size_type pos) -> + reference +{ + if(pos >= size()) + throw std::out_of_range( + "json::array index out of bounds"); + return tab_->begin()[pos]; +} + +auto +array:: +at(size_type pos) const -> + const_reference +{ + if(pos >= size()) + throw std::out_of_range( + "json::array index out of bounds"); + return tab_->begin()[pos]; +} + +auto +array:: +operator[](size_type pos) -> + reference +{ + return tab_->begin()[pos]; +} + +auto +array:: +operator[](size_type pos) const -> + const_reference +{ + return tab_->begin()[pos]; +} + +auto +array:: +data() noexcept -> + value_type* +{ + if(! tab_) + return nullptr; + return tab_->begin(); +} + +auto +array:: +data() const noexcept -> + value_type const* +{ + if(! tab_) + return nullptr; + return tab_->begin(); +} + +//------------------------------------------------------------------------------ +// +// Iterators +// +//------------------------------------------------------------------------------ + +auto +array:: +begin() noexcept -> + iterator +{ + if(! tab_) + return nullptr; + return tab_->begin(); +} + +auto +array:: +begin() const noexcept -> + const_iterator +{ + if(! tab_) + return nullptr; + return tab_->begin(); +} + +auto +array:: +end() noexcept -> + iterator +{ + if(! tab_) + return nullptr; + return tab_->end(); +} + +auto +array:: +end() const noexcept -> + const_iterator +{ + if(! tab_) + return nullptr; + return tab_->end(); +} + +auto +array:: +rbegin() noexcept -> + reverse_iterator +{ + if(! tab_) + return reverse_iterator(nullptr); + return reverse_iterator(tab_->end()); +} + +auto +array:: +rbegin() const noexcept -> + const_reverse_iterator +{ + if(! tab_) + return const_reverse_iterator(nullptr); + return const_reverse_iterator(tab_->end()); +} + +auto +array:: +rend() noexcept -> + reverse_iterator +{ + if(! tab_) + return reverse_iterator(nullptr); + return reverse_iterator(tab_->begin()); +} + +auto +array:: +rend() const noexcept -> + const_reverse_iterator +{ + if(! tab_) + return const_reverse_iterator(nullptr); + return const_reverse_iterator(tab_->begin()); +} + +//------------------------------------------------------------------------------ +// +// Capacity +// +//------------------------------------------------------------------------------ + +bool +array:: +empty() const noexcept +{ + return ! tab_ || tab_->size == 0; +} + +auto +array:: +size() const noexcept -> + size_type +{ + if(! tab_) + return 0; + return tab_->size; +} + +auto +array:: +max_size() const noexcept -> + size_type +{ + return (std::numeric_limits< + size_type>::max)() / sizeof(value_type); +} + +void +array:: +reserve(size_type new_capacity) +{ + // minimum size is 3 + if( new_capacity > 0 && + new_capacity < 3) + new_capacity = 3; + + // don't shrink + if( tab_ && + new_capacity <= tab_->capacity()) + return; + new_capacity = (std::max)( + tab_ ? ((tab_->capacity() * 3 + 1) / 2) : 0, + new_capacity); + + auto tab = table::create(new_capacity, sp_); + if(! tab_) + { + tab_ = tab; + return; + } + + for(size_type i = 0; i < tab_->size; ++i) + ::new(&tab->begin()[i]) value_type( + std::move(tab_->begin()[i])); + tab->size = tab_->size; + std::swap(tab, tab_); + table::destroy(tab, sp_); +} + +auto +array:: +capacity() const noexcept -> + size_type +{ + if(! tab_) + return 0; + return tab_->capacity(); +} + +void +array:: +shrink_to_fit() noexcept +{ + if(! tab_ || + tab_->capacity() <= tab_->size) + return; + auto tab = table::create(tab_->size, sp_); + for(size_type i = 0; i < tab_->size; ++i) + ::new(&tab->begin()[i]) value_type( + std::move(tab_->begin()[i])); + tab->size = tab_->size; + std::swap(tab, tab_); + table::destroy(tab, sp_); +} + +//------------------------------------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------------------------------------ + +void +array:: +clear() noexcept +{ + if(! tab_) + return; + destroy(begin(), end()); + tab_->size = 0; +} + +auto +array:: +insert( + const_iterator before, + value_type const& v) -> + iterator +{ + return emplace_impl(before, v); +} + +auto +array:: +insert( + const_iterator before, + value_type&& v) -> + iterator +{ + return emplace_impl( + before, std::move(v)); +} + +auto +array:: +insert( + const_iterator before, + size_type count, + value_type const& v) -> + iterator +{ + auto pos = before - begin(); + reserve(size() + count); + cleanup_insert c(pos, count, *this); + while(count--) + { + ::new(&begin()[pos++]) + value_type(v, sp_); + ++c.valid; + } + c.ok = true; + return begin() + c.pos; +} + +auto +array:: +insert( + const_iterator before, + std::initializer_list list) -> + iterator +{ + auto pos = before - begin(); + reserve(size() + list.size()); + cleanup_insert c( + pos, list.size(), *this); + for(auto it = list.begin(); + it != list.end(); ++it) + { + ::new(&begin()[pos++]) value_type( + std::move(*it), sp_); + ++c.valid; + } + c.ok = true; + return begin() + c.pos; +} + +auto +array:: +erase(const_iterator pos) -> + iterator +{ + auto it = data() + (pos - begin()); + destroy(it, it + 1); + move(it, it + 1, 1); + --tab_->size; + return it; +} + +auto +array:: +erase( + const_iterator first, + const_iterator last) -> + iterator +{ + auto const n = last - first; + auto it = data() + (first - begin()); + destroy(it, it + n); + move(it, it + n, n); + tab_->size -= n; + return it; +} + +void +array:: +push_back(value_type const& v) +{ + emplace_impl(end(), v); +} + +void +array:: +push_back(value_type&& v) +{ + emplace_impl(end(), std::move(v)); +} + +void +array:: +pop_back() +{ + back().~value(); + --tab_->size; +} + +void +array:: +resize(size_type count) +{ + resize(count, kind::null); +} + +void +array:: +resize( + size_type count, + value_type const& v) +{ + if(count > size()) + { + reserve(count); + while(count--) + emplace_impl(end(), v); + } + else if(count < size()) + { + tab_->size = count; + count = size() - count; + for(size_type i = size() - 1; + count-- > 0; --i) + tab_->begin()[i].~value(); + } +} + +void +array:: +swap(array& other) noexcept +{ + BOOST_ASSERT(*sp_ == *other.sp_); + std::swap(tab_, other.tab_); +} + +//------------------------------------------------------------------------------ + +void +array:: +destroy( + value* first, + value* last) +{ + while(first != last) + (*first++).~value(); +} + +void +array:: +move( + value* to, + value* from, + size_type n) noexcept +{ + if(to > from) + { + // backwards + to += n; + from += n; + while(n--) + { + ::new(&*--to) value_type( + std::move(*--from)); + from->~value(); + } + } + else + { + while(n--) + { + ::new(&*to++) value_type( + std::move(*from)); + (*from++).~value(); + } + } +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/basic_parser.hpp b/include/boost/json/impl/basic_parser.hpp new file mode 100644 index 00000000..4e5f1bb3 --- /dev/null +++ b/include/boost/json/impl/basic_parser.hpp @@ -0,0 +1,76 @@ + // +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_BASIC_PARSER_HPP +#define BOOST_JSON_IMPL_BASIC_PARSER_HPP + +#include + +namespace boost { +namespace beast { +namespace json { + +template +std::size_t +basic_parser:: +write_some( + ConstBufferSequence const& buffers, + error_code& ec) +{ + static_assert( + net::is_const_buffer_sequence::value, + "ConstBufferSequence type requirements not met"); + + std::size_t bytes_used = 0; + for(auto const b : beast::buffers_range_ref(buffers)) + { + bytes_used += write_some(b, ec); + if(ec) + break; + } + return bytes_used; +} + +template +std::size_t +basic_parser:: +write( + ConstBufferSequence const& buffers, + error_code& ec) +{ + static_assert( + net::is_const_buffer_sequence::value, + "ConstBufferSequence type requirements not met"); + + std::size_t bytes_used = 0; + auto it = + net::buffer_sequence_begin(buffers); + auto end = + net::buffer_sequence_end(buffers); + if(it == end) + { + ec = {}; + return 0; + } + for(--end; it != end; ++it) + { + bytes_used += + write_some(*it, ec); + if(ec) + return bytes_used; + } + bytes_used += write(*it, ec); + return bytes_used; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/basic_parser.ipp b/include/boost/json/impl/basic_parser.ipp new file mode 100644 index 00000000..9e51a216 --- /dev/null +++ b/include/boost/json/impl/basic_parser.ipp @@ -0,0 +1,967 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_BASIC_PARSER_IPP +#define BOOST_JSON_IMPL_BASIC_PARSER_IPP + +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +/* References: + + https://www.json.org/ + + RFC 7159: The JavaScript Object Notation (JSON) Data Interchange Format + https://tools.ietf.org/html/rfc7159 + + character + '0020' . '10ffff' - '"' - '\' + '\' escape + + escape + '"' + '\' + '/' + 'b' + 'n' + 'r' + 't' + 'u' hex hex hex hex + + hex + digit + 'A' . 'F' + 'a' . 'f' + + number + int frac exp + + ws + "" + '0009' ws + '000A' ws + '000D' ws + '0020' ws +*/ + +enum class basic_parser::state : char +{ + json, + element, + ws, + value, + + object1, object2, object3, object4, colon, + array1, array2, array3, array4, + string1, string2, string3, + true1, true2, true3, true4, + false1, false2, false3, false4, false5, + null1, null2, null3, null4, + + number, number_mant1, number_mant2, + number_fract1, number_fract2, number_fract3, + number_exp, number_exp_sign, + number_exp_digits1, number_exp_digits2, number_end, + + end +}; + +basic_parser:: +basic_parser() +{ + reset(); +} + +bool +basic_parser::is_done() const noexcept +{ + return stack_.front() == state::end; +} + +void +basic_parser:: +reset() +{ + stack_.clear(); + stack_.push_front(state::end); + stack_.push_front(state::json); +} + +//------------------------------------------------------------------------------ + +// Append the digit to the +// value, which must be unsigned. +// Returns `false` on overflow. +bool +basic_parser:: +append_digit( + number::mantissa_type* value, + char digit) +{ + number::mantissa_type temp = + *value * 10; + if(temp < *value) + return false; + number::mantissa_type result = + temp + digit; + if(result < temp) + return false; + *value = result; + return true; +} + +// Append the digit to the signed exponent +bool +basic_parser:: +append_digit( + number::exponent_type* value, + char digit, bool neg) +{ + if(neg) + { + if(! *value) + { + *value = -digit; + } + else + { + *value *= 10; + *value -= digit; + } + } + else + { + *value *= 10; + *value += digit; + } + return true; +} + +//------------------------------------------------------------------------------ + +void +basic_parser:: +write_eof(error_code& ec) +{ + // write a null, this is invalid no matter + // what state we are in, to get a descriptive + // error. + // + // VFALCO we might want to return error::partial_data + + auto const fail = + [this, &ec] + { + char c = 0; + write_some( + boost::asio::const_buffer( + &c, 1), ec); + BOOST_ASSERT(ec); + }; + + while(stack_.front() != state::end) + { + // pop all states that + // allow "" (empty string) + switch(stack_.front()) + { + case state::number_mant2: + case state::number_fract1: + case state::number_fract3: + case state::number_exp: + case state::number_exp_digits2: + stack_.front() = state::number_end; + write_some( + net::const_buffer{}, ec); + if(ec) + return; + break; + + case state::ws: + stack_.pop_front(); + break; + + default: + return fail(); + } + } + ec = {}; +} + +//------------------------------------------------------------------------------ + +std::size_t +basic_parser:: +write_some( + net::const_buffer buffer, + error_code& ec) +{ + auto p = static_cast< + char const*>(buffer.data()); + auto n = buffer.size(); + auto const p0 = p; + auto const p1 = p0 + n; + static_string<4096> temp; + ec.assign(0, ec.category()); + BOOST_ASSERT(stack_.front() != state::end); + auto const maybe_flush = + [&] + { + if(temp.size() != temp.max_size()) + return; + if(is_key_) + this->on_key_data( + {temp.data(), temp.size()}, ec); + else + this->on_string_data( + {temp.data(), temp.size()}, ec); + }; +loop: + switch(stack_.front()) + { + case state::json: + this->on_document_begin(ec); + if(ec) + goto finish; + stack_.front() = state::element; + temp.clear(); + is_key_ = false; + goto loop; + + case state::element: + stack_.front() = state::ws; + stack_.push_front(state::value); + stack_.push_front(state::ws); + goto loop; + + case state::ws: + while(p < p1) + { + if(! is_ws(*p)) + { + stack_.pop_front(); + goto loop; + } + ++p; + } + break; + + case state::value: + { + if(p >= p1) + break; + switch(*p) + { + // object + case '{': + stack_.front() = state::object1; + goto loop; + + // array + case '[': + ++p; + stack_.front() = state::array1; + this->on_array_begin(ec); + goto loop; + + // string + case '"': + stack_.front() = state::string1; + goto loop; + + // number + case '0': + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + case '-': + stack_.front() = state::number; + goto loop; + + // true + case 't': + if(p + 4 <= p1) + { + if( + p[1] != 'r' || + p[2] != 'u' || + p[3] != 'e') + { + ec = error::syntax; + goto finish; + } + p = p + 4; + stack_.front() = state::true4; + goto loop; + } + ++p; + stack_.front() = state::true1; + goto loop; + + // false + case 'f': + if(p + 5 <= p1) + { + if( + p[1] != 'a' || + p[2] != 'l' || + p[3] != 's' || + p[4] != 'e') + { + ec = error::syntax; + goto finish; + } + p = p + 5; + stack_.front() = state::false5; + goto loop; + } + ++p; + stack_.front() = state::false1; + goto loop; + + // null + case 'n': + if(p + 4 <= p1) + { + if( + p[1] != 'u' || + p[2] != 'l' || + p[3] != 'l') + { + ec = error::syntax; + goto finish; + } + p = p + 4; + stack_.front() = state::null4; + goto loop; + } + ++p; + stack_.front() = state::null1; + goto loop; + + default: + ec = error::syntax; + goto finish; + } + break; + } + + //-------------------------------------------------------------------------- + // + // object + // + + // beginning of object + case state::object1: + BOOST_ASSERT(*p == '{'); + ++p; + this->on_object_begin(ec); + if(ec) + goto finish; + stack_.front() = state::object2; + stack_.push_front(state::ws); + goto loop; + + // first key or end of object + case state::object2: + if(p >= p1) + break; + if(*p == '}') + { + ++p; + stack_.front() = state::object4; + goto loop; + } + stack_.front() = state::object3; + stack_.push_front(state::element); + stack_.push_front(state::colon); + stack_.push_front(state::ws); + stack_.push_front(state::string1); + is_key_ = true; + goto loop; + + case state::object3: + if(p >= p1) + break; + if(*p == '}') + { + ++p; + stack_.front() = state::object4; + goto loop; + } + if(*p != ',') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::object3; + stack_.push_front(state::element); + stack_.push_front(state::colon); + stack_.push_front(state::ws); + stack_.push_front(state::string1); + stack_.push_front(state::ws); + is_key_ = true; + goto loop; + + case state::object4: + this->on_object_end(ec); + if(ec) + goto finish; + stack_.pop_front(); + goto loop; + + case state::colon: + if(p >= p1) + break; + if(*p != ':') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.pop_front(); + goto loop; + + //-------------------------------------------------------------------------- + // + // array + // + + case state::array1: + stack_.front() = state::array2; + stack_.push_front(state::ws); + goto loop; + + case state::array2: + if(p >= p1) + break; + if(*p == ']') + { + ++p; + stack_.front() = state::array4; + goto loop; + } + stack_.front() = state::array3; + stack_.push_front(state::element); + goto loop; + + case state::array3: + if(p >= p1) + break; + if(*p == ']') + { + ++p; + stack_.front() = state::array4; + goto loop; + } + if(*p != ',') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::array3; + stack_.push_front(state::element); + stack_.push_front(state::ws); + goto loop; + + case state::array4: + this->on_array_end(ec); + if(ec) + goto finish; + stack_.pop_front(); + goto loop; + + //-------------------------------------------------------------------------- + // + // string + // + + // double quote opening string + case state::string1: + if(p >= p1) + break; + if(*p != '\"') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::string2; + goto loop; + + // characters + case state::string2: + while(p < p1) + { + if(*p == '\"') + { + ++p; + if(is_key_) + this->on_key_end({temp.data(), + temp.size()}, ec); + else + this->on_string_end({temp.data(), + temp.size()}, ec); + if(ec) + goto finish; + temp.clear(); + is_key_ = false; + stack_.pop_front(); + goto loop; + } + if(*p == '\\') + { + ++p; + stack_.front() = state::string3; + goto loop; + } + if(is_control(*p)) + { + ec = error::syntax; + goto finish; + } + // TODO UTF-8 + maybe_flush(); + temp.push_back(*p++); + } + break; + + // escape + case state::string3: + if(p >= p1) + break; + switch(*p) + { + case '\"': + maybe_flush(); + temp.push_back('\"'); + break; + + case '\\': + maybe_flush(); + temp.push_back('\\'); + break; + + case '/': + maybe_flush(); + temp.push_back('/'); + break; + + case 'b': + maybe_flush(); + temp.push_back('\x08'); + break; + + case 'f': + maybe_flush(); + temp.push_back('\x08'); + break; + + case 'n': + maybe_flush(); + temp.push_back('\x0a'); + break; + + case 'r': + maybe_flush(); + temp.push_back('\x0d'); + break; + + case 't': + maybe_flush(); + temp.push_back('\x09'); + break; + + case 'u': + BOOST_ASSERT(false); + break; + + default: + ec = error::syntax; + goto finish; + } + ++p; + stack_.front()=state::string2; + goto loop; + + //-------------------------------------------------------------------------- + + // + // true + // + + case state::true1: + if(p >= p1) + break; + if(*p != 'r') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::true2; + BOOST_FALLTHROUGH; + + case state::true2: + if(p >= p1) + break; + if(*p != 'u') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::true3; + BOOST_FALLTHROUGH; + + case state::true3: + if(p >= p1) + break; + if(*p != 'e') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::true4; + BOOST_FALLTHROUGH; + + case state::true4: + this->on_bool(true, ec); + if(ec) + goto finish; + stack_.pop_front(); + goto loop; + + // + // false + // + + case state::false1: + if(p >= p1) + break; + if(*p != 'a') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::false2; + BOOST_FALLTHROUGH; + + case state::false2: + if(p >= p1) + break; + if(*p != 'l') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::false3; + BOOST_FALLTHROUGH; + + case state::false3: + if(p >= p1) + break; + if(*p != 's') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::false4; + BOOST_FALLTHROUGH; + + case state::false4: + if(p >= p1) + break; + if(*p != 'e') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::false5; + BOOST_FALLTHROUGH; + + case state::false5: + this->on_bool(false, ec); + if(ec) + goto finish; + stack_.pop_front(); + goto loop; + + // + // null + // + + case state::null1: + if(p >= p1) + break; + if(*p != 'u') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::null2; + BOOST_FALLTHROUGH; + + case state::null2: + if(p >= p1) + break; + if(*p != 'l') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::null3; + BOOST_FALLTHROUGH; + + case state::null3: + if(p >= p1) + break; + if(*p != 'l') + { + ec = error::syntax; + goto finish; + } + ++p; + stack_.front() = state::null4; + BOOST_FALLTHROUGH; + + case state::null4: + this->on_null(ec); + if(ec) + goto finish; + stack_.pop_front(); + goto loop; + + // + // number + // + + case state::number: + BOOST_ASSERT(p < p1); + n_mant_ = 0; + n_exp_ = 0; + if(*p == '-') + { + ++p; + n_neg_ = true; + } + else + { + n_neg_ = false; + } + stack_.front() = state::number_mant1; + goto loop; + + case state::number_mant1: + if(p >= p1) + break; + if(! is_digit(*p)) + { + // expected mantissa digit + ec = error::syntax; + goto finish; + } + if(*p != '0') + { + stack_.front() = state::number_mant2; + goto loop; + } + ++p; + stack_.front() = state::number_fract1; + goto loop; + + case state::number_mant2: + while(p < p1) + { + if(! is_digit(*p)) + { + stack_.front() = state::number_fract1; + goto loop; + } + if(! append_digit(&n_mant_, *p++ - '0')) + { + ec = error::mantissa_overflow; + goto finish; + } + } + break; + + case state::number_fract1: + if(p >= p1) + break; + if(*p == '.') + { + ++p; + stack_.front() = state::number_fract2; + goto loop; + } + if(is_digit(*p)) + { + // unexpected digit after zero + ec = error::syntax; + goto finish; + } + stack_.front() = state::number_exp; + goto loop; + + case state::number_fract2: + if(p >= p1) + break; + if(! is_digit(*p)) + { + // expected mantissa fraction digit + ec = error::syntax; + goto finish; + } + stack_.front() = state::number_fract3; + goto loop; + + case state::number_fract3: + while(p < p1) + { + if(! is_digit(*p)) + { + stack_.front() = state::number_exp; + goto loop; + } + if(! append_digit(&n_mant_, *p++ - '0')) + { + ec = error::mantissa_overflow; + goto finish; + } + --n_exp_; + } + break; + + case state::number_exp: + if(p >= p1) + break; + if(*p == 'e' || *p == 'E') + { + ++p; + stack_.front() = state::number_exp_sign; + goto loop; + } + stack_.front() = state::number_end; + goto loop; + + case state::number_exp_sign: + if(p >= p1) + break; + if(*p == '+') + { + ++p; + n_exp_neg_ = false; + } + else if(*p == '-') + { + ++p; + n_exp_neg_ = true; + } + else + { + n_exp_neg_ = false; + } + stack_.front() = state::number_exp_digits1; + goto loop; + + case state::number_exp_digits1: + if(p >= p1) + break; + if(! is_digit(*p)) + { + // expected exponent digit + ec = error::syntax; + goto finish; + } + stack_.front() = state::number_exp_digits2; + goto loop; + + case state::number_exp_digits2: + while(p < p1) + { + if(! is_digit(*p)) + { + stack_.front() = state::number_end; + goto loop; + } + if(! append_digit(&n_exp_, + *p++ - '0', n_exp_neg_)) + { + ec = error::exponent_overflow; + goto finish; + } + } + break; + + case state::number_end: + this->on_number(number( + n_mant_, n_exp_, n_neg_), ec); + if(ec) + goto finish; + stack_.pop_front(); + goto loop; + + // + // (end) + // + + case state::end: + /* + if(p < p1) + { + // unexpected extra characters + ec = error::syntax; + goto finish; + } + */ + break; + } + +finish: + return p - p0; +} + +//------------------------------------------------------------------------------ + +// Called to parse the rest of the document, this +// can be optimized by assuming no more data is coming. +std::size_t +basic_parser:: +write( + net::const_buffer buffer, + error_code& ec) +{ + auto bytes_used = + write_some(buffer, ec); + if(! ec) + write_eof(ec); + return bytes_used; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/error.hpp b/include/boost/json/impl/error.hpp new file mode 100644 index 00000000..e5a7a8ee --- /dev/null +++ b/include/boost/json/impl/error.hpp @@ -0,0 +1,46 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_ERROR_HPP +#define BOOST_JSON_IMPL_ERROR_HPP + +#include + +namespace boost { +namespace system { +template<> +struct is_error_code_enum<::boost::beast::json::error> +{ + static bool const value = true; +}; +template<> +struct is_error_condition_enum<::boost::beast::json::condition> +{ + static bool const value = true; +}; +} // system +} // boost + +namespace boost { +namespace beast { +namespace json { + +BOOST_BEAST_DECL +error_code +make_error_code(error e); + +BOOST_BEAST_DECL +error_condition +make_error_condition(condition c); + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/error.ipp b/include/boost/json/impl/error.ipp new file mode 100644 index 00000000..dd48eff8 --- /dev/null +++ b/include/boost/json/impl/error.ipp @@ -0,0 +1,157 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_ERROR_IPP +#define BOOST_JSON_IMPL_ERROR_IPP + +#include + +namespace boost { +namespace beast { +namespace json { + +namespace detail { + +class error_codes : public error_category +{ +public: + const char* + name() const noexcept override + { + return "boost.beast.json"; + } + + BOOST_BEAST_DECL + std::string + message(int ev) const override + { + switch(static_cast(ev)) + { + default: + case error::syntax: return + "The serialized JSON object contains a syntax error"; + + case error::extra_data: return + "Unexpected extra data encountered while parsing"; + + case error::mantissa_overflow: return + "A mantissa overflowed while parsing"; + + case error::exponent_overflow: return + "An exponent overflowed while parsing"; + + case error::too_deep: return + "The parser reached the maximum allowed depth"; + + // + + case error::integer_overflow: return + "An integer assignment overflowed"; + + case error::expected_object: return + "Expected a value of kind object"; + + case error::expected_array: return + "Expected a value of kind array"; + + case error::expected_string: return + "Expected a value of kind string"; + + case error::expected_number: return + "Expected a value of kind number"; + + case error::expected_bool: return + "Expected a value of kind bool"; + + case error::expected_null: return + "Expected a value of kind null"; + + // + + case error::key_not_found: return + "The key was not found in the object"; + } + } + + BOOST_BEAST_DECL + error_condition + default_error_condition(int ev) const noexcept override + { + switch(static_cast(ev)) + { + default: + return {ev, *this}; + + case error::syntax: + case error::extra_data: + case error::mantissa_overflow: + case error::exponent_overflow: + case error::too_deep: + return condition::parse_error; + + case error::integer_overflow: + case error::expected_object: + case error::expected_array: + case error::expected_string: + case error::expected_number: + case error::expected_bool: + case error::expected_null: + return condition::assign_error; + } + } +}; + +class error_conditions : public error_category +{ +public: + BOOST_BEAST_DECL + const char* + name() const noexcept override + { + return "boost.beast"; + } + + BOOST_BEAST_DECL + std::string + message(int cv) const override + { + switch(static_cast(cv)) + { + default: + case condition::parse_error: + return "A JSON parsing error occurred"; + case condition::assign_error: + return "An error occurred during assignment"; + } + } +}; + +} // detail + +error_code +make_error_code(error e) +{ + static detail::error_codes const cat{}; + return error_code{static_cast< + std::underlying_type::type>(e), cat}; +} + +error_condition +make_error_condition(condition c) +{ + static detail::error_conditions const cat{}; + return error_condition{static_cast< + std::underlying_type::type>(c), cat}; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/iterator.ipp b/include/boost/json/impl/iterator.ipp new file mode 100644 index 00000000..6060b3ca --- /dev/null +++ b/include/boost/json/impl/iterator.ipp @@ -0,0 +1,214 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_ITERATOR_IMPL_IPP +#define BOOST_JSON_ITERATOR_IMPL_IPP + +#include + +namespace boost { +namespace beast { +namespace json { + +const_iterator:: +node:: +~node() +{ + if(it) + { + if(v->is_object()) + { + obj_it.~const_iterator(); + } + else + { + // VFALCO Why not? + //arr.it.~const_iterator(); + } + } +} + +const_iterator:: +node:: +node( + value const& v_, + bool it_) noexcept + : v(&v_) + , it(it_) +{ + if(it) + { + BOOST_ASSERT( + v->is_structured()); + if(v->is_object()) + ::new(&obj_it) + object::const_iterator( + v->as_object().begin()); + else + ::new(&arr_it) + array::const_iterator( + v->as_array().begin()); + } +} + +const_iterator:: +node:: +node(node const& other) noexcept + : v(other.v) + , key(other.key) +{ + it = other.it; + if(it) + ::new(&obj_it) + object::const_iterator( + other.obj_it); + else + ::new(&arr_it) + array::const_iterator( + other.arr_it); +} + +bool +const_iterator:: +node:: +last() const noexcept +{ + if(! it) + return true; + if(v->is_object()) + return obj_it == + v->as_object().end(); + return arr_it == + v->as_array().end(); +} + +//------------------------------------------------------------------------------ + +const_iterator:: +const_iterator( + value const& jv) +{ + stack_.emplace_front( + jv, false); +} + +auto +const_iterator:: +operator*() const noexcept -> + value_type +{ + auto const& n = stack_.front(); + if(! n.it) + { + BOOST_ASSERT( + stack_.size() == 1); + return { + stack_.size() - 1, + n.key, + *n.v, + true, + false }; + } + BOOST_ASSERT(n.v->is_structured()); + if(n.v->is_object()) + { + if(n.obj_it != + n.v->as_object().end()) + return { + stack_.size(), + n.obj_it->first, + n.obj_it->second, + std::next(n.obj_it) == + n.v->as_object().end(), + false }; + return { + stack_.size() - 1, + "", + *n.v, + stack_.size() == 1 + || stack_[1].last(), + true}; + } + if(n.arr_it != + n.v->as_array().end()) + return { + stack_.size(), + "", + *n.arr_it, + std::next(n.arr_it) == + n.v->as_array().end(), + false }; + return { + stack_.size() - 1, + "", + *n.v, + stack_.size() == 1 + || stack_[1].last(), + true}; +} + +const_iterator& +const_iterator:: +operator++() noexcept +{ + auto& n = stack_.front(); + if(! n.it) + { + if(n.v->is_structured()) + { + stack_.pop_front(); + stack_.emplace_front( + *n.v, true); + } + else + { + stack_.pop_front(); + } + } + else if(n.v->is_object()) + { + if(n.obj_it == + n.v->as_object().end()) + { + stack_.pop_front(); + } + else + { + auto const& jv = + n.obj_it->second; + if(jv.is_structured()) + stack_.emplace_front( + jv, true); + ++n.obj_it; + } + } + else + { + if(n.arr_it == + n.v->as_array().end()) + { + stack_.pop_front(); + } + else + { + auto const& jv = *n.arr_it; + if(jv.is_structured()) + stack_.emplace_front( + jv, true); + ++n.arr_it; + } + } + return *this; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/number.ipp b/include/boost/json/impl/number.ipp new file mode 100644 index 00000000..08c6d191 --- /dev/null +++ b/include/boost/json/impl/number.ipp @@ -0,0 +1,454 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_NUMBER_IPP +#define BOOST_JSON_IMPL_NUMBER_IPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +//------------------------------------------------------------------------------ + +struct number::pow10 +{ + std::size_t + size() const noexcept + { + return size_; + } + + mantissa_type const* + begin() const noexcept + { + return begin_; + } + + mantissa_type const* + end() const noexcept + { + return end_; + } + + mantissa_type + operator[]( + exponent_type n) const + { + return begin_[n]; + } + + static + pow10 const& + get() noexcept + { + struct pow10_impl : pow10 + { + pow10_impl() + { + static mantissa_type constexpr list[] = { + 1ULL, + 10ULL, + 100ULL, + 1000ULL, + 10000ULL, + 100000ULL, + 1000000ULL, + 10000000ULL, + 100000000ULL, + 1000000000ULL, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL + }; + size_ = std::extent< + decltype(list)>::value; + begin_ = &list[0]; + end_ = &list[size_]; + } + }; + + static pow10_impl const tab; + return tab; + } + +protected: + std::size_t size_; + mantissa_type const* begin_; + mantissa_type const* end_; +}; + +//------------------------------------------------------------------------------ + +number:: +number( + mantissa_type mant, + exponent_type exp, + bool sign) noexcept +{ + auto const as_double = + [&] + { + double d = + static_cast(mant) * + std::pow(10, exp); + if(sign) + d *= -1; + return d; + }; + + if(exp == 0) + { + if(! sign) + { + assign_unsigned(mant); + } + else if(mant <= static_cast( + (std::numeric_limits::max)()) + 1) + { + assign_signed(static_cast(mant)); + } + else + { + assign_double(as_double()); + } + } + else + { + auto const d = as_double(); + if(! sign) + { + auto v = static_cast(d); + if(v == d) + assign_unsigned(v); + else + assign_double(d); + } + else + { + auto v = static_cast(d); + if(v == d) + assign_signed(v); + else + assign_double(d); + } + } +} + +//------------------------------------------------------------------------------ + +number:: +number(short i) noexcept +{ + assign_signed(i); +} + +number:: +number(int i) noexcept +{ + assign_signed(i); +} + +number:: +number(long i) noexcept +{ + assign_signed(i); +} + +number:: +number(long long i) noexcept +{ + assign_signed(i); +} + +number:: +number(unsigned short i) noexcept +{ + assign_unsigned(i); +} + +number:: +number(unsigned int i) noexcept +{ + assign_unsigned(i); +} + +number:: +number(unsigned long i) noexcept +{ + assign_unsigned(i); +} + +number:: +number(unsigned long long i) noexcept +{ + assign_unsigned(i); +} + +number:: +number(float f) noexcept +{ + assign_double(f); +} + +number:: +number(double f) noexcept +{ + assign_double(f); +} + +//------------------------------------------------------------------------------ + +bool +number:: +is_int64() const noexcept +{ + switch(k_) + { + case type_int64: + return true; + + case type_uint64: + return int64_ >= 0; + + case type_double: + return static_cast( + double_) == double_; + + default: + case type_ieee: + break; + } + return false; +} + +bool +number:: +is_uint64() const noexcept +{ + switch(k_) + { + case type_int64: + return int64_ >= 0; + + case type_uint64: + return true; + + case type_double: + return static_cast< + unsigned long long>( + double_) == double_; + + default: + case type_ieee: + break; + } + return false; +} + +std::int_least64_t +number:: +get_int64() const noexcept +{ + switch(k_) + { + case type_int64: + return int64_; + + case type_uint64: + return static_cast< + long long>(uint64_); + + case type_double: + return static_cast< + long long>(double_); + + default: + case type_ieee: + break; + } + return 0; +} + +std::uint_least64_t +number:: +get_uint64() const noexcept +{ + switch(k_) + { + case type_int64: + return static_cast< + unsigned long long>(int64_); + + case type_uint64: + return uint64_; + + case type_double: + return static_cast< + unsigned long long>(double_); + + default: + case type_ieee: + break; + } + return 0; +} + +double +number:: +get_double() const noexcept +{ + switch(k_) + { + case type_int64: + return static_cast(int64_); + + case type_uint64: + return static_cast(uint64_); + + case type_double: + return double_; + + default: + case type_ieee: + break; + } + return 0; +} + +string_view +number:: +print( + char* buf, + std::size_t buf_size) const noexcept +{ + int n; + switch(k_) + { + case type_int64: + n = snprintf(buf, buf_size, + "%lld", int64_); + break; + + case type_uint64: + n = snprintf(buf, buf_size, + "%llu", uint64_); + break; + + case type_double: + n = snprintf(buf, buf_size, + "%e", double_); + break; + + default: + case type_ieee: + n = snprintf(buf, buf_size, + "_unimpl"); + break; + } + return { buf, static_cast< + std::size_t>(n) }; +} + +//------------------------------------------------------------------------------ + +void +number:: +assign_signed(long long i) noexcept +{ + k_ = type_int64; + int64_ = i; +} + +void +number:: +assign_unsigned(unsigned long long i) noexcept +{ + k_ = type_uint64; + uint64_ = i; +} + +void +number:: +assign_double(double f) noexcept +{ + k_ = type_double; + double_ = f; +} + +//------------------------------------------------------------------------------ + +std::ostream& +operator<<(std::ostream& os, number const& n) +{ + char buf[number::max_string_chars]; + os << n.print(buf, sizeof(buf)); + return os; +} + +bool +operator==( + number const& lhs, + number const& rhs) noexcept +{ + switch(lhs.k_) + { + case number::type_int64: + return + rhs.is_int64() && + lhs.get_int64() == + rhs.get_int64(); + + case number::type_uint64: + return + rhs.is_uint64() && + lhs.get_uint64() == + rhs.get_uint64(); + + case number::type_double: + return + lhs.get_double() == + rhs.get_double(); + + default: + case number::type_ieee: + break; + } + return false; +} + +bool +operator!=( + number const& lhs, + number const& rhs) noexcept +{ + return !(lhs == rhs); +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/object.hpp b/include/boost/json/impl/object.hpp new file mode 100644 index 00000000..f91806ec --- /dev/null +++ b/include/boost/json/impl/object.hpp @@ -0,0 +1,932 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_OBJECT_HPP +#define BOOST_JSON_IMPL_OBJECT_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +//------------------------------------------------------------------------------ + +struct object::list_hook +{ +public: + element* prev_; + element* next_; +}; + +//------------------------------------------------------------------------------ + +struct object::element + : public list_hook +{ + value v_; + element* local_next_; + + BOOST_BEAST_DECL + string_view + key() const noexcept; + + struct cleanup + { + std::size_t size; + storage_ptr const& sp; + std::size_t n; + + void + operator()(char* p) + { + sp->deallocate(p, + size, alignof(element)); + } + }; + + template + static + element* + allocate( + storage_ptr const& sp, + key_type key, + Arg&& arg) + { + auto up = + prepare_allocate(sp, key); + auto const p = up.get(); + auto const n = up.get_deleter().n; + auto e = ::new(up.get()) element( + std::forward(arg), sp); + up.release(); + detail::varint_write( + p + sizeof(element), key.size()); + std::memcpy( + p + sizeof(element) + n, + key.data(), + key.size()); + p[sizeof(element) + + n + key.size()] = '\0'; + boost::ignore_unused(e); + BOOST_ASSERT( + *e->v_.get_storage() == *sp); + return reinterpret_cast(p); + } + + BOOST_BEAST_DECL + static + void + destroy( + element const* e, + storage_ptr const& sp); + +private: + template + element( + Arg&& arg, + storage_ptr sp) + : v_(std::forward(arg), + std::move(sp)) + { + } + + BOOST_BEAST_DECL + static + std::unique_ptr + prepare_allocate( + storage_ptr const& sp, + key_type key); +}; + +//------------------------------------------------------------------------------ + +class object::hasher +{ + BOOST_BEAST_DECL + static + std::pair< + std::uint64_t, std::uint64_t> + init(std::true_type) noexcept; + + BOOST_BEAST_DECL + static + std::pair< + std::uint32_t, std::uint32_t> + init(std::false_type) noexcept; + +public: + BOOST_BEAST_DECL + std::size_t + operator()(key_type key) const noexcept; +}; + +//------------------------------------------------------------------------------ + +class object::key_equal +{ +public: + bool + operator()( + beast::string_view lhs, + beast::string_view rhs) const noexcept + { + return lhs == rhs; + } +}; + +//------------------------------------------------------------------------------ + +class object::pointer +{ + reference t_; + +public: + pointer(reference t) + : t_(t) + { + } + + reference* + operator->() noexcept + { + return &t_; + } +}; + +//------------------------------------------------------------------------------ + +class object::const_pointer +{ + const_reference t_; + +public: + const_pointer( + const_reference t) + : t_(t) + { + } + + const_reference* + operator->() noexcept + { + return &t_; + } +}; + +//------------------------------------------------------------------------------ + +class object::iterator +{ + element* e_ = nullptr; + + friend class object; + + iterator(element* e) + : e_(e) + { + } + +public: + using value_type = object::value_type; + using pointer = object::pointer; + using reference = object::reference; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + iterator() = default; + + iterator( + iterator const&) = default; + + iterator& operator=( + iterator const&) = default; + + bool + operator==( + iterator const& other) const noexcept + { + return e_ == other.e_; + } + + bool + operator!=( + iterator const& other) const noexcept + { + return e_ != other.e_; + } + + bool + operator==( + const_iterator const& other) const noexcept; + + bool + operator!=( + const_iterator const& other) const noexcept; + + iterator& + operator++() noexcept + { + e_ = e_->next_; + return *this; + } + + iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + iterator& + operator--() noexcept + { + e_ = e_->prev_; + return *this; + } + + iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + pointer + operator->() const noexcept + { + return reference{ + e_->key(), e_->v_ }; + } + + reference + operator*() const noexcept + { + return { + e_->key(), e_->v_ }; + } +}; + +//------------------------------------------------------------------------------ + +class object::const_iterator +{ + element* e_ = nullptr; + + friend class object; + + const_iterator(element* e) + : e_(e) + { + } + +public: + using value_type = object::value_type; + using pointer = object::const_pointer; + using reference = object::const_reference; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + + const_iterator( + const_iterator const&) = default; + + const_iterator& operator=( + const_iterator const&) = default; + + const_iterator(iterator it) + : e_(it.e_) + { + } + + const_iterator& + operator=(iterator it) noexcept + { + e_ = it.e_; + return *this; + } + + bool + operator==( + const_iterator const& other) const noexcept + { + return e_ == other.e_; + } + + bool + operator!=( + const_iterator const& other) const noexcept + { + return e_ != other.e_; + } + + bool + operator==( + iterator const& other) const noexcept + { + return e_ == other.e_; + } + + bool + operator!=( + iterator const& other) const noexcept + { + return e_ != other.e_; + } + + const_iterator& + operator++() noexcept + { + e_ = e_->next_; + return *this; + } + + const_iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + const_iterator& + operator--() noexcept + { + e_ = e_->prev_; + return *this; + } + + const_iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + pointer + operator->() const noexcept + { + return const_reference{ + e_->key(), e_->v_ }; + } + + reference + operator*() const noexcept + { + return { + e_->key(), e_->v_ }; + } +}; + +inline +bool +object:: +iterator:: +operator==( + const_iterator const& other) const noexcept +{ + return e_ == other.e_; +} + +inline +bool +object:: +iterator:: +operator!=( + const_iterator const& other) const noexcept +{ + return e_ != other.e_; +} + +//------------------------------------------------------------------------------ + +class object::local_iterator +{ + element* e_ = nullptr; + + friend class object; + + local_iterator(element* e) + : e_(e) + { + } + +public: + using value_type = object::value_type; + + using reference = object::reference; + + local_iterator() = default; + + local_iterator( + local_iterator const&) = default; + + local_iterator& operator=( + local_iterator const&) = default; + + bool + operator==( + local_iterator const& other) const noexcept + { + return e_ == other.e_; + } + + bool + operator!=( + local_iterator const& other) const noexcept + { + return e_ != other.e_; + } + + bool + operator==( + iterator const& other) const noexcept + { + return e_ == other.e_; + } + + bool + operator!=( + iterator const& other) const noexcept + { + return e_ != other.e_; + } + + local_iterator& + operator++() noexcept + { + e_ = e_->local_next_; + return *this; + } + + local_iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + const_pointer + operator->() const noexcept + { + return const_reference{ + e_->key(), e_->v_ }; + } + + const_reference + operator*() const noexcept + { + return { + e_->key(), e_->v_ }; + } +}; + +//------------------------------------------------------------------------------ + +class object::const_local_iterator +{ + element const* e_ = nullptr; + + friend class object; + + const_local_iterator(element const* e) + : e_(e) + { + } + +public: + using value_type = object::value_type; + + using reference = object::const_reference; + + const_local_iterator() = default; + + const_local_iterator( + const_local_iterator const&) = default; + + const_local_iterator& operator=( + const_local_iterator const&) = default; + + bool + operator==( + const_local_iterator const& other) const noexcept + { + return e_ == other.e_; + } + + bool + operator!=( + const_local_iterator const& other) const noexcept + { + return e_ != other.e_; + } + + bool + operator==( + iterator const& other) const noexcept + { + return e_ == other.e_; + } + + bool + operator!=( + iterator const& other) const noexcept + { + return e_ != other.e_; + } + + const_local_iterator& + operator++() noexcept + { + e_ = e_->local_next_; + return *this; + } + + const_local_iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + const_pointer + operator->() const noexcept + { + return const_reference{ + e_->key(), e_->v_ }; + } + + const_reference + operator*() const noexcept + { + return { + e_->key(), e_->v_ }; + } +}; + +//------------------------------------------------------------------------------ + +class object::node_type +{ + element* e_ = nullptr; + storage_ptr sp_; + + friend class object; + + node_type( + element* e, + storage_ptr sp) + : e_(e) + , sp_(std::move(sp)) + { + } + +public: + using key_type = beast::string_view; + using mapped_type = json::value; + + node_type() = default; + node_type(node_type const&) = delete; + + ~node_type() + { + if(e_) + element::destroy(e_, sp_); + } + + node_type(node_type&& other) + : e_(boost::exchange( + other.e_, nullptr)) + , sp_(boost::exchange( + other.sp_, nullptr)) + { + } + + node_type& + operator=(node_type&& other) + { + if(e_) + { + element::destroy(e_, sp_); + e_ = nullptr; + sp_ = nullptr; + } + if(other.e_) + { + e_ = boost::exchange( + other.e_, nullptr); + sp_ = std::move(other.sp_); + } + return *this; + } + + storage_ptr const& + get_storage() const noexcept + { + return sp_; + } + + bool + empty() const noexcept + { + return e_ == nullptr; + } + + explicit + operator bool() const noexcept + { + return e_ != nullptr; + } + + key_type + key() const noexcept + { + return e_->key(); + } + + mapped_type& + value() noexcept + { + return e_->v_; + } + + mapped_type const& + value() const noexcept + { + return e_->v_; + } +}; + +struct object::insert_return_type +{ + iterator position; + bool inserted; + node_type node; +}; + + +//------------------------------------------------------------------------------ + +template +object:: +object( + InputIt first, + InputIt last) + : sp_(default_storage()) +{ + construct(first, last, 0, + iter_cat{}); +} + +template +object:: +object( + InputIt first, + InputIt last, + size_type capacity) + : sp_(default_storage()) +{ + construct(first, last, capacity, + iter_cat{}); +} + +template +object:: +object( + InputIt first, + InputIt last, + storage_ptr store) + : sp_(std::move(store)) +{ + construct(first, last, 0, + iter_cat{}); +} + +template +object:: +object( + InputIt first, + InputIt last, + size_type capacity, + storage_ptr store) + : sp_(std::move(store)) +{ + construct(first, last, capacity, + iter_cat{}); +} + +//------------------------------------------------------------------------------ + +template +auto +object:: +insert(P&& p)-> + std::pair +{ + return insert(end(), std::forward

(p)); +} + +template +auto +object:: +insert(const_iterator before, P&& p) -> + std::pair +{ + value_type v(std::forward

(p)); + return emplace_impl(before, v.first, + std::move(v.second)); +} + +template +void +object:: +insert(InputIt first, InputIt last) +{ + insert(first, last, iter_cat{}); +} + +template +auto +object:: +insert_or_assign( + key_type key, M&& obj) -> + std::pair +{ + return insert_or_assign(end(), key, + std::forward(obj)); +} + +template +auto +object:: +insert_or_assign( + const_iterator before, + key_type key, + M&& obj) -> + std::pair +{ + auto const hash = hasher{}(key); + auto e = prepare_insert(&before, key, hash); + if(e) + { + e->v_ = std::forward(obj); + return { iterator(e), false }; + } + e = element::allocate(sp_, key, + std::forward(obj)); + finish_insert(before, e, hash); + return { iterator(e), true, }; +} + +template +auto +object:: +emplace(key_type key, Arg&& arg) -> + std::pair +{ + return emplace_impl(end(), key, + std::forward(arg)); +} + +template +auto +object:: +emplace(const_iterator before, + key_type key, Arg&& arg) -> + std::pair +{ + return emplace_impl(before, key, + std::forward(arg)); +} + +inline +auto +object:: +hash_function() const -> + hasher +{ + return hasher{}; +} + +inline +auto +object:: +key_eq() const -> + key_equal +{ + return key_equal{}; +} + +//------------------------------------------------------------------------------ + +template +void +object:: +construct( + InputIt first, + InputIt last, + size_type capacity, + std::forward_iterator_tag) +{ + reserve(std::max(capacity, + std::distance(first, last))); + while(first != last) + { + value_type v(*first++); + emplace_impl(end(), v.first, + std::move(v.second)); + } +} + +template +void +object:: +construct( + InputIt first, + InputIt last, + size_type capacity, + std::input_iterator_tag) +{ + reserve(capacity); + while(first != last) + { + value_type v(*first++); + emplace_impl(end(), v.first, + std::move(v.second)); + } +} + +template +void +object:: +insert(InputIt first, InputIt last, + std::forward_iterator_tag) +{ + reserve(size() + + std::distance(first, last)); + while(first != last) + { + value_type v(*first++); + emplace_impl(end(), v.first, + std::move(v.second)); + } +} + +template +void +object:: +insert(InputIt first, InputIt last, + std::input_iterator_tag) +{ + while(first != last) + { + value_type v(*first++); + emplace_impl(end(), v.first, + std::move(v.second)); + } +} + +template +auto +object:: +emplace_impl( + const_iterator before, + key_type key, + Arg&& arg) -> + std::pair +{ + auto const hash = hasher{}(key); + auto e = prepare_insert( + &before, key, hash); + if(e) + return { iterator(e), false }; + e = element::allocate(sp_, key, + std::forward(arg)); + finish_insert(before, e, hash); + return { iterator(e), true }; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/object.ipp b/include/boost/json/impl/object.ipp new file mode 100644 index 00000000..8ac5986b --- /dev/null +++ b/include/boost/json/impl/object.ipp @@ -0,0 +1,1268 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_OBJECT_IPP +#define BOOST_JSON_IMPL_OBJECT_IPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +//------------------------------------------------------------------------------ + +string_view +object:: +element:: +key() const noexcept +{ + auto p =reinterpret_cast< + char const*>(this + 1); + auto const result = + detail::varint_read(p); + return { + p + result.second, + static_cast( + result.first) }; +} + +void +object:: +element:: +destroy( + element const* e, + storage_ptr const& sp) +{ + auto const len = e->key().size(); + auto const n = + detail::varint_size(len); + e->~element(); + sp->deallocate( + const_cast(e), + sizeof(element) + n + len + 1, + alignof(element)); +} + +auto +object:: +element:: +prepare_allocate( + storage_ptr const& sp, + key_type key) -> + std::unique_ptr +{ + auto const n = static_cast( + detail::varint_size(key.size())); + auto const size = + sizeof(element) + n + key.size() + 1; + auto p = reinterpret_cast( + sp->allocate(size, alignof(element))); + return { p, cleanup{size, sp, n} }; +} + +//------------------------------------------------------------------------------ + +class object::table +{ +public: + // number of values in the object + std::size_t count = 0; + + // number of buckets in table + std::size_t bucket_count = 0; + + // insertion-order list of all objects + element* head; + + // this always points to end_element + element* end; + + list_hook end_element; + + table() noexcept + : end(reinterpret_cast< + element*>(&end_element)) + { + head = end; + } + + element*& + bucket(std::size_t n) noexcept + { + return reinterpret_cast< + element**>(this + 1)[n]; + } + + static + void + destroy( + table* tab, + storage_ptr const& sp) noexcept + { + auto const count = + tab->bucket_count; + tab->~table(); + sp->deallocate( + tab, + sizeof(table) + + count * sizeof(element*), + alignof(table)); + } + + static + void + destroy_list( + table* tab, + storage_ptr const& sp) noexcept + { + for(auto it = tab->head; + it != tab->end;) + { + auto next = it->next_; + element::destroy(it, sp); + it = next; + } + } + + static + table* + allocate( + table* from, + size_type bucket_count, + storage_ptr const& sp) + { + auto tab = ::new(sp->allocate( + sizeof(table) + + bucket_count * + sizeof(element*), + alignof(table))) table; + + if(from) + { + tab->count = from->count; + tab->bucket_count = bucket_count; + if(from->head != from->end) + { + tab->head = from->head; + tab->end->prev_ = + from->end->prev_; + tab->end->prev_->next_ = + tab->end; + } + else + { + tab->head = tab->end; + } + } + else + { + tab->count = 0; + tab->bucket_count = bucket_count; + tab->head = tab->end; + } + for(size_type i = 0; + i < bucket_count; ++i) + tab->bucket(i) = tab->end; + if(from) + destroy(from, sp); + return tab; + } +}; + +//------------------------------------------------------------------------------ + +std::pair< + std::uint64_t, + std::uint64_t> +object:: +hasher:: +init(std::true_type) noexcept +{ + return { + 0x100000001B3ULL, + 0xcbf29ce484222325ULL + }; +} + +std::pair< + std::uint32_t, + std::uint32_t> +object:: +hasher:: +init(std::false_type) noexcept +{ + return { + 0x01000193UL, + 0x811C9DC5UL + }; +} + +std::size_t +object:: +hasher:: +operator()(key_type key) const noexcept +{ + std::size_t prime; + std::size_t hash; + std::tie(prime, hash) = init( + std::integral_constant= + sizeof(unsigned long long)>{}); + for(auto p = key.begin(), + end = key.end(); p != end; ++p) + hash = (*p ^ hash) * prime; + return hash; +} + +//------------------------------------------------------------------------------ + +struct object::cleanup_replace +{ + object& obj; + table* tab; + bool ok = false; + + explicit + cleanup_replace( + object& obj_) + : obj(obj_) + , tab(boost::exchange( + obj_.tab_, nullptr)) + { + } + + ~cleanup_replace() + { + if(! ok) + { + if(obj.tab_) + { + table::destroy_list( + obj.tab_, obj.sp_); + table::destroy( + obj.tab_, obj.sp_); + } + obj.tab_ = tab; + } + else + { + if(tab) + { + table::destroy_list( + tab, obj.sp_); + table::destroy( + tab, obj.sp_); + } + } + } +}; + +//------------------------------------------------------------------------------ +// +// Special Members +// +//------------------------------------------------------------------------------ + +object:: +~object() +{ + if(tab_) + { + table::destroy_list(tab_, sp_); + table::destroy(tab_, sp_); + } +} + +object:: +object() + : object(0, default_storage()) +{ +} + +object:: +object(size_type capacity) + : object(capacity, default_storage()) +{ +} + +object:: +object(storage_ptr store) + : object(0, std::move(store)) +{ +} + +object:: +object( + size_type capacity, + storage_ptr store) + : sp_(std::move(store)) +{ + reserve(capacity); +} + +object:: +object(object&& other) noexcept + : sp_(other.sp_) + , tab_(boost::exchange( + other.tab_, nullptr)) +{ +} + +object:: +object( + object&& other, + storage_ptr store) noexcept + : sp_(std::move(store)) +{ + *this = std::move(other); +} + +object:: +object(object const& other) + : object(other, other.get_storage()) +{ +} + +object:: +object( + object const& other, + storage_ptr store) + : sp_(std::move(store)) +{ + *this = other; +} + +object:: +object( + std::initializer_list list) + : object( + list, + list.size(), + default_storage()) +{ +} + +object:: +object( + std::initializer_list list, + size_type capacity) + : object( + list, + capacity, + default_storage()) +{ +} + +object:: +object( + std::initializer_list list, + storage_ptr store) + : object( + list, + list.size(), + std::move(store)) +{ +} + +object:: +object( + std::initializer_list list, + size_type capacity, + storage_ptr store) + : sp_(std::move(store)) +{ + reserve(std::max( + capacity, list.size())); + for(auto it = list.begin(); + it != list.end(); ++it) + emplace_impl(end(), it->first, + std::move(it->second)); +} + +object& +object:: +operator=(object&& other) +{ + if(*other.sp_ == *sp_) + { + if(tab_) + { + table::destroy_list(tab_, sp_); + table::destroy(tab_, sp_); + } + tab_ = other.tab_; + other.tab_ = nullptr; + } + else + { + *this = const_cast< + object const&>(other); + } + return *this; +} + +object& +object:: +operator=(object const& other) +{ + cleanup_replace c(*this); + reserve(other.size()); + for(auto v : other) + emplace_impl(end(), v.first, + v.second); + c.ok = true; + return *this; +} + +object& +object:: +operator=( + std::initializer_list list) +{ + cleanup_replace c(*this); + reserve(list.size()); + for(auto it = list.begin(); + it != list.end(); ++it) + emplace_impl(end(), it->first, + std::move(it->second)); + c.ok = true; + return *this; +} + +storage_ptr const& +object:: +get_storage() const noexcept +{ + return sp_; +} + +//------------------------------------------------------------------------------ +// +// Iterators +// +//------------------------------------------------------------------------------ + +auto +object:: +begin() noexcept -> + iterator +{ + if(! tab_) + return {}; + return tab_->head; +} + +auto +object:: +begin() const noexcept -> + const_iterator +{ + if(! tab_) + return {}; + return tab_->head; +} + +auto +object:: +cbegin() const noexcept -> + const_iterator +{ + if(! tab_) + return {}; + return tab_->head; +} + +auto +object:: +end() noexcept -> + iterator +{ + if(! tab_) + return {}; + return tab_->end; +} + +auto +object:: +end() const noexcept -> + const_iterator +{ + if(! tab_) + return {}; + return tab_->end; +} + +auto +object:: +cend() const noexcept -> + const_iterator +{ + if(! tab_) + return {}; + return tab_->end; +} + +//------------------------------------------------------------------------------ +// +// Capacity +// +//------------------------------------------------------------------------------ + +bool +object:: +empty() const noexcept +{ + return ! tab_ || + tab_->count == 0; +} + +auto +object:: +size() const noexcept -> + size_type +{ + if(! tab_) + return 0; + return tab_->count; +} + +auto +object:: +max_size() const noexcept -> + size_type +{ + return (std::numeric_limits< + size_type>::max)(); +} + +//------------------------------------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------------------------------------ + +void +object:: +clear() noexcept +{ + if(! tab_) + return; + table::destroy_list(tab_, sp_); + table::destroy(tab_, sp_); + tab_ = nullptr; +} + +void +object:: +insert( + std::initializer_list list) +{ + reserve(size() + list.size()); + for(auto&& v : list) + emplace_impl(end(), v.first, + std::move(v.second)); +} + +auto +object:: +insert(node_type&& nh) -> + insert_return_type +{ + return insert(end(), std::move(nh)); +} + +auto +object:: +insert( + const_iterator before, + node_type&& nh) -> + insert_return_type +{ + if(! nh.e_) + return { end(), false, {} }; + auto const hash = hasher{}(nh.e_->key()); + auto e = prepare_insert( + &before, nh.key(), hash); + if(e) + return { iterator(e), false, std::move(nh) }; + e = nh.e_; + finish_insert(before, e, hash); + nh.e_ = nullptr; + return { iterator(e), true, {} }; +} + +auto +object:: +erase(const_iterator pos) -> + iterator +{ + auto e = pos.e_->next_; + remove(pos.e_); + element::destroy(pos.e_, sp_); + return e; +} + +auto +object:: +erase( + const_iterator first, + const_iterator last) -> + iterator +{ + if(! tab_) + return first.e_; + auto e = first.e_; + while(e != last.e_) + { + auto next = e->next_; + remove(e); + element::destroy(e, sp_); + e = next; + } + return e; +} + +auto +object:: +erase(key_type key) -> + size_type +{ + auto it = find(key); + if(it == end()) + return 0; + erase(it); + return true; +} + +void +object:: +swap(object& other) noexcept +{ + // undefined if storage not equal + BOOST_ASSERT(*sp_ == *other.sp_); + std::swap(tab_, other.tab_); + std::swap(mf_, other.mf_); +} + +auto +object:: +extract(const_iterator pos) -> + node_type +{ + remove(pos.e_); + return { pos.e_, sp_ }; +} + +auto +object:: +extract(key_type key) -> + node_type +{ + auto it = find(key); + if(it == end()) + return {}; + return extract(it); +} + +void +object:: +merge(object&) +{ + // TODO +} + +void +object:: +merge(object&&) +{ + // TODO +} + +//------------------------------------------------------------------------------ +// +// Lookup +// +//------------------------------------------------------------------------------ + +auto +object:: +at(key_type key) -> + value& +{ + auto it = find(key); + if(it == end()) + BOOST_THROW_EXCEPTION( + std::out_of_range( + "key not found")); + return it->second; +} + +auto +object:: +at(key_type key) const -> + value const& +{ + auto it = find(key); + if(it == end()) + BOOST_THROW_EXCEPTION( + std::out_of_range( + "key not found")); + return it->second; +} + +auto +object:: +operator[](key_type key) -> + value& +{ + auto const result = + emplace_impl(end(), key, kind::null); + return result.first->second; +} + +auto +object:: +operator[](key_type key) const -> + value const& +{ + return find(key)->second; +} + +auto +object:: +count(key_type key) const -> + size_type +{ + if(find(key) == end()) + return 0; + return 1; +} + +auto +object:: +count( + key_type key, + std::size_t hash) const -> + size_type +{ + if(find(key, hash) == end()) + return 0; + return 1; +} + +auto +object:: +find(key_type key) -> + iterator +{ + return find(key, hasher{}(key)); +} + +auto +object:: +find( + key_type key, + std::size_t hash) -> + iterator +{ + auto it = static_cast< + object const&>(*this).find( + key, hash); + return iterator(it.e_); +} + +auto +object:: +find(key_type key) const -> + const_iterator +{ + return find(key, hasher{}(key)); +} + +auto +object:: +find( + key_type key, + std::size_t hash) const -> + const_iterator +{ + auto e = find_element(key, hash); + if(! e) + return end(); + return e; +} + +bool +object:: +contains(key_type key) const +{ + return find(key) != end(); +} + +bool +object:: +contains( + key_type key, + std::size_t hash) const +{ + return find(key, hash) != end(); +} + +//------------------------------------------------------------------------------ +// +// Bucket Interface +// +//------------------------------------------------------------------------------ + +auto +object:: +begin(size_type n) noexcept -> + local_iterator +{ + if(! tab_) + return {}; + return tab_->bucket(n); +} + +auto +object:: +begin(size_type n) const noexcept -> + const_local_iterator +{ + if(! tab_) + return {}; + return tab_->bucket(n); +} + +auto +object:: +cbegin(size_type n) noexcept -> + const_local_iterator +{ + if(! tab_) + return {}; + return tab_->bucket(n); +} + +auto +object:: +end(size_type n) noexcept -> + local_iterator +{ + boost::ignore_unused(n); + if(! tab_) + return {}; + return tab_->end; +} + +auto +object:: +end(size_type n) const noexcept -> + const_local_iterator +{ + boost::ignore_unused(n); + if(! tab_) + return {}; + return tab_->end; +} + +auto +object:: +cend(size_type n) noexcept -> + const_local_iterator +{ + boost::ignore_unused(n); + if(! tab_) + return {}; + return tab_->end; +} + +auto +object:: +bucket_count() const noexcept -> + size_type +{ + if(! tab_) + return 0; + return tab_->bucket_count; +} + +auto +object:: +max_bucket_count() const noexcept -> + size_type +{ + return (std::numeric_limits< + size_type>::max)(); +} + +auto +object:: +bucket_size(size_type n) const noexcept -> + size_type +{ + if(! tab_) + return 0; + size_type size = 0; + for(auto e = tab_->bucket(n); + e != tab_->end; ++e) + ++size; + return size; +} + +auto +object:: +bucket(key_type key) const noexcept -> + size_type +{ + if(! tab_) + return 0; // undefined + return hasher{}(key) % + bucket_count(); +} + +//------------------------------------------------------------------------------ + +namespace detail { + +struct primes +{ + using value_type = std::size_t; + using iterator = std::size_t const*; + + std::size_t const* begin_; + std::size_t const* end_; + + iterator + begin() const noexcept + { + return begin_; + } + + iterator + end() const noexcept + { + return end_; + } +}; + +// Taken from Boost.Intrusive and Boost.MultiIndex code, +// thanks to Ion Gaztanaga and Joaquin M Lopez Munoz. + +template +primes +get_primes(std::false_type) noexcept +{ + static std::size_t constexpr list[] = { + 0UL, + + 3UL, 7UL, + 11UL, 17UL, + 29UL, 53UL, + 97UL, 193UL, + 389UL, 769UL, + 1543UL, 3079UL, + 6151UL, 12289UL, + 24593UL, 49157UL, + 98317UL, 196613UL, + 393241UL, 786433UL, + 1572869UL, 3145739UL, + 6291469UL, 12582917UL, + 25165843UL, 50331653UL, + 100663319UL, 201326611UL, + 402653189UL, 805306457UL, + 1610612741UL, 3221225473UL, + + 4294967291UL, 4294967295UL + }; + return { + &list[0], + &list[std::extent< + decltype(list)>::value] }; +} + +template +primes +get_primes(std::true_type) noexcept +{ + static std::size_t constexpr list[] = { + 0ULL, + + 3ULL, 7ULL, + 11ULL, 17ULL, + 29ULL, 53ULL, + 97ULL, 193ULL, + 389ULL, 769ULL, + 1543ULL, 3079ULL, + 6151ULL, 12289ULL, + 24593ULL, 49157ULL, + 98317ULL, 196613ULL, + 393241ULL, 786433ULL, + 1572869ULL, 3145739ULL, + 6291469ULL, 12582917ULL, + 25165843ULL, 50331653ULL, + 100663319ULL, 201326611ULL, + 402653189ULL, 805306457ULL, + 1610612741ULL, 3221225473ULL, + + 6442450939ULL, 12884901893ULL, + 25769803751ULL, 51539607551ULL, + 103079215111ULL, 206158430209ULL, + 412316860441ULL, 824633720831ULL, + 1649267441651ULL, 3298534883309ULL, + 6597069766657ULL, 13194139533299ULL, + 26388279066623ULL, 52776558133303ULL, + 105553116266489ULL, 211106232532969ULL, + 422212465066001ULL, 844424930131963ULL, + 1688849860263953ULL, 3377699720527861ULL, + 6755399441055731ULL, 13510798882111483ULL, + 27021597764222939ULL, 54043195528445957ULL, + 108086391056891903ULL, 216172782113783843ULL, + 432345564227567621ULL, 864691128455135207ULL, + 1729382256910270481ULL, 3458764513820540933ULL, + 6917529027641081903ULL, 13835058055282163729ULL, + 18446744073709551557ULL, 18446744073709551615ULL + }; + return { + &list[0], + &list[std::extent< + decltype(list)>::value] }; +} + +BOOST_BEAST_DECL +primes +get_primes() noexcept +{ + return get_primes( + std::integral_constant= + sizeof(unsigned long long)>{}); +} + +} // detail + +//------------------------------------------------------------------------------ +// +// Hash Policy +// +//------------------------------------------------------------------------------ + +float +object:: +load_factor() const noexcept +{ + if(! tab_) + return 0; + return static_cast( + size()) / bucket_count(); +} + +float +object:: +max_load_factor() const +{ + return mf_; +} + +void +object:: +max_load_factor(float ml) +{ + mf_ = ml; +} + +void +object:: +rehash(size_type count) +{ + // snap to nearest prime + auto const ptab = + detail::get_primes(); + count = *std::lower_bound( + ptab.begin(), ptab.end(), count); + auto const bc = bucket_count(); + if(count == bc) + return; + if(count < bc) + { + count = (std::max)( + count, *std::lower_bound( + ptab.begin(), ptab.end(), + static_cast( + std::ceil(size() / + max_load_factor())))); + if(count >= bc) + return; + } + // rehash + tab_ = table::allocate( + tab_, count, sp_); + for(auto e = tab_->head; + e != tab_->end; e = e->next_) + { + auto const n = bucket(e->key()); + auto& head = tab_->bucket(n); + e->local_next_ = head; + head = e; + } +} + +void +object:: +reserve(size_type count) +{ + rehash(static_cast< + size_type>(std::ceil( + count / max_load_factor()))); +} + +//------------------------------------------------------------------------------ + +auto +object:: +constrain_hash( + std::size_t hash, + size_type bucket_count) noexcept -> + size_type +{ + return hash % bucket_count; +} + +auto +object:: +find_element( + key_type key, + std::size_t hash) const noexcept -> + element* +{ + auto bc = bucket_count(); + if(bc == 0) + return nullptr; + auto e = tab_->bucket( + constrain_hash(hash, bc)); + auto eq = key_eq(); + while(e != tab_->end) + { + if(eq(key, e->key())) + return e; + e = e->local_next_; + } + return nullptr; +} + +auto +object:: +prepare_insert( + const_iterator* before, + key_type key, + std::size_t hash) -> + element* +{ + auto bc = bucket_count(); + if(bc > 0) + { + auto e = find_element(key, hash); + if(e) + return e; + } + if(size() + 1 > bc * max_load_factor() + || bc == 0) + { + auto const at_end = *before == end(); + rehash(static_cast( + (std::ceil)( + float(size()+1) / + max_load_factor()))); + if(at_end) + *before = end(); + } + return nullptr; +} + +void +object:: +finish_insert( + const_iterator before, + element* e, + std::size_t hash) +{ + auto const bn = constrain_hash( + hash, tab_->bucket_count); + auto& head = tab_->bucket(bn); + e->local_next_ = head; + head = e; + if(tab_->head == tab_->end) + { + BOOST_ASSERT(before.e_ == tab_->end); + tab_->head = e; + tab_->end->prev_ = e; + e->next_ = tab_->end; + } + else + { + e->prev_ = before.e_->prev_; + e->next_ = before.e_; + e->prev_->next_ = e; + e->next_->prev_ = e; + if(tab_->head == before.e_) + tab_->head = e; + } + ++tab_->count; +} + +void +object:: +remove(element* e) +{ + if(e == tab_->head) + { + tab_->head = e->next_; + } + else + { + e->prev_->next_ = e->next_; + e->next_->prev_ = e->prev_; + } + auto& head = tab_->bucket( + bucket(e->key())); + if(head != e) + { + auto it = head; + BOOST_ASSERT(it != tab_->end); + while(it->local_next_ != e) + { + it = it->local_next_; + BOOST_ASSERT(it != tab_->end); + } + it->local_next_ = e->local_next_; + } + else + { + head = head->local_next_; + } + --tab_->count; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/parse_file.ipp b/include/boost/json/impl/parse_file.ipp new file mode 100644 index 00000000..a22bba05 --- /dev/null +++ b/include/boost/json/impl/parse_file.ipp @@ -0,0 +1,61 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_PARSE_FILE_IPP +#define BOOST_JSON_IMPL_PARSE_FILE_IPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +void +parse_file( + char const* path, + basic_parser& parser, + error_code& ec) +{ + file f; + f.open(path, beast::file_mode::scan, ec); + if(ec) + return; + flat_buffer b; + auto remain = f.size(ec); + if(ec) + return; + while(remain > 0) + { + auto amount = beast::detail::clamp(remain); + auto mb = b.prepare(amount); + b.commit(f.read(mb.data(), mb.size(), ec)); + if(ec) + return; + if(remain == b.size()) + break; + auto bytes_used = + parser.write_some(b.data(), ec); + if(ec) + return; + remain -= b.size(); + b.consume(bytes_used); + } + parser.write(b.data(), ec); + if(ec) + return; + // finished +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/parser.hpp b/include/boost/json/impl/parser.hpp new file mode 100644 index 00000000..82d5970e --- /dev/null +++ b/include/boost/json/impl/parser.hpp @@ -0,0 +1,47 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_PARSER_HPP +#define BOOST_JSON_IMPL_PARSER_HPP + +namespace boost { +namespace beast { +namespace json { + +template +void +parser:: +assign(T&& t) +{ + auto& jv = *stack_.front(); + BOOST_ASSERT(! jv.is_object()); + if(obj_) + { + BOOST_ASSERT(jv.is_null()); + jv = std::forward(t); + stack_.pop_front(); + } + else if(stack_.front()->is_array()) + { + BOOST_ASSERT(s_.empty()); + jv.as_array().emplace_back( + std::forward(t)); + } + else + { + BOOST_ASSERT(jv.is_null()); + jv = std::forward(t); + } +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/parser.ipp b/include/boost/json/impl/parser.ipp new file mode 100644 index 00000000..5199fd63 --- /dev/null +++ b/include/boost/json/impl/parser.ipp @@ -0,0 +1,273 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_PARSER_IPP +#define BOOST_JSON_IMPL_PARSER_IPP + +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +parser:: +parser() +{ +} + +parser:: +parser(storage_ptr const& store) + : jv_(store) +{ +} + +value const& +parser:: +get() const noexcept +{ + return jv_; +} + +value +parser:: +release() noexcept +{ + return std::move(jv_); +} + +void +parser:: +on_document_begin(error_code&) +{ + stack_.clear(); + stack_.push_front(&jv_); + s_.clear(); + obj_ = false; +} + +void +parser:: +on_object_begin(error_code& ec) +{ + if(stack_.size() >= max_depth_) + { + ec = error::too_deep; + return; + } + auto& jv = *stack_.front(); + BOOST_ASSERT(! jv.is_object()); + if(obj_) + { + BOOST_ASSERT(jv.is_null()); + jv.emplace_object(); + } + else if(jv.is_array()) + { + jv.as_array().emplace_back( + kind::object); + stack_.push_front( + &jv.as_array().back()); + } + else + { + BOOST_ASSERT(jv.is_null()); + jv = kind::object; + } + obj_ = true; +} + +void +parser:: +on_object_end(error_code&) +{ + BOOST_ASSERT( + stack_.front()->is_object()); + stack_.pop_front(); + if(! stack_.empty()) + { + auto const& jv = stack_.front(); + BOOST_ASSERT( + jv->is_array() || jv->is_object()); + obj_ = jv->is_object(); + } +} + +void +parser:: +on_array_begin(error_code& ec) +{ + if(stack_.size() >= max_depth_) + { + ec = error::too_deep; + return; + } + auto& jv = *stack_.front(); + BOOST_ASSERT(! jv.is_object()); + if(obj_) + { + BOOST_ASSERT(jv.is_null()); + jv.emplace_array(); + } + else if(jv.is_array()) + { + BOOST_ASSERT(s_.empty()); + jv.as_array().emplace_back( + kind::array); + stack_.push_front( + &jv.as_array().back()); + } + else + { + BOOST_ASSERT(jv.is_null()); + jv = kind::array; + } + obj_ = false; +} + +void +parser:: +on_array_end(error_code&) +{ + BOOST_ASSERT( + stack_.front()->is_array()); + stack_.pop_front(); + if(! stack_.empty()) + { + auto const& jv = stack_.front(); + BOOST_ASSERT( + jv->is_array() || jv->is_object()); + obj_ = jv->is_object(); + } +} + +void +parser:: +on_key_data( + string_view s, + error_code&) +{ + s_.append(s.data(), s.size()); +} + +void +parser:: +on_key_end( + string_view s, + error_code&) +{ + auto& jv = *stack_.front(); + if(! s_.empty()) + { + s_.append(s.data(), s.size()); + s = {s_.data(), s_.size()}; + } + if(jv.is_object()) + { + stack_.push_front( + &jv.as_object().emplace(s, + kind::null).first->second); + } + else if(stack_.front()->is_array()) + { + BOOST_ASSERT(s_.empty()); + jv.as_array().emplace_back(kind::null); + } + else + { + BOOST_ASSERT(jv.is_null()); + // nothing to do + } + s_.clear(); +} + +void +parser:: +on_string_data( + string_view s, error_code&) +{ + auto& jv = *stack_.front(); + BOOST_ASSERT(! jv.is_object()); + if(! jv.is_string()) + { + if(obj_) + { + BOOST_ASSERT(jv.is_null()); + jv.emplace_string().append( + s.data(), s.size()); + } + else if(stack_.front()->is_array()) + { + BOOST_ASSERT(s_.empty()); + jv.as_array().emplace_back(kind::string); + stack_.push_front( + &jv.as_array().back()); + stack_.front()->as_string().append( + s.data(), s.size()); + } + else + { + BOOST_ASSERT(jv.is_null()); + jv.emplace_string().append( + s.data(), s.size()); + } + } + else + { + stack_.front()->as_string().append( + s.data(), s.size()); + } +} + +void +parser:: +on_string_end( + string_view s, + error_code& ec) +{ + on_string_data(s, ec); + BOOST_ASSERT(stack_.front()->is_string()); + stack_.pop_front(); + if(! stack_.empty()) + { + auto const& jv = stack_.front(); + BOOST_ASSERT( + jv->is_array() || jv->is_object()); + obj_ = jv->is_object(); + } +} + +void +parser:: +on_number(number n, error_code&) +{ + assign(n); +} + +void +parser:: +on_bool(bool b, error_code&) +{ + assign(b); +} + +void +parser:: +on_null(error_code&) +{ + assign(null); +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/serializer.ipp b/include/boost/json/impl/serializer.ipp new file mode 100644 index 00000000..c8fc0d9d --- /dev/null +++ b/include/boost/json/impl/serializer.ipp @@ -0,0 +1,260 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_SERIALIZER_IPP +#define BOOST_JSON_IMPL_SERIALIZER_IPP + +#include + +namespace boost { +namespace beast { +namespace json { + +serializer:: +impl:: +impl(value const& jv) + : jv_(jv) + , it_(jv) +{ +} + +bool +serializer:: +impl:: +is_done() const noexcept +{ + return state_ == state::done; +} + +std::size_t +serializer:: +impl:: +next(net::mutable_buffer b) +{ + auto const p0 = + reinterpret_cast(b.data()); + auto p = p0; + auto n = b.size(); + while(n > 0) + { + switch(state_) + { + case state::next: + ++it_; + if(it_ == end) + { + state_ = state::done; + break; + } + BOOST_FALLTHROUGH; + + case state::init: + str_ = it_->key; + if(! str_.empty()) + { + *p++ = '\"'; + --n; + state_ = state::key; + } + else + { + state_ = state::value; + } + break; + + case state::key: + case state::string: + // Write escaped/UTF-8 string + if(n >= str_.size()) + { + std::memcpy(p, + str_.data(), str_.size()); + p += str_.size(); + n -= str_.size(); + if(state_ == state::string) + { + if(last_) + str_ = {"\"", 1}; + else + str_ = {"\",", 2}; + state_ = state::literal; + } + else + { + str_ = {"\":", 2}; + state_ = state::key_literal; + } + break; + } + else + { + std::memcpy(p, + str_.data(), n); + p += n; + str_ = { + str_.data() + n, + str_.size() - n}; + n = 0; + } + break; + + case state::literal: + case state::key_literal: + // Copy content of str_ without escapes + if(n >= str_.size()) + { + std::memcpy(p, + str_.data(), str_.size()); + p += str_.size(); + n -= str_.size(); + if(state_ == state::literal) + state_ = state::next; + else + state_ = state::value; + } + else + { + std::memcpy(p, + str_.data(), n); + p += n; + str_ = { + str_.data() + n, + str_.size() - n}; + n = 0; + } + break; + + case state::value: + { + auto const& e = *it_; + if(! e.end) + { + switch(e.value.kind()) + { + case kind::object: + *p++ = '{'; + --n; + state_ = state::next; + break; + case kind::array: + *p++ = '['; + --n; + state_ = state::next; + break; + case kind::string: + *p++ = '\"'; + str_ = e.value.as_string(); + last_ = e.last; + state_ = state::string; + break; + case kind::number: + str_ = e.value.as_number().print( + buf_, sizeof(buf_)); + if(! e.last) + { + buf_[str_.size()] = ','; + str_ = {buf_, str_.size() + 1}; + } + state_ = state::literal; + break; + case kind::boolean: + if(e.value.as_bool()) + { + if(! e.last) + str_ = {"true,", 5}; + else + str_ = {"true", 4}; + } + else + { + if(! e.last) + str_ = {"false,", 6}; + else + str_ = {"false", 5}; + } + state_ = state::literal; + break; + case kind::null: + if(! e.last) + str_ = {"null,", 5}; + else + str_ = {"null", 4}; + state_ = state::literal; + break; + } + } + else if(e->value.is_object()) + { + if(! e.last) + str_ = {"},", 2}; + else + str_ = {"}", 1}; + state_ = state::literal; + } + else if(e->value.is_array()) + { + if(! e.last) + str_ = {"],", 2}; + else + str_ = {"]", 1}; + state_ = state::literal; + } + break; + } + + case state::done: + goto finish; + } + } +finish: + return p - p0; +} + +void +serializer:: +impl:: +append(char c, + net::mutable_buffer& b) +{ + BOOST_ASSERT(b.size() >= 1); + *reinterpret_cast(b.data()) = c; + b += 1; +} + +void +serializer:: +impl:: +append( + char const* s, std::size_t n, + net::mutable_buffer& b) +{ + BOOST_ASSERT(b.size() >= n); + std::memcpy(b.data(), s, n); + b += n; +} + +//------------------------------------------------------------------------------ + +serializer:: +~serializer() +{ + get_base().~base(); +} + +serializer:: +serializer(value const& jv) +{ + ::new(&get_base()) impl(jv); +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/storage.hpp b/include/boost/json/impl/storage.hpp new file mode 100644 index 00000000..26d017a6 --- /dev/null +++ b/include/boost/json/impl/storage.hpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_STORAGE_HPP +#define BOOST_JSON_IMPL_STORAGE_HPP + +#include + +namespace boost { +namespace beast { +namespace json { + +template +storage_ptr +make_storage_ptr(Allocator const& a) +{ + return storage_ptr( + new detail::storage_adaptor(a)); +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/storage.ipp b/include/boost/json/impl/storage.ipp new file mode 100644 index 00000000..e5cf3cff --- /dev/null +++ b/include/boost/json/impl/storage.ipp @@ -0,0 +1,113 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_STORAGE_IPP +#define BOOST_JSON_IMPL_STORAGE_IPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +storage_ptr:: +~storage_ptr() +{ + if(p_) + p_->release(); +} + +storage_ptr:: +storage_ptr(storage_ptr&& other) noexcept + : p_(boost::exchange(other.p_, nullptr)) +{ +} + +storage_ptr:: +storage_ptr(storage_ptr const& other) noexcept + : p_(other.p_) +{ + if(p_) + p_->addref(); +} + +storage_ptr& +storage_ptr:: +operator=(storage_ptr&& other) noexcept +{ + BOOST_ASSERT(this != &other); + if(p_) + p_->release(); + p_ = boost::exchange(other.p_, nullptr); + return *this; +} + +storage_ptr& +storage_ptr:: +operator=(storage_ptr const& other) noexcept +{ + if(other.p_) + other.p_->addref(); + if(p_) + p_->release(); + p_ = other.p_; + return *this; +} + +storage_ptr:: +storage_ptr(storage* p) noexcept + : p_(p) +{ +} + +storage* +storage_ptr:: +release() noexcept +{ + return boost::exchange(p_, nullptr); +} + +//------------------------------------------------------------------------------ + +namespace detail { + +inline +storage_ptr& +raw_default_storage() noexcept +{ + static storage_ptr sp = + make_storage_ptr( + std::allocator{}); + return sp; +} + +} // detail + +storage_ptr +default_storage() +{ + return detail::raw_default_storage(); +} + +void +default_storage(storage_ptr sp) +{ + detail::raw_default_storage() = std::move(sp); +} + + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/value.hpp b/include/boost/json/impl/value.hpp new file mode 100644 index 00000000..e4e1022c --- /dev/null +++ b/include/boost/json/impl/value.hpp @@ -0,0 +1,574 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_VALUE_HPP +#define BOOST_JSON_IMPL_VALUE_HPP + +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +//------------------------------------------------------------------------------ + +class value::pointer +{ + reference t_; + +public: + pointer(reference t) + : t_(t) + { + } + + reference* + operator->() noexcept + { + return &t_; + } +}; + +//------------------------------------------------------------------------------ + +class value::const_pointer +{ + const_reference t_; + +public: + const_pointer( + const_reference t) + : t_(t) + { + } + + const_reference* + operator->() noexcept + { + return &t_; + } +}; + +//------------------------------------------------------------------------------ + +class value::iterator +{ + union + { + array::iterator arr_it_; + object::iterator obj_it_; + }; + bool arr_; + + friend class value; + +public: + using value_type = value::value_type; + using pointer = value::pointer; + using reference = value::reference; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + ~iterator() + { + if(! arr_) + obj_it_.~iterator(); + } + + iterator() noexcept + : arr_it_{} + , arr_(true) + { + } + + iterator(iterator const& other) noexcept + : arr_(other.arr_) + { + if(arr_) + arr_it_ = other.arr_it_; + else + ::new(&obj_it_) + object::iterator(other.obj_it_); + } + + iterator(object::iterator it) + : obj_it_(it) + , arr_(false) + { + } + + iterator(array::iterator it) + : arr_it_(it) + , arr_(true) + { + } + + iterator& + operator=(iterator const& other) noexcept + { + if(! arr_) + obj_it_.~iterator(); + arr_ = other.arr_; + if(arr_) + arr_it_ = other.arr_it_; + else + ::new(&obj_it_) + object::iterator(other.obj_it_); + return *this; + } + + bool + operator==( + iterator const& other) const noexcept + { + if(arr_ != other.arr_) + return false; + if(arr_) + return arr_it_ == other.arr_it_; + return obj_it_ == other.obj_it_; + } + + bool + operator!=( + iterator const& other) const noexcept + { + return ! (*this == other); + } + + bool + operator==( + const_iterator const& other) const noexcept; + + bool + operator!=( + const_iterator const& other) const noexcept; + + iterator& + operator++() noexcept + { + if(arr_) + ++arr_it_; + else + ++obj_it_; + return *this; + } + + iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + iterator& + operator--() noexcept + { + if(arr_) + --arr_it_; + else + --obj_it_; + return *this; + } + + iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + reference + operator*() const noexcept + { + if(arr_) + return reference{ + "", *arr_it_ }; + return reference{ + obj_it_->first, + obj_it_->second }; + } + + pointer + operator->() const noexcept + { + return *(*this); + } +}; + +//------------------------------------------------------------------------------ + +class value::const_iterator +{ + union + { + array::const_iterator arr_it_; + object::const_iterator obj_it_; + }; + bool arr_; + + friend class value; + +public: + using value_type = value::value_type; + using pointer = value::const_pointer; + using reference = value::const_reference; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + ~const_iterator() + { + if(! arr_) + obj_it_.~const_iterator(); + } + + const_iterator() noexcept + : arr_it_{} + , arr_(true) + { + } + + const_iterator( + const_iterator const& other) noexcept + : arr_(other.arr_) + { + if(arr_) + arr_it_ = other.arr_it_; + else + ::new(&obj_it_) + object::const_iterator( + other.obj_it_); + } + + const_iterator( + object::const_iterator it) + : obj_it_(it) + , arr_(false) + { + } + + const_iterator( + array::const_iterator it) + : arr_it_(it) + , arr_(true) + { + } + + const_iterator& + operator=(const_iterator const& other) noexcept + { + if(! arr_) + obj_it_.~const_iterator(); + arr_ = other.arr_; + if(arr_) + arr_it_ = other.arr_it_; + else + ::new(&obj_it_) + object::const_iterator( + other.obj_it_); + return *this; + } + + bool + operator==( + const_iterator const& other) const noexcept + { + if(arr_ != other.arr_) + return false; + if(arr_) + return arr_it_ == other.arr_it_; + return obj_it_ == other.obj_it_; + } + + bool + operator!=( + const_iterator const& other) const noexcept + { + return ! (*this == other); + } + + bool + operator==( + iterator const& other) const noexcept; + + bool + operator!=( + iterator const& other) const noexcept; + + const_iterator& + operator++() noexcept + { + if(arr_) + ++arr_it_; + else + ++obj_it_; + return *this; + } + + const_iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + const_iterator& + operator--() noexcept + { + if(arr_) + --arr_it_; + else + --obj_it_; + return *this; + } + + const_iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + reference + operator*() const noexcept + { + if(arr_) + return reference{ + "", *arr_it_ }; + return reference{ + obj_it_->first, + obj_it_->second }; + } + + pointer + operator->() const noexcept + { + return *(*this); + } +}; + +//------------------------------------------------------------------------------ + +template +auto +value:: +insert_or_assign( + key_type key, M&& obj) -> + std::pair +{ + BOOST_ASSERT(is_object()); + return obj_.insert_or_assign( + key, std::forward(obj)); +} + +template +auto +value:: +insert_or_assign( + const_iterator before, + key_type key, + M&& obj) -> + std::pair +{ + BOOST_ASSERT(is_object()); + BOOST_ASSERT(! before.arr_); + return obj_.insert_or_assign( + before.obj_it_, key, + std::forward(obj)); +} + +template +auto +value:: +emplace(key_type key, Arg&& arg) -> + std::pair +{ + BOOST_ASSERT(is_object()); + return obj_.emplace(key, + std::forward(arg)); +} + +template +auto +value:: +emplace( + const_iterator before, + key_type key, Arg&& arg) -> + std::pair +{ + BOOST_ASSERT(is_object()); + BOOST_ASSERT(! before.arr_); + return obj_.emplace( + key, before.obj_it_, + std::forward(arg)); +} + +template +auto +value:: +emplace( + const_iterator before, + Arg&& arg) -> + iterator +{ + BOOST_ASSERT(is_array()); + BOOST_ASSERT(before.arr_); + return arr_.emplace(before.arr_it_, + std::forward(arg)); +} + +template +value& +value:: +emplace_back(Arg&& arg) +{ + BOOST_ASSERT(is_array()); + return *arr_.emplace(arr_.end(), + std::forward(arg)); +} + +//------------------------------------------------------------------------------ + +namespace detail { + +template +struct is_range : std::false_type +{ +}; + +template +struct is_range().begin(), + std::declval().end() + )>> : std::true_type +{ +}; + +} // detail + +//------------------------------------------------------------------------------ +// +// assign to value +// + +// range +template::value + && ! std::is_same::value + && has_to_json::value + >::type +> +void +to_json(T const& t, value& v) +{ + v.reset(json::kind::array); + for(auto const& e : t) + v.as_array().push_back(e); +} + +// string +inline +void +to_json(string_view t, value& v) +{ + v.emplace_string().assign( + t.data(), t.size()); +} + +// string +inline +void +to_json(char const* t, value& v) +{ + v.emplace_string() = t; +} + +// number +template::value && + ! std::is_same::value>::type +> +inline +void +to_json(T t, value& v) +{ + v.emplace_number() = t; +} + +// bool +inline +void +to_json(bool b, value& v) +{ + v.emplace_bool() = b; +} + +// null +inline +void +to_json(std::nullptr_t, value& v) +{ + v.emplace_null(); +} + +//------------------------------------------------------------------------------ +// +// assign value to +// + +// integer + +template::value>::type +> +void +from_json(T& t, value const& v) +{ + if(v.is_int64()) + { + auto const rhs = v.get_int64(); + if( rhs > (std::numeric_limits::max)() || + rhs < (std::numeric_limits::min)()) + throw system_error( + error::integer_overflow); + t = static_cast(rhs); + } + else if(v.is_uint64()) + { + auto const rhs = v.get_uint64(); + if(rhs > (std::numeric_limits::max)()) + throw system_error( + error::integer_overflow); + t = static_cast(rhs); + } + else + { + throw system_error( + error::expected_number); + } +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/impl/value.ipp b/include/boost/json/impl/value.ipp new file mode 100644 index 00000000..9337aff1 --- /dev/null +++ b/include/boost/json/impl/value.ipp @@ -0,0 +1,1149 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_IMPL_VALUE_IPP +#define BOOST_JSON_IMPL_VALUE_IPP + +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +//------------------------------------------------------------------------------ +// +// Special members +// +//------------------------------------------------------------------------------ + +value:: +~value() +{ + clear_impl(); +} + +value:: +value(value&& other) noexcept +{ + switch(other.kind_) + { + case json::kind::object: + ::new(&obj_) object( + std::move(other.obj_)); + other.obj_.~object(); + ::new(&other.nat_.sp_) + storage_ptr(obj_.get_storage()); + break; + + case json::kind::array: + ::new(&arr_) array( + std::move(other.arr_)); + other.arr_.~array(); + ::new(&other.nat_.sp_) + storage_ptr(arr_.get_storage()); + break; + + case json::kind::string: + ::new(&str_) string( + std::move(other.str_)); + other.str_.~string(); + ::new(&other.nat_.sp_) storage_ptr( + str_.get_allocator().get_storage()); + break; + + case json::kind::number: + ::new(&nat_.sp_) + storage_ptr(other.nat_.sp_); + ::new(&nat_.num_) number( + other.nat_.num_); + other.nat_.num_.~number(); + break; + + case json::kind::boolean: + ::new(&nat_.sp_) + storage_ptr(other.nat_.sp_); + nat_.bool_ = other.nat_.bool_; + break; + + case json::kind::null: + ::new(&nat_.sp_) + storage_ptr(other.nat_.sp_); + break; + } + kind_ = other.kind_; + other.kind_ = json::kind::null; +} + +value:: +value( + value&& other, + storage_ptr store) +{ + move(std::move(store), std::move(other)); +} + +value:: +value(value const& other) + : value(other, other.get_storage()) +{ +} + +value:: +value( + value const& other, + storage_ptr store) +{ + copy(std::move(store), other); +} + +value& +value:: +operator=(value&& other) +{ + auto sp = release_storage(); + clear_impl(); + move(std::move(sp), std::move(other)); + return *this; +} + +value& +value:: +operator=(value const& other) +{ + if(this != &other) + { + auto sp = release_storage(); + clear_impl(); + copy(std::move(sp), other); + } + return *this; +} + +//------------------------------------------------------------------------------ +// +// Construction and Assignment +// +//------------------------------------------------------------------------------ + +value:: +value() noexcept + : value( + json::kind::null, + default_storage()) +{ +} + +value:: +value(storage_ptr store) noexcept + : value( + json::kind::null, + std::move(store)) +{ +} + +value:: +value(json::kind k) noexcept + : value( + k, + default_storage()) +{ +} + +value:: +value( + json::kind k, + storage_ptr store) noexcept +{ + construct(k, std::move(store)); +} + +value:: +value(object obj) noexcept + : obj_(std::move(obj)) + , kind_(json::kind::object) +{ +} + +value:: +value( + object obj, + storage_ptr store) + : obj_( + std::move(obj), + std::move(store)) + , kind_(json::kind::object) +{ +} + +value:: +value(array arr) noexcept + : arr_(std::move(arr)) + , kind_(json::kind::array) +{ +} + +value:: +value( + array arr, + storage_ptr store) + : arr_(std::move(arr), std::move(store)) + , kind_(json::kind::array) +{ +} + +value:: +value(string str) noexcept + : str_(std::move(str)) + , kind_(json::kind::string) +{ +} + +#if defined(BOOST_LIBSTDCXX_VERSION) +// workaround for missing std::string +// ctors in some stdlib versions +value:: +value( + string str, + storage_ptr store) + : str_(string::allocator_type( + std::move(store))) + , kind_(json::kind::string) +{ + str_ = std::move(str); +} +#else +value:: +value( + string str, + storage_ptr store) + : str_(std::move(str), + string::allocator_type( + std::move(store))) + , kind_(json::kind::string) +{ +} +#endif + +value:: +value(number num) + : value(num, + default_storage()) +{ +} + +value:: +value( + number num, + storage_ptr store) + : kind_(json::kind::number) +{ + ::new(&nat_.num_) number(num); + ::new(&nat_.sp_) storage_ptr( + std::move(store)); +} + +value:: +value(std::initializer_list< + std::pair> init) + : value(init, + default_storage()) +{ +} + +value:: +value(std::initializer_list< + std::pair> init, + storage_ptr store) + : value(json::kind::object, std::move(store)) +{ + for(auto& e : init) + obj_.emplace(e.first, + std::move(e.second)); +} + +#if 0 +value:: +value(std::initializer_list init) + : value(init, + default_storage()) +{ +} + +value:: +value(std::initializer_list< + std::pair> init, + storage_ptr store) + : value(json::kind::array, std::move(store)) +{ + for(auto& e : init) + arr_.emplace_back( + std::move(e), + get_storage()); +} +#endif + +value& +value:: +operator=(object obj) +{ + object tmp( + std::move(obj), + get_storage()); + clear_impl(); + ::new(&obj_) object( + std::move(tmp)); + kind_ = json::kind::object; + return *this; +} + +value& +value:: +operator=(array arr) +{ + array tmp( + std::move(arr), + get_storage()); + clear_impl(); + ::new(&arr_) array( + std::move(tmp)); + kind_ = json::kind::array; + return *this; +} + +value& +value:: +operator=(string str) +{ +#if defined(BOOST_LIBSTDCXX_VERSION) + // workaround for missing std::string + // ctors in some stdlib versions + auto tmp = string( + string::allocator_type( + get_storage())); + tmp = std::move(str); +#else + auto tmp = string( + std::move(str), + string::allocator_type( + get_storage())); +#endif + clear_impl(); + ::new(&str_) string( + std::move(tmp)); + kind_ = json::kind::string; + return *this; +} + +//------------------------------------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------------------------------------ + +void +value:: +reset(json::kind k) noexcept +{ + auto sp = release_storage(); + clear_impl(); + construct(k, std::move(sp)); +} + +//------------------------------------------------------------------------------ +// +// Structured +// +//------------------------------------------------------------------------------ + +// +// Capacity +// + +bool +value:: +empty() const +{ + BOOST_ASSERT(this->is_structured()); + if(is_object()) + return obj_.empty(); + return arr_.empty(); +} + +auto +value:: +size() const -> + size_type +{ + BOOST_ASSERT(this->is_structured()); + if(is_object()) + return obj_.size(); + return arr_.size(); +} + +// +// Iterator +// + +auto +value:: +begin() -> + iterator +{ + BOOST_ASSERT(this->is_structured()); + if(is_object()) + return obj_.begin(); + return arr_.begin(); +} + +auto +value:: +begin() const -> + const_iterator +{ + BOOST_ASSERT(this->is_structured()); + if(is_object()) + return obj_.begin(); + return arr_.begin(); +} + +auto +value:: +cbegin() -> + const_iterator +{ + BOOST_ASSERT(this->is_structured()); + if(is_object()) + return obj_.cbegin(); + return arr_.cbegin(); +} + +auto +value:: +end() -> + iterator +{ + BOOST_ASSERT(this->is_structured()); + if(is_object()) + return obj_.end(); + return arr_.end(); +} + +auto +value:: +end() const -> + const_iterator +{ + BOOST_ASSERT(this->is_structured()); + if(is_object()) + return obj_.end(); + return arr_.end(); +} + +auto +value:: +cend() -> + const_iterator +{ + BOOST_ASSERT(this->is_structured()); + if(is_object()) + return obj_.cend(); + return arr_.cend(); +} + +auto +value:: +rbegin() -> + reverse_iterator +{ + BOOST_ASSERT(this->is_structured()); + return reverse_iterator(end()); +} + +auto +value:: +rbegin() const -> + const_reverse_iterator +{ + BOOST_ASSERT(this->is_structured()); + return const_reverse_iterator(end()); +} + +auto +value:: +crbegin() -> + const_reverse_iterator +{ + BOOST_ASSERT(this->is_structured()); + return const_reverse_iterator(cend()); +} + +auto +value:: +rend() -> + reverse_iterator +{ + BOOST_ASSERT(this->is_structured()); + return reverse_iterator(begin()); +} + +auto +value:: +rend() const -> + const_reverse_iterator +{ + BOOST_ASSERT(this->is_structured()); + return const_reverse_iterator(begin()); +} + +auto +value:: +crend() -> + const_reverse_iterator +{ + BOOST_ASSERT(this->is_structured()); + return const_reverse_iterator(cbegin()); +} + +// +// Lookup +// + +value& +value:: +at(key_type key) +{ + BOOST_ASSERT(this->is_object()); + return obj_.at(key); +} + +value const& +value:: +at(key_type key) const +{ + BOOST_ASSERT(this->is_object()); + return obj_.at(key); +} + +value& +value:: +operator[](key_type key) +{ + // implicit conversion to object + if(this->is_null()) + *this = json::kind::object; + BOOST_ASSERT(this->is_object()); + return obj_[key]; +} + +value const& +value:: +operator[](key_type key) const +{ + BOOST_ASSERT(this->is_object()); + return obj_[key]; +} + +auto +value:: +count(key_type key) const -> + size_type +{ + BOOST_ASSERT(this->is_object()); + return obj_.count(key); +} + +auto +value:: +find(key_type key) -> + iterator +{ + BOOST_ASSERT(this->is_object()); + return obj_.find(key); +} + +auto +value:: +find(key_type key) const -> + const_iterator +{ + BOOST_ASSERT(this->is_object()); + return obj_.find(key); +} + +bool +value:: +contains(key_type key) const +{ + BOOST_ASSERT(this->is_object()); + return obj_.contains(key); +} + +// +// Elements +// + +auto +value:: +at(size_type pos) -> + reference +{ + BOOST_ASSERT(is_array()); + return { "", arr_.at(pos) }; +} + +auto +value:: +at(size_type pos) const -> + const_reference +{ + BOOST_ASSERT(is_array()); + return { "", arr_.at(pos) }; +} + +value& +value:: +operator[](size_type i) +{ + BOOST_ASSERT(is_array()); + return arr_[i]; +} + +value const& +value:: +operator[](size_type i) const +{ + BOOST_ASSERT(is_array()); + return arr_[i]; +} + +auto +value:: +front() -> + reference +{ + BOOST_ASSERT(is_structured()); + if(is_object()) + return *obj_.begin(); + return { "", arr_.front() }; +} + +auto +value:: +front() const -> + const_reference +{ + BOOST_ASSERT(is_structured()); + if(is_object()) + return *obj_.begin(); + return { "", arr_.front() }; +} + +auto +value:: +back() -> + reference +{ + BOOST_ASSERT(is_structured()); + if(is_object()) + { + auto it = obj_.end(); + return *--it; + } + return { "", arr_.back() }; +} + +auto +value:: +back() const -> + const_reference +{ + BOOST_ASSERT(is_structured()); + if(is_object()) + { + auto it = obj_.end(); + return *--it; + } + return { "", arr_.back() }; +} + +// +// Modifiers +// + +void +value:: +clear() noexcept +{ + switch(kind_) + { + case json::kind::object: + obj_.clear(); + break; + case json::kind::array: + arr_.clear(); + break; + case json::kind::string: + str_.clear(); + break; + default: + break; + } +} + +auto +value:: +erase(key_type key) -> + size_type +{ + BOOST_ASSERT(is_object()); + return obj_.erase(key); +} + +auto +value:: +erase(const_iterator pos) -> + iterator +{ + BOOST_ASSERT(is_structured()); + if(is_object()) + return obj_.erase(pos.obj_it_); + return arr_.erase(pos.arr_it_); +} + +auto +value:: +erase( + const_iterator first, + const_iterator last) -> + iterator +{ + BOOST_ASSERT(is_structured()); + if(is_object()) + { + BOOST_ASSERT(! first.arr_); + BOOST_ASSERT(! last.arr_); + return obj_.erase( + first.obj_it_, last.obj_it_); + } + BOOST_ASSERT(first.arr_); + BOOST_ASSERT(last.arr_); + return arr_.erase( + first.arr_it_, last.arr_it_); +} + + +void +value:: +pop_back() +{ + BOOST_ASSERT(is_structured()); + if(is_object()) + { + auto it = obj_.end(); + obj_.erase(--it); + return; + } + auto it = arr_.end(); + arr_.erase(--it); +} + +//------------------------------------------------------------------------------ + +// private + +storage_ptr +value:: +get_storage() const noexcept +{ + switch(kind_) + { + case json::kind::object: + return obj_.get_storage(); + + case json::kind::array: + return arr_.get_storage(); + + case json::kind::string: + return str_.get_allocator().get_storage(); + + default: + break; + } + return nat_.sp_; +} + +storage_ptr +value:: +release_storage() noexcept +{ + switch(kind_) + { + case json::kind::object: + return obj_.get_storage(); + + case json::kind::array: + return arr_.get_storage(); + + case json::kind::string: + return str_.get_allocator().get_storage(); + + default: + break; + } + return std::move(nat_.sp_); +} + +void +value:: +construct( + json::kind k, + storage_ptr sp) noexcept +{ + switch(k) + { + case json::kind::object: + // requires: noexcept construction + ::new(&obj_) object(std::move(sp)); + break; + + case json::kind::array: + // requires: noexcept construction + ::new(&arr_) array(std::move(sp)); + break; + + case json::kind::string: + // requires: noexcept construction + ::new(&str_) string( + string::allocator_type( + std::move(sp))); + break; + + case json::kind::number: + ::new(&nat_.num_) number{}; + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + break; + + case json::kind::boolean: + ::new(&nat_.bool_) bool{}; + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + break; + + case json::kind::null: + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + break; + } + kind_ = k; +} + +// doesn't set kind_ +void +value:: +clear_impl() noexcept +{ + switch(kind_) + { + case json::kind::object: + obj_.~object(); + break; + + case json::kind::array: + arr_.~array(); + break; + + case json::kind::string: + str_.~string(); + break; + + case json::kind::number: + nat_.num_.~number(); + + case json::kind::boolean: + case json::kind::null: + nat_.sp_.~storage_ptr(); + break; + } +} + +// unchecked +void +value:: +move( + storage_ptr sp, + value&& other) +{ + switch(other.kind_) + { + case json::kind::object: + #ifndef BOOST_NO_EXCEPTIONS + try + { + #endif + ::new(&obj_) object( + std::move(other.obj_), sp); + #ifndef BOOST_NO_EXCEPTIONS + } + catch(...) + { + kind_ = json::kind::null; + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + throw; + } + #endif + sp = other.get_storage(); + other.obj_.~object(); + ::new(&other.nat_.sp_) + storage_ptr(std::move(sp)); + break; + + case json::kind::array: + #ifndef BOOST_NO_EXCEPTIONS + try + { + #endif + ::new(&arr_) array( + std::move(other.arr_), sp); + #ifndef BOOST_NO_EXCEPTIONS + } + catch(...) + { + kind_ = json::kind::null; + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + throw; + } + #endif + sp = other.get_storage(); + other.arr_.~array(); + ::new(&other.nat_.sp_) + storage_ptr(std::move(sp)); + break; + + case json::kind::string: + #ifndef BOOST_NO_EXCEPTIONS + try + #endif + { + // workaround for missing std::string + // ctors in some stdlib versions + #if defined(BOOST_LIBSTDCXX_VERSION) + auto tmp = string(typename + string::allocator_type(sp)); + tmp = std::move(other.str_); + ::new(&str_) string(std::move(tmp)); + #else + ::new(&str_) string( + std::move(other.str_), typename + string::allocator_type(sp)); + #endif + } + #ifndef BOOST_NO_EXCEPTIONS + catch(...) + { + kind_ = json::kind::null; + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + throw; + } + #endif + sp = other.get_storage(); + other.str_.~string(); + ::new(&other.nat_.sp_) + storage_ptr(std::move(sp)); + break; + + case json::kind::number: + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + ::new(&nat_.num_) number( + other.nat_.num_); + break; + + case json::kind::boolean: + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + nat_.bool_ = other.nat_.bool_; + break; + + case json::kind::null: + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + break; + } + kind_ = other.kind_; + other.kind_ = json::kind::null; +} + +// unchecked +void +value:: +copy( + storage_ptr sp, + value const& other) +{ + switch(other.kind_) + { + case json::kind::object: + #ifndef BOOST_NO_EXCEPTIONS + try + { + #endif + ::new(&obj_) object( + other.obj_, sp); + #ifndef BOOST_NO_EXCEPTIONS + } + catch(...) + { + kind_ = json::kind::null; + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + throw; + } + #endif + break; + + case json::kind::array: + #ifndef BOOST_NO_EXCEPTIONS + try + { + #endif + ::new(&arr_) array( + other.arr_, sp); + + #ifndef BOOST_NO_EXCEPTIONS + } + catch(...) + { + kind_ = json::kind::null; + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + throw; + } + #endif + break; + + case json::kind::string: + #ifndef BOOST_NO_EXCEPTIONS + try + { + #endif + #if defined(BOOST_LIBSTDCXX_VERSION) + // workaround for missing std::string + // ctors in some stdlib versions + auto tmp = string(typename + string::allocator_type(sp)); + tmp = other.str_; + ::new(&str_) string(std::move(tmp)); + #else + ::new(&str_) string( + other.str_, typename + string::allocator_type(sp)); + #endif + #ifndef BOOST_NO_EXCEPTIONS + } + catch(...) + { + kind_ = json::kind::null; + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + throw; + } + #endif + break; + + case json::kind::number: + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + ::new(&nat_.num_) number( + other.nat_.num_); + break; + + case json::kind::boolean: + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + nat_.bool_ = other.nat_.bool_; + break; + + case json::kind::null: + ::new(&nat_.sp_) + storage_ptr(std::move(sp)); + break; + } + kind_ = other.kind_; +} + +//------------------------------------------------------------------------------ + +// friends + +std::ostream& +operator<<(std::ostream& os, value const& jv) +{ + switch(jv.kind()) + { + case json::kind::object: + os << '{'; + for(auto it = jv.as_object().begin(), + last = jv.as_object().end(); + it != last;) + { + os << '\"' << it->first << "\":"; + os << it->second; + if(++it != last) + os << ','; + } + os << '}'; + break; + + case json::kind::array: + os << '['; + for(auto it = jv.as_array().begin(), + last = jv.as_array().end(); + it != last;) + { + os << *it; + if(++it != last) + os << ','; + } + os << ']'; + break; + + case json::kind::string: + os << '\"' << jv.as_string() << '\"'; + break; + + case json::kind::number: + os << jv.as_number(); + break; + + case json::kind::boolean: + if(jv.as_bool()) + os << "true"; + else + os << "false"; + break; + + case json::kind::null: + os << "null"; + break; + } + return os; +} + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/iterator.hpp b/include/boost/json/iterator.hpp new file mode 100644 index 00000000..c9d9d846 --- /dev/null +++ b/include/boost/json/iterator.hpp @@ -0,0 +1,149 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_ITERATOR_HPP +#define BOOST_JSON_ITERATOR_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +struct end_t +{ +}; + +BOOST_BEAST_INLINE_VARIABLE(end, end_t) + +/** A generator to perform a depth-first traversal of a JSON value. +*/ +class const_iterator +{ + struct node + { + union + { + array::const_iterator arr_it; + object::const_iterator obj_it; + }; + value const* v; + string_view key; + bool it; + + BOOST_BEAST_DECL + ~node(); + + BOOST_BEAST_DECL + node( + value const& v_, + bool it_) noexcept; + + BOOST_BEAST_DECL + node(node const& other) noexcept; + + BOOST_BEAST_DECL + bool + last() const noexcept; + }; + + detail::stack stack_; + +public: + struct value_type + { + std::size_t depth; + string_view key; + json::value const& value; + bool last; + bool end; + + value_type const* + operator->() const noexcept + { + return this; + } + }; + + BOOST_BEAST_DECL + explicit + const_iterator( + value const& jv); + + BOOST_BEAST_DECL + value_type + operator*() const noexcept; + + BOOST_BEAST_DECL + const_iterator& + operator++() noexcept; + + void + operator++(int) noexcept + { + ++*this; + } + + value_type + operator->() const noexcept + { + return *(*this); + } + + friend + bool + operator==( + const_iterator const& lhs, + end_t) noexcept + { + return lhs.stack_.empty(); + } + + friend + bool + operator==( + end_t, + const_iterator const& rhs) noexcept + { + return rhs.stack_.empty(); + } + + friend + bool + operator!=( + const_iterator const& lhs, + end_t) noexcept + { + return ! lhs.stack_.empty(); + } + + friend + bool + operator!=( + end_t, + const_iterator const& rhs) noexcept + { + return ! rhs.stack_.empty(); + } +}; + +} // json +} // beast +} // boost + +#if BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/json.natvis b/include/boost/json/json.natvis new file mode 100644 index 00000000..1f8bd43d --- /dev/null +++ b/include/boost/json/json.natvis @@ -0,0 +1,38 @@ + + + + object{obj_} + array{arr_} + string{str_} + {nat_.num_} + {nat_.bool_} + null + + obj_ + arr_ + str_ + nat_.sp_ + nat_.num_ + nat_.bool_ + + + + + + second + + + diff --git a/include/boost/json/kind.hpp b/include/boost/json/kind.hpp new file mode 100644 index 00000000..cd864604 --- /dev/null +++ b/include/boost/json/kind.hpp @@ -0,0 +1,37 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_KIND_HPP +#define BOOST_JSON_KIND_HPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +enum class kind +{ + object, + array, + string, + number, + boolean, + null +}; + +BOOST_BEAST_INLINE_VARIABLE(null, std::nullptr_t) + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/number.hpp b/include/boost/json/number.hpp new file mode 100644 index 00000000..968ed309 --- /dev/null +++ b/include/boost/json/number.hpp @@ -0,0 +1,237 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_NUMBER_HPP +#define BOOST_JSON_NUMBER_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +/** The representation of parsed numbers. +*/ +class number +{ + struct base10_ieee + { + unsigned + long long mant; + short exp; + bool sign; + }; + + enum kind + { + type_int64, + type_uint64, + type_double, + type_ieee + }; + + union + { + unsigned + long long uint64_; + long long int64_; + double double_; + base10_ieee ieee_; + }; + + kind k_; + +public: + static std::size_t constexpr + max_string_chars = + 1 + // '-' + 19 + // unsigned 64-bit mantissa + 1 + // 'e' + 1 + // '-' + 5; // unsigned 16-bit exponent + + using mantissa_type = + unsigned long long; + + using exponent_type = + short; + + number() = default; + number(number const&) = default; + number& operator=( + number const&) = default; + + /** Construct a number from mantissa, exponent, and sign + */ + BOOST_BEAST_DECL + number( + mantissa_type mant, + exponent_type exp, + bool sign) noexcept; + + /// Construct a number from a signed integer + BOOST_BEAST_DECL + number(short v) noexcept; + + /// Construct a number from a signed integer + BOOST_BEAST_DECL + number(int v) noexcept; + + /// Construct a number from a signed integer + BOOST_BEAST_DECL + number(long v) noexcept; + + /// Construct a number from a signed integer + BOOST_BEAST_DECL + number(long long v) noexcept; + + /// Construct a number from an unsigned integer + BOOST_BEAST_DECL + number(unsigned short v) noexcept; + + /// Construct a number from an unsigned integer + BOOST_BEAST_DECL + number(unsigned int v) noexcept; + + /// Construct a number from an unsigned integer + BOOST_BEAST_DECL + number(unsigned long v) noexcept; + + /// Construct a number from an unsigned integer + BOOST_BEAST_DECL + number(unsigned long long v) noexcept; + + /// Construct a number from a floating point value + BOOST_BEAST_DECL + number(float v) noexcept; + + /// Construct a number from a floating point value + BOOST_BEAST_DECL + number(double v) noexcept; + + /// Return true if the number is negative + BOOST_BEAST_DECL + bool + is_negative() const noexcept; + + /// Return true if the number is integral + BOOST_BEAST_DECL + bool + is_integral() const noexcept; + + /// Return true if the number can be represented with a signed 64-bit integer + BOOST_BEAST_DECL + bool + is_int64() const noexcept; + + /// Return true if the number can be represented with an unsigned 64-bit integer + BOOST_BEAST_DECL + bool + is_uint64() const noexcept; + + /** Return the number as a signed 64-bit integer + + The return value is undefined unless @ref is_int64 returns `true`. + */ + BOOST_BEAST_DECL + std::int_least64_t + get_int64() const noexcept; + + /** Return the number as an unsigned 64-bit integer + + The return value is undefined unless @ref is_uint64 returns `true`. + */ + BOOST_BEAST_DECL + std::uint_least64_t + get_uint64() const noexcept; + + /** Return the number as floating point + */ + BOOST_BEAST_DECL + double + get_double() const noexcept; + + /** Convert the number to a string. + + The destination must contain at least + @ref max_string_chars bytes of valid storage. + + @return A string view representing the number as + as string. Storage for the view comes from `dest`. + */ + BOOST_BEAST_DECL + string_view + print( + char* buf, + std::size_t buf_size) const noexcept; + +private: + struct pow10; + + BOOST_BEAST_DECL + void + assign_signed( + long long i) noexcept; + + BOOST_BEAST_DECL + void + assign_unsigned( + unsigned long long i) noexcept; + + BOOST_BEAST_DECL + void + assign_double(double f) noexcept; + + friend + std::ostream& + operator<<(std::ostream& os, number const& n); + + friend + bool + operator==( + number const& lhs, + number const& rhs) noexcept; + + friend + bool + operator!=( + number const& lhs, + number const& rhs) noexcept; + +}; + +BOOST_BEAST_DECL +bool +operator==( + number const& lhs, + number const& rhs) noexcept; + +BOOST_BEAST_DECL +bool +operator!=( + number const& lhs, + number const& rhs) noexcept; + +BOOST_BEAST_DECL +std::ostream& +operator<<(std::ostream& os, number const& n); + +} // json +} // beast +} // boost + +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/object.hpp b/include/boost/json/object.hpp new file mode 100644 index 00000000..b60dfdbb --- /dev/null +++ b/include/boost/json/object.hpp @@ -0,0 +1,560 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_OBJECT_HPP +#define BOOST_JSON_OBJECT_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class value; + +//------------------------------------------------------------------------------ + +/** The native type of object values + + This copies the interface of `std::unordered_map` with + one important distinction: the order of insertions is + preserved. +*/ +class object +{ + struct list_hook; + struct element; + class table; + + storage_ptr sp_; + table* tab_ = nullptr; + float mf_ = 1.0; + +public: + using key_type = beast::string_view; + using mapped_type = value; + using value_type = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = std::pair; + using const_reference = std::pair; + + class hasher; + class key_equal; + class pointer; + class const_pointer; + class iterator; + class const_iterator; + class local_iterator; + class const_local_iterator; + class node_type; + struct insert_return_type; + + //-------------------------------------------------------------------------- + // + // Special Members + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + ~object(); + + BOOST_BEAST_DECL + object(); + + BOOST_BEAST_DECL + object( + size_type capacity); + + BOOST_BEAST_DECL + object( + storage_ptr store); + + BOOST_BEAST_DECL + object( + size_type capacity, + storage_ptr store); + + template + BOOST_BEAST_DECL + object( + InputIt first, + InputIt last); + + template + BOOST_BEAST_DECL + object( + InputIt first, + InputIt last, + size_type capacity); + + template + BOOST_BEAST_DECL + object( + InputIt first, + InputIt last, + storage_ptr store); + + template + BOOST_BEAST_DECL + object( + InputIt first, + InputIt last, + size_type capacity, + storage_ptr store); + + BOOST_BEAST_DECL + object(object&& other) noexcept; + + BOOST_BEAST_DECL + object( + object&& other, + storage_ptr store) noexcept; + + BOOST_BEAST_DECL + object( + object const& other); + + BOOST_BEAST_DECL + object( + object const& other, + storage_ptr store); + + BOOST_BEAST_DECL + object( + std::initializer_list list); + + BOOST_BEAST_DECL + object( + std::initializer_list list, + size_type capacity); + + BOOST_BEAST_DECL + object( + std::initializer_list list, + storage_ptr store); + + BOOST_BEAST_DECL + object( + std::initializer_list list, + size_type capacity, + storage_ptr store); + + BOOST_BEAST_DECL + object& + operator=(object&& other); + + BOOST_BEAST_DECL + object& + operator=(object const& other); + + BOOST_BEAST_DECL + object& + operator=( + std::initializer_list list); + + BOOST_BEAST_DECL + storage_ptr const& + get_storage() const noexcept; + + //-------------------------------------------------------------------------- + // + // Iterators + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + iterator + begin() noexcept; + + BOOST_BEAST_DECL + const_iterator + begin() const noexcept; + + BOOST_BEAST_DECL + const_iterator + cbegin() const noexcept; + + BOOST_BEAST_DECL + iterator + end() noexcept; + + BOOST_BEAST_DECL + const_iterator + end() const noexcept; + + BOOST_BEAST_DECL + const_iterator + cend() const noexcept; + + //-------------------------------------------------------------------------- + // + // Capacity + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + bool + empty() const noexcept; + + BOOST_BEAST_DECL + size_type + size() const noexcept; + + BOOST_BEAST_DECL + size_type + max_size() const noexcept; + + //-------------------------------------------------------------------------- + // + // Modifiers + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + void + clear() noexcept; + + template::value>::type +#endif + > + std::pair + insert(P&& p); + + template::value>::type +#endif + > + std::pair + insert( + const_iterator before, + P&& p); + + template + void + insert(InputIt first, InputIt last); + + BOOST_BEAST_DECL + void + insert(std::initializer_list< + value_type> list); + + BOOST_BEAST_DECL + insert_return_type + insert(node_type&& nh); + + BOOST_BEAST_DECL + insert_return_type + insert( + const_iterator before, + node_type&& nh); + + template + std::pair + insert_or_assign( + key_type key, M&& obj); + + template + std::pair + insert_or_assign( + const_iterator before, + key_type key, + M&& obj); + + template + std::pair + emplace(key_type key, Arg&& arg); + + template + std::pair + emplace( + const_iterator before, + key_type key, Arg&& arg); + + BOOST_BEAST_DECL + iterator + erase(const_iterator pos); + + BOOST_BEAST_DECL + iterator + erase( + const_iterator first, + const_iterator last); + + BOOST_BEAST_DECL + size_type + erase(key_type key); + + BOOST_BEAST_DECL + void + swap(object& other) noexcept; + + BOOST_BEAST_DECL + node_type + extract(const_iterator pos); + + BOOST_BEAST_DECL + node_type + extract(key_type key); + + BOOST_BEAST_DECL + void + merge(object& source); + + BOOST_BEAST_DECL + void + merge(object&& source); + + //-------------------------------------------------------------------------- + // + // Lookup + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + value& + at(key_type key); + + BOOST_BEAST_DECL + value const& + at(key_type key) const; + + BOOST_BEAST_DECL + value& + operator[](key_type key); + + BOOST_BEAST_DECL + value const& + operator[](key_type key) const; + + BOOST_BEAST_DECL + size_type + count(key_type key) const; + + BOOST_BEAST_DECL + size_type + count( + key_type key, + std::size_t hash) const; + + BOOST_BEAST_DECL + iterator + find(key_type key); + + BOOST_BEAST_DECL + iterator + find( + key_type key, + std::size_t hash); + + BOOST_BEAST_DECL + const_iterator + find(key_type key) const; + + BOOST_BEAST_DECL + const_iterator + find( + key_type key, + std::size_t hash) const; + + BOOST_BEAST_DECL + bool + contains(key_type key) const; + + BOOST_BEAST_DECL + bool + contains( + key_type key, + std::size_t hash ) const; + + //-------------------------------------------------------------------------- + // + // Bucket Interface + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + local_iterator + begin(size_type n) noexcept; + + BOOST_BEAST_DECL + const_local_iterator + begin(size_type n) const noexcept; + + BOOST_BEAST_DECL + const_local_iterator + cbegin(size_type n) noexcept; + + BOOST_BEAST_DECL + local_iterator + end(size_type n) noexcept; + + BOOST_BEAST_DECL + const_local_iterator + end(size_type n) const noexcept; + + BOOST_BEAST_DECL + const_local_iterator + cend(size_type n) noexcept; + + BOOST_BEAST_DECL + size_type + bucket_count() const noexcept; + + BOOST_BEAST_DECL + size_type + max_bucket_count() const noexcept; + + BOOST_BEAST_DECL + size_type + bucket_size(size_type n) const noexcept; + + BOOST_BEAST_DECL + size_type + bucket(key_type key) const noexcept; + + //-------------------------------------------------------------------------- + // + // Hash Policy + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + float + load_factor() const noexcept; + + BOOST_BEAST_DECL + float + max_load_factor() const; + + BOOST_BEAST_DECL + void + max_load_factor(float ml); + + BOOST_BEAST_DECL + void + rehash(size_type count); + + BOOST_BEAST_DECL + void + reserve(size_type count); + + //-------------------------------------------------------------------------- + // + // Observers + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + hasher + hash_function() const; + + BOOST_BEAST_DECL + key_equal + key_eq() const; + +private: + struct cleanup_replace; + + template + using iter_cat = typename + std::iterator_traits::iterator_category; + + template + void + construct( + InputIt first, + InputIt last, + size_type capacity, + std::forward_iterator_tag); + + template + void + construct( + InputIt first, + InputIt last, + size_type capacity, + std::input_iterator_tag); + + template + void + insert( + InputIt first, + InputIt last, + std::forward_iterator_tag); + + template + void + insert( + InputIt first, + InputIt last, + std::input_iterator_tag); + + template + std::pair + emplace_impl( + const_iterator before, + key_type key, + Arg&& arg); + + BOOST_BEAST_DECL + static + size_type + constrain_hash( + std::size_t hash, + size_type bucket_count) noexcept; + + BOOST_BEAST_DECL + element* + find_element( + key_type key, + std::size_t hash) const noexcept; + + BOOST_BEAST_DECL + element* + prepare_insert( + const_iterator* before, + key_type key, + std::size_t hash); + + BOOST_BEAST_DECL + void + finish_insert( + const_iterator before, + element* e, + std::size_t hash); + + BOOST_BEAST_DECL + void + remove(element* e); +}; + +} // json +} // beast +} // boost + +// Must be included here for this file to stand alone +#include + +// headers for this file are at the bottom of value.hpp + +#endif diff --git a/include/boost/json/parse_file.hpp b/include/boost/json/parse_file.hpp new file mode 100644 index 00000000..44b9d6cb --- /dev/null +++ b/include/boost/json/parse_file.hpp @@ -0,0 +1,38 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_PARSE_FILE_HPP +#define BOOST_JSON_PARSE_FILE_HPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +/** Parse the contents of a file as JSON. +*/ +BOOST_BEAST_DECL +void +parse_file( + char const* path, + basic_parser& parser, + error_code& ec); + +} // json +} // beast +} // boost + +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/parser.hpp b/include/boost/json/parser.hpp new file mode 100644 index 00000000..f5f93d10 --- /dev/null +++ b/include/boost/json/parser.hpp @@ -0,0 +1,153 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_PARSER_HPP +#define BOOST_JSON_PARSER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class parser : public basic_parser +{ + static std::size_t const + default_max_depth = 32; + + value jv_; + detail::stack stack_; + std::string s_; + unsigned long max_depth_ = + default_max_depth; + bool obj_; + +public: + BOOST_BEAST_DECL + parser(); + + BOOST_BEAST_DECL + parser(storage_ptr const& store); + + /** Returns the maximum allowed depth of input JSON. + */ + std::size_t + max_depth() const noexcept + { + return max_depth_; + } + + /** Set the maximum allowed depth of input JSON. + */ + void + max_depth(unsigned long levels) noexcept + { + max_depth_ = levels; + } + + BOOST_BEAST_DECL + value const& + get() const noexcept; + + BOOST_BEAST_DECL + value + release() noexcept; + +private: + template + void + assign(T&& t); + + BOOST_BEAST_DECL + void + reset(); + + BOOST_BEAST_DECL + void + on_document_begin( + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_object_begin( + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_object_end( + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_array_begin( + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_array_end( + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_key_data( + string_view s, + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_key_end( + string_view s, + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_string_data( + string_view s, + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_string_end( + string_view s, + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_number( + number n, + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_bool( + bool b, + error_code& ec) override; + + BOOST_BEAST_DECL + void + on_null(error_code&) override; +}; + +} // json +} // beast +} // boost + +#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/serializer.hpp b/include/boost/json/serializer.hpp new file mode 100644 index 00000000..aa9c07fe --- /dev/null +++ b/include/boost/json/serializer.hpp @@ -0,0 +1,143 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_SERIALIZER_HPP +#define BOOST_JSON_SERIALIZER_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class serializer +{ + struct base + { + virtual ~base() = default; + + virtual + bool + is_done() const noexcept = 0; + + virtual + std::size_t + next(net::mutable_buffer) = 0; + }; + + class impl : public base + { + enum class state + { + next, + init, + key, + value, + literal, + key_literal, + string, + done + }; + + value const& jv_; + const_iterator it_; + string_view str_; + char buf_[number::max_string_chars + 1]; + state state_ = state::init; + bool last_; + + public: + BOOST_BEAST_DECL + impl(value const& jv); + + BOOST_BEAST_DECL + bool + is_done() const noexcept override; + + BOOST_BEAST_DECL + std::size_t + next(net::mutable_buffer) override; + + private: + BOOST_BEAST_DECL + void + append(char c, + net::mutable_buffer& b); + + BOOST_BEAST_DECL + void + append( + char const* s, std::size_t n, + net::mutable_buffer& b); + }; + + typename std::aligned_storage< + sizeof(impl)>::type buf_; + + base& + get_base() noexcept + { + return *reinterpret_cast< + base*>(&buf_); + } + + base const& + get_base() const noexcept + { + return *reinterpret_cast< + base const*>(&buf_); + } + +public: + BOOST_BEAST_DECL + ~serializer(); + + BOOST_BEAST_DECL + explicit + serializer(value const& jv); + + template + std::size_t + next(MutableBufferSequence const& buffers) + { + std::size_t bytes_transferred = 0; + for(auto it = net::buffer_sequence_begin(buffers), + end = net::buffer_sequence_end(buffers); + it != end; ++it) + bytes_transferred += next( + net::mutable_buffer(*it)); + return bytes_transferred; + } + + bool + is_done() const noexcept + { + return get_base().is_done(); + } + + std::size_t + next(net::mutable_buffer buffer) + { + return get_base().next(buffer); + } +}; + +} // json +} // beast +} // boost + +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/src.hpp b/include/boost/json/src.hpp new file mode 100644 index 00000000..d1b4a789 --- /dev/null +++ b/include/boost/json/src.hpp @@ -0,0 +1,41 @@ +// +// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_BEAST_SRC_EXTRA_HPP +#define BOOST_BEAST_SRC_EXTRA_HPP + +/* + +This file is meant to be included once, in a translation unit of +the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined. + +*/ + +#define BOOST_BEAST_SOURCE + +#include + +#if defined(BOOST_BEAST_HEADER_ONLY) +# error Do not compile Beast library source with BOOST_BEAST_HEADER_ONLY defined +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/boost/json/storage.hpp b/include/boost/json/storage.hpp new file mode 100644 index 00000000..836fe389 --- /dev/null +++ b/include/boost/json/storage.hpp @@ -0,0 +1,220 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_STORAGE_HPP +#define BOOST_JSON_STORAGE_HPP + +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +/** Abstract interface to a memory resource used with JSON. +*/ +struct storage +{ + static std::size_t constexpr max_align = + sizeof(max_align_t); + + virtual + ~storage() = default; + + virtual + void + addref() noexcept = 0; + + virtual + void + release() noexcept = 0; + + virtual + void* + allocate( + std::size_t n, + std::size_t align = + max_align) = 0; + + virtual + void + deallocate( + void* p, + std::size_t n, + std::size_t align = + max_align) noexcept = 0; + + virtual + bool + is_equal( + storage const& other) const noexcept = 0; + + friend + bool + operator==( + storage const& lhs, + storage const& rhs) noexcept + { + return &lhs == &rhs || lhs.is_equal(rhs); + } + + friend + bool + operator!=( + storage const& lhs, + storage const& rhs) noexcept + { + return !(lhs == rhs); + } +}; + +//------------------------------------------------------------------------------ + +/** Manages a type-erased storage object and options for a set of JSON values. +*/ +class storage_ptr +{ + storage* p_ = nullptr; + +public: + storage_ptr() = default; + + BOOST_BEAST_DECL + ~storage_ptr(); + + BOOST_BEAST_DECL + storage_ptr(storage_ptr&&) noexcept; + + BOOST_BEAST_DECL + storage_ptr(storage_ptr const&) noexcept; + + BOOST_BEAST_DECL + storage_ptr& + operator=(storage_ptr&&) noexcept; + + BOOST_BEAST_DECL + storage_ptr& + operator=(storage_ptr const&) noexcept; + + /** Constructor + + @param p A pointer to a storage object. Ownership of the + object is transferred; the reference count is not adjusted. + */ + BOOST_BEAST_DECL + storage_ptr(storage* p) noexcept; + + /** Return ownership of the managed storage object. + */ + BOOST_BEAST_DECL + storage* + release() noexcept; + + explicit + operator bool() const noexcept + { + return p_ != nullptr; + } + + storage* + get() const noexcept + { + return p_; + } + + storage* + operator->() const noexcept + { + return p_; + } + + storage& + operator*() const noexcept + { + return *p_; + } +}; + +inline +bool +operator==(storage_ptr const& lhs, storage_ptr const& rhs) noexcept +{ + return lhs.get() == rhs.get(); +} + +inline +bool +operator==(storage* lhs, storage_ptr const& rhs) noexcept +{ + return lhs == rhs.get(); +} + +inline +bool +operator==(storage_ptr const& lhs, storage* rhs) noexcept +{ + return lhs.get() == rhs; +} + +inline +bool +operator!=(storage_ptr const& lhs, storage_ptr const& rhs) noexcept +{ + return lhs.get() != rhs.get(); +} + +inline +bool +operator!=(storage* lhs, storage_ptr const& rhs) noexcept +{ + return lhs != rhs.get(); +} + +inline +bool +operator!=(storage_ptr const& lhs, storage* rhs) noexcept +{ + return lhs.get() != rhs; +} + +//------------------------------------------------------------------------------ + +/** Construct a storage pointer to the specified allocator +*/ +template +storage_ptr +make_storage_ptr(Allocator const& a); + +/** Return a pointer to the current default storage +*/ +BOOST_BEAST_DECL +storage_ptr +default_storage(); + +/** Set the current default storage + + This function may not be called concurrently, + or concurrent with @ref default_storage. +*/ +BOOST_BEAST_DECL +void +default_storage(storage_ptr sp); + +} // json +} // beast +} // boost + +#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/include/boost/json/string.hpp b/include/boost/json/string.hpp new file mode 100644 index 00000000..d2812b90 --- /dev/null +++ b/include/boost/json/string.hpp @@ -0,0 +1,35 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_STRING_HPP +#define BOOST_JSON_STRING_HPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class value; + +/** The native type of string values +*/ +using string = + std::basic_string< + char, + std::char_traits, + allocator>; + +} // json +} // beast +} // boost + +#endif diff --git a/include/boost/json/value.hpp b/include/boost/json/value.hpp new file mode 100644 index 00000000..ab1f9f69 --- /dev/null +++ b/include/boost/json/value.hpp @@ -0,0 +1,942 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_VALUE_HPP +#define BOOST_JSON_VALUE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class value; + +/** Customization point for assigning to and from class types. +*/ +template +struct value_exchange final +#ifndef BOOST_BEAST_DOXYGEN + : detail::primary_template +#endif +{ + static + void + to_json(T const& t, value& v) + { + detail::call_to_json(t, v); + } + + static + void + from_json(T& t, value const& v) + { + detail::call_from_json(t, v); + } +}; + +/** Trait to determine if a type can be assigned to a json value. +*/ +template +using has_from_json = +#ifdef BOOST_BEAST_DOXYGEN + __see_below__; +#else + std::integral_constant>>::value || + detail::has_adl_from_json< + detail::remove_cr>::value || + detail::has_mf_from_json< + detail::remove_cr>::value>; +#endif + +/** Returns `true` if a JSON value can be constructed from `T` +*/ +template +using has_to_json = +#ifdef BOOST_BEAST_DOXYGEN + __see_below__; +#else + std::integral_constant>>::value || + detail::has_adl_to_json< + detail::remove_cr>::value || + detail::has_mf_to_json< + detail::remove_cr>::value>; +#endif + +//------------------------------------------------------------------------------ + +/** A JSON value. +*/ +class value +{ + friend class value_test; + + struct native + { + union + { + number num_; + bool bool_; + }; + storage_ptr sp_; + }; + + union + { + object obj_; + array arr_; + string str_; + native nat_; + }; + + json::kind kind_; + +public: + //-------------------------------------------------------------------------- + // + // Special members + // + //-------------------------------------------------------------------------- + + /// Destroy a value and all of its contents + BOOST_BEAST_DECL + ~value(); + + /// Move constructor + BOOST_BEAST_DECL + value(value&& other) noexcept; + + /// Move construct a value, using the specified storage + BOOST_BEAST_DECL + value( + value&& other, + storage_ptr store); + + /// Construct a copy of a value + BOOST_BEAST_DECL + value(value const& other); + + /// Construct a copy of a value using the specified storage + BOOST_BEAST_DECL + value( + value const& other, + storage_ptr store); + + /// Move-assign a value + BOOST_BEAST_DECL + value& operator=(value&& other); + + /// Assign a copy of a value + BOOST_BEAST_DECL + value& operator=(value const& other); + + //-------------------------------------------------------------------------- + // + // Construction and Assignment + // + //-------------------------------------------------------------------------- + + /** Construct a null value using the default storage. + */ + BOOST_BEAST_DECL + value() noexcept; + + /** Construct a null value using the specified storage. + + The value and all of its contents will use the + specified storage object. + */ + BOOST_BEAST_DECL + explicit + value(storage_ptr store) noexcept; + + /** Construct a value using the default storage + + The value and all of its contents will use the + specified storage object. + */ + BOOST_BEAST_DECL + value(json::kind k) noexcept; + + /** Construct a value using the specified storage. + + The value and all of its contents will use the specified + storage object. + + @param k The kind of JSON value. + + @param store The storage to use. + */ + BOOST_BEAST_DECL + value( + json::kind k, + storage_ptr store) noexcept; + + /** Construct a value from an object. + */ + BOOST_BEAST_DECL + value(object obj) noexcept; + + /** Construct a value from an object using the specified storage + */ + BOOST_BEAST_DECL + value(object obj, storage_ptr store); + + /** Construct a value from an array. + */ + BOOST_BEAST_DECL + value(array arr) noexcept; + + /** Construct a value from an array using the specified storage + */ + BOOST_BEAST_DECL + value(array arr, storage_ptr store); + + /** Construct a value from a string. + */ + BOOST_BEAST_DECL + value(string str) noexcept; + + /** Construct a value from a string using the specified storage + */ + BOOST_BEAST_DECL + value(string str, storage_ptr store); + + /** Construct a value from a nujmber + */ + BOOST_BEAST_DECL + value(number num); + + /** Construct a value from a nujmber using the specified storage + */ + BOOST_BEAST_DECL + value(number num, storage_ptr store); + + /** Construct an object from an initializer list. + */ + BOOST_BEAST_DECL + value(std::initializer_list< + std::pair> init); + + /** Construct an object from an initializer list using the specified storage + */ + BOOST_BEAST_DECL + value(std::initializer_list< + std::pair> init, + storage_ptr store); +#if 0 + /** Construct an array from an initializer list. + */ + BOOST_BEAST_DECL + value(std::initializer_list init); + + /** Construct an array from an initializer list using the specified storage. + */ + BOOST_BEAST_DECL + value(std::initializer_list init, + storage_ptr store); +#endif + + /** Assign a value from an object + */ + BOOST_BEAST_DECL + value& + operator=(object obj); + + /** Assign a value from an array + */ + BOOST_BEAST_DECL + value& + operator=(array arr); + + /** Assign a value from a string + */ + BOOST_BEAST_DECL + value& + operator=(string str); + + //-------------------------------------------------------------------------- + // + // Modifiers + // + //-------------------------------------------------------------------------- + + /** Reset the json to the specified type. + + This changes the value to hold a value of the + specified type. Any previous contents are cleared. + + @param k The kind to set. If the new kind is an + object, array, or string the resulting value will be + empty. Otherwise, the value will be in an undefined, + valid state. + */ + BOOST_BEAST_DECL + void + reset(json::kind k = json::kind::null) noexcept; + + /** Reset the json to the specified type. + + This changes the value to hold a value of the + specified type. Any previous contents are cleared. + + @param k The kind to set. If the new kind is an + object, array, or string the resulting value will be + empty. Otherwise, the value will be in an undefined, + valid state. + */ + BOOST_BEAST_DECL + value& + operator=(json::kind k) noexcept + { + reset(k); + return *this; + } + + /** Set the value to an empty object, and return it. + + This calls `reset(json::kind::object)` and returns + `as_object()`. The previous contents of the value + are destroyed. + */ + object& + emplace_object() noexcept + { + reset(json::kind::object); + return as_object(); + } + + /** Set the value to an empty array, and return it. + + This calls `reset(json::kind::array)` and returns + `as_array()`. The previous contents of the value + are destroyed. + */ + array& + emplace_array() noexcept + { + reset(json::kind::array); + return as_array(); + } + + /** Set the value to an empty string, and return it. + + This calls `reset(json::kind::string)` and returns + `as_string()`. The previous contents of the value + are destroyed. + */ + string& + emplace_string() noexcept + { + reset(json::kind::string); + return as_string(); + } + + /** Set the value to an uninitialized number, and return it. + + This calls `reset(json::kind::number)` and returns + `as_number()`. The previous contents of the value + are destroyed. + */ + number& + emplace_number() noexcept + { + reset(json::kind::number); + return as_number(); + } + + /** Set the value to an uninitialized boolean, and return it. + + This calls `reset(json::kind::boolean)` and returns + `as_bool()`. The previous contents of the value + are destroyed. + */ + bool& + emplace_bool() noexcept + { + reset(json::kind::boolean); + return as_bool(); + } + + /// Set the value to a null + void + emplace_null() noexcept + { + reset(json::kind::null); + } + + //-------------------------------------------------------------------------- + // + // Exchange + // + //-------------------------------------------------------------------------- + + /// Construct a value from another type + template< + class T + #ifndef BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + has_to_json::value>::type + #endif + > + value(T const& t) + : value(t, default_storage()) + { + } + + /// Construct a value from another type using the specified storage + template< + class T + #ifndef BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + has_to_json::value>::type + #endif + > + value(T const& t, storage_ptr store) + : value(std::move(store)) + { + value_exchange< + detail::remove_cr + >::to_json(t, *this); + } + + /// Assign a value from another type + template< + class T + #ifndef BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + has_to_json::value>::type + #endif + > + value& + operator=(T const& t) + { + value_exchange< + detail::remove_cr + >::to_json(t, *this); + return *this; + } + + /** Try to assign a value to another type + + @throws system error Thrown upon failure + */ + template + void + store(T& t) const + { + // If this assert goes off, it means that there are no known + // ways to convert a JSON value into a user defined type `T`. + // There are three ways to fix this: + // + // 1. Add the member function `T::from_json(value const&)`, + // + // 2. Add the free function `from_json(T&, value const&)` + // in the same namespace as T, or + // + // 3. Specialize `json::value_exchange` for `T`, and provide + // the static member `from_json(T&, value const&)`. + + static_assert( + has_from_json::value, + "Destination type is unknown"); + value_exchange< + detail::remove_cr + >::from_json(t, *this); + } + + //-------------------------------------------------------------------------- + // + // Observers + // + //-------------------------------------------------------------------------- + + /// Returns the kind of this JSON value + json::kind + kind() const noexcept + { + return kind_; + } + + /// Returns `true` if this is an object + bool + is_object() const noexcept + { + return kind_ == json::kind::object; + } + + /// Returns `true` if this is an array + bool + is_array() const noexcept + { + return kind_ == json::kind::array; + } + + /// Returns `true` if this is a string + bool + is_string() const noexcept + { + return kind_ == json::kind::string; + } + + /// Returns `true` if this is a number + bool + is_number() const noexcept + { + return kind_ == json::kind::number; + } + + bool + is_bool() const noexcept + { + return kind_ == json::kind::boolean; + } + + bool + is_null() const noexcept + { + return kind_ == json::kind::null; + } + + //--- + + /// Returns `true` if this is not an array or object + bool + is_primitive() const noexcept + { + switch(kind_) + { + case json::kind::object: + case json::kind::array: + return false; + default: + return true; + } + + } + + /// Returns `true` if this is an array or object + bool + is_structured() const noexcept + { + return ! is_primitive(); + } + + /// Returns `true` if this is a number representable as a `std::int64_t` + bool + is_int64() const noexcept + { + return + kind_ == json::kind::number && + nat_.num_.is_int64(); + } + + /// Returns `true` if this is a number representable as a `std::uint64_t` + bool + is_uint64() const noexcept + { + return + kind_ == json::kind::number && + nat_.num_.is_uint64(); + } + + /** Returns `true` if this is a number representable as a `double` + + The return value will always be the same as the + value returned from @ref is_number. + */ + bool + is_double() const noexcept + { + return kind_ == json::kind::number; + } + + //-------------------------------------------------------------------------- + // + // Accessors + // + //-------------------------------------------------------------------------- + + BOOST_BEAST_DECL + storage_ptr + get_storage() const noexcept; + + object& + as_object() noexcept + { + BOOST_ASSERT(is_object()); + return obj_; + } + + object const& + as_object() const noexcept + { + BOOST_ASSERT(is_object()); + return obj_; + } + + array& + as_array() noexcept + { + BOOST_ASSERT(is_array()); + return arr_; + } + + array const& + as_array() const noexcept + { + BOOST_ASSERT(is_array()); + return arr_; + } + + string& + as_string() noexcept + { + BOOST_ASSERT(is_string()); + return str_; + } + + string const& + as_string() const noexcept + { + BOOST_ASSERT(is_string()); + return str_; + } + + number& + as_number() noexcept + { + BOOST_ASSERT(is_number()); + return nat_.num_; + } + + number const& + as_number() const noexcept + { + BOOST_ASSERT(is_number()); + return nat_.num_; + } + + std::int64_t + get_int64() const noexcept + { + BOOST_ASSERT(is_int64()); + return nat_.num_.get_int64(); + } + + std::uint64_t + get_uint64() const noexcept + { + BOOST_ASSERT(is_uint64()); + return nat_.num_.get_uint64(); + } + + double + get_double() const noexcept + { + BOOST_ASSERT(is_double()); + return nat_.num_.get_double(); + } + + bool& + as_bool() noexcept + { + BOOST_ASSERT(is_bool()); + return nat_.bool_; + } + + bool const& + as_bool() const noexcept + { + BOOST_ASSERT(is_bool()); + return nat_.bool_; + } + + //-------------------------------------------------------------------------- + // + // Structured + // + //-------------------------------------------------------------------------- + + using key_type = beast::string_view; + using mapped_type = value; + using value_type = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = std::pair; + using const_reference = std::pair; + class pointer; + class const_pointer; + class iterator; + class const_iterator; + using reverse_iterator = + std::reverse_iterator; + using const_reverse_iterator = + std::reverse_iterator; + + // + // Capacity + // + + BOOST_BEAST_DECL + bool + empty() const; + + BOOST_BEAST_DECL + size_type + size() const; + + // + // Iterators + // + + BOOST_BEAST_DECL + iterator + begin(); + + BOOST_BEAST_DECL + const_iterator + begin() const; + + BOOST_BEAST_DECL + const_iterator + cbegin(); + + BOOST_BEAST_DECL + iterator + end(); + + BOOST_BEAST_DECL + const_iterator + end() const; + + BOOST_BEAST_DECL + const_iterator + cend(); + + BOOST_BEAST_DECL + reverse_iterator + rbegin(); + + BOOST_BEAST_DECL + const_reverse_iterator + rbegin() const; + + BOOST_BEAST_DECL + const_reverse_iterator + crbegin(); + + BOOST_BEAST_DECL + reverse_iterator + rend(); + + BOOST_BEAST_DECL + const_reverse_iterator + rend() const; + + BOOST_BEAST_DECL + const_reverse_iterator + crend(); + + // + // Lookup + // + + BOOST_BEAST_DECL + value& + at(key_type key); + + BOOST_BEAST_DECL + value const& + at(key_type key) const; + + BOOST_BEAST_DECL + value& + operator[](key_type key); + + BOOST_BEAST_DECL + value const& + operator[](key_type key) const; + + BOOST_BEAST_DECL + size_type + count(key_type key) const; + + BOOST_BEAST_DECL + iterator + find(key_type key); + + BOOST_BEAST_DECL + const_iterator + find(key_type key) const; + + BOOST_BEAST_DECL + bool + contains(key_type key) const; + + // + // Elements + // + + BOOST_BEAST_DECL + reference + at(size_type pos); + + BOOST_BEAST_DECL + const_reference + at(size_type pos) const; + + BOOST_BEAST_DECL + value& + operator[](size_type i); + + BOOST_BEAST_DECL + value const& + operator[](size_type i) const; + + BOOST_BEAST_DECL + reference + front(); + + BOOST_BEAST_DECL + const_reference + front() const; + + BOOST_BEAST_DECL + reference + back(); + + BOOST_BEAST_DECL + const_reference + back() const; + + // Modifiers + + BOOST_BEAST_DECL + void + clear() noexcept; + + template + std::pair + insert_or_assign( + key_type key, M&& obj); + + template + std::pair + insert_or_assign( + const_iterator before, + key_type key, + M&& obj); + + template + std::pair + emplace(key_type key, Arg&& arg); + + template + std::pair + emplace( + const_iterator before, + key_type key, Arg&& arg); + + template + iterator + emplace( + const_iterator before, + Arg&& arg); + + BOOST_BEAST_DECL + size_type + erase(key_type key); + + BOOST_BEAST_DECL + iterator + erase(const_iterator pos); + + BOOST_BEAST_DECL + iterator + erase( + const_iterator first, + const_iterator last); + + template + value& + emplace_back(Arg&& arg); + + BOOST_BEAST_DECL + void + pop_back(); + + //-------------------------------------------------------------------------- + +private: + BOOST_BEAST_DECL + storage_ptr + release_storage() noexcept; + + BOOST_BEAST_DECL + void + construct( + json::kind, storage_ptr) noexcept; + + BOOST_BEAST_DECL + void + clear_impl() noexcept; + + BOOST_BEAST_DECL + void + move(storage_ptr, value&&); + + BOOST_BEAST_DECL + void + copy(storage_ptr, value const&); + + BOOST_BEAST_DECL + friend + std::ostream& + operator<<( + std::ostream& os, + value const& jv); +}; + +} // json +} // beast +} // boost + +#include +#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#include +#endif + +// These must come after array and object +#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..a7614209 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,40 @@ +# +# Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/boostorg/json +# + +source_group (json FILES ${PROJECT_SOURCE_DIR}/include/boost/json.hpp) +source_group (TREE ${PROJECT_SOURCE_DIR}/include/boost/json PREFIX json FILES ${PROJECT_FILES}) + +GroupSources(test "/") + +add_executable (json-tests + ${BEAST_FILES} + ${PROJECT_FILES} + Jamfile + lib.cpp + _detail_stack.cpp + allocator.cpp + array.cpp + assign_string.cpp + assign_vector.cpp + basic_parser.cpp + error.cpp + iterator.cpp + json.cpp + kind.cpp + number.cpp + object.cpp + parse_file.cpp + parser.cpp + serializer.cpp + storage.cpp + string.cpp + value.cpp +) + +add_test(json-tests json-tests) diff --git a/test/Jamfile b/test/Jamfile new file mode 100644 index 00000000..8aab8a65 --- /dev/null +++ b/test/Jamfile @@ -0,0 +1,51 @@ +# +# Copyright (c) 2013-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/boostorg/json +# + +local SOURCES = + _detail_stack.cpp + allocator.cpp + array.cpp + assign_string.cpp + assign_vector.cpp + basic_parser.cpp + error.cpp + iterator.cpp + json.cpp + kind.cpp + number.cpp + object.cpp + parse_file.cpp + parser.cpp + serializer.cpp + storage.cpp + string.cpp + value.cpp + ; + +local RUN_TESTS ; + +for local f in $(SOURCES) +{ + RUN_TESTS += [ run $(f) lib.cpp ] ; +} + +alias run-tests : $(RUN_TESTS) ; + +exe fat-tests : + $(SOURCES) + lib.cpp + ; + +explicit fat-tests ; + +run $(SOURCES) + lib.cpp + : : : : run-fat-tests ; + +explicit run-fat-tests ; diff --git a/test/_detail_stack.cpp b/test/_detail_stack.cpp new file mode 100644 index 00000000..92d04a7f --- /dev/null +++ b/test/_detail_stack.cpp @@ -0,0 +1,55 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { +namespace detail { + +class stack_test : public unit_test::suite +{ +public: + void + testStack() + { + stack s; + BEAST_EXPECT(s.empty()); + BEAST_EXPECT(s.capacity() > 0); + BEAST_EXPECT(s.size() == 0); + s.emplace_front("1"); + BEAST_EXPECT(s.size() == 1); + BEAST_EXPECT(s[0] == "1"); + s.emplace_front("2"); + BEAST_EXPECT(s.size() == 2); + BEAST_EXPECT(s[0] == "2"); + s.pop_front(); + BEAST_EXPECT(s.size() == 1); + BEAST_EXPECT(s[0] == "1"); + s.pop_front(); + BEAST_EXPECT(s.empty()); + } + + void run() override + { + testStack(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,stack); + +} // detail +} // json +} // beast +} // boost diff --git a/test/allocator.cpp b/test/allocator.cpp new file mode 100644 index 00000000..3f0aa573 --- /dev/null +++ b/test/allocator.cpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/BeastLounge +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class allocator_test : public unit_test::suite +{ +public: + void + testAllocator() + { + auto a1 = make_storage_ptr( + std::allocator{}); + auto a2 = a1; + auto a3 = std::move(a2); + a2 = a1; + a3 = std::move(a2); + + std::vector> v( + allocator(make_storage_ptr( + std::allocator{}))); + v.resize(100); + BEAST_EXPECT(v.capacity() >= 100); + } + + void + run() + { + testAllocator(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,allocator); + +} // json +} // beast +} // boost diff --git a/test/array.cpp b/test/array.cpp new file mode 100644 index 00000000..f54e1651 --- /dev/null +++ b/test/array.cpp @@ -0,0 +1,832 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/BeastLounge +// + +// Test that header file is self-contained. +#include + +#include + +namespace boost { +namespace beast { +namespace json { + +class array_test : public unit_test::suite +{ +public: + struct unique_storage : storage + { + void + addref() noexcept override + { + } + + void + release() noexcept override + { + } + + void* + allocate( + std::size_t n, + std::size_t) override + { + return std::allocator< + char>{}.allocate(n); + } + + void + deallocate( + void* p, + std::size_t n, + std::size_t) noexcept override + { + auto cp = + reinterpret_cast(p); + return std::allocator< + char>{}.deallocate(cp, n); + } + bool + is_equal( + storage const& other + ) const noexcept override + { + auto p = dynamic_cast< + unique_storage const*>(&other); + if(! p) + return false; + return this == p; + } + }; + + struct fail_storage : storage + { + std::size_t fail_max = 1; + std::size_t fail = 0; + + void + addref() noexcept override + { + } + + void + release() noexcept override + { + } + + void* + allocate( + std::size_t n, + std::size_t) override + { + if(++fail == fail_max) + { + ++fail_max; + fail = 0; + throw std::bad_alloc{}; + } + return std::allocator< + char>{}.allocate(n); + } + + void + deallocate( + void* p, + std::size_t n, + std::size_t) noexcept override + { + auto cp = + reinterpret_cast(p); + return std::allocator< + char>{}.deallocate(cp, n); + } + bool + is_equal( + storage const& other + ) const noexcept override + { + auto p = dynamic_cast< + fail_storage const*>(&other); + if(! p) + return false; + return this == p; + } + }; + + void + check(array const& arr) + { + BEAST_EXPECT(arr.size() == 3); + BEAST_EXPECT(arr[0].is_number()); + BEAST_EXPECT(arr[1].is_bool()); + BEAST_EXPECT(arr[2].is_string()); + } + + void + check( + array const& arr, + storage_ptr const& sp) + { + check(arr); + BEAST_EXPECT(arr.get_storage() == sp); + BEAST_EXPECT(arr[0].get_storage() == sp); + BEAST_EXPECT(arr[1].get_storage() == sp); + BEAST_EXPECT(arr[2].get_storage() == sp); + } + + void + testSpecial() + { + unique_storage us; + storage_ptr sp(&us); + storage_ptr sp0 = + default_storage(); + BEAST_EXPECT(*sp != *sp0); + + // array() + { + array arr; + BEAST_EXPECT(arr.empty()); + BEAST_EXPECT(arr.size() == 0); + } + + // array(storage) + { + array arr(sp); + BEAST_EXPECT(arr.get_storage() == sp); + BEAST_EXPECT(arr.get_storage() != sp0); + } + + // array(size_type) + { + array arr(3); + BEAST_EXPECT(arr.size() == 3); + for(auto const& v : arr) + BEAST_EXPECT(v.is_null()); + } + + // array(size_type, storage) + { + array arr(3, sp); + BEAST_EXPECT(arr.size() == 3); + for(auto const& v : arr) + BEAST_EXPECT(v.get_storage() == sp); + } + + // array(size_type, value) + { + array arr(3, kind::boolean); + BEAST_EXPECT(arr.size() == 3); + for(auto const& v : arr) + BEAST_EXPECT(v.is_bool()); + } + + // array(size_type, value, storage) + { + array arr(3, kind::boolean, sp); + BEAST_EXPECT(arr.size() == 3); + for(auto const& v : arr) + BEAST_EXPECT(v.get_storage() == sp); + } + + // array(InputIt, InputIt) + { + std::initializer_list list = + { 1, true, "hello" }; + array arr(list.begin(), list.end()); + check(arr, sp0); + } + + // array(InputIt, InputIt, storage) + { + std::initializer_list list = + { 1, true, "hello" }; + array arr(list.begin(), list.end(), sp); + check(arr, sp); + } + + // array(array const&) + { + std::initializer_list list = + { 1, true, "hello" }; + array arr1(list.begin(), list.end(), sp); + array arr2 = arr1; + check(arr2, sp); + } + + // array(array const&, storage) + { + std::initializer_list list = + { 1, true, "hello" }; + array arr1(list.begin(), list.end()); + array arr2(arr1, sp); + BEAST_EXPECT(arr2.size() == 3); + BEAST_EXPECT( + arr1[0].get_storage() != + arr2[0].get_storage()); + BEAST_EXPECT( + arr1[1].get_storage() != + arr2[1].get_storage()); + BEAST_EXPECT( + arr1[2].get_storage() != + arr2[2].get_storage()); + } + + // array(array&&) + { + std::initializer_list list = + { 1, true, "hello" }; + array arr1(list.begin(), list.end(), sp); + array arr2 = std::move(arr1); + BEAST_EXPECT(arr1.empty()); + BEAST_EXPECT( + arr1.get_storage() == + arr2.get_storage()); + check(arr2, sp); + } + + // array(array&&, storage) + { + std::initializer_list list = + { 1, true, "hello" }; + array arr1(list.begin(), list.end()); + array arr2(std::move(arr1), sp); + BEAST_EXPECT(! arr1.empty()); + BEAST_EXPECT( + arr1.get_storage() != + arr2.get_storage()); + check(arr2, sp); + } + + // array(init_list) + { + array arr({1, true, "hello"}); + check(arr, sp0); + } + + // array(init_list, storage) + { + array arr({1, true, "hello"}, sp); + check(arr, sp); + } + + // operator=(array&&) + { + { + array arr1({1, true, "hello"}); + array arr2({nullptr, kind::object, 1.f}); + arr2 = std::move(arr1); + BEAST_EXPECT(arr1.empty()); + BEAST_EXPECT( + arr1.get_storage() == + arr2.get_storage()); + check(arr2, sp0); + } + { + array arr1({1, true, "hello"}); + array arr2({nullptr, kind::object, 1.f}, sp); + arr2 = std::move(arr1); + BEAST_EXPECT(! arr1.empty()); + BEAST_EXPECT( + arr1.get_storage() != + arr2.get_storage()); + check(arr2, sp); + } + } + + // operator=(array const&) + { + { + array arr1({1, true, "hello"}); + array arr2({nullptr, kind::object, 1.f}); + arr2 = arr1; + BEAST_EXPECT(! arr1.empty()); + check(arr2, sp0); + } + { + array arr1({1, true, "hello"}); + array arr2({nullptr, kind::object, 1.f}, sp); + arr2 = arr1; + BEAST_EXPECT(! arr1.empty()); + BEAST_EXPECT( + arr1.get_storage() != + arr2.get_storage()); + check(arr2, sp); + } + } + + // operator=(init_list) + { + { + std::initializer_list list = + { 1, true, "hello" }; + array arr({nullptr, kind::object, 1.f}); + arr = list; + check(arr, sp0); + } + { + std::initializer_list list = + { 1, true, "hello" }; + array arr({nullptr, kind::object, 1.f}, sp); + arr = list; + check(arr, sp); + } + } + } + + void + testElements() + { + // at(pos) + { + array arr({1, true, "hello"}); + BEAST_EXPECT(arr.at(0).is_number()); + BEAST_EXPECT(arr.at(1).is_bool()); + BEAST_EXPECT(arr.at(2).is_string()); + try + { + arr.at(3); + BEAST_FAIL(); + } + catch(std::out_of_range const&) + { + BEAST_PASS(); + } + } + + // at(pos) const + { + array const arr({1, true, "hello"}); + BEAST_EXPECT(arr.at(0).is_number()); + BEAST_EXPECT(arr.at(1).is_bool()); + BEAST_EXPECT(arr.at(2).is_string()); + try + { + arr.at(3); + BEAST_FAIL(); + } + catch(std::out_of_range const&) + { + BEAST_PASS(); + } + } + + // operator[](size_type) + { + array arr({1, true, "hello"}); + BEAST_EXPECT(arr[0].is_number()); + BEAST_EXPECT(arr[1].is_bool()); + BEAST_EXPECT(arr[2].is_string()); + } + + // operator[](size_type) const + { + array const arr({1, true, "hello"}); + BEAST_EXPECT(arr[0].is_number()); + BEAST_EXPECT(arr[1].is_bool()); + BEAST_EXPECT(arr[2].is_string()); + } + + // front() + { + array arr({1, true, "hello"}); + BEAST_EXPECT(arr.front().is_number()); + } + + // front() const + { + array const arr({1, true, "hello"}); + BEAST_EXPECT(arr.front().is_number()); + } + + // back() + { + array arr({1, true, "hello"}); + BEAST_EXPECT(arr.back().is_string()); + } + + // back() const + { + array const arr({1, true, "hello"}); + BEAST_EXPECT(arr.back().is_string()); + } + + // data() + { + array arr({1, true, "hello"}); + BEAST_EXPECT(arr.data() == &arr[0]); + } + + // data() const + { + array const arr({1, true, "hello"}); + BEAST_EXPECT(arr.data() == &arr[0]); + } + } + + void + testIterators() + { + array arr({1, true, "hello"}); + auto const& ac(arr); + { + auto it = arr.begin(); + BEAST_EXPECT(it->is_number()); ++it; + BEAST_EXPECT(it->is_bool()); it++; + BEAST_EXPECT(it->is_string()); ++it; + BEAST_EXPECT(it == arr.end()); + } + { + auto it = arr.cbegin(); + BEAST_EXPECT(it->is_number()); ++it; + BEAST_EXPECT(it->is_bool()); it++; + BEAST_EXPECT(it->is_string()); ++it; + BEAST_EXPECT(it == arr.cend()); + } + { + auto it = ac.begin(); + BEAST_EXPECT(it->is_number()); ++it; + BEAST_EXPECT(it->is_bool()); it++; + BEAST_EXPECT(it->is_string()); ++it; + BEAST_EXPECT(it == ac.end()); + } + { + auto it = arr.end(); + --it; BEAST_EXPECT(it->is_string()); + it--; BEAST_EXPECT(it->is_bool()); + --it; BEAST_EXPECT(it->is_number()); + BEAST_EXPECT(it == arr.begin()); + } + { + auto it = arr.cend(); + --it; BEAST_EXPECT(it->is_string()); + it--; BEAST_EXPECT(it->is_bool()); + --it; BEAST_EXPECT(it->is_number()); + BEAST_EXPECT(it == arr.cbegin()); + } + { + auto it = ac.end(); + --it; BEAST_EXPECT(it->is_string()); + it--; BEAST_EXPECT(it->is_bool()); + --it; BEAST_EXPECT(it->is_number()); + BEAST_EXPECT(it == ac.begin()); + } + } + + void + testCapacity() + { + // empty() + { + array arr; + BEAST_EXPECT(arr.empty()); + arr.emplace_back(1); + BEAST_EXPECT(! arr.empty()); + } + + // size() + { + array arr; + BEAST_EXPECT(arr.size() == 0); + arr.emplace_back(1); + BEAST_EXPECT(arr.size() == 1); + } + + // max_size() + { + array arr; + BEAST_EXPECT(arr.max_size() > 0); + } + + // reserve() + { + array arr; + arr.reserve(50); + BEAST_EXPECT(arr.capacity() >= 50); + } + + // capacity() + { + array arr; + BEAST_EXPECT(arr.capacity() == 0); + } + + // shrink_to_fit() + { + array arr; + arr.reserve(50); + BEAST_EXPECT(arr.capacity() >= 50); + arr.shrink_to_fit(); + BEAST_EXPECT(arr.capacity() == 0); + } + } + + void + testModifiers() + { + // clear + { + array arr({1, true, "hello"}); + arr.clear(); + BEAST_EXPECT(arr.capacity() > 0); + } + + // insert(before, value_type const&) + { + array arr({1, "hello"}); + value v(true); + arr.insert(arr.begin() + 1, v); + check(arr); + } + + // insert(before, value_type const&) + { + array arr({1, "hello"}); + arr.insert(arr.begin() + 1, true); + check(arr); + } + // insert(before, size_type, value_type const&) + { + array arr({1, "hello"}); + arr.insert(arr.begin() + 1, 3, true); + BEAST_EXPECT(arr[0].is_number()); + BEAST_EXPECT(arr[1].is_bool()); + BEAST_EXPECT(arr[2].is_bool()); + BEAST_EXPECT(arr[3].is_bool()); + BEAST_EXPECT(arr[4].is_string()); + } + + // insert(before, InputIt, InputIt) + { + std::initializer_list< + value> list = {1, true}; + array arr({"hello"}); + arr.insert(arr.begin(), + list.begin(), list.end()); + check(arr); + } + + // insert(before, init_list) + { + array arr({"hello"}); + arr.insert(arr.begin(), {1, true}); + check(arr); + } + + // emplace(before, arg) + { + array arr({1, "hello"}); + auto it = arr.emplace( + arr.begin() + 1, true); + BEAST_EXPECT(it == arr.begin() + 1); + check(arr); + } + + // erase(pos) + { + array arr({1, true, nullptr, "hello"}); + arr.erase(arr.begin() + 2); + check(arr); + } + + // push_back(value const&) + { + array arr({1, true}); + value v("hello"); + arr.push_back(v); + BEAST_EXPECT( + v.as_string() == "hello"); + check(arr); + } + + // push_back(value&&) + { + array arr({1, true}); + value v("hello"); + arr.push_back(std::move(v)); + BEAST_EXPECT(v.is_null()); + check(arr); + } + + // emplace_back(arg) + { + array arr({1, true}); + arr.emplace_back("hello"); + check(arr); + } + + // pop_back() + { + array arr({1, true, "hello", nullptr}); + arr.pop_back(); + check(arr); + } + + // resize(size_type) + { + array arr; + arr.resize(3); + BEAST_EXPECT(arr.size() == 3); + BEAST_EXPECT(arr[0].is_null()); + BEAST_EXPECT(arr[1].is_null()); + BEAST_EXPECT(arr[2].is_null()); + } + + // resize(size_type, value_type const&) + { + array arr; + value v(kind::boolean); + arr.resize(3, v); + BEAST_EXPECT(arr.size() == 3); + BEAST_EXPECT(arr[0].is_bool()); + BEAST_EXPECT(arr[1].is_bool()); + BEAST_EXPECT(arr[2].is_bool()); + } + + // swap + { + array arr1({1, true, "hello"}); + array arr2; + arr1.swap(arr2); + check(arr2); + BEAST_EXPECT(arr1.empty()); + } + } + + void + testExceptions() + { + // operator=(array const&) + { + array arr0({1, true, "hello"}); + fail_storage fs; + storage_ptr sp(&fs); + array arr1; + while(fs.fail < 200) + { + try + { + array arr(sp); + arr.emplace_back(nullptr); + arr = arr0; + arr1 = arr; + break; + } + catch(std::bad_alloc const&) + { + } + } + check(arr1); + } + + // operator=(init_list) + { + std::initializer_list list( + {1, true, "hello"}); + fail_storage fs; + storage_ptr sp(&fs); + array arr1; + while(fs.fail < 200) + { + try + { + array arr(sp); + arr.emplace_back(nullptr); + arr = list; + arr1 = arr; + break; + } + catch(std::bad_alloc const&) + { + } + } + check(arr1); + } + + // insert(before, count, value_type const&) + { + fail_storage fs; + storage_ptr sp(&fs); + array arr1; + while(fs.fail < 200) + { + try + { + array arr({1, true}, sp); + arr.insert(arr.begin() + 1, + 3, kind::null); + arr1 = arr; + break; + } + catch(std::bad_alloc const&) + { + } + } + BEAST_EXPECT(arr1.size() == 5); + BEAST_EXPECT(arr1[0].is_number()); + BEAST_EXPECT(arr1[1].is_null()); + BEAST_EXPECT(arr1[2].is_null()); + BEAST_EXPECT(arr1[3].is_null()); + BEAST_EXPECT(arr1[4].is_bool()); + } + + #if _ITERATOR_DEBUG_LEVEL == 0 + // insert(before, InputIt, InputIt) + { + std::initializer_list list( + {1, true, "hello"}); + fail_storage fs; + storage_ptr sp(&fs); + array arr1; + while(fs.fail < 200) + { + try + { + array arr(sp); + arr.insert(arr.end(), + list.begin(), list.end()); + arr1 = arr; + break; + } + catch(std::bad_alloc const&) + { + } + } + check(arr1); + } + #endif + + // emplace(before, arg) + { + fail_storage fs; + storage_ptr sp(&fs); + array arr1; + while(fs.fail < 200) + { + try + { + array arr({1, nullptr}, sp); + arr.emplace(arr.begin() + 1, true); + arr1 = arr; + break; + } + catch(std::bad_alloc const&) + { + } + } + BEAST_EXPECT(arr1.size() == 3); + BEAST_EXPECT(arr1[0].is_number()); + BEAST_EXPECT(arr1[1].is_bool()); + BEAST_EXPECT(arr1[2].is_null()); + } + + #if _ITERATOR_DEBUG_LEVEL == 0 + // emplace(before, arg) + { + fail_storage fs; + storage_ptr sp(&fs); + array arr1; + while(fs.fail < 200) + { + try + { + array arr({1, "hello"}, sp); + arr.emplace(arr.begin() + 1, true); + arr1 = arr; + break; + } + catch(std::bad_alloc const&) + { + } + } + check(arr1); + BEAST_EXPECT(arr1.size() == 3); + BEAST_EXPECT(arr1[0].is_number()); + BEAST_EXPECT(arr1[1].is_bool()); + BEAST_EXPECT(arr1[2].is_string()); + } + #endif + } + + void + run() override + { + testSpecial(); + testElements(); + testIterators(); + testCapacity(); + testModifiers(); + testExceptions(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,array); + +} // json +} // beast +} // boost diff --git a/test/assign_string.cpp b/test/assign_string.cpp new file mode 100644 index 00000000..5d562856 --- /dev/null +++ b/test/assign_string.cpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class assign_string_test : public unit_test::suite +{ +public: + void + testAssign() + { + value jv = "test"; + std::string s; + try + { + jv.store(s); + BEAST_EXPECT(s == "test"); + } + catch(std::exception const&) + { + BEAST_FAIL(); + } + } + + void run() override + { + testAssign(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,assign_string); + +} // json +} // beast +} // boost diff --git a/test/assign_vector.cpp b/test/assign_vector.cpp new file mode 100644 index 00000000..e45e5350 --- /dev/null +++ b/test/assign_vector.cpp @@ -0,0 +1,58 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class assign_vector_test : public unit_test::suite +{ +public: + void + testAssign() + { + value jv(kind::array); + { + auto& a = jv.as_array(); + a.push_back(1); + a.push_back(2); + a.push_back(3); + } + std::vector v; + try + { + jv.store(v); + BEAST_EXPECT(v.size() == 3); + BEAST_EXPECT(v[0] == 1); + BEAST_EXPECT(v[1] == 2); + BEAST_EXPECT(v[2] == 3); + } + catch(std::exception const&) + { + BEAST_FAIL(); + } + } + + void run() override + { + testAssign(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,assign_vector); + +} // json +} // beast +} // boost diff --git a/test/basic_parser.cpp b/test/basic_parser.cpp new file mode 100644 index 00000000..2e52dcfe --- /dev/null +++ b/test/basic_parser.cpp @@ -0,0 +1,322 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include + +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class basic_parser_test : public beast::unit_test::suite +{ +public: + struct test_parser + : basic_parser + { + test_parser() = default; + + void + on_document_begin( + error_code&) override + { + } + + void + on_object_begin( + error_code&) override + { + } + + void + on_object_end( + error_code&) override + { + } + + void + on_array_begin( + error_code&) override + { + } + + void + on_array_end( + error_code&) override + { + } + + void + on_key_data( + string_view, + error_code&) override + { + } + + void + on_key_end( + string_view, + error_code&) override + { + } + + void + on_string_data( + string_view, + error_code&) override + { + } + + void + on_string_end( + string_view, + error_code&) override + { + } + + void + on_number( + number, + error_code&) override + { + } + + void + on_bool( + bool, + error_code&) override + { + } + + void + on_null(error_code&) override + { + } + }; + + void + good(string_view s) + { + error_code ec; + for(std::size_t i = 0; + i < s.size() - 1; ++i) + { + // write_some with 1 buffer + { + test_parser p; + auto used = p.write_some( + net::const_buffer(s.data(), i), ec); + BEAST_EXPECT(used == i); + BEAST_EXPECT(! p.is_done()); + if(! BEAST_EXPECTS(! ec, ec.message())) + continue; + used = p.write_some(net::const_buffer( + s.data() + i, s.size() - i), ec); + BEAST_EXPECT(used == s.size() - i); + if(! BEAST_EXPECTS(! ec, ec.message())) + continue; + p.write({}, ec); + BEAST_EXPECTS(! ec, ec.message()); + BEAST_EXPECT(p.is_done()); + } + // write_some with 1 buffer sequence + { + test_parser p; + std::array< + net::const_buffer, 2> b; + b[0] = {s.data(), i}; + b[1] = {s.data()+i, s.size()-i}; + auto used = p.write_some(b, ec); + BEAST_EXPECT(used = s.size()); + BEAST_EXPECTS(! ec, ec.message()); + p.write({}, ec); + BEAST_EXPECTS(! ec, ec.message()); + } + // write with 1 buffer sequence + { + test_parser p; + std::array< + net::const_buffer, 2> b; + b[0] = {s.data(), i}; + b[1] = {s.data()+i, s.size()-i}; + auto used = p.write(b, ec); + BEAST_EXPECT(used = s.size()); + BEAST_EXPECTS(! ec, ec.message()); + } + // write with 1 buffer + { + test_parser p; + auto used = p.write( + {s.data(), s.size()}, ec); + BEAST_EXPECT(used = s.size()); + BEAST_EXPECTS(! ec, ec.message()); + } + } + } + + void + bad(string_view s) + { + error_code ec; + test_parser p; + auto const used = p.write_some( + boost::asio::const_buffer( + s.data(), s.size()), ec); + if(! ec) + { + if(p.is_done()) + { + if(BEAST_EXPECT(used != s.size())) + return; + } + else + { + p.write_eof(ec); + if(BEAST_EXPECT(ec)) + return; + } + } + else + { + pass(); + return; + } + log << "fail: \"" << s << "\"\n"; + } + + void + testObject() + { + good("{}"); + good("{ }"); + good("{ \t }"); + good("{ \"x\" : null }"); + good("{ \"x\" : {} }"); + good("{ \"x\" : { \"y\" : null } }"); + + bad ("{"); + bad ("{{}}"); + } + + void + testArray() + { + good("[]"); + good("[ ]"); + good("[ \t ]"); + good("[ \"\" ]"); + good("[ \" \" ]"); + good("[ \"x\" ]"); + good("[ \"x\", \"y\" ]"); + bad ("["); + bad ("[ \"x\", ]"); + } + + void + testString() + { + good("\"" "x" "\""); + good("\"" "xy" "\""); + good("\"" "x y" "\""); + + bad ("\"" "\t" "\""); + } + + void + testNumber() + { + good("0"); + good("0.0"); + good("0.10"); + good("0.01"); + good("1"); + good("10"); + good("1.5"); + good("10.5"); + good("10.25"); + good("10.25e0"); + good("1e1"); + good("1e10"); + good("1e+0"); + good("1e+1"); + good("0e+10"); + good("0e-0"); + good("0e-1"); + good("0e-10"); + good("1E+1"); + good("-0"); + good("-1"); + good("-1e1"); + + bad (""); + bad ("-"); + bad ("00"); + bad ("00."); + bad ("00.0"); + bad ("1a"); + bad ("."); + bad ("1."); + bad ("1+"); + bad ("0.0+"); + bad ("0.0e+"); + bad ("0.0e-"); + bad ("0.0e0-"); + bad ("0.0e"); + + } + + void + testMonostates() + { + good("true"); + good(" true"); + good("true "); + good("\ttrue"); + good("true\t"); + good("\r\n\t true\r\n\t "); + bad ("truu"); + bad ("tu"); + bad ("t"); + + good("false"); + bad ("fals"); + bad ("fel"); + bad ("f"); + + good("null"); + bad ("nul"); + bad ("no"); + bad ("n"); + } + + void run() override + { + log << + "sizeof(basic_parser) == " << + sizeof(basic_parser) << "\n"; + testObject(); + testArray(); + testString(); + testNumber(); + testMonostates(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,basic_parser); + +} // json +} // beast +} // boost diff --git a/test/error.cpp b/test/error.cpp new file mode 100644 index 00000000..3e8b9f98 --- /dev/null +++ b/test/error.cpp @@ -0,0 +1,70 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class error_test : public unit_test::suite +{ +public: + void check(error e) + { + auto const ec = make_error_code(e); + ec.category().name(); + BEAST_EXPECT(! ec.message().empty()); + } + + void check(condition c, error e) + { + { + auto const ec = make_error_code(e); + BEAST_EXPECT(ec.category().name() != nullptr); + BEAST_EXPECT(! ec.message().empty()); + BEAST_EXPECT(ec == c); + } + { + auto ec = make_error_condition(c); + BEAST_EXPECT(ec.category().name() != nullptr); + BEAST_EXPECT(! ec.message().empty()); + BEAST_EXPECT(ec == c); + } + } + + void run() override + { + check(condition::parse_error, error::syntax); + check(condition::parse_error, error::extra_data); + check(condition::parse_error, error::mantissa_overflow); + check(condition::parse_error, error::exponent_overflow); + check(condition::parse_error, error::too_deep); + + check(condition::assign_error, error::integer_overflow); + check(condition::assign_error, error::expected_object); + check(condition::assign_error, error::expected_array); + check(condition::assign_error, error::expected_string); + check(condition::assign_error, error::expected_number); + check(condition::assign_error, error::expected_bool); + check(condition::assign_error, error::expected_null); + + check(error::key_not_found); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,error); + +} // json +} // beast +} // boost diff --git a/test/iterator.cpp b/test/iterator.cpp new file mode 100644 index 00000000..cce373d7 --- /dev/null +++ b/test/iterator.cpp @@ -0,0 +1,119 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include + +namespace boost { +namespace beast { +namespace json { + +class iterator_test : public beast::unit_test::suite +{ +public: + void + testIterator() + { + value jv = { + {"pi", 3.141}, + {"happy", true}, + {"name", "Niels"}, + {"nothing", nullptr}, + {"answer", { + {"everything", 42} + }}, +#if 0 + {"list", {1, 0, 2}}, + {"object", { + {"currency", "USD"}, + {"value", 42.99} + }} +#endif + }; + + auto& arr = jv.as_object(). + emplace("arr", kind::array).first->second.as_array(); + arr.emplace_back(1); + arr.emplace_back(2); + arr.emplace_back(3); + + const_iterator it(jv); + while(it != end) + { + auto const& e = *it; + log << std::string(e.depth*4, ' '); + if(! e.end) + { + if(! e.key.empty()) + log << "\"" << e.key << "\" : "; + switch(e.value.kind()) + { + case kind::object: + log << "{"; + break; + case kind::array: + log << "["; + break; + case kind::string: + log << '\"' << e.value.as_string() << "\""; + if(! e.last) + log << ","; + break; + case kind::number: + log << e.value.as_number(); + if(! e.last) + log << ","; + break; + case kind::boolean: + log << (e.value.as_bool() ? + "true" : "false"); + if(! e.last) + log << ","; + break; + case kind::null: + log << "null"; + if(! e.last) + log << ","; + break; + } + log.flush(); + } + else if(e->value.is_object()) + { + log << "}"; + if(! e.last) + log << ","; + } + else if(e->value.is_array()) + { + log << "]"; + if(! e.last) + log << ","; + } + log << "\n"; + log.flush(); + ++it; + } + } + + void + run() override + { + testIterator(); + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,iterator); + +} // json +} // beast +} // boost diff --git a/test/json.cpp b/test/json.cpp new file mode 100644 index 00000000..1ab46915 --- /dev/null +++ b/test/json.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/BeastLounge +// + +// Test that header file is self-contained. +#include diff --git a/test/kind.cpp b/test/kind.cpp new file mode 100644 index 00000000..9f6c2b80 --- /dev/null +++ b/test/kind.cpp @@ -0,0 +1,42 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class kind_test : public unit_test::suite +{ +public: + BOOST_STATIC_ASSERT( + std::is_enum::value); + + void + testEnum() + { + pass(); + } + + void run() override + { + testEnum(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,kind); + +} // json +} // beast +} // boost diff --git a/test/lib.cpp b/test/lib.cpp new file mode 100644 index 00000000..44c8f17e --- /dev/null +++ b/test/lib.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#include + diff --git a/test/number.cpp b/test/number.cpp new file mode 100644 index 00000000..f7922c55 --- /dev/null +++ b/test/number.cpp @@ -0,0 +1,324 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class number_test : public unit_test::suite +{ +public: + template + void + check(I v + ,typename std::enable_if< + std::is_signed::value && + std::is_integral::value + >::type* = 0) noexcept + { + number n(v); + number n2(0); + n2 = n; + BEAST_EXPECT(n == n2); + BEAST_EXPECT(n.is_int64()); + BEAST_EXPECT(n.get_int64() == v); + BEAST_EXPECT(n.get_double() == v); + } + + template + void + check(U v + ,typename std::enable_if< + std::is_unsigned::value + >::type* = 0) noexcept + { + number n(v); + number n2(0); + n2 = n; + BEAST_EXPECT(n == n2); + BEAST_EXPECT(n.is_uint64()); + BEAST_EXPECT(number(static_cast< + number::mantissa_type>(v), + 0, false) == n); + BEAST_EXPECT(n.get_uint64() == v); + BEAST_EXPECT(n.get_double() == v); + } + + template + void + check(F v + ,typename std::enable_if< + std::is_floating_point< + F>::value>::type* = 0) noexcept + { + number n(v); + number n2(0); + n2 = n; + BEAST_EXPECT(n == n2); + BEAST_EXPECT(static_cast( + n.get_double()) == v); + } + + template + void + approx(F v) noexcept + { + number n(v); + number n2(0); + n2 = n; + BEAST_EXPECT(n == n2); + // VFALCO Unfortunately the results are not + // quite exact due to power of 10 conversion. + #if 0 + auto v1 = n.get_double(); + auto const d = std::fabs(v1 - v); + BEAST_EXPECT(static_cast( + n.get_double()) == v); + #endif + } + + void + testConstruction() + { + // Only perform light checking on + // the result of the construction. + + { + number n((short)1); + BEAST_EXPECT(n.get_int64() == 1); + } + { + number n((int)2); + BEAST_EXPECT(n.get_int64() == 2); + } + { + number n((long)3); + BEAST_EXPECT(n.get_int64() == 3); + } + { + number n((long long)4); + BEAST_EXPECT(n.get_int64() == 4); + } + { + number n((unsigned short)1); + BEAST_EXPECT(n.get_uint64() == 1); + } + { + number n((unsigned int)2); + BEAST_EXPECT(n.get_uint64() == 2); + } + { + number n((unsigned long)3); + BEAST_EXPECT(n.get_uint64() == 3); + } + { + number n((unsigned long long)4); + BEAST_EXPECT(n.get_uint64() == 4); + } + { + number n((float)1.5f); + BEAST_EXPECT(n.get_double() == 1.5); + } + { + number n((double)2.5); + BEAST_EXPECT(n.get_double() == 2.5); + } + } + + void + testAssignment() + { + // Only perform light checking on + // the result of the construction. + + { + number n; + n = (short)1; + BEAST_EXPECT(n.get_int64() == 1); + n = (int)2; + BEAST_EXPECT(n.get_int64() == 2); + n = (long)3; + BEAST_EXPECT(n.get_int64() == 3); + n = (long long)4; + BEAST_EXPECT(n.get_int64() == 4); + n = (unsigned short)5; + BEAST_EXPECT(n.get_uint64() == 5); + n = (unsigned int)6; + BEAST_EXPECT(n.get_uint64() == 6); + n = (unsigned long)7; + BEAST_EXPECT(n.get_uint64() == 7); + n = (unsigned long long)8; + BEAST_EXPECT(n.get_uint64() == 8); + n = (float)1.5; + BEAST_EXPECT(n.get_double() == 1.5); + n = (double)2.5; + BEAST_EXPECT(n.get_double() == 2.5); + n = (double)3.5l; + BEAST_EXPECT(n.get_double() == 3.5); + } + } + + void + testComparison() + { + // Only perform light checking on + // the result of the comparison. + + number n = 1; + BEAST_EXPECT(n == ((short)1)); + BEAST_EXPECT(n == ((int)1)); + BEAST_EXPECT(n == ((long)1)); + BEAST_EXPECT(n == ((long long)1)); + BEAST_EXPECT(n == ((unsigned short)1)); + BEAST_EXPECT(n == ((unsigned int)1)); + BEAST_EXPECT(n == ((unsigned long)1)); + BEAST_EXPECT(n == ((unsigned long long)1)); + + n = 1.f; + BEAST_EXPECT(n == 1.f); + + n = 1.; + BEAST_EXPECT(n == 1.); + } + + void + testValues() + { + // Test the range of each convertible numeric + // type, make sure they round-trip correctly. + + check(1.0/7); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + check((std::numeric_limits::max)()/2); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + check((std::numeric_limits::max)()/2); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + check((std::numeric_limits::max)()/2); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + check((std::numeric_limits::max)()/2); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + check((std::numeric_limits::max)()/2); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + check((std::numeric_limits::max)()/2); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + check((std::numeric_limits::max)()/2); + + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + check((std::numeric_limits::max)()/2); + check((std::numeric_limits::min)()); + check((std::numeric_limits::max)()); + check((std::numeric_limits::min)()/2); + + // not exact + approx((std::numeric_limits::max)()/2); + } + + template + void + check_output(V v) + { + char buf[number::max_string_chars]; + auto const s = number(v).print(buf, sizeof(buf)); + BEAST_EXPECTS( + s == std::to_string(v).c_str(), + std::string(s.data(), s.size())); + } + + void + testOstream() + { + check_output(1); + check_output(2); + check_output(3); + + // These don't match std::to_string + /* + check_output(1.5f); + check_output(2.5); + check_output(3.5l); + */ + + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()/2); + check_output((std::numeric_limits::max)()/2); + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()/2); + check_output((std::numeric_limits::max)()/2); + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()/2); + check_output((std::numeric_limits::max)()/2); + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()/2); + check_output((std::numeric_limits::max)()/2); + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()/2); + check_output((std::numeric_limits::max)()/2); + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()/2); + check_output((std::numeric_limits::max)()/2); + check_output((std::numeric_limits::min)()); + check_output((std::numeric_limits::max)()); + check_output((std::numeric_limits::min)()/2); + check_output((std::numeric_limits::max)()/2); + } + + void + run() override + { + testConstruction(); + testAssignment(); + testComparison(); + testValues(); + testOstream(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,number); + +} // json +} // beast +} // boost diff --git a/test/object.cpp b/test/object.cpp new file mode 100644 index 00000000..25a62dac --- /dev/null +++ b/test/object.cpp @@ -0,0 +1,1060 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/BeastLounge +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class object_test : public unit_test::suite +{ +public: + struct unique_storage : storage + { + void + addref() noexcept override + { + } + + void + release() noexcept override + { + } + + void* + allocate( + std::size_t n, + std::size_t) override + { + return std::allocator< + char>{}.allocate(n); + } + + void + deallocate( + void* p, + std::size_t n, + std::size_t) noexcept override + { + auto cp = + reinterpret_cast(p); + return std::allocator< + char>{}.deallocate(cp, n); + } + bool + is_equal( + storage const& other + ) const noexcept override + { + auto p = dynamic_cast< + unique_storage const*>(&other); + if(! p) + return false; + return this == p; + } + }; + + struct fail_storage : storage + { + std::size_t fail_max = 1; + std::size_t fail = 0; + + void + addref() noexcept override + { + } + + void + release() noexcept override + { + } + + void* + allocate( + std::size_t n, + std::size_t) override + { + if(++fail == fail_max) + { + ++fail_max; + fail = 0; + throw std::bad_alloc{}; + } + return std::allocator< + char>{}.allocate(n); + } + + void + deallocate( + void* p, + std::size_t n, + std::size_t) noexcept override + { + auto cp = + reinterpret_cast(p); + return std::allocator< + char>{}.deallocate(cp, n); + } + bool + is_equal( + storage const& other + ) const noexcept override + { + auto p = dynamic_cast< + fail_storage const*>(&other); + if(! p) + return false; + return this == p; + } + }; + + void + check( + object const& obj, + std::size_t bucket_count) + { + BEAST_EXPECT(! obj.empty()); + BEAST_EXPECT(obj.size() == 3); + BEAST_EXPECT( + obj.bucket_count() == bucket_count); + BEAST_EXPECT(obj["a"].as_number() == 1); + BEAST_EXPECT(obj["b"].as_bool()); + BEAST_EXPECT(obj["c"].as_string() == "hello"); + + // ordering, storage + + auto it = obj.begin(); + BEAST_EXPECT(it->first == "a"); + BEAST_EXPECT( + *it->second.get_storage() == + *obj.get_storage()); + + ++it; + BEAST_EXPECT(it->first == "b"); + BEAST_EXPECT( + *it->second.get_storage() == + *obj.get_storage()); + + it++; + BEAST_EXPECT(it->first == "c"); + BEAST_EXPECT( + *it->second.get_storage() == + *obj.get_storage()); + } + + void + testSpecial() + { + unique_storage us; + storage_ptr sp(&us); + storage_ptr sp0 = + default_storage(); + BEAST_EXPECT(*sp != *sp0); + + // object() + { + object obj; + BEAST_EXPECT(obj.empty()); + BEAST_EXPECT(obj.size() == 0); + BEAST_EXPECT(obj.bucket_count() == 0); + BEAST_EXPECT(*obj.get_storage() == *sp0); + } + + // object(size_type) + { + object obj(50); + BEAST_EXPECT(obj.empty()); + BEAST_EXPECT(obj.size() == 0); + BEAST_EXPECT(obj.bucket_count() == 53); + BEAST_EXPECT(*obj.get_storage() == *sp0); + } + + // object(storage_ptr) + { + object obj(sp); + BEAST_EXPECT(*obj.get_storage() == *sp); + } + + // object(size_type, storage_ptr) + { + object obj(50, sp); + BEAST_EXPECT(obj.empty()); + BEAST_EXPECT(obj.size() == 0); + BEAST_EXPECT(obj.bucket_count() == 53); + BEAST_EXPECT(*obj.get_storage() == *sp); + } + + { + std::initializer_list> init = { + {"a", 1}, + {"b", true}, + {"c", "hello"}}; + { + // object(InputIt, InputIt) + object obj(init.begin(), init.end()); + check(obj, 3); + } + { + // object(InputIt, InputIt, size_type) + object obj(init.begin(), init.end(), 5); + check(obj, 7); + } + { + // object(InputIt, InputIt, storage_ptr) + object obj(init.begin(), init.end(), sp); + BEAST_EXPECT(*obj.get_storage() == *sp); + check(obj, 3); + } + { + // object(InputIt, InputIt, size_type, storage_ptr) + object obj(init.begin(), init.end(), 5, sp); + BEAST_EXPECT(! obj.empty()); + BEAST_EXPECT(obj.size() == 3); + BEAST_EXPECT(obj.bucket_count() == 7); + BEAST_EXPECT(*obj.get_storage() == *sp); + check(obj, 7); + } + } + + // object(object&&) + { + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"} + }); + object obj2(std::move(obj1)); + BEAST_EXPECT( + *obj1.get_storage() == + *obj2.get_storage()); + BEAST_EXPECT(obj1.empty()); + BEAST_EXPECT(obj1.size() == 0); + check(obj2, 3); + } + + // object(object&&, storage_ptr) + { + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"} + }); + object obj2(std::move(obj1), sp); + BEAST_EXPECT(! obj1.empty()); + BEAST_EXPECT( + *obj1.get_storage() != + *obj2.get_storage()); + check(obj2, 3); + } + + // object(object const&) + { + + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"} + }); + object obj2(obj1); + BEAST_EXPECT(! obj1.empty()); + BEAST_EXPECT( + *obj1.get_storage() == + *obj2.get_storage()); + check(obj2, 3); + } + + // object(object const&, storage_ptr) + { + + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"} + }); + object obj2(obj1, sp); + BEAST_EXPECT(! obj1.empty()); + BEAST_EXPECT( + *obj1.get_storage() != + *obj2.get_storage()); + check(obj2, 3); + } + + // object(init_list) + { + object obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"} + }); + check(obj, 3); + } + + // object(init_list, size_type) + { + object obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"} + }, + 5); + check(obj, 7); + } + + // object(init_list, storage_ptr) + { + object obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"} + }, + sp); + BEAST_EXPECT( + *obj.get_storage() == *sp); + check(obj, 3); + } + + // object(init_list, size_type, storage_ptr) + { + object obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"} + }, + 5, sp); + BEAST_EXPECT( + *obj.get_storage() == *sp); + check(obj, 7); + } + + // operator=(object&&) + { + { + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + object obj2; + obj2 = std::move(obj1); + check(obj2, 3); + BEAST_EXPECT(obj1.empty()); + BEAST_EXPECT( + *obj1.get_storage() == *sp0); + BEAST_EXPECT( + *obj1.get_storage() == + *obj2.get_storage()); + } + { + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + object obj2(sp); + obj2 = std::move(obj1); + check(obj2, 3); + check(obj1, 3); + BEAST_EXPECT( + *obj1.get_storage() != + *obj2.get_storage()); + } + } + + // operator=(object const&) + { + { + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + object obj2; + obj2 = obj1; + check(obj1, 3); + check(obj2, 3); + BEAST_EXPECT( + *obj1.get_storage() == + *obj2.get_storage()); + } + { + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + object obj2(sp); + obj2 = obj1; + check(obj1, 3); + check(obj2, 3); + BEAST_EXPECT( + *obj1.get_storage() != + *obj2.get_storage()); + } + } + + // operator=(init_list) + { + { + object obj; + obj = { + {"a", 1}, + {"b", true}, + {"c", "hello"} }, + BEAST_EXPECT( + *obj.get_storage() == *sp0); + check(obj, 3); + } + { + object obj(sp); + obj = { + {"a", 1}, + {"b", true}, + {"c", "hello"} }, + BEAST_EXPECT( + *obj.get_storage() == *sp); + check(obj, 3); + } + } + } + + void + testIterators() + { + object obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + { + auto it = obj.begin(); + BEAST_EXPECT(it->first == "a"); ++it; + BEAST_EXPECT(it->first == "b"); it++; + BEAST_EXPECT(it->first == "c"); ++it; + BEAST_EXPECT(it == obj.end()); + } + { + auto it = obj.cbegin(); + BEAST_EXPECT(it->first == "a"); ++it; + BEAST_EXPECT(it->first == "b"); it++; + BEAST_EXPECT(it->first == "c"); ++it; + BEAST_EXPECT(it == obj.cend()); + } + { + auto it = static_cast< + object const&>(obj).begin(); + BEAST_EXPECT(it->first == "a"); ++it; + BEAST_EXPECT(it->first == "b"); it++; + BEAST_EXPECT(it->first == "c"); ++it; + BEAST_EXPECT(it == static_cast< + object const&>(obj).end()); + } + { + auto it = obj.end(); + --it; BEAST_EXPECT(it->first == "c"); + it--; BEAST_EXPECT(it->first == "b"); + --it; BEAST_EXPECT(it->first == "a"); + BEAST_EXPECT(it == obj.begin()); + } + { + auto it = obj.cend(); + --it; BEAST_EXPECT(it->first == "c"); + it--; BEAST_EXPECT(it->first == "b"); + --it; BEAST_EXPECT(it->first == "a"); + BEAST_EXPECT(it == obj.cbegin()); + } + { + auto it = static_cast< + object const&>(obj).end(); + --it; BEAST_EXPECT(it->first == "c"); + it--; BEAST_EXPECT(it->first == "b"); + --it; BEAST_EXPECT(it->first == "a"); + BEAST_EXPECT(it == static_cast< + object const&>(obj).begin()); + } + } + + void + testModifiers() + { + // clear + { + object obj; + obj.emplace("x", 1); + BEAST_EXPECT(! obj.empty()); + obj.clear(); + BEAST_EXPECT(obj.empty()); + } + + // insert(value_type&&) + { + object obj; + auto v = object::value_type("a", 1); + auto result = obj.insert(std::move(v)); + BEAST_EXPECT(v.second.is_null()); + BEAST_EXPECT(result.second); + BEAST_EXPECT(result.first->first == "a"); + auto v2 = object::value_type("a", 2); + BEAST_EXPECT( + obj.insert(std::move(v2)).first == result.first); + BEAST_EXPECT( + ! obj.insert(std::move(v2)).second); + } + + // insert(value_type const&) + { + object obj; + auto v = object::value_type("a", 1); + auto result = obj.insert(v); + BEAST_EXPECT(! v.second.is_null()); + BEAST_EXPECT(result.second); + BEAST_EXPECT(result.first->first == "a"); + auto v2 = object::value_type("a", 2); + BEAST_EXPECT( + obj.insert(v2).first == result.first); + BEAST_EXPECT(! obj.insert(v2).second); + } + + // insert(P&&) + { + { + object obj; + auto result = obj.insert( + std::make_pair("x", 1)); + BEAST_EXPECT(result.second); + BEAST_EXPECT(result.first->first == "x"); + BEAST_EXPECT(result.first->second.as_number() == 1); + } + { + object obj; + auto const p = std::make_pair("x", 1); + auto result = obj.insert(p); + BEAST_EXPECT(result.second); + BEAST_EXPECT(result.first->first == "x"); + BEAST_EXPECT(result.first->second.as_number() == 1); + } + } + + // insert(before, value_type const&) + { + object obj; + obj.emplace("a", 1); + obj.emplace("c", "hello"); + object::value_type const p("b", true); + obj.insert(obj.find("c"), p); + check(obj, 3); + } + + // insert(before, value_type&&) + { + object obj; + obj.emplace("a", 1); + obj.emplace("c", "hello"); + obj.insert(obj.find("c"), { "b", true }); + check(obj, 3); + } + + // insert(before, P&&) + { + { + object obj; + obj.emplace("a", 1); + obj.emplace("c", "hello"); + obj.insert(obj.find("c"), + std::make_pair("b", true)); + check(obj, 3); + } + { + object obj; + obj.emplace("a", 1); + obj.emplace("c", "hello"); + auto const p = + std::make_pair("b", true); + obj.insert(obj.find("c"), p); + check(obj, 3); + } + } + + // insert(InputIt, InputIt) + { + std::initializer_list> init = { + {"a", 1}, + {"b", true}, + {"c", "hello"}}; + object obj; + obj.insert(init.begin(), init.end()); + check(obj, 3); + } + + // insert(init_list) + { + object obj; + obj.emplace("a", 1); + obj.insert({ + { "b", true }, + { "c", "hello" }}); + check(obj, 3); + } + + // insert(node_type&&) + { + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + object obj2; + obj2.insert(obj1.extract(obj1.begin())); + obj2.insert(obj1.extract("b")); + auto result = + obj2.insert(obj1.extract(obj1.find("c"))); + check(obj2, 3); + BEAST_EXPECT(obj1.empty()); + BEAST_EXPECT(result.inserted == true); + BEAST_EXPECT(result.node.empty()); + BEAST_EXPECT(result.position->first == "c"); + + // failed insertion + result = obj2.insert(obj1.extract( + obj1.insert({"a", 1}).first)); + BEAST_EXPECT(result.inserted == false); + BEAST_EXPECT(! result.node.empty()); + BEAST_EXPECT(result.position->first == "a"); + + } + + // insert(before, node_type&&) + { + object obj1({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + object obj2; + obj2.insert(obj1.extract(obj1.begin())); + obj2.insert(obj1.extract(obj1.find("c"))); + auto result = + obj2.insert(obj2.find("c"), obj1.extract("b")); + check(obj2, 3); + BEAST_EXPECT(obj1.empty()); + BEAST_EXPECT(result.inserted == true); + BEAST_EXPECT(result.node.empty()); + BEAST_EXPECT(result.position->first == "b"); + + // failed insertion + result = obj2.insert(obj2.find("c"), + obj1.extract(obj1.insert({"b", 1}).first)); + BEAST_EXPECT(result.inserted == false); + BEAST_EXPECT(! result.node.empty()); + BEAST_EXPECT(result.position->first == "b"); + } + + // insert_or_assign(key, obj); + { + { + object obj({{"a", 1}}); + obj.insert_or_assign("b", true); + obj.insert_or_assign("c", "hello"); + check(obj, 3); + } + { + object obj({{"a", 1}}); + BEAST_EXPECT( + ! obj.insert_or_assign("a", 2).second); + BEAST_EXPECT(obj["a"].as_number() == 2); + } + } + + // insert_or_assign(before, key, obj); + { + { + object obj({{"a", 1}}); + obj.insert_or_assign("c", "hello"); + obj.insert_or_assign(obj.find("c"), "b", true); + check(obj, 3); + } + { + object obj({{"a", 1}}); + obj.insert_or_assign("b", true); + obj.insert_or_assign("c", "hello"); + BEAST_EXPECT(! obj.insert_or_assign( + obj.find("b"), "a", 2).second); + BEAST_EXPECT(obj["a"].as_number() == 2); + } + } + + // emplace(key, arg) + { + object obj; + obj.emplace("a", 1); + obj.emplace("b", true); + obj.emplace("c", "hello"); + check(obj, 3); + } + + // emplace(before, key, arg) + { + object obj; + obj.emplace("a", 1); + obj.emplace("c", "hello"); + obj.emplace(obj.find("c"), "b", true); + check(obj, 3); + } + + // erase(pos) + { + object obj({ + {"d", nullptr }, + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + auto it = obj.erase(obj.begin()); + BEAST_EXPECT(it->first == "a"); + BEAST_EXPECT(it->second.as_number() == 1); + check(obj, 7); + } + + // erase(first, last) + { + object obj({ + {"a", 1}, + {"b", true}, + {"b2", 2}, + {"b3", 3}, + {"b4", 4}, + {"c", "hello"}}); + auto first = obj.find("b2"); + auto last = std::next(first, 3); + auto it = obj.erase(first, last); + BEAST_EXPECT(it->first == "c"); + BEAST_EXPECT( + it->second.as_string() == "hello"); + check(obj, 7); + } + + // erase(key) + { + object obj({ + {"a", 1}, + {"b", true}, + {"b2", 2}, + {"c", "hello"}}); + BEAST_EXPECT(obj.erase("b2") == 1); + check(obj, 7); + } + } + + void + testLookup() + { + // at(key) + { + object obj; + try + { + obj.at("a"); + BEAST_FAIL(); + } + catch(std::out_of_range const&) + { + BEAST_PASS(); + } + } + + // at(key) const + { + object const obj; + try + { + obj.at("a"); + BEAST_FAIL(); + } + catch(std::out_of_range const&) + { + BEAST_PASS(); + } + } + + // operator[](key) + { + object obj; + obj["a"].emplace_bool() = true; + BEAST_EXPECT(obj.find("a") != obj.end()); + } + + // operator[](key) const + { + object const obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + BEAST_EXPECT(obj["a"].is_number()); + } + + // count(key) + { + object const obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + BEAST_EXPECT(obj.count("b") == 1); + BEAST_EXPECT(obj.count("d") == 0); + } + + // count(key, hash) + { + object const obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + auto const hash = + obj.hash_function()("b"); + BEAST_EXPECT(obj.count("b", hash) == 1); + } + + // find(key) + { + object const obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + BEAST_EXPECT( + obj.find("b")->second.is_bool()); + } + + // find(key, hash) + { + object const obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + auto const hash = + obj.hash_function()("c"); + BEAST_EXPECT(obj.find( + "c", hash)->second.is_string()); + } + + // contains(key) + { + object const obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + BEAST_EXPECT(obj.contains("a")); + BEAST_EXPECT(! obj.contains("d")); + } + + // find(key, hash) + { + object const obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + BEAST_EXPECT(obj.contains( + "b", obj.hash_function()("b"))); + BEAST_EXPECT(! obj.contains( + "d", obj.hash_function()("d"))); + } + + + } + + void + testBuckets() + { + } + + void + testHashPolicy() + { + object obj; + for(std::size_t i = 0; i < 1000; ++i) + obj.emplace( + std::to_string(i), + i); + } + + void + testObservers() + { + // hash_function + { + object obj; + object::hasher h = obj.hash_function(); + boost::ignore_unused(h); + } + + // key_eq + { + object obj; + object::key_equal eq = obj.key_eq(); + BEAST_EXPECT(eq("a", "a")); + } + } + + void + testNodeType() + { + BOOST_STATIC_ASSERT( + std::is_same< + object::node_type::key_type, + object::key_type>::value); + + BOOST_STATIC_ASSERT( + std::is_same< + object::node_type::mapped_type, + object::mapped_type>::value); + + // node_type() + { + object::node_type nh; + BEAST_EXPECT(nh.empty()); + BEAST_EXPECT(! nh); + BEAST_EXPECT( + nh.get_storage() == nullptr); + } + + // node_type(node_type&&) + { + { + object::node_type nh1; + object::node_type nh2(std::move(nh1)); + BEAST_EXPECT(nh1.empty()); + BEAST_EXPECT(nh2.empty()); + BEAST_EXPECT( + nh1.get_storage() == nullptr); + BEAST_EXPECT( + nh2.get_storage() == nullptr); + } + { + object obj({ + {"a", 1}, + {"b", true}, + {"c", "hello"}}); + object::node_type nh; + BEAST_EXPECT(nh.empty()); + nh = obj.extract("b"); + BEAST_EXPECT(! nh.empty()); + BEAST_EXPECT(nh.key() == "b"); + BEAST_EXPECT( + nh.get_storage() == + obj.get_storage()); + auto nh2 = std::move(nh); + BEAST_EXPECT(nh.empty()); + BEAST_EXPECT( + nh.get_storage() == nullptr); + BEAST_EXPECT(! nh2.empty()); + BEAST_EXPECT(nh2.key() == "b"); + BEAST_EXPECT( + nh2.get_storage() == + obj.get_storage()); + } + } + } + + void + testExceptions() + { + // operator=(object const&) + { + object obj0({ + { "a", 1 }, + { "b", true }, + { "c", "hello" }}); + fail_storage fs; + storage_ptr sp(&fs); + object obj1; + while(fs.fail < 200) + { + try + { + object obj(sp); + obj.emplace("a", 2); + obj = obj0; + obj1 = obj; + break; + } + catch(std::bad_alloc const&) + { + } + } + check(obj1, 3); + } + + // operator=(object&&) + { + fail_storage fs; + storage_ptr sp(&fs); + object obj1; + while(fs.fail < 200) + { + try + { + object obj0({ + { "a", 1 }, + { "b", true }, + { "c", "hello" }}); + object obj(sp); + obj.emplace("a", 2); + obj = std::move(obj0); + obj1 = obj; + break; + } + catch(std::bad_alloc const&) + { + } + } + check(obj1, 3); + } + + // operator=(init_list) + { + fail_storage fs; + storage_ptr sp(&fs); + object obj1; + while(fs.fail < 200) + { + try + { + object obj(sp); + obj.emplace("a", 2); + obj = { + { "a", 1 }, + { "b", true }, + { "c", "hello" }}; + obj1 = obj; + break; + } + catch(std::bad_alloc const&) + { + } + } + check(obj1, 3); + } + } + + void + run() override + { + testSpecial(); + testIterators(); + testModifiers(); + testLookup(); + testBuckets(); + testHashPolicy(); + testObservers(); + testNodeType(); + testExceptions(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,object); + +} // json +} // beast +} // boost diff --git a/test/parse_file.cpp b/test/parse_file.cpp new file mode 100644 index 00000000..b409e975 --- /dev/null +++ b/test/parse_file.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/BeastLounge +// + +// Test that header file is self-contained. +#include diff --git a/test/parser.cpp b/test/parser.cpp new file mode 100644 index 00000000..09e03dd1 --- /dev/null +++ b/test/parser.cpp @@ -0,0 +1,84 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/BeastLounge +// + +// Test that header file is self-contained. +#include + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class parser_test : public unit_test::suite +{ +public: + void + testParser() + { + string_view in = +R"xx({ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +})xx" + ; + parser p; + error_code ec; + p.write({in.data(), in.size()}, ec); + if(BEAST_EXPECTS(! ec, ec.message())) + { + std::stringstream ss; + ss << p.get(); + BEAST_EXPECT(ss.str() == + "{\"glossary\":{\"title\":\"example glossary\",\"GlossDiv\":" + "{\"title\":\"S\",\"GlossList\":{\"GlossEntry\":{\"ID\":\"SGML\"," + "\"SortAs\":\"SGML\",\"GlossTerm\":\"Standard Generalized Markup " + "Language\",\"Acronym\":\"SGML\",\"Abbrev\":\"ISO 8879:1986\"," + "\"GlossDef\":{\"para\":\"A meta-markup language, used to create " + "markup languages such as DocBook.\",\"GlossSeeAlso\":[\"GML\",\"XML\"]}," + "\"GlossSee\":\"markup\"}}}}}" + ); + } + } + + void + run() + { + log << + "sizeof(parser) == " << + sizeof(parser) << "\n"; + testParser(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,parser); + +} // json +} // beast +} // boost diff --git a/test/serializer.cpp b/test/serializer.cpp new file mode 100644 index 00000000..2728622b --- /dev/null +++ b/test/serializer.cpp @@ -0,0 +1,68 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/BeastLounge +// + +// Test that header file is self-contained. +#include + +#include + +namespace boost { +namespace beast { +namespace json { + +class serializer_test : public unit_test::suite +{ +public: + void + testSerializer() + { + value jv = { + {"pi", 3.141}, + {"happy", true}, + {"name", "Niels"}, + {"nothing", nullptr}, + {"answer", { + {"everything", 42} + }}, +#if 0 + {"list", {1, 0, 2}}, + {"object", { + {"currency", "USD"}, + {"value", 42.99} + }} +#endif + }; + + auto& arr = jv.as_object(). + emplace("arr", kind::array).first->second.as_array(); + arr.emplace_back(1); + arr.emplace_back(2); + arr.emplace_back(3); + + serializer sr(jv); + char buf[2048]; + auto const n = + sr.next({buf, sizeof(buf)}); + string_view s(buf, n); + log << s << "\n"; + } + + void + run() + { + testSerializer(); + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,serializer); + +} // json +} // beast +} // boost diff --git a/test/storage.cpp b/test/storage.cpp new file mode 100644 index 00000000..1805f149 --- /dev/null +++ b/test/storage.cpp @@ -0,0 +1,191 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace boost { +namespace beast { +namespace json { + +class storage_test : public unit_test::suite +{ +public: + struct unique_storage : storage + { + void + addref() noexcept override + { + } + + void + release() noexcept override + { + } + + void* + allocate( + std::size_t n, + std::size_t) override + { + return std::allocator< + char>{}.allocate(n); + } + + void + deallocate( + void* p, + std::size_t n, + std::size_t) noexcept override + { + auto cp = + reinterpret_cast(p); + return std::allocator< + char>{}.deallocate(cp, n); + } + bool + is_equal( + storage const& other + ) const noexcept override + { + auto p = dynamic_cast< + unique_storage const*>(&other); + if(! p) + return false; + return this == p; + } + }; + + void + testStoragePtr() + { + auto p = new detail::storage_adaptor< + std::allocator>({}); + + // default ctor + { + storage_ptr s; + BEAST_EXPECT(s == nullptr); + } + + // move ctor + { + p->addref(); + storage_ptr s1(p); + storage_ptr s2(std::move(s1)); + BEAST_EXPECT(s1 == nullptr); + BEAST_EXPECT(s2 == p); + } + + // copy ctor + { + p->addref(); + storage_ptr s1(p); + storage_ptr s2(s1); + BEAST_EXPECT(s1 == p); + BEAST_EXPECT(s2 == p); + BEAST_EXPECT(s1 == s2); + } + + // move assign + { + p->addref(); + storage_ptr s1(p); + storage_ptr s2; + s2 = std::move(s1); + BEAST_EXPECT(s1 == nullptr); + BEAST_EXPECT(s2 == p); + } + + // copy assign + { + p->addref(); + storage_ptr s1(p); + storage_ptr s2; + s2 = s1; + BEAST_EXPECT(s1 == p); + BEAST_EXPECT(s2 == p); + BEAST_EXPECT(s1 == s2); + } + + // construction + { + p->addref(); + storage_ptr sp(p); + BEAST_EXPECT(sp == p); + } + + // release + { + p->addref(); + storage_ptr sp( + storage_ptr{p}.release()); + BEAST_EXPECT(sp == p); + } + + // bool, get, ->, * + { + p->addref(); + storage_ptr sp(p); + BEAST_EXPECT(sp); + BEAST_EXPECT(sp.get() == p); + BEAST_EXPECT(sp.operator->() == p); + BEAST_EXPECT(&sp.operator*() == p); + sp = nullptr; + BEAST_EXPECT(! sp); + } + + // converting assignment + { + p->addref(); + storage_ptr sp; + sp = p; + BEAST_EXPECT(sp == p); + sp = nullptr; + BEAST_EXPECT(! sp); + } + + // equality + { + unique_storage us1; + unique_storage us2; + storage_ptr spu1(&us1); + storage_ptr spu2(&us2); + storage_ptr sp = make_storage_ptr( + std::allocator{}); + storage_ptr spd = + default_storage(); + BEAST_EXPECT(us1 != us2); + BEAST_EXPECT(us1 != *spd); + BEAST_EXPECT(us2 != *spd); + BEAST_EXPECT(us1 != *sp); + BEAST_EXPECT(us2 != *sp); + BEAST_EXPECT( + *spd == *default_storage()); + BEAST_EXPECT( + *sp != *default_storage()); + } + + p->release(); + } + + void run() override + { + testStoragePtr(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,storage); + +} // json +} // beast +} // boost diff --git a/test/string.cpp b/test/string.cpp new file mode 100644 index 00000000..af6f181b --- /dev/null +++ b/test/string.cpp @@ -0,0 +1,33 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/BeastLounge +// + +// Test that header file is self-contained. +#include + +#include + +namespace boost { +namespace beast { +namespace json { + +class string_test : public unit_test::suite +{ +public: + void + run() override + { + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,string); + +} // json +} // beast +} // boost diff --git a/test/value.cpp b/test/value.cpp new file mode 100644 index 00000000..5d23bd12 --- /dev/null +++ b/test/value.cpp @@ -0,0 +1,725 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Test that header file is self-contained. +#include + +#include +#include +#include + +namespace boost { +namespace beast { +namespace json { + +namespace value_test_ns { + +struct T1 +{ + void + to_json(value&) const + { + } +}; + +struct T2 +{ +}; + +void +to_json(T2 const&, value&) +{ +} + +struct T3 +{ +}; + +} // value_test_ns + +template<> +struct value_exchange +{ + static + void + to_json(value_test_ns::T3, value&) + { + } +}; + +//------------------------------------------------------------------------------ + +class value_test : public unit_test::suite +{ +public: + struct unique_storage : storage + { + void + addref() noexcept override + { + } + + void + release() noexcept override + { + } + + void* + allocate( + std::size_t n, + std::size_t) override + { + return std::allocator< + char>{}.allocate(n); + } + + void + deallocate( + void* p, + std::size_t n, + std::size_t) noexcept override + { + auto cp = + reinterpret_cast(p); + return std::allocator< + char>{}.deallocate(cp, n); + } + bool + is_equal( + storage const& other + ) const noexcept override + { + auto p = dynamic_cast< + unique_storage const*>(&other); + if(! p) + return false; + return this == p; + } + }; + + void + testSpecial() + { + // move ctor + { + value jv1; + value jv2(std::move(jv1)); + } + + // copy ctor + { + value jv1; + value jv2(jv1); + } + + // move assign + { + value jv1; + value jv2; + jv2 = std::move(jv1); + } + + // copy assign + { + value jv1; + value jv2; + jv2 = jv1; + } + } + + void + testConstruct() + { + unique_storage us; + storage_ptr sp(&us); + storage_ptr sp0 = + default_storage(); + + // default ctor + { + value jv; + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + + // storage ctor + { + value jv1(sp); + BEAST_EXPECT(jv1.is_null()); + BEAST_EXPECT(jv1.get_storage() == sp); + BEAST_EXPECT(jv1.get_storage() != sp0); + + auto sp2 = sp; + value jv2(std::move(sp2)); + BEAST_EXPECT(jv2.is_null()); + BEAST_EXPECT(jv1.get_storage() == sp); + BEAST_EXPECT(jv1.get_storage() != sp0); + BEAST_EXPECT(jv2.get_storage() == sp); + BEAST_EXPECT(jv2.get_storage() != sp0); + } + + // kind construct + { + value jv(kind::object); + BEAST_EXPECT(jv.is_object()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + value jv(kind::array); + BEAST_EXPECT(jv.is_array()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + value jv(kind::string); + BEAST_EXPECT(jv.is_string()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + value jv(kind::number); + BEAST_EXPECT(jv.is_number()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + value jv(kind::boolean); + BEAST_EXPECT(jv.is_bool()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + value jv(kind::null); + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + + // kind, storage construct + { + value jv(kind::object, sp); + BEAST_EXPECT(jv.is_object()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + value jv(kind::array, sp); + BEAST_EXPECT(jv.is_array()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + value jv(kind::string, sp); + BEAST_EXPECT(jv.is_string()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + value jv(kind::number, sp); + BEAST_EXPECT(jv.is_number()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + value jv(kind::boolean, sp); + BEAST_EXPECT(jv.is_bool()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + value jv(kind::null, sp); + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + + // construct from containers + { + { + object obj; + value jv(obj); + BEAST_EXPECT(jv.is_object()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + array arr; + value jv(arr); + BEAST_EXPECT(jv.is_array()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + string str; + value jv(str); + BEAST_EXPECT(jv.is_string()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + value jv(number(1)); + BEAST_EXPECT(jv.is_number()); + BEAST_EXPECT(jv.is_int64()); + BEAST_EXPECT(jv.is_uint64()); + BEAST_EXPECT(jv.is_double()); + BEAST_EXPECT(jv.get_int64() == 1); + BEAST_EXPECT(jv.get_uint64() == 1); + BEAST_EXPECT(jv.get_double() == 1); + BEAST_EXPECT(jv.as_number() == 1); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + // + { + object obj; + value jv(obj, sp); + BEAST_EXPECT(jv.is_object()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + array arr; + value jv(arr, sp); + BEAST_EXPECT(jv.is_array()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + string str; + value jv(str, sp); + BEAST_EXPECT(jv.is_string()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + value jv(number(1), sp); + BEAST_EXPECT(jv.is_number()); + BEAST_EXPECT(jv.is_int64()); + BEAST_EXPECT(jv.is_uint64()); + BEAST_EXPECT(jv.is_double()); + BEAST_EXPECT(jv.get_int64() == 1); + BEAST_EXPECT(jv.get_uint64() == 1); + BEAST_EXPECT(jv.get_double() == 1); + BEAST_EXPECT(jv.as_number() == 1); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + } + + // assign from containers + { + { + object obj; + value jv; + + jv = obj; + BEAST_EXPECT(jv.is_object()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + + jv.reset(); + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + + jv = std::move(obj); + BEAST_EXPECT(jv.is_object()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + object obj; + value jv(sp); + + jv = obj; + BEAST_EXPECT(jv.is_object()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + + jv.reset(); + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + + jv = std::move(obj); + BEAST_EXPECT(jv.is_object()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + array arr; + value jv; + + jv = arr; + BEAST_EXPECT(jv.is_array()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + + jv.reset(); + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + + jv = std::move(arr); + BEAST_EXPECT(jv.is_array()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + array arr; + value jv(sp); + + jv = arr; + BEAST_EXPECT(jv.is_array()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + + jv.reset(); + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + + jv = std::move(arr); + BEAST_EXPECT(jv.is_array()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + string str; + value jv; + + jv = str; + BEAST_EXPECT(jv.is_string()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + + jv.reset(); + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + + jv = std::move(str); + BEAST_EXPECT(jv.is_string()); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + string str; + value jv(sp); + + jv = str; + BEAST_EXPECT(jv.is_string()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + + jv.reset(); + BEAST_EXPECT(jv.is_null()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + + jv = std::move(str); + BEAST_EXPECT(jv.is_string()); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + { + number n(1); + value jv; + jv = n; + BEAST_EXPECT(jv.is_number()); + BEAST_EXPECT(jv.is_int64()); + BEAST_EXPECT(jv.is_uint64()); + BEAST_EXPECT(jv.is_double()); + BEAST_EXPECT(jv.get_int64() == 1); + BEAST_EXPECT(jv.get_uint64() == 1); + BEAST_EXPECT(jv.get_double() == 1); + BEAST_EXPECT(jv.as_number() == 1); + BEAST_EXPECT(jv.get_storage() != sp); + BEAST_EXPECT(jv.get_storage() == sp0); + } + { + number n(1); + value jv(sp); + jv = n; + BEAST_EXPECT(jv.is_number()); + BEAST_EXPECT(jv.is_int64()); + BEAST_EXPECT(jv.is_uint64()); + BEAST_EXPECT(jv.is_double()); + BEAST_EXPECT(jv.get_int64() == 1); + BEAST_EXPECT(jv.get_uint64() == 1); + BEAST_EXPECT(jv.get_double() == 1); + BEAST_EXPECT(jv.as_number() == 1); + BEAST_EXPECT(jv.get_storage() == sp); + BEAST_EXPECT(jv.get_storage() != sp0); + } + } + } + + void + testModifiers() + { + // reset + { + value jv; + + jv.reset(kind::object); + BEAST_EXPECT(jv.is_object()); + + jv.reset(); + BEAST_EXPECT(jv.is_null()); + + jv.reset(kind::array); + BEAST_EXPECT(jv.is_array()); + + jv.reset(kind::string); + BEAST_EXPECT(jv.is_string()); + + jv.reset(kind::number); + BEAST_EXPECT(jv.is_number()); + + jv.reset(kind::boolean); + BEAST_EXPECT(jv.is_bool()); + + jv.reset(kind::null); + BEAST_EXPECT(jv.is_null()); + } + + // assign + { + value jv; + + jv = kind::object; + BEAST_EXPECT(jv.is_object()); + + jv = kind::null; + BEAST_EXPECT(jv.is_null()); + + jv = kind::array; + BEAST_EXPECT(jv.is_array()); + + jv = kind::string; + BEAST_EXPECT(jv.is_string()); + + jv = kind::number; + BEAST_EXPECT(jv.is_number()); + + jv = kind::boolean; + BEAST_EXPECT(jv.is_bool()); + + jv = kind::null; + BEAST_EXPECT(jv.is_null()); + } + + // emplace + { + { + value jv; + object& obj = jv.emplace_object(); + BEAST_EXPECT(jv.is_object()); + obj.clear(); + } + { + value jv; + array& arr = jv.emplace_array(); + BEAST_EXPECT(jv.is_array()); + arr.clear(); + } + { + value jv; + string& str = jv.emplace_string(); + BEAST_EXPECT(jv.is_string()); + str.clear(); + } + { + value jv; + number& n= jv.emplace_number(); + BEAST_EXPECT(jv.is_number()); + n = 0; + } + { + value jv; + bool& b= jv.emplace_bool(); + BEAST_EXPECT(jv.is_bool()); + b = false; + } + } + } + + void + testExchange() + { + // construct from T + { + value v3("Hello!"); + value(std::string("Hello!")); + value(short{0}); + value(int{0}); + value(long{0}); + value((long long)0); + value((unsigned short)0); + value((unsigned int)0); + value((unsigned long)0); + value((unsigned long long)0); + value(float{0}); + value(double{0}); + value(true); + value(false); + value v4(null); + value(nullptr); + } + + // assign from T + { + value jv; + jv = "Hello!"; + jv = std::string("Hello!"); + jv = short{}; + jv = int{}; + jv = long{}; + jv = (long long)0; + jv = (unsigned short)0; + jv = (unsigned int)0; + jv = (unsigned long)0; + jv = (unsigned long long)0; + jv = float{}; + jv = double{}; + jv = true; + jv = false; + jv = null; + jv = nullptr; + } + } + + void + testAccessors() + { + // raw + value jv; + value const& jc(jv); + { + jv = kind::object; + BEAST_EXPECT( + jv.as_object().size() == 0); + jc.as_object(); + } + { + jv = kind::array; + BEAST_EXPECT( + jv.as_array().size() == 0); + jc.as_array(); + } + { + jv = "x"; + jv.as_string() = "y"; + BEAST_EXPECT(jc.as_string() == "y"); + } + { + jv = signed{}; + BEAST_EXPECT(jc.get_int64() == 0); + jv.as_number() = -1; + BEAST_EXPECT(jc.get_int64() == -1); + } + { + jv = unsigned{}; + jv.as_number() = 2; + BEAST_EXPECT(jc.get_uint64() == 2); + } + { + jv = bool{}; + jv.as_bool() = true; + BEAST_EXPECT(jc.as_bool()); + } + } + + void + testStructured() + { + // empty() + { + value v; + v = kind::object; + BEAST_EXPECT(v.empty()); + v = kind::array; + BEAST_EXPECT(v.empty()); + } + + // size() + { + value v; + v = kind::object; + v.as_object().emplace("x", 1); + BEAST_EXPECT(v.size() == 1); + v = kind::array; + v.as_array().emplace_back(1); + BEAST_EXPECT(v.size() == 1); + } + } + + void + testCustomization() + { + using namespace value_test_ns; + + // to_json + { + T1 t; + value jv(t); + } + { + T2 t; + value jv(t); + } + { + T3 t; + value jv(t); + } + } + + BOOST_STATIC_ASSERT( + detail::is_range>::value); + + BOOST_STATIC_ASSERT( + detail::is_range>::value); + + void run() override + { + log << + "sizeof(value) == " << + sizeof(value) << "\n"; + log << + "sizeof(object) == " << + sizeof(object) << "\n"; + log << + "sizeof(array) == " << + sizeof(array) << "\n"; + log << + "sizeof(string) == " << + sizeof(string) << "\n"; + log << + "sizeof(number) == " << + sizeof(number) << "\n"; + + testSpecial(); + testConstruct(); + testModifiers(); + testExchange(); + testAccessors(); + testStructured(); + testCustomization(); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,json,value); + +} // json +} // beast +} // boost +