diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..a3ce056a --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +--- +BasedOnStyle: WebKit +BreakBeforeBraces: Attach +SpaceAfterTemplateKeyword: false +... diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml deleted file mode 100644 index 5a20f848..00000000 --- a/.github/workflows/cmake-windows.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: CMake Windows Build - -on: [push, pull_request] - -env: - BUILD_TYPE: Release - -jobs: - windows-build: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v2 - with: - submodules: 'true' - - - name: Restore artifacts, or run vcpkg, build and cache artifacts - uses: lukka/run-vcpkg@v7 - id: runvcpkg - with: - vcpkgArguments: 'discord-rpc zlib nlohmann-json openssl cpp-httplib[openssl]' - vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' - vcpkgGitCommitId: '16ee2ecb31788c336ace8bb14c21801efb6836e4' - vcpkgTriplet: 'x64-windows-static' - - - name: Create Build Environment - run: cmake -E make_directory ${{github.workspace}}/build-windows - - - name: Configure CMake - shell: bash - working-directory: ${{github.workspace}}/build-windows - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static - - - name: Build - working-directory: ${{github.workspace}}/build-windows - shell: bash - run: cmake --build . --config $BUILD_TYPE - - - name: Archive artifacts - uses: actions/upload-artifact@v2 - with: - name: BeamMP-Launcher.exe - path: ${{github.workspace}}/build-windows/Release/BeamMP-Launcher.exe diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 00000000..39d65328 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,152 @@ +name: Linux + +on: [push] + + +env: + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" + VCPKG_FORCE_SYSTEM_BINARIES: 1 + CMAKE_BUILD_TYPE: "Release" + DEBIAN_FRONTEND: "noninteractive" + +jobs: + x86_64-matrix: + runs-on: ubuntu-22.04 + strategy: + matrix: + include: + - distro: debian + version: 11 + - distro: debian + version: 12 + - distro: ubuntu + version: 22.04 + - distro: ubuntu + version: 20.04 + container: + image: ${{ matrix.distro }}:${{ matrix.version }} + steps: + - name: get-cmake + uses: lukka/get-cmake@v3.28.1 + + - name: Export GitHub Actions cache environment variables + uses: actions/github-script@v6 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - name: Install git + run: | + apt-get update -y + apt-get install -y git + + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + + - name: Git config safe directory + shell: bash + run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/1.5-git-safe.sh + + - name: Install Dependencies + run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/1-install-deps.sh + + - name: Create Build Environment + run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/2-configure.sh '-DCMAKE_TOOLCHAIN_FILE=./vcpkg/scripts/buildsystems/vcpkg.cmake' + + - name: Build Launcher + run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/3-build.sh + + - name: Archive launcher artifact + uses: actions/upload-artifact@v2 + with: + name: BeamMP-Launcher.${{ matrix.distro }}.${{ matrix.version }}.x86_64 + path: ./bin/BeamMP-Launcher + + - name: Archive launcher debug info artifact + uses: actions/upload-artifact@v2 + with: + name: debuginfo.${{ matrix.distro }}.${{ matrix.version }}.x86_64 + path: ./bin/BeamMP-Launcher.debug + + - name: Build Tests + run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/3-build-tests.sh + + - name: Install Runtime Dependencies + run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/4-install-runtime-deps.sh + + - name: Test + run: ./bin/BeamMP-Launcher-tests + +# arm64-matrix: +# runs-on: [Linux, ARM64] +# env: +# VCPKG_DEFAULT_TRIPLET: "arm64-linux" +# strategy: +# matrix: +# include: +# - distro: debian +# version: 11 +# - distro: debian +# version: 12 +# - distro: ubuntu +# version: 22.04 +# - distro: ubuntu +# version: 20.04 +# container: +# image: ${{ matrix.distro }}:${{ matrix.version }} +# steps: +# - name: get-cmake +# uses: lukka/get-cmake@v3.28.1 +# +# - name: Export GitHub Actions cache environment variables +# uses: actions/github-script@v6 +# with: +# script: | +# core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); +# core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); +# +# - name: Install git +# run: | +# apt-get update -y +# apt-get install -y git +# +# - uses: actions/checkout@v2 +# with: +# submodules: 'recursive' +# +# - name: Git config safe directory +# shell: bash +# run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/1.5-git-safe.sh +# +# - name: Install Dependencies +# run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/1-install-deps.sh +# +# - name: Create Build Environment +# run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/2-configure.sh '-DCMAKE_TOOLCHAIN_FILE=./vcpkg/scripts/buildsystems/vcpkg.cmake' +# +# - name: Build Launcher +# run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/3-build.sh +# +# - name: Archive launcher artifact +# uses: actions/upload-artifact@v2 +# with: +# name: BeamMP-Launcher.${{ matrix.distro }}.${{ matrix.version }}.arm64 +# path: ./bin/BeamMP-Launcher +# +# - name: Archive launcher debug info artifact +# uses: actions/upload-artifact@v2 +# with: +# name: debuginfo.${{ matrix.distro }}.${{ matrix.version }}.arm64 +# path: ./bin/BeamMP-Launcher.debug +# +# - name: Build Tests +# run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/3-build-tests.sh +# +# - name: Install Runtime Dependencies +# run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/4-install-runtime-deps.sh +# +# - name: Test +# run: ./bin/BeamMP-Launcher-tests +# diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml deleted file mode 100644 index 88e2913d..00000000 --- a/.github/workflows/release-build.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Release Create & Build -on: - push: - # Sequence of patterns matched against refs/tags - tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 - -env: - BUILD_TYPE: Release - -jobs: - create-release: - runs-on: ubuntu-latest - name: Create Release - outputs: - upload_url: ${{ steps.create_release.outputs.upload_url }} - steps: - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false - body: | - Files included in this release: - - `BeamMP-Launcher.exe` windows build - - upload-release-files-windows: - name: Upload Windows Release Files - runs-on: windows-latest - needs: create-release - steps: - - uses: actions/checkout@v2 - with: - submodules: 'true' - - - name: Restore artifacts, or run vcpkg, build and cache artifacts - uses: lukka/run-vcpkg@main - id: runvcpkg - with: - vcpkgArguments: 'discord-rpc zlib nlohmann-json openssl cpp-httplib[openssl]' - vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' - vcpkgGitCommitId: '16ee2ecb31788c336ace8bb14c21801efb6836e4' - vcpkgTriplet: 'x64-windows-static' - - - name: Create Build Environment - run: cmake -E make_directory ${{github.workspace}}/build-windows - - - name: Configure CMake - shell: bash - working-directory: ${{github.workspace}}/build-windows - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static - - - name: Build - working-directory: ${{github.workspace}}/build-windows - shell: bash - run: cmake --build . --config $BUILD_TYPE - - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.create-release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps - asset_path: ${{github.workspace}}/build-windows/Release/BeamMP-Launcher.exe - asset_name: BeamMP-Launcher.exe - asset_content_type: application/vnd.microsoft.portable-executable diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 00000000..e1856e99 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,43 @@ +name: Windows + +on: [push] + +env: + VCPKG_DEFAULT_TRIPLET: x64-windows-static + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" + CMAKE_BUILD_TYPE: "Release" + +jobs: + windows-build: + runs-on: windows-latest + steps: + - name: Export GitHub Actions cache environment variables + uses: actions/github-script@v6 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + + - name: Setup vcpkg + uses: lukka/run-vcpkg@v11 + with: + runVcpkgInstall: true + + - name: Create Build Environment + shell: bash + run: ./scripts/windows/1-configure.sh + + - name: Build Launcher + shell: bash + run: bash ./scripts/windows/2-build.sh + + - name: Archive launcher artifact + uses: actions/upload-artifact@v2 + with: + name: BeamMP-Launcher-windows + path: ./bin/Release/BeamMP-Launcher.exe + diff --git a/.gitignore b/.gitignore index c58e6c04..ef4d94fa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ cmake-build-release *.log /*.sh /*.obj -/*.exe \ No newline at end of file +/*.exe +bin/ +compile_commands.json diff --git a/.gitmodules b/.gitmodules index b37131e0..871375bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ -[submodule "evpp"] - path = evpp - url = https://github.com/BeamMP/evpp.git +[submodule "deps/BeamMP-Protocol"] + path = deps/BeamMP-Protocol + url = https://github.com/BeamMP/BeamMP-Protocol +[submodule "vcpkg"] + path = vcpkg + url = https://github.com/Microsoft/vcpkg.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 75640577..bb62a053 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,42 +1,162 @@ -cmake_minimum_required(VERSION 3.10) -project(Launcher) +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) if (WIN32) - message(STATUS "MSVC -> forcing use of statically-linked runtime.") - STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) - STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) -endif(WIN32) + set(VCPKG_TARGET_TRIPLET x64-windows-static) +endif() -set(CMAKE_CXX_STANDARD 20) +include(cmake/Vcpkg.cmake) # needs to happen before project() -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") +project( + "BeamMP-Launcher" # replace this + VERSION 2.0.100 + LANGUAGES CXX +) -file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h") +message(STATUS "MSVC -> forcing use of statically-linked runtime.") +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + +include(cmake/StandardSettings.cmake) +include(cmake/StaticAnalyzers.cmake) +include(cmake/Git.cmake) + +add_subdirectory(deps/BeamMP-Protocol) + +# below are options which should be changed + +### SETTINGS ### + +# add all headers (.h, .hpp) to this +set(PRJ_HEADERS + src/Http.h + src/Launcher.h + src/Platform.h + src/Identity.h + src/Version.h + src/Hashing.h + src/Compression.h + src/Config.h + src/ServerNetwork.h + src/ClientNetwork.h +) +# add all source files (.cpp) to this, except the one with main() +set(PRJ_SOURCES + src/Launcher.cpp + src/Launcher.cpp + src/PlatformWindows.cpp + src/PlatformLinux.cpp + src/Identity.cpp + src/Http.cpp + src/Version.cpp + src/Hashing.cpp + src/Config.cpp + src/ServerNetwork.cpp + src/ClientNetwork.cpp +) +# set the source file containing main() +set(PRJ_MAIN src/main.cpp) +# set the source file containing the test's main +set(PRJ_TEST_MAIN tests/test_main.cpp) +# set include paths not part of libraries +set(PRJ_INCLUDE_DIRS src) +# set compile features (e.g. standard version) +set(PRJ_COMPILE_FEATURES cxx_std_20) +# set #defines (test enable/disable not included here) +set(PRJ_DEFINITIONS CPPHTTPLIB_OPENSSL_SUPPORT CPPHTTPLIB_ZLIB_SUPPORT) +# add all libraries used by the project (WARNING: also set them in vcpkg.json!) +set(PRJ_LIBRARIES + fmt::fmt + doctest::doctest + Threads::Threads + spdlog::spdlog + httplib::httplib + Boost::system + Boost::iostreams + Boost::thread + Boost::filesystem + cryptopp::cryptopp + ZLIB::ZLIB + OpenSSL::SSL + OpenSSL::Crypto + protocol + zstd::libzstd_static +) + +# add dependency find_package calls and similar here +find_package(fmt CONFIG REQUIRED) +find_package(doctest CONFIG REQUIRED) +find_package(spdlog CONFIG REQUIRED) find_package(httplib CONFIG REQUIRED) -find_package(nlohmann_json CONFIG REQUIRED) +find_package(Boost REQUIRED COMPONENTS system iostreams thread filesystem) +find_package(cryptopp CONFIG REQUIRED) +find_package(ZLIB REQUIRED) +find_package(OpenSSL REQUIRED) +find_package(zstd CONFIG REQUIRED) + +# to enable multithreading and the Threads::Threads dependency +include(FindThreads) + +### END SETTINGS ### + +# DONT change anything beyond this point unless you've read the cmake bible and +# swore on it not to bonk up the ci/cd pipelines with your changes. + +#################### -add_executable(${PROJECT_NAME} ${source_files}) -set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher") +# enables compile_commands.json for clang-related tools (such as the clang LS) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# build debug builds by default (if not specified otherwise) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +if(UNIX) + # this will allow to use same _DEBUG macro available in both Linux as well as Windows - MSCV environment. Easy to put Debug specific code. + add_compile_options("$<$:-D_DEBUG>") +endif(UNIX) + +include(cmake/CompilerWarnings.cmake) + +# set MT library for msvc - this is required (says documentation) +# linux/mac/etc should simply ignore this by default. + +set(PRJ_DEFINITIONS ${PRJ_DEFINITIONS} + PRJ_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} + PRJ_VERSION_MINOR=${PROJECT_VERSION_MINOR} + PRJ_VERSION_PATCH=${PROJECT_VERSION_PATCH} + PRJ_GIT_HASH="${PRJ_GIT_HASH}" +) + +add_executable(${PROJECT_NAME} ${PRJ_HEADERS} ${PRJ_SOURCES} ${PRJ_MAIN}) +target_link_libraries(${PROJECT_NAME} ${PRJ_LIBRARIES}) +target_include_directories(${PROJECT_NAME} PRIVATE ${PRJ_INCLUDE_DIRS}) +target_compile_features(${PROJECT_NAME} PRIVATE ${PRJ_COMPILE_FEATURES}) +target_compile_definitions(${PROJECT_NAME} PRIVATE ${PRJ_DEFINITIONS} ${PRJ_WARNINGS} + DOCTEST_CONFIG_DISABLE # disables all test code in the final executable +) if (WIN32) - find_package(ZLIB REQUIRED) - find_package(OpenSSL REQUIRED) - #-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static - set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}) - include_directories(${VcpkgRoot}/include) - link_directories(${VcpkgRoot}/lib) - target_link_libraries(${PROJECT_NAME} PRIVATE - ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json) - - if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - target_link_libraries(${PROJECT_NAME} PRIVATE ${VcpkgRoot}/lib/discord-rpc.lib) + target_compile_definitions(${PROJECT_NAME} PRIVATE -DPLATFORM_WINDOWS=1) +else () + target_compile_definitions(${PROJECT_NAME} PRIVATE -DPLATFORM_LINUX=1) +endif () + +# setup all warnings (from cmake/CompilerWarnings.cmake) +set_project_warnings(${PROJECT_NAME}) + +if(${PROJECT_NAME}_ENABLE_UNIT_TESTING) + message(STATUS "Unit tests are enabled and will be built as '${PROJECT_NAME}-tests'") + add_executable(${PROJECT_NAME}-tests ${PRJ_HEADERS} ${PRJ_SOURCES} ${PRJ_TEST_MAIN}) + target_link_libraries(${PROJECT_NAME}-tests ${PRJ_LIBRARIES}) + target_include_directories(${PROJECT_NAME}-tests PRIVATE ${PRJ_INCLUDE_DIRS}) + target_compile_features(${PROJECT_NAME}-tests PRIVATE ${PRJ_COMPILE_FEATURES}) + target_compile_definitions(${PROJECT_NAME}-tests PRIVATE ${PRJ_DEFINITIONS} ${PRJ_WARNINGS}) + set_project_warnings(${PROJECT_NAME}-tests) + if (WIN32) + target_compile_definitions(${PROJECT_NAME}-tests PRIVATE -DPLATFORM_WINDOWS=1) else () - target_link_libraries(${PROJECT_NAME} PRIVATE ${VcpkgRoot}/debug/lib/discord-rpc.lib) - endif() -else(WIN32) #MINGW - add_definitions("-D_WIN32_WINNT=0x0600") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static") - target_link_libraries(${PROJECT_NAME} discord-rpc ssl crypto ws2_32 ssp crypt32 z) -endif(WIN32) -target_include_directories(${PROJECT_NAME} PRIVATE "include") \ No newline at end of file + target_compile_definitions(${PROJECT_NAME}-tests PRIVATE -DPLATFORM_LINUX=1) + endif () +endif() + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0ad25db4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 6dd998d4..850a913e --- a/README.md +++ b/README.md @@ -1,10 +1,119 @@ -# BeamMP-Launcher - -The launcher is the way we communitcate to outside the game, it does a few automated actions such as but not limited to: downloading the mod, launching the game, and create a connection to a server. - - -Copyright (c) 2019-present Anonymous275. -BeamMP Launcher code is not in the public domain and is not free software. -One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries, -the only permission that has been granted is to use the software in its compiled form as distributed from the BeamMP.com website. -Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. +# Base Template for C++ based projects +## How to use + +TODO: section on how to initialize with this + +## Name and Version + +In order to specify the project name, executable name and name of the unit tests executable, edit the CMakeLists.txt. +In there, it says: + +```cmake +project( + "MY_PROJECT" # replace this + VERSION 0.1.0 + LANGUAGES CXX +) +``` + +Edit the `MY_PROJECT` to a name that suits your project, and adjust the `VERSION` if necessary. + +Further, go into `vcpkg.json` and edit the two lines shown here, similarly to how you did in the CMakeLists.txt: + +```json +{ + "name": "my-project", + "version-string": "0.1.0", + ... +``` + +Keep in mind the name constraints and requirements for vcpkg. + +## Adding source files + +To add source files, add them to the following section in the CMakeLists.txt: + +```cmake +# add all headers (.h, .hpp) to this +set(PRJ_HEADERS ) +# add all source files (.cpp) to this, except the one with main() +set(PRJ_SOURCES ) +``` + +For example, to add `mysource.cpp`, `somefile.cpp` and `myheader.h`, proceed as follows: + +```cmake +# add all headers (.h, .hpp) to this +set(PRJ_HEADERS mysource.cpp somefile.cpp) +# add all source files (.cpp) to this, except the one with main() +set(PRJ_SOURCES myheader.h) +``` + +## Building + +To build, run cmake (`bin` will be the output directory, `.` the source directory): + +```sh +cmake -S . -B bin +``` + +This will configure it with all default settings. +Then build with: + +```sh +cmake --build bin --parallel +``` + +## Adding dependencies + +First, find your dependency on vcpkg (for example using [this website](https://vcpkg.io/en/packages.html)). For example, you may be looking for lionkor's `commandline` library, and you will find `lionkor-commandline`. + +Second, note down the name, in our example `lionkor-commandline`, and add it to the vcpkg.json, like so: + +Before: +```json +{ + ... + "dependencies": [ + "fmt", + "doctest" + ] +} +``` + +After: +```json +{ + ... + "dependencies": [ + "fmt", + "doctest", + "lionkor-commandline" + ] +} +``` + + +Third, go to CMakeLists.txt and find the following: + +```cmake +# add dependency find_package calls and similar here +find_package(fmt CONFIG REQUIRED) +find_package(doctest CONFIG REQUIRED) +... +``` +and add your `find_package` call or equivalent below it. + +Last, find this section (above the previous): +```cmake +# add all libraries used by the project (WARNING: also set them in vcpkg.json!) +set(PRJ_LIBRARIES + fmt::fmt + ... +) +``` +and add your library there. The exact expression used here differs from library to library, but should be easy to find in their documentation. When in doubt, it's simply the name of the library. + + + + diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 00000000..2d617bb9 --- /dev/null +++ b/cmake/CompilerWarnings.cmake @@ -0,0 +1,111 @@ +# from here: +# +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md +# Courtesy of Jason Turner +# License here: https://github.com/cpp-best-practices/cppbestpractices/blob/master/LICENSE +# +# This version has been modified by the owners of the current respository. +# Modifications have mostly been marked with "modified" or similar, though this is not +# strictly required. + +function(set_project_warnings project_name) + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss + # of data + /w14254 # 'operator': conversion from 'type1:field_bits' to + # 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class + # virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not + # virtual instances of this class may not be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable + # declared in the for-loop is used outside the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing + # an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected + # operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend + # 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may + # cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined + # conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wshadow # warn the user if a variable declaration shadows one from a + # parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a + # non-virtual destructor. This helps catch hard to + # track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual + # function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output + # (ie printf) + # modified; added more errors / warnings + # some have been set to be errors, but the option _WARNINGS_AS_ERRORS + # (see below) should still be used in strict pipelines. + -Werror=uninitialized + -Werror=float-equal + -Werror=write-strings + -Werror=strict-aliasing -fstrict-aliasing + -Werror=missing-declarations + -Werror=ctor-dtor-privacy + -Wunused-result + -Werror=implicit-fallthrough + -Wmissing-include-dirs + ) + + if (${PROJECT_NAME}_WARNINGS_AS_ERRORS) + set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror) + set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX) + endif() + + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks + # do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were + # probably wanted + # -Wuseless-cast # warn if you perform a cast to the same type (modified: removed) + ) + + if(MSVC) + set(PRJ_WARNINGS ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PRJ_WARNINGS ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PRJ_WARNINGS ${GCC_WARNINGS}) + else() + message(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") + endif() + + target_compile_options(${project_name} PUBLIC ${PRJ_WARNINGS}) + + if(NOT TARGET ${project_name}) + message(AUTHOR_WARNING "${project_name} is not a target, thus no compiler warning were added.") + endif() +endfunction() diff --git a/cmake/Git.cmake b/cmake/Git.cmake new file mode 100644 index 00000000..2cc68282 --- /dev/null +++ b/cmake/Git.cmake @@ -0,0 +1,26 @@ +find_package(Git) +if(${PROJECT_NAME}_CHECKOUT_GIT_SUBMODULES) + if(Git_FOUND) + message(STATUS "Git found, submodule update and init") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(SEND_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules. This may result in missing dependencies.") + endif() + else() + message(SEND_ERROR "git required for checking out submodules, but not found. Submodules will not be checked out - this may result in missing dependencies.") + endif() +endif() + +if(Git_FOUND) + execute_process( + COMMAND git log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + OUTPUT_VARIABLE PRJ_GIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else() + message(STATUS "Git not found - the version will not include a git hash.") + set(PRJ_GIT_HASH "unknown") +endif() diff --git a/cmake/StandardSettings.cmake b/cmake/StandardSettings.cmake new file mode 100644 index 00000000..3fb439dc --- /dev/null +++ b/cmake/StandardSettings.cmake @@ -0,0 +1,44 @@ +# Modified, original version from https://github.com/filipdutescu/modern-cpp-template (Unlicense) + +option(${PROJECT_NAME}_WARNINGS_AS_ERRORS "Treat compiler warnings as errors." OFF) +option(${PROJECT_NAME}_CHECKOUT_GIT_SUBMODULES "If git is found, initialize all submodules." ON) +option(${PROJECT_NAME}_ENABLE_UNIT_TESTING "Enable unit tests for the projects (from the `test` subfolder)." ON) +option(${PROJECT_NAME}_ENABLE_CLANG_TIDY "Enable static analysis with Clang-Tidy." OFF) +option(${PROJECT_NAME}_ENABLE_CPPCHECK "Enable static analysis with Cppcheck." OFF) +# TODO Implement code coverage +# option(${PROJECT_NAME}_ENABLE_CODE_COVERAGE "Enable code coverage through GCC." OFF) +option(${PROJECT_NAME}_ENABLE_DOXYGEN "Enable Doxygen documentation builds of source." OFF) + +# Generate compile_commands.json for clang based tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Export all symbols when building a shared library +if(BUILD_SHARED_LIBS) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF) + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +endif() + +option(${PROJECT_NAME}_ENABLE_LTO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)." ON) +if(${PROJECT_NAME}_ENABLE_LTO) + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(SEND_ERROR "IPO is not supported: ${output}.") + endif() +endif() + +option(${PROJECT_NAME}_ENABLE_CCACHE "Enable the usage of Ccache, in order to speed up rebuild times." ON) +find_program(CCACHE_FOUND ccache) +if(CCACHE_FOUND) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) +endif() + +option(${PROJECT_NAME}_ENABLE_SANITIZER "Enable sanitizer to detect memory errors, undefined behavior, etc. (slows down the executable)." OFF) +if(${PROJECT_NAME}_ENABLE_SANITIZER) + add_compile_options(-fsanitize=address,undefined) + add_link_options(-fsanitize=address,undefined) +endif() diff --git a/cmake/StaticAnalyzers.cmake b/cmake/StaticAnalyzers.cmake new file mode 100644 index 00000000..ed149f80 --- /dev/null +++ b/cmake/StaticAnalyzers.cmake @@ -0,0 +1,20 @@ +if(${PROJECT_NAME}_ENABLE_CLANG_TIDY) + find_program(CLANGTIDY clang-tidy) + if(CLANGTIDY) + set(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY} -extra-arg=-Wno-unknown-warning-option) + message("Clang-Tidy finished setting up.") + else() + message(SEND_ERROR "Clang-Tidy requested but executable not found.") + endif() +endif() + +if(${PROJECT_NAME}_ENABLE_CPPCHECK) + find_program(CPPCHECK cppcheck) + if(CPPCHECK) + set(CMAKE_CXX_CPPCHECK ${CPPCHECK} --suppress=missingInclude --enable=all + --inline-suppr --inconclusive) + message("Cppcheck finished setting up.") + else() + message(SEND_ERROR "Cppcheck requested but executable not found.") + endif() +endif() diff --git a/cmake/Vcpkg.cmake b/cmake/Vcpkg.cmake new file mode 100644 index 00000000..0b8a724d --- /dev/null +++ b/cmake/Vcpkg.cmake @@ -0,0 +1,18 @@ +if(NOT CMAKE_TOOLCHAIN_FILE) + if(NOT EXISTS ${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake) + find_package(Git) + if(Git_FOUND) + # shallow-clone vcpkg (we only need the latest revision) + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --depth=1 --init --recursive vcpkg + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(SEND_ERROR "Checking out vcpkg in source tree failed with ${GIT_SUBMOD_RESULT}.") + endif() + else() + message(FATAL_ERROR "Could not find git or vcpkg.cmake. Please either, install git and re-run cmake (or run `git submodule update --init --recursive`), or install vcpkg and add `-DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake` to your cmake invocation. Please try again after making those changes.") + endif() + endif() + set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake) +endif() + diff --git a/deps/BeamMP-Protocol b/deps/BeamMP-Protocol new file mode 160000 index 00000000..74b4eee8 --- /dev/null +++ b/deps/BeamMP-Protocol @@ -0,0 +1 @@ +Subproject commit 74b4eee8fe2a2ca5b955347eaa2b54248bcbb456 diff --git a/evpp b/evpp deleted file mode 160000 index 44e10774..00000000 --- a/evpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 44e10774e20598dbdd06da8718db1b97c37e3d87 diff --git a/include/Discord/discord_info.h b/include/Discord/discord_info.h deleted file mode 100755 index f099edf1..00000000 --- a/include/Discord/discord_info.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/16/2020 -/// -#pragma once -#include -void Discord_Main(); -std::string GetDName(); -std::string GetDTag(); -std::string GetDID(); -void DAboard(); - diff --git a/include/Discord/discord_register.h b/include/Discord/discord_register.h deleted file mode 100644 index 16fb42f3..00000000 --- a/include/Discord/discord_register.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#if defined(DISCORD_DYNAMIC_LIB) -#if defined(_WIN32) -#if defined(DISCORD_BUILDING_SDK) -#define DISCORD_EXPORT __declspec(dllexport) -#else -#define DISCORD_EXPORT __declspec(dllimport) -#endif -#else -#define DISCORD_EXPORT __attribute__((visibility("default"))) -#endif -#else -#define DISCORD_EXPORT -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command); -DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId); - -#ifdef __cplusplus -} -#endif diff --git a/include/Discord/discord_rpc.h b/include/Discord/discord_rpc.h deleted file mode 100644 index 8609bf7d..00000000 --- a/include/Discord/discord_rpc.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once -#include - -// clang-format off - -#if defined(DISCORD_DYNAMIC_LIB) -# if defined(_WIN32) -# if defined(DISCORD_BUILDING_SDK) -# define DISCORD_EXPORT __declspec(dllexport) -# else -# define DISCORD_EXPORT __declspec(dllimport) -# endif -# else -# define DISCORD_EXPORT __attribute__((visibility("default"))) -# endif -#else -# define DISCORD_EXPORT -#endif - -// clang-format on - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct DiscordRichPresence { - const char* state; /* max 128 bytes */ - const char* details; /* max 128 bytes */ - int64_t startTimestamp; - int64_t endTimestamp; - const char* largeImageKey; /* max 32 bytes */ - const char* largeImageText; /* max 128 bytes */ - const char* smallImageKey; /* max 32 bytes */ - const char* smallImageText; /* max 128 bytes */ - const char* partyId; /* max 128 bytes */ - int partySize; - int partyMax; - const char* matchSecret; /* max 128 bytes */ - const char* joinSecret; /* max 128 bytes */ - const char* spectateSecret; /* max 128 bytes */ - int8_t instance; -} DiscordRichPresence; - -typedef struct DiscordUser { - const char* userId; - const char* username; - const char* discriminator; - const char* avatar; -} DiscordUser; - -typedef struct DiscordEventHandlers { - void (*ready)(const DiscordUser* request); - void (*disconnected)(int errorCode, const char* message); - void (*errored)(int errorCode, const char* message); - void (*joinGame)(const char* joinSecret); - void (*spectateGame)(const char* spectateSecret); - void (*joinRequest)(const DiscordUser* request); -} DiscordEventHandlers; - -#define DISCORD_REPLY_NO 0 -#define DISCORD_REPLY_YES 1 -#define DISCORD_REPLY_IGNORE 2 - -DISCORD_EXPORT void Discord_Initialize(const char* applicationId, - DiscordEventHandlers* handlers, - int autoRegister, - const char* optionalSteamId); -DISCORD_EXPORT void Discord_Shutdown(void); - -/* checks for incoming messages, dispatches callbacks */ -DISCORD_EXPORT void Discord_RunCallbacks(void); - -/* If you disable the lib starting its own io thread, you'll need to call this from your own */ -#ifdef DISCORD_DISABLE_IO_THREAD -DISCORD_EXPORT void Discord_UpdateConnection(void); -#endif - -DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence); -DISCORD_EXPORT void Discord_ClearPresence(void); - -DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); - -DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers); - -#ifdef __cplusplus -} /* extern "C" */ -#endif diff --git a/include/Logger.h b/include/Logger.h deleted file mode 100755 index 2d2ae11a..00000000 --- a/include/Logger.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 4/2/2020. -/// -#pragma once -#include -#include -void InitLog(); -void except(const std::string& toPrint); -void fatal(const std::string& toPrint); -void debug(const std::string& toPrint); -void error(const std::string& toPrint); -void info(const std::string& toPrint); -void warn(const std::string& toPrint); diff --git a/include/Network/network.h b/include/Network/network.h deleted file mode 100755 index 4c3bdf63..00000000 --- a/include/Network/network.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/18/2020 -/// - - -#pragma once -#include -void NetReset(); -extern bool Dev; -extern int ping; - -[[noreturn]] void CoreNetwork(); -extern int ProxyPort; -extern int ClientID; -extern int LastPort; -extern bool ModLoaded; -extern bool Terminate; -extern int DEFAULT_PORT; -extern uint64_t UDPSock; -extern uint64_t TCPSock; -extern std::string Branch; -extern bool TCPTerminate; -extern std::string LastIP; -extern std::string MStatus; -extern std::string UlStatus; -extern std::string PublicKey; -extern std::string PrivateKey; -extern std::string ListOfMods; -int KillSocket(uint64_t Dead); -void UUl(const std::string& R); -void UDPSend(std::string Data); -bool CheckBytes(int32_t Bytes); -void GameSend(std::string Data); -void SendLarge(std::string Data); -std::string TCPRcv(uint64_t Sock); -void SyncResources(uint64_t TCPSock); -std::string GetAddr(const std::string&IP); -void ServerParser(const std::string& Data); -std::string Login(const std::string& fields); -void TCPSend(const std::string&Data,uint64_t Sock); -void TCPClientMain(const std::string& IP,int Port); -void UDPClientMain(const std::string& IP,int Port); -void TCPGameServer(const std::string& IP, int Port); - diff --git a/include/Security/Game.h b/include/Security/Game.h deleted file mode 100755 index 77d1ffc7..00000000 --- a/include/Security/Game.h +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/19/2020 -/// -#pragma once -extern unsigned long GamePID; diff --git a/include/Security/Init.h b/include/Security/Init.h deleted file mode 100755 index c6f3db70..00000000 --- a/include/Security/Init.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/18/2020 -/// -#pragma once -#include -void PreGame(const std::string& GamePath); -std::string CheckVer(const std::string &path); -void InitGame(const std::string& Dir); -std::string GetGameDir(); -void LegitimacyCheck(); -void CheckLocalKey(); \ No newline at end of file diff --git a/include/Startup.h b/include/Startup.h deleted file mode 100755 index 74871463..00000000 --- a/include/Startup.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/18/2020 -/// -#pragma once -#include -#include -#include - -void InitLauncher(int argc, char* argv[]); -std::string GetEP(char*P = nullptr); -std::string GetGamePath(); -std::string GetVer(); -std::string GetEN(); -void StartProxy(); -void ConfigInit(); -extern bool Dev; - -struct VersionParser { - explicit VersionParser(const std::string& from_string); - std::strong_ordering operator<=>(VersionParser const& rhs) const noexcept; - bool operator==(VersionParser const& rhs) const noexcept; - std::vector split; - std::vector data; -}; diff --git a/include/Zlib/Compressor.h b/include/Zlib/Compressor.h deleted file mode 100755 index 0a1d8155..00000000 --- a/include/Zlib/Compressor.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/24/2020 -/// -#pragma once -#include -std::string Comp(std::string Data); -std::string DeComp(std::string Compressed); diff --git a/include/hashpp.h b/include/hashpp.h deleted file mode 100644 index 08b5eb8b..00000000 --- a/include/hashpp.h +++ /dev/null @@ -1,4642 +0,0 @@ -#if !defined(HASHPP_H) -#define HASHPP_H - -/* - - Copyright (c) 2012-2021 Johnny (pseud. Dread) - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - ---------------------------------------------------------------------- - - hash++ : header-only hash implementations in C++ - This header file contains implementations of Message Digest - and Secure Hash Algorithm family hash algorithms for others - to view internal mechanisms of said algorithms, as well as - for substitution in sources absent the need for heavier - crypto-related libraries such as OpenSSL or Crypto++. - -*/ - -#define GU64B(x, y, z) do { \ - (x) = ( ((uint64_t) (y)[(z)]) << 56 ) \ - | ( ((uint64_t) (y)[(z) + 1]) << 48 ) \ - | ( ((uint64_t) (y)[(z) + 2]) << 40 ) \ - | ( ((uint64_t) (y)[(z) + 3]) << 32 ) \ - | ( ((uint64_t) (y)[(z) + 4]) << 24 ) \ - | ( ((uint64_t) (y)[(z) + 5]) << 16 ) \ - | ( ((uint64_t) (y)[(z) + 6]) << 8 ) \ - | ( ((uint64_t) (y)[(z) + 7]) ); \ -} while(0) \ - -#define PU64B(x, y, z) do { \ - (y)[(z) ] = (uint8_t) ( (x) >> 56 ); \ - (y)[(z) + 1] = (uint8_t) ( (x) >> 48 ); \ - (y)[(z) + 2] = (uint8_t) ( (x) >> 40 ); \ - (y)[(z) + 3] = (uint8_t) ( (x) >> 32 ); \ - (y)[(z) + 4] = (uint8_t) ( (x) >> 24 ); \ - (y)[(z) + 5] = (uint8_t) ( (x) >> 16 ); \ - (y)[(z) + 6] = (uint8_t) ( (x) >> 8 ); \ - (y)[(z) + 7] = (uint8_t) ( (x) ); \ -} while(0) \ - -#define PU128B(l,h,y,z) do { \ - PU64B(((l) >> 61) ^ ((h) << 3), (y), (z)); \ - PU64B(((l) << 3), (y), (z) + 8); \ -} while(0) \ - -#include -#include -#include -#include -#if defined(HASHPP_INCLUDE_METRICS) -#include -#endif - -namespace hashpp { - enum class ALGORITHMS : uint8_t { - // MDX Family - MD5, MD4, MD2, - - // SHA-X Family - SHA1, SHA2_224, SHA2_256, - SHA2_384, SHA2_512, SHA2_512_224, - SHA2_512_256 /*, SHA3_224, SHA3_256, - SHA3_384, SHA3_512, SHAKE128, - SHAKE256 */ - }; - - // class containing common data and methods to be - // derived from by algorithm classes for common use - // internally - class common { - public: - // helper functions to rotate left - constexpr uint32_t rl32(uint32_t x, uint32_t y) noexcept { - return (x << y) | (x >> (32 - y)); - } - constexpr uint64_t rl64(uint64_t x, uint64_t y) noexcept { - return (x << y) | (x >> (64 - y)); - } - - // helper functions to rotate right - constexpr uint32_t rr32(uint32_t x, uint32_t y) noexcept { - return (x >> y) | (x << (32 - y)); - } - constexpr uint64_t rr64(uint64_t x, uint64_t y) noexcept { - return (x >> y) | (x << (64 - y)); - } - - // hex table for converting bytes to representable - // hexadecimal strings for output via getHash - const char* hexTable[256] = { - "00", "01", "02", "03", "04", "05", "06", "07", - "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", - "10", "11", "12", "13", "14", "15", "16", "17", - "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", - "20", "21", "22", "23", "24", "25", "26", "27", - "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", - "30", "31", "32", "33", "34", "35", "36", "37", - "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", - "40", "41", "42", "43", "44", "45", "46", "47", - "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", - "50", "51", "52", "53", "54", "55", "56", "57", - "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", - "60", "61", "62", "63", "64", "65", "66", "67", - "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", - "70", "71", "72", "73", "74", "75", "76", "77", - "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", - "80", "81", "82", "83", "84", "85", "86", "87", - "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", - "90", "91", "92", "93", "94", "95", "96", "97", - "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", - "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", - "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", - "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", - "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", - "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", - "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", - "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", - "d8", "d9", "da", "db", "dc", "dd", "de", "df", - "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", - "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", - "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", - "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" - }; - - // get hexadecimal hash from data - std::string getHash(const std::string& data) { - this->ctx_init(); - this->ctx_update(reinterpret_cast(const_cast(data.c_str())), data.length()); - this->ctx_final(); - - return this->bytesToHexString(); - } - - // get hexadecimal hash from file - std::string getHash(const std::filesystem::path& path) { - std::ifstream file(path, std::ios::binary); - std::vector buf(1024 * 1024, 0); - - this->ctx_init(); - while (file) { - file.read(buf.data(), buf.size()); - this->ctx_update(reinterpret_cast(buf.data()), file.gcount()); - } - this->ctx_final(); - - return this->bytesToHexString(); - } - - // get hexadecimal HMAC from key-data pair - std::string getHMAC(const std::string& key, const std::string& data) { - return this->HMAC(key, data); - } - - protected: - // virtual functions to be overridden by each algorithm implementation. - virtual std::vector getBytes() = 0; - virtual void ctx_init() = 0; - virtual void ctx_update(const uint8_t*, size_t) = 0; - virtual void ctx_final() = 0; - - // H as defined in https://www.rfc-editor.org/rfc/rfc2104 for - // Keyed-Hashing for Message Authentication - virtual std::string _H(const std::string&, const std::string&) = 0; - virtual std::string HMAC(const std::string&, const std::string&) = 0; - - // Helper function to convert hex string to bytes and return them in a vector - std::vector fromHex(const std::string& hex) { - std::vector bytes; - for (size_t i = 0; i < hex.length(); i += 2) { - std::string byteString = hex.substr(i, 2); - uint8_t byte = static_cast(strtol(byteString.c_str(), NULL, 16)); - bytes.push_back(byte); - } - return bytes; - } - - private: - std::string bytesToHexString() { - const std::vector digest = this->getBytes(); - std::string hash; - - for (const auto& d : digest) { - hash += this->hexTable[d]; - } - - return hash; - } - }; - - // Message Digest (MDX) hash family - excluding MD6 - namespace MD { - class MD5 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 16); - } - - // private members - private: - const uint8_t BLOCK_SIZE = 64, DIGEST_SIZE = 16; - - typedef struct { - uint64_t size; - uint32_t buf[4]; - uint8_t in[64], digest[16]; - } CTX; - - // CTX context instance - CTX context = { 0 }; - - // per-round shift amounts - // as per: https://en.wikipedia.org/wiki/MD5#Pseudocode - const uint8_t S[64] = { - 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 - }; - - // as per: https://en.wikipedia.org/wiki/MD5#Pseudocode - const uint32_t K[64] = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 - }; - - // pad data for when we need to... well.. pad to appropriate size - uint8_t pad[64] = { - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - // MD5 algorithm-defined constants - // as per: https://en.wikipedia.org/wiki/MD5#Pseudocode - const uint32_t A = 0x67452301; - const uint32_t B = 0xefcdab89; - const uint32_t C = 0x98badcfe; - const uint32_t D = 0x10325476; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - // private class methods - private: - // initialize our context for this hash function - inline void ctx_init() override; - inline void ctx_transform(const uint32_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - // auxiliary functions defined by the algorithm - // as per: https://en.wikipedia.org/wiki/MD5#Algorithm - constexpr uint32_t F(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t G(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t H(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t I(const uint32_t B, const uint32_t C, const uint32_t D); - }; - class MD4 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 16); - } - - // private members - private: - const uint8_t BLOCK_SIZE = 64, DIGEST_SIZE = 16; - - typedef struct { - uint64_t size; - uint32_t buf[4]; - uint8_t in[64], digest[16]; - } CTX; - - // CTX context instance - CTX context = { 0 }; - - const uint8_t S[64] = { - 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 - }; - - const uint32_t K[64] = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 - }; - - // pad data for when we need to... well.. pad to appropriate size - uint8_t pad[64] = { - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - // algorithm-defined constants - const uint32_t A = 0x67452301; - const uint32_t B = 0xefcdab89; - const uint32_t C = 0x98badcfe; - const uint32_t D = 0x10325476; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - // private class methods - private: - // initialize our context for this hash function - inline void ctx_init() override; - inline void ctx_transform(const uint32_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - // auxiliary functions defined by the algorithm - // as per: http://practicalcryptography.com/hashes/md4-hash/ - constexpr uint32_t F(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t G(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t H(const uint32_t B, const uint32_t C, const uint32_t D); - - // round functions - constexpr void R1(uint32_t& a, const uint32_t b, const uint32_t c, const uint32_t d, const uint32_t k, const uint32_t s); - constexpr void R2(uint32_t& a, const uint32_t b, const uint32_t c, const uint32_t d, const uint32_t k, const uint32_t s); - constexpr void R3(uint32_t& a, const uint32_t b, const uint32_t c, const uint32_t d, const uint32_t k, const uint32_t s); - }; - class MD2 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 16); - } - - // private members - private: - const uint8_t BLOCK_SIZE = 16, DIGEST_SIZE = 16; - - typedef struct { - uint8_t buf[16], state[48], checksum[16], digest[16]; - uint64_t size; - } CTX; - - CTX context = { 0 }; - - // S-table values for MD2 algorithm - // as per: https://en.wikipedia.org/wiki/MD2_(hash_function)#Description - const uint8_t S[256] = { - 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, - 0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA, - 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, - 0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A, - 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21, - 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, - 0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6, - 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, - 0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02, - 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F, - 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, - 0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52, - 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, - 0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39, - 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A, - 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14 - }; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - inline void ctx_init() override; - inline void ctx_transform(const uint8_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - }; - - // MD5 - inline void hashpp::MD::MD5::ctx_init() { - this->context = { - 0, - {this->A, this->B, this->C, this->D} - }; - } - inline void hashpp::MD::MD5::ctx_transform(const uint32_t* data) { - uint32_t results[4] = { - this->context.buf[0], // a0 - this->context.buf[1], // b0 - this->context.buf[2], // c0 - this->context.buf[3] // d0 - }; - - uint32_t E, j, t; - for (uint32_t i = 0; i < 64; ++i) { - switch (i / 16) { - case 0: - { - E = this->F(results[1], results[2], results[3]); - j = i; - break; - } - case 1: - { - E = this->G(results[1], results[2], results[3]); - j = ((i * 5) + 1) % 16; - break; - } - case 2: - { - E = this->H(results[1], results[2], results[3]); - j = ((i * 3) + 5) % 16; - break; - } - default: - { - E = this->I(results[1], results[2], results[3]); - j = (i * 7) % 16; - break; - } - } - - t = results[3]; - results[3] = results[2]; results[2] = results[1]; - results[1] = results[1] + this->rl32(results[0] + E + K[i] + data[j], S[i]); - results[0] = t; - } - - for (uint32_t z = 0; z < 4; z++) { - this->context.buf[z] += results[z]; - } - } - inline void hashpp::MD::MD5::ctx_update(const uint8_t* data, size_t len) { - uint32_t input[16], offset = this->context.size % 64; - this->context.size += static_cast(len); - - for (uint32_t i = 0; i < len; ++i) { - this->context.in[offset++] = static_cast(*(data + i)); - - if (offset % 64 == 0) { - for (uint32_t j = 0; j < 16; ++j) { - input[j] = static_cast(this->context.in[(j * 4) + 3]) << 24 | - static_cast(this->context.in[(j * 4) + 2]) << 16 | - static_cast(this->context.in[(j * 4) + 1]) << 8 | - static_cast(this->context.in[(j * 4)]); - } - this->ctx_transform(input); - offset = 0; - } - } - } - inline void hashpp::MD::MD5::ctx_final() { - uint32_t input[16]; - uint32_t offset = this->context.size % 64; - uint32_t plen = offset < 56 ? 56 - offset : (56 + 64) - offset; - - this->ctx_update(this->pad, plen); - this->context.size -= static_cast(plen); - - for (uint32_t j = 0; j < 14; ++j) { - input[j] = static_cast(this->context.in[(j * 4) + 3]) << 24 | - static_cast(this->context.in[(j * 4) + 2]) << 16 | - static_cast(this->context.in[(j * 4) + 1]) << 8 | - static_cast(this->context.in[(j * 4)]); - } - input[14] = static_cast(this->context.size * 8); - input[15] = static_cast((this->context.size * 8) >> 32); - - this->ctx_transform(input); - - for (uint32_t i = 0; i < 4; ++i) { - this->context.digest[(i * 4) + 0] = static_cast((this->context.buf[i] & 0x000000FF)); - this->context.digest[(i * 4) + 1] = static_cast((this->context.buf[i] & 0x0000FF00) >> 8); - this->context.digest[(i * 4) + 2] = static_cast((this->context.buf[i] & 0x00FF0000) >> 16); - this->context.digest[(i * 4) + 3] = static_cast((this->context.buf[i] & 0xFF000000) >> 24); - } - } - inline std::string MD5::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string MD5::HMAC(const std::string& key, const std::string& data) { - uint8_t k[64] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint32_t hashpp::MD::MD5::F(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) | (~B & D)); } - constexpr uint32_t hashpp::MD::MD5::G(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & D) | (C & ~D)); } - constexpr uint32_t hashpp::MD::MD5::H(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B ^ C ^ D)); } - constexpr uint32_t hashpp::MD::MD5::I(const uint32_t B, const uint32_t C, const uint32_t D) { return (C ^ (B | ~D)); } - - // MD4 - inline void hashpp::MD::MD4::ctx_init() { - this->context = { - 0, - {this->A, this->B, this->C, this->D} - }; - } - inline void hashpp::MD::MD4::ctx_transform(const uint32_t* data) { - uint32_t results[4] { - this->context.buf[0], // a0 - this->context.buf[1], // b0 - this->context.buf[2], // c0 - this->context.buf[3] // d0 - }; - - // perform rounds as described by the algorithm - // as per: http://practicalcryptography.com/hashes/md4-hash/ - for (uint32_t i = 0, j = 0; i < 4; ++i, j += 4) { - this->R1(results[0], results[1], results[2], results[3], data[j], 3); - this->R1(results[3], results[0], results[1], results[2], data[j + 1], 7); - this->R1(results[2], results[3], results[0], results[1], data[j + 2], 11); - this->R1(results[1], results[2], results[3], results[0], data[j + 3], 19); - } - - for (uint32_t i = 0; i < 4; ++i) { - this->R2(results[0], results[1], results[2], results[3], data[0 + i], 3); - this->R2(results[3], results[0], results[1], results[2], data[4 + i], 5); - this->R2(results[2], results[3], results[0], results[1], data[8 + i], 9); - this->R2(results[1], results[2], results[3], results[0], data[12 + i], 13); - } - - for (uint32_t i = 0, _h = 0; i < 4; ++i) { - i == 1 ? _h = 2 : 0; i == 2 ? _h = 1 : 0; i == 3 ? _h = 3 : 0; - this->R3(results[0], results[1], results[2], results[3], data[0 + _h], 3); - this->R3(results[3], results[0], results[1], results[2], data[8 + _h], 9); - this->R3(results[2], results[3], results[0], results[1], data[4 + _h], 11); - this->R3(results[1], results[2], results[3], results[0], data[12 + _h], 15); - } - - for (uint32_t z = 0; z < 4; z++) { - this->context.buf[z] += results[z]; - } - } - inline void hashpp::MD::MD4::ctx_update(const uint8_t* data, size_t len) { - uint32_t input[16], offset = this->context.size % 64; - this->context.size += static_cast(len); - - for (uint32_t i = 0; i < len; ++i) { - this->context.in[offset++] = static_cast(*(data + i)); - - if (offset % 64 == 0) { - for (uint32_t j = 0; j < 16; ++j) { - input[j] = static_cast(this->context.in[(j * 4) + 3]) << 24 | - static_cast(this->context.in[(j * 4) + 2]) << 16 | - static_cast(this->context.in[(j * 4) + 1]) << 8 | - static_cast(this->context.in[(j * 4)]); - } - this->ctx_transform(input); - offset = 0; - } - } - } - inline void hashpp::MD::MD4::ctx_final() { - uint32_t input[16]; - uint32_t offset = this->context.size % 64, plen = offset < 56 ? 56 - offset : (56 + 64) - offset; - - this->ctx_update(this->pad, plen); - this->context.size -= static_cast(plen); - - for (uint32_t j = 0; j < 14; ++j) { - input[j] = static_cast(this->context.in[(j * 4) + 3]) << 24 | - static_cast(this->context.in[(j * 4) + 2]) << 16 | - static_cast(this->context.in[(j * 4) + 1]) << 8 | - static_cast(this->context.in[(j * 4)]); - } - input[14] = static_cast(this->context.size * 8); - input[15] = static_cast((this->context.size * 8) >> 32); - - this->ctx_transform(input); - - // move to digest as big-endian - // as per: https://stackoverflow.com/questions/19275955/convert-little-endian-to-big-endian/19276193 - for (uint32_t i = 0; i < 4; ++i) { - this->context.digest[(i * 4) + 0] = static_cast((this->context.buf[i] & 0x000000FF)); - this->context.digest[(i * 4) + 1] = static_cast((this->context.buf[i] & 0x0000FF00) >> 8); - this->context.digest[(i * 4) + 2] = static_cast((this->context.buf[i] & 0x00FF0000) >> 16); - this->context.digest[(i * 4) + 3] = static_cast((this->context.buf[i] & 0xFF000000) >> 24); - } - } - inline std::string MD4::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string MD4::HMAC(const std::string& key, const std::string& data) { - uint8_t k[64] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint32_t hashpp::MD::MD4::F(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) | (~B & D)); } - constexpr uint32_t hashpp::MD::MD4::G(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) | (B & D) | (C & D)); } - constexpr uint32_t hashpp::MD::MD4::H(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B ^ C ^ D)); } - constexpr void hashpp::MD::MD4::R1(uint32_t& a, const uint32_t b, const uint32_t c, const uint32_t d, const uint32_t k, const uint32_t s) { - a = this->rl32(a + this->F(b, c, d) + k, s); - } - constexpr void hashpp::MD::MD4::R2(uint32_t& a, const uint32_t b, const uint32_t c, const uint32_t d, const uint32_t k, const uint32_t s) { - a = this->rl32(a + this->G(b, c, d) + k + static_cast(0x5A827999), s); - } - constexpr void hashpp::MD::MD4::R3(uint32_t& a, const uint32_t b, const uint32_t c, const uint32_t d, const uint32_t k, const uint32_t s) { - a = this->rl32(a + this->H(b, c, d) + k + static_cast(0x6ED9EBA1), s); - } - - // MD2 - inline void hashpp::MD::MD2::ctx_init() { - memset(this->context.state, 0, 48); - memset(this->context.checksum, 0, 16); - memset(this->context.buf, 0, 16); - memset(this->context.digest, 0, 16); - this->context.size = 0; - } - inline void hashpp::MD::MD2::ctx_transform(const uint8_t* data) { - uint32_t j, k, t; - - for (j = 0; j < 16; ++j) { - this->context.state[j + 16] = data[j]; - this->context.state[j + 32] = (this->context.state[j + 16] ^ this->context.state[j]); - } - - t = 0; - for (j = 0; j < 18; ++j) { - for (k = 0; k < 48; ++k) { - this->context.state[k] ^= this->S[t]; - t = this->context.state[k]; - } - t = (t + j) & 0xFF; - } - - t = this->context.checksum[15]; - for (j = 0; j < 16; ++j) { - this->context.checksum[j] ^= this->S[data[j] ^ t]; - t = this->context.checksum[j]; - } - } - inline void hashpp::MD::MD2::ctx_update(const uint8_t* data, size_t len) { - for (uint16_t i = 0; i < len; ++i) { - this->context.buf[this->context.size] = data[i]; - this->context.size++; - if (this->context.size == 16) { - ctx_transform(this->context.buf); - this->context.size = 0; - } - } - } - inline void hashpp::MD::MD2::ctx_final() { - uint32_t pad = 16 - this->context.size; - while (this->context.size < 16) { - this->context.buf[this->context.size++] = pad; - } - ctx_transform(this->context.buf); - ctx_transform(this->context.checksum); - memcpy(this->context.digest, this->context.state, 16); - } - inline std::string MD2::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string MD2::HMAC(const std::string& key, const std::string& data) { - uint8_t k[16] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - } - - // Secure Hash Algorithm (SHA) hash family - namespace SHA { - // SHA algorithms, such as SHA2-224, SHA2-384, - // SHA2-512-224, and SHA2-512-256 are simply - // truncated versions of their accompanying - // algorithm. - // - // for instance, SHA2-512-256 is SHA-512 - // truncated to a 256-bit digest length. - // likewise, SHA2-224 is SHA2-256 truncated - // to a 224-bit digest length. - // - // these truncations are achieved via the - // omission of trailing H-constants from - // the digest output, as well as different - // H-constant values from the orignal - // algorithm. - - class SHA1 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 20); - } - - private: - const uint8_t BLOCK_SIZE = 64, DIGEST_SIZE = 20; - - typedef struct { - uint32_t state[5], k[4], size; - uint64_t bitsize; - uint8_t data[64], digest[20]; - } CTX; - - CTX context = { 0 }; - - // constants (H) defined by SHA-1 algorithm - // as per: https://datatracker.ietf.org/doc/html/rfc3174 - const uint32_t H[5] = { - 0x67452301, - 0xEFCDAB89, - 0x98BADCFE, - 0x10325476, - 0xc3d2e1f0 - }; - - // more constants (K)... as per above - const uint32_t K[4] = { - 0x5a827999, - 0x6ed9eba1, - 0x8f1bbcdc, - 0xca62c1d6 - }; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - inline void ctx_init() override; - inline void ctx_transform(const uint8_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - // SHA-1 functions defined by the algorithm - constexpr uint32_t A(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t B(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t C(const uint32_t A); - - // ...and more defined functions... - constexpr uint32_t F(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t G(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t J(const uint32_t B, const uint32_t C, const uint32_t D); - }; - class SHA2_224 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 28); - } - - private: - const uint8_t BLOCK_SIZE = 64, DIGEST_SIZE = 28; - - typedef struct { - uint32_t state[8]; - uint64_t size, bitsize; - uint8_t data[64], digest[28]; - } CTX; - - CTX context = { 0 }; - - // constants (H) defined by SHA2-224 algorithm - // as per: https://datatracker.ietf.org/doc/html/rfc3874 - const uint32_t H[8] = { - 0xC1059ED8, - 0x367CD507, - 0x3070DD17, - 0xF70E5939, - 0xFFC00B31, - 0x68581511, - 0x64F98FA7, - 0xBEFA4FA4 - }; - - // more constants (K)... as per above - const uint32_t K[64] = { - 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, - 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, - 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, - 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, - 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, - 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, - 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, - 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, - 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, - 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, - 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, - 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, - 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, - 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, - 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, - 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 - }; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - inline void ctx_init() override; - inline void ctx_transform(const uint8_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - constexpr uint32_t A(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t F(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t G(const uint32_t B, const uint32_t C, const uint32_t D); - - // Sigma functions - // as per: https://datatracker.ietf.org/doc/html/rfc6234 - constexpr uint32_t SIGMA0(const uint32_t A); - constexpr uint32_t SIGMA1(const uint32_t A); - constexpr uint32_t SIGMA2(const uint32_t A); - constexpr uint32_t SIGMA3(const uint32_t A); - }; - class SHA2_256 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 32); - } - - private: - const uint8_t BLOCK_SIZE = 64, DIGEST_SIZE = 32; - - typedef struct { - uint32_t state[8], size; - uint64_t bitsize; - uint8_t data[64], digest[32]; - } CTX; - - CTX context = { 0 }; - - // constants (H) defined by SHA-256 algorithm - // as per: https://datatracker.ietf.org/doc/html/rfc6234 - const uint32_t H[8] = { - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19 - }; - - // more constants (K)... as per above - const uint32_t K[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - }; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - inline void ctx_init() override; - inline void ctx_transform(const uint8_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - constexpr uint32_t A(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t F(const uint32_t B, const uint32_t C, const uint32_t D); - constexpr uint32_t G(const uint32_t B, const uint32_t C, const uint32_t D); - - // Sigma functions - // as per: https://datatracker.ietf.org/doc/html/rfc6234 - constexpr uint32_t SIGMA0(const uint32_t A); - constexpr uint32_t SIGMA1(const uint32_t A); - constexpr uint32_t SIGMA2(const uint32_t A); - constexpr uint32_t SIGMA3(const uint32_t A); - }; - class SHA2_384 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 48); - } - - private: - const uint8_t BLOCK_SIZE = 128, DIGEST_SIZE = 48; - - typedef struct { - uint64_t state[8], count[2]; - uint8_t data[128], digest[48]; - } CTX; - - CTX context = { 0 }; - - // constants (H) defined by SHA-512 algorithm - // as per: https://datatracker.ietf.org/doc/html/rfc4634 - const uint64_t H[8] = { - 0xCBBB9D5DC1059ED8, - 0x629A292A367CD507, - 0x9159015A3070DD17, - 0x152FECD8F70E5939, - 0x67332667FFC00B31, - 0x8EB44A8768581511, - 0xDB0C2E0D64F98FA7, - 0x47B5481DBEFA4FA4 - }; - - // more constants (K)... as per above - const uint64_t K[80] = { - 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, - 0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, - 0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, - 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694, - 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, - 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, - 0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, - 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70, - 0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, - 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B, - 0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30, - 0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8, - 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, - 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, - 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC, - 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, - 0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, - 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B, - 0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, - 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817 - }; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - inline void ctx_init() override; - inline void ctx_transform(const uint8_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - constexpr uint64_t F(const uint64_t A, const uint64_t B, const uint64_t C); - constexpr uint64_t G(const uint64_t A, const uint64_t B, const uint64_t C); - - // Sigma functions - // as per: https://datatracker.ietf.org/doc/html/rfc4634 - constexpr uint64_t SIGMA0(const uint64_t A); - constexpr uint64_t SIGMA1(const uint64_t A); - constexpr uint64_t SIGMA2(const uint64_t A); - constexpr uint64_t SIGMA3(const uint64_t A); - }; - class SHA2_512 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 64); - } - - private: - const uint8_t BLOCK_SIZE = 128, DIGEST_SIZE = 64; - - typedef struct { - uint64_t state[8], count[2]; - uint8_t data[128], digest[64]; - } CTX; - - CTX context = { 0 }; - - // constants (H) defined by SHA-512 algorithm - // as per: https://datatracker.ietf.org/doc/html/rfc4634 - const uint64_t H[8] = { - 0x6A09E667F3BCC908, - 0xBB67AE8584CAA73B, - 0x3C6EF372FE94F82B, - 0xA54FF53A5F1D36F1, - 0x510E527FADE682D1, - 0x9B05688C2B3E6C1F, - 0x1F83D9ABFB41BD6B, - 0x5BE0CD19137E2179 - }; - - // more constants (K)... as per above - const uint64_t K[80] = { - 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, - 0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, - 0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, - 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694, - 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, - 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, - 0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, - 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70, - 0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, - 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B, - 0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30, - 0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8, - 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, - 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, - 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC, - 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, - 0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, - 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B, - 0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, - 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817 - }; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - inline void ctx_init() override; - inline void ctx_transform(const uint8_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - constexpr uint64_t F(const uint64_t A, const uint64_t B, const uint64_t C); - constexpr uint64_t G(const uint64_t A, const uint64_t B, const uint64_t C); - - // Sigma functions - // as per: https://datatracker.ietf.org/doc/html/rfc4634 - constexpr uint64_t SIGMA0(const uint64_t A); - constexpr uint64_t SIGMA1(const uint64_t A); - constexpr uint64_t SIGMA2(const uint64_t A); - constexpr uint64_t SIGMA3(const uint64_t A); - }; - class SHA2_512_224 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 28); - } - - private: - const uint8_t BLOCK_SIZE = 128, DIGEST_SIZE = 28; - - typedef struct { - uint64_t state[8], count[2]; - uint8_t data[128], digest[32]; - } CTX; - - CTX context = { 0 }; - - // constants (H) defined by SHA-512/224 algorithm - // as per: https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512_224.pdf - const uint64_t H[8] = { - 0x8C3D37C819544DA2, - 0x73E1996689DCD4D6, - 0x1DFAB7AE32FF9C82, - 0x679DD514582F9FCF, - 0x0F6D2B697BD44DA8, - 0x77E36F7304C48942, - 0x3F9D85A86A1D36C8, - 0x1112E6AD91D692A1, - }; - - // more constants (K)... as per above - const uint64_t K[80] = { - 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, - 0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, - 0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, - 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694, - 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, - 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, - 0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, - 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70, - 0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, - 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B, - 0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30, - 0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8, - 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, - 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, - 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC, - 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, - 0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, - 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B, - 0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, - 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817 - }; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - inline void ctx_init() override; - inline void ctx_transform(const uint8_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - constexpr uint64_t F(const uint64_t A, const uint64_t B, const uint64_t C); - constexpr uint64_t G(const uint64_t A, const uint64_t B, const uint64_t C); - - // Sigma functions - // as per: https://datatracker.ietf.org/doc/html/rfc4634 - constexpr uint64_t SIGMA0(const uint64_t A); - constexpr uint64_t SIGMA1(const uint64_t A); - constexpr uint64_t SIGMA2(const uint64_t A); - constexpr uint64_t SIGMA3(const uint64_t A); - }; - class SHA2_512_256 : public common { - protected: - std::vector getBytes() override { - return std::vector(context.digest, context.digest + 32); - } - - private: - const uint8_t BLOCK_SIZE = 128, DIGEST_SIZE = 32; - - typedef struct { - uint64_t state[8], count[2]; - uint8_t data[128], digest[32]; - } CTX; - - CTX context = { 0 }; - - // constants (H) defined by SHA-512/256 algorithm - // as per: https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA512_256.pdf - const uint64_t H[8] = { - 0x22312194FC2BF72C, - 0x9F555FA3C84C64C2, - 0x2393B86B6F53B151, - 0x963877195940EABD, - 0x96283EE2A88EFFE3, - 0xBE5E1E2553863992, - 0x2B0199FC2C85B8AA, - 0x0EB72DDC81C52CA2 - }; - - // more constants (K)... as per above - const uint64_t K[80] = { - 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, - 0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, - 0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, - 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694, - 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, - 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, - 0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, - 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70, - 0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, - 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B, - 0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30, - 0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8, - 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, - 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, - 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC, - 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, - 0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, - 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B, - 0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, - 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817 - }; - - // the byte 0x36 repeated 'B' times where 'B' => block size - std::vector ipad = { - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 - }; - - // the byte 0x5c repeated 'B' times - std::vector opad = { - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c - }; - - inline void ctx_init() override; - inline void ctx_transform(const uint8_t* data); - inline void ctx_update(const uint8_t* data, size_t len) override; - inline void ctx_final() override; - - inline std::string _H(const std::string& a, const std::string& b) override; - inline std::string HMAC(const std::string& key, const std::string& data) override; - - constexpr uint64_t F(const uint64_t A, const uint64_t B, const uint64_t C); - constexpr uint64_t G(const uint64_t A, const uint64_t B, const uint64_t C); - - // Sigma functions - // as per: https://datatracker.ietf.org/doc/html/rfc4634 - constexpr uint64_t SIGMA0(const uint64_t A); - constexpr uint64_t SIGMA1(const uint64_t A); - constexpr uint64_t SIGMA2(const uint64_t A); - constexpr uint64_t SIGMA3(const uint64_t A); - }; - - // SHA-1 - inline void hashpp::SHA::SHA1::ctx_init() { - this->context = { - {this->H[0], this->H[1], this->H[2], this->H[3], this->H[4]}, - {this->K[0], this->K[1], this->K[2], this->K[3]}, - 0, 0 - }; - } - inline void hashpp::SHA::SHA1::ctx_transform(const uint8_t* data) { - uint32_t t, m[80], i = 0, j = 0; - - for (; i < 16; ++i, j += 4) { - m[i] = this->A(data[j], data[j + 1], data[j + 2], data[j + 3]); - } - for (; i < 80; ++i) { - m[i] = this->B(m[i - 3], m[i - 8], m[i - 14], m[i - 16]); - m[i] = this->C(m[i]); - } - - uint32_t results[5] = { - this->context.state[0], - this->context.state[1], - this->context.state[2], - this->context.state[3], - this->context.state[4] - }; - - for (i = 0; i < 20; ++i) { - t = this->rl32(results[0], 5) + (F(results[1], results[2], results[3])) + results[4] + this->context.k[0] + m[i]; - results[4] = results[3]; - results[3] = results[2]; - results[2] = this->rl32(results[1], 30); - results[1] = results[0]; - results[0] = t; - } - for (; i < 40; ++i) { - t = this->rl32(results[0], 5) + (J(results[1], results[2], results[3])) + results[4] + this->context.k[1] + m[i]; - results[4] = results[3]; - results[3] = results[2]; - results[2] = this->rl32(results[1], 30); - results[1] = results[0]; - results[0] = t; - } - for (; i < 60; ++i) { - t = this->rl32(results[0], 5) + ((G(results[1], results[2], results[3]))) + results[4] + this->context.k[2] + m[i]; - results[4] = results[3]; - results[3] = results[2]; - results[2] = this->rl32(results[1], 30); - results[1] = results[0]; - results[0] = t; - } - for (; i < 80; ++i) { - t = this->rl32(results[0], 5) + (J(results[1], results[2], results[3])) + results[4] + this->context.k[3] + m[i]; - results[4] = results[3]; - results[3] = results[2]; - results[2] = this->rl32(results[1], 30); - results[1] = results[0]; - results[0] = t; - } - - for (uint32_t z = 0; z < 5; z++) { - this->context.state[z] += results[z]; - } - } - inline void hashpp::SHA::SHA1::ctx_update(const uint8_t* data, size_t len) { - size_t i; - - for (i = 0; i < len; ++i) { - this->context.data[this->context.size] = data[i]; - this->context.size++; - if (this->context.size == 64) { - this->ctx_transform(this->context.data); - this->context.bitsize += 512; - this->context.size = 0; - } - } - } - inline void hashpp::SHA::SHA1::ctx_final() { - uint32_t L = this->context.size; - - if (this->context.size < 56) { - this->context.data[L++] = 0x80; - while (L < 56) { - this->context.data[L++] = 0x00; - } - } - else { - this->context.data[L++] = 0x80; - while (L < 64) { - this->context.data[L++] = 0x00; - } - this->ctx_transform(this->context.data); - memset(this->context.data, 0, 56); - } - - this->context.bitsize += static_cast(this->context.size) * 8; - this->context.data[63] = this->context.bitsize; - this->context.data[62] = this->context.bitsize >> 8; - this->context.data[61] = this->context.bitsize >> 16; - this->context.data[60] = this->context.bitsize >> 24; - this->context.data[59] = this->context.bitsize >> 32; - this->context.data[58] = this->context.bitsize >> 40; - this->context.data[57] = this->context.bitsize >> 48; - this->context.data[56] = this->context.bitsize >> 56; - this->ctx_transform(this->context.data); - - for (L = 0; L < 4; ++L) { - this->context.digest[L] = (this->context.state[0] >> (24 - L * 8)) & 0x000000ff; - this->context.digest[L + 4] = (this->context.state[1] >> (24 - L * 8)) & 0x000000ff; - this->context.digest[L + 8] = (this->context.state[2] >> (24 - L * 8)) & 0x000000ff; - this->context.digest[L + 12] = (this->context.state[3] >> (24 - L * 8)) & 0x000000ff; - this->context.digest[L + 16] = (this->context.state[4] >> (24 - L * 8)) & 0x000000ff; - } - } - inline std::string SHA1::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string SHA1::HMAC(const std::string& key, const std::string& data) { - uint8_t k[64] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint32_t hashpp::SHA::SHA1::A(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t D) { - return ((A << 24) + (B << 16) + (C << 8) + (D)); - } - constexpr uint32_t hashpp::SHA::SHA1::B(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t D) { - return ((A ^ B ^ C ^ D)); - } - constexpr uint32_t hashpp::SHA::SHA1::C(const uint32_t A) { return ((A << 1) | (A >> 31)); } - constexpr uint32_t hashpp::SHA::SHA1::F(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) ^ (~B & D)); } - constexpr uint32_t hashpp::SHA::SHA1::G(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) ^ (B & D) ^ (C & D)); } - constexpr uint32_t hashpp::SHA::SHA1::J(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B ^ C ^ D)); } - - // SHA2-224 - inline void hashpp::SHA::SHA2_224::ctx_init() { - this->context = { - {this->H[0], this->H[1], this->H[2], this->H[3], - this->H[4], this->H[5], this->H[6], this->H[7]}, - 0, 0 - }; - } - inline void hashpp::SHA::SHA2_224::ctx_transform(const uint8_t* data) { - uint32_t m[64], i = 0, j = 0; - - for (; i < 16; ++i, j += 4) { - m[i] = this->A(data[j], data[j + 1], data[j + 2], data[j + 3]); - } for (; i < 64; ++i) { - m[i] = this->SIGMA3(m[i - 2]) + m[i - 7] + this->SIGMA2(m[i - 15]) + m[i - 16]; - } - - uint32_t results[8] = { - this->context.state[0], - this->context.state[1], - this->context.state[2], - this->context.state[3], - this->context.state[4], - this->context.state[5], - this->context.state[6], - this->context.state[7] - }; - - uint32_t t1, t2; - for (i = 0; i < 64; ++i) { - t1 = results[7] + this->SIGMA1(results[4]) + this->F(results[4], results[5], results[6]) + this->K[i] + m[i]; - t2 = this->SIGMA0(results[0]) + this->G(results[0], results[1], results[2]); - results[7] = results[6]; - results[6] = results[5]; - results[5] = results[4]; - results[4] = results[3] + t1; - results[3] = results[2]; - results[2] = results[1]; - results[1] = results[0]; - results[0] = t1 + t2; - } - - for (uint32_t z = 0; z < 8; z++) { - this->context.state[z] += results[z]; - } - } - inline void hashpp::SHA::SHA2_224::ctx_update(const uint8_t* data, size_t len) { - uint32_t i; - - for (i = 0; i < len; ++i) { - this->context.data[this->context.size] = data[i]; - this->context.size++; - if (this->context.size == 64) { - this->ctx_transform(this->context.data); - this->context.bitsize += 512; - this->context.size = 0; - } - } - } - inline void hashpp::SHA::SHA2_224::ctx_final() { - uint32_t i = this->context.size; - - if (this->context.size < 56) { - this->context.data[i++] = 0x80; - while (i < 56) { - this->context.data[i++] = 0x00; - } - } - else { - this->context.data[i++] = 0x80; - while (i < 64) { - this->context.data[i++] = 0x00; - } - this->ctx_transform(this->context.data); - memset(this->context.data, 0, 56); - } - - this->context.bitsize += static_cast(this->context.size) * 8; - this->context.data[63] = this->context.bitsize; - this->context.data[62] = this->context.bitsize >> 8; - this->context.data[61] = this->context.bitsize >> 16; - this->context.data[60] = this->context.bitsize >> 24; - this->context.data[59] = this->context.bitsize >> 32; - this->context.data[58] = this->context.bitsize >> 40; - this->context.data[57] = this->context.bitsize >> 48; - this->context.data[56] = this->context.bitsize >> 56; - this->ctx_transform(this->context.data); - - for (i = 0; i < 4; ++i) { - this->context.digest[i] = (this->context.state[0] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 4] = (this->context.state[1] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 8] = (this->context.state[2] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 12] = (this->context.state[3] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 16] = (this->context.state[4] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 20] = (this->context.state[5] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 24] = (this->context.state[6] >> (24 - i * 8)) & 0x000000ff; - } - } - inline std::string SHA2_224::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string SHA2_224::HMAC(const std::string& key, const std::string& data) { - uint8_t k[64] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint32_t hashpp::SHA::SHA2_224::A(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t D) { - return ((A << 24) + (B << 16) + (C << 8) + (D)); - } - constexpr uint32_t hashpp::SHA::SHA2_224::F(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) ^ (~B & D)); } - constexpr uint32_t hashpp::SHA::SHA2_224::G(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) ^ (B & D) ^ (C & D)); } - constexpr uint32_t hashpp::SHA::SHA2_224::SIGMA0(const uint32_t A) { return (this->rr32(A, 2) ^ this->rr32(A, 13) ^ this->rr32(A, 22)); } - constexpr uint32_t hashpp::SHA::SHA2_224::SIGMA1(const uint32_t A) { return (this->rr32(A, 6) ^ this->rr32(A, 11) ^ this->rr32(A, 25)); } - constexpr uint32_t hashpp::SHA::SHA2_224::SIGMA2(const uint32_t A) { return (this->rr32(A, 7) ^ this->rr32(A, 18) ^ ((A) >> 3)); } - constexpr uint32_t hashpp::SHA::SHA2_224::SIGMA3(const uint32_t A) { return (this->rr32(A, 17) ^ this->rr32(A, 19) ^ ((A) >> 10)); } - - // SHA2-256 - inline void hashpp::SHA::SHA2_256::ctx_init() { - this->context = { - {this->H[0], this->H[1], this->H[2], this->H[3], - this->H[4], this->H[5], this->H[6], this->H[7]}, - 0, 0 - }; - } - inline void hashpp::SHA::SHA2_256::ctx_transform(const uint8_t* data) { - uint32_t m[64], i = 0, j = 0; - - for (; i < 16; ++i, j += 4) { - m[i] = this->A(data[j], data[j + 1], data[j + 2], data[j + 3]); - } - for (; i < 64; ++i) { - m[i] = this->SIGMA3(m[i - 2]) + m[i - 7] + this->SIGMA2(m[i - 15]) + m[i - 16]; - } - - uint32_t results[8] = { - this->context.state[0], - this->context.state[1], - this->context.state[2], - this->context.state[3], - this->context.state[4], - this->context.state[5], - this->context.state[6], - this->context.state[7] - }; - - uint32_t t1, t2; - for (i = 0; i < 64; ++i) { - t1 = results[7] + this->SIGMA1(results[4]) + this->F(results[4], results[5], results[6]) + this->K[i] + m[i]; - t2 = this->SIGMA0(results[0]) + this->G(results[0], results[1], results[2]); - results[7] = results[6]; - results[6] = results[5]; - results[5] = results[4]; - results[4] = results[3] + t1; - results[3] = results[2]; - results[2] = results[1]; - results[1] = results[0]; - results[0] = t1 + t2; - } - - for (uint32_t z = 0; z < 8; z++) { - this->context.state[z] += results[z]; - } - } - inline void hashpp::SHA::SHA2_256::ctx_update(const uint8_t* data, size_t len) { - uint32_t i; - - for (i = 0; i < len; ++i) { - this->context.data[this->context.size] = data[i]; - this->context.size++; - if (this->context.size == 64) { - this->ctx_transform(this->context.data); - this->context.bitsize += 512; - this->context.size = 0; - } - } - } - inline void hashpp::SHA::SHA2_256::ctx_final() { - uint32_t i = this->context.size; - - if (this->context.size < 56) { - this->context.data[i++] = 0x80; - while (i < 56) { - this->context.data[i++] = 0x00; - } - } - else { - this->context.data[i++] = 0x80; - while (i < 64) { - this->context.data[i++] = 0x00; - } - this->ctx_transform(this->context.data); - memset(this->context.data, 0, 56); - } - - this->context.bitsize += static_cast(this->context.size) * 8; - this->context.data[63] = this->context.bitsize; - this->context.data[62] = this->context.bitsize >> 8; - this->context.data[61] = this->context.bitsize >> 16; - this->context.data[60] = this->context.bitsize >> 24; - this->context.data[59] = this->context.bitsize >> 32; - this->context.data[58] = this->context.bitsize >> 40; - this->context.data[57] = this->context.bitsize >> 48; - this->context.data[56] = this->context.bitsize >> 56; - this->ctx_transform(this->context.data); - - for (i = 0; i < 4; ++i) { - this->context.digest[i] = (this->context.state[0] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 4] = (this->context.state[1] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 8] = (this->context.state[2] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 12] = (this->context.state[3] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 16] = (this->context.state[4] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 20] = (this->context.state[5] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 24] = (this->context.state[6] >> (24 - i * 8)) & 0x000000ff; - this->context.digest[i + 28] = (this->context.state[7] >> (24 - i * 8)) & 0x000000ff; - } - } - inline std::string SHA2_256::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string SHA2_256::HMAC(const std::string& key, const std::string& data) { - uint8_t k[64] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint32_t hashpp::SHA::SHA2_256::A(const uint32_t A, const uint32_t B, const uint32_t C, const uint32_t D) { - return ((A << 24) + (B << 16) + (C << 8) + (D)); - } - constexpr uint32_t hashpp::SHA::SHA2_256::F(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) ^ (~B & D)); } - constexpr uint32_t hashpp::SHA::SHA2_256::G(const uint32_t B, const uint32_t C, const uint32_t D) { return ((B & C) ^ (B & D) ^ (C & D)); } - constexpr uint32_t hashpp::SHA::SHA2_256::SIGMA0(const uint32_t A) { return (this->rr32(A, 2) ^ this->rr32(A, 13) ^ this->rr32(A, 22)); } - constexpr uint32_t hashpp::SHA::SHA2_256::SIGMA1(const uint32_t A) { return (this->rr32(A, 6) ^ this->rr32(A, 11) ^ this->rr32(A, 25)); } - constexpr uint32_t hashpp::SHA::SHA2_256::SIGMA2(const uint32_t A) { return (this->rr32(A, 7) ^ this->rr32(A, 18) ^ ((A) >> 3)); } - constexpr uint32_t hashpp::SHA::SHA2_256::SIGMA3(const uint32_t A) { return (this->rr32(A, 17) ^ this->rr32(A, 19) ^ ((A) >> 10)); } - - // SHA2-384 - inline void hashpp::SHA::SHA2_384::ctx_init() { - this->context = { - {this->H[0], this->H[1], this->H[2], this->H[3], - this->H[4], this->H[5], this->H[6], this->H[7]}, - {0, 0} - }; - } - inline void hashpp::SHA::SHA2_384::ctx_transform(const uint8_t* data) { - uint64_t W[80]; - uint32_t i; - - uint64_t results[8] = { - this->context.state[0], - this->context.state[1], - this->context.state[2], - this->context.state[3], - this->context.state[4], - this->context.state[5], - this->context.state[6], - this->context.state[7] - }; - - for (i = 0; i < 16; i++) { - GU64B(W[i], data, 8 * i); - uint64_t t1, t2; - t1 = (results[7]) + this->SIGMA1((results[4])) + this->F((results[4]), (results[5]), (results[6])) + (this->K[i]) + (W[i]); - t2 = this->SIGMA0((results[0])) + this->G((results[0]), (results[1]), (results[2])); - (results[7]) = (results[6]); - (results[6]) = (results[5]); - (results[5]) = (results[4]); - (results[4]) = (results[3]) + t1; - (results[3]) = (results[2]); - (results[2]) = (results[1]); - (results[1]) = (results[0]); - (results[0]) = t1 + t2; - } - - for (i = 16; i < 80; i++) { - W[i] = this->SIGMA3(W[i - 2]) + W[i - 7] + this->SIGMA2(W[i - 15]) + W[i - 16]; - uint64_t t1, t2; - t1 = (results[7]) + this->SIGMA1((results[4])) + this->F((results[4]), (results[5]), (results[6])) + (this->K[i]) + (W[i]); - t2 = this->SIGMA0((results[0])) + this->G((results[0]), (results[1]), (results[2])); - (results[7]) = (results[6]); - (results[6]) = (results[5]); - (results[5]) = (results[4]); - (results[4]) = (results[3]) + t1; - (results[3]) = (results[2]); - (results[2]) = (results[1]); - (results[1]) = (results[0]); - (results[0]) = t1 + t2; - } - - for (uint32_t z = 0; z < 8; z++) { - this->context.state[z] += results[z]; - } - } - inline void hashpp::SHA::SHA2_384::ctx_update(const uint8_t* data, size_t len) { - uint32_t left, fill, rlen = len; - const uint8_t* ptr = data; - - if (len != 0) { - - left = this->context.count[0] & 0x7F; fill = 128 - left; - - this->context.count[0] += len; - if ((this->context.count[0]) < (len)) { - (this->context.count[1])++; - } - - if ((left > 0) && (rlen >= fill)) { - memcpy(this->context.data + left, ptr, fill); - this->ctx_transform(this->context.data); - ptr += fill; - rlen -= fill; - left = 0; - } - - while (rlen >= 128) { - this->ctx_transform(ptr); - ptr += 128; - rlen -= 128; - } - - if (rlen > 0) { - memcpy(this->context.data + left, ptr, rlen); - } - } - } - inline void hashpp::SHA::SHA2_384::ctx_final() { - uint32_t block_present = 0; - uint8_t last_padded_block[2 * 128]; - - memset(last_padded_block, 0, sizeof(last_padded_block)); - - block_present = this->context.count[0] % 128; - if (block_present != 0) { - memcpy(last_padded_block, this->context.data, block_present); - } - - last_padded_block[block_present] = 0x80; - - if (block_present > (128 - 1 - (2 * sizeof(uint64_t)))) { - PU128B(this->context.count[0], this->context.count[1], last_padded_block, 2 * (128 - sizeof(uint64_t))); - this->ctx_transform(last_padded_block); - this->ctx_transform(last_padded_block + 128); - } - else { - PU128B(this->context.count[0], this->context.count[1], - last_padded_block, - 128 - (2 * sizeof(uint64_t))); - this->ctx_transform(last_padded_block); - } - - PU64B(this->context.state[0], this->context.digest, 0); - PU64B(this->context.state[1], this->context.digest, 8); - PU64B(this->context.state[2], this->context.digest, 16); - PU64B(this->context.state[3], this->context.digest, 24); - PU64B(this->context.state[4], this->context.digest, 32); - PU64B(this->context.state[5], this->context.digest, 40); - } - inline std::string SHA2_384::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string SHA2_384::HMAC(const std::string& key, const std::string& data) { - uint8_t k[128] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint64_t hashpp::SHA::SHA2_384::F(const uint64_t A, const uint64_t B, const uint64_t C) { return ((A & B) ^ (~A & C)); } - constexpr uint64_t hashpp::SHA::SHA2_384::G(const uint64_t A, const uint64_t B, const uint64_t C) { return ((A & B) ^ (A & C) ^ (B & C)); } - constexpr uint64_t hashpp::SHA::SHA2_384::SIGMA0(const uint64_t A) { return this->rr64(A, 28) ^ this->rr64(A, 34) ^ this->rr64(A, 39); } - constexpr uint64_t hashpp::SHA::SHA2_384::SIGMA1(const uint64_t A) { return this->rr64(A, 14) ^ this->rr64(A, 18) ^ this->rr64(A, 41); } - constexpr uint64_t hashpp::SHA::SHA2_384::SIGMA2(const uint64_t A) { return this->rr64(A, 1) ^ this->rr64(A, 8) ^ (A >> 7); } - constexpr uint64_t hashpp::SHA::SHA2_384::SIGMA3(const uint64_t A) { return this->rr64(A, 19) ^ this->rr64(A, 61) ^ (A >> 6); } - - // SHA2-512 - inline void hashpp::SHA::SHA2_512::ctx_init() { - this->context = { - {this->H[0], this->H[1], this->H[2], this->H[3], - this->H[4], this->H[5], this->H[6], this->H[7]}, - {0, 0} - }; - } - void hashpp::SHA::SHA2_512::ctx_transform(const uint8_t* data) { - uint64_t W[80]; - uint32_t i; - - uint64_t results[8] = { - this->context.state[0], - this->context.state[1], - this->context.state[2], - this->context.state[3], - this->context.state[4], - this->context.state[5], - this->context.state[6], - this->context.state[7] - }; - - for (i = 0; i < 16; i++) { - GU64B(W[i], data, 8 * i); - uint64_t t1, t2; - t1 = (results[7]) + this->SIGMA1((results[4])) + this->F((results[4]), (results[5]), (results[6])) + (this->K[i]) + (W[i]); - t2 = this->SIGMA0((results[0])) + this->G((results[0]), (results[1]), (results[2])); - (results[7]) = (results[6]); - (results[6]) = (results[5]); - (results[5]) = (results[4]); - (results[4]) = (results[3]) + t1; - (results[3]) = (results[2]); - (results[2]) = (results[1]); - (results[1]) = (results[0]); - (results[0]) = t1 + t2; - } - - for (i = 16; i < 80; i++) { - W[i] = this->SIGMA3(W[i - 2]) + W[i - 7] + this->SIGMA2(W[i - 15]) + W[i - 16]; - uint64_t t1, t2; - t1 = (results[7]) + this->SIGMA1((results[4])) + this->F((results[4]), (results[5]), (results[6])) + (this->K[i]) + (W[i]); - t2 = this->SIGMA0((results[0])) + this->G((results[0]), (results[1]), (results[2])); - (results[7]) = (results[6]); - (results[6]) = (results[5]); - (results[5]) = (results[4]); - (results[4]) = (results[3]) + t1; - (results[3]) = (results[2]); - (results[2]) = (results[1]); - (results[1]) = (results[0]); - (results[0]) = t1 + t2; - } - - for (uint32_t z = 0; z < 8; z++) { - this->context.state[z] += results[z]; - } - } - inline void hashpp::SHA::SHA2_512::ctx_update(const uint8_t* data, size_t len) { - uint32_t left, fill, rlen = len; - const uint8_t* ptr = data; - - if (len != 0) { - - left = this->context.count[0] & 0x7F; fill = 128 - left; - - this->context.count[0] += len; - if ((this->context.count[0]) < (len)) { - (this->context.count[1])++; - } - - if ((left > 0) && (rlen >= fill)) { - memcpy(this->context.data + left, ptr, fill); - this->ctx_transform(this->context.data); - ptr += fill; - rlen -= fill; - left = 0; - } - - while (rlen >= 128) { - this->ctx_transform(ptr); - ptr += 128; - rlen -= 128; - } - - if (rlen > 0) { - memcpy(this->context.data + left, ptr, rlen); - } - } - } - inline void hashpp::SHA::SHA2_512::ctx_final() { - uint32_t block_present = 0; - uint8_t last_padded_block[2 * 128]; - - memset(last_padded_block, 0, sizeof(last_padded_block)); - - block_present = this->context.count[0] % 128; - if (block_present != 0) { - memcpy(last_padded_block, this->context.data, block_present); - } - - last_padded_block[block_present] = 0x80; - - if (block_present > (128 - 1 - (2 * sizeof(uint64_t)))) { - PU128B(this->context.count[0], this->context.count[1], last_padded_block, 2 * (128 - sizeof(uint64_t))); - this->ctx_transform(last_padded_block); - this->ctx_transform(last_padded_block + 128); - } - else { - PU128B(this->context.count[0], this->context.count[1], - last_padded_block, - 128 - (2 * sizeof(uint64_t))); - this->ctx_transform(last_padded_block); - } - - PU64B(this->context.state[0], this->context.digest, 0); - PU64B(this->context.state[1], this->context.digest, 8); - PU64B(this->context.state[2], this->context.digest, 16); - PU64B(this->context.state[3], this->context.digest, 24); - PU64B(this->context.state[4], this->context.digest, 32); - PU64B(this->context.state[5], this->context.digest, 40); - PU64B(this->context.state[6], this->context.digest, 48); - PU64B(this->context.state[7], this->context.digest, 56); - } - inline std::string SHA2_512::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string SHA2_512::HMAC(const std::string& key, const std::string& data) { - uint8_t k[128] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint64_t hashpp::SHA::SHA2_512::F(const uint64_t A, const uint64_t B, const uint64_t C) { return ((A & B) ^ (~A & C)); } - constexpr uint64_t hashpp::SHA::SHA2_512::G(const uint64_t A, const uint64_t B, const uint64_t C) { return ((A & B) ^ (A & C) ^ (B & C)); } - constexpr uint64_t hashpp::SHA::SHA2_512::SIGMA0(const uint64_t A) { return this->rr64(A, 28) ^ this->rr64(A, 34) ^ this->rr64(A, 39); } - constexpr uint64_t hashpp::SHA::SHA2_512::SIGMA1(const uint64_t A) { return this->rr64(A, 14) ^ this->rr64(A, 18) ^ this->rr64(A, 41); } - constexpr uint64_t hashpp::SHA::SHA2_512::SIGMA2(const uint64_t A) { return this->rr64(A, 1) ^ this->rr64(A, 8) ^ (A >> 7); } - constexpr uint64_t hashpp::SHA::SHA2_512::SIGMA3(const uint64_t A) { return this->rr64(A, 19) ^ this->rr64(A, 61) ^ (A >> 6); } - - // SHA2-512-224 - inline void hashpp::SHA::SHA2_512_224::ctx_init() { - this->context = { - {this->H[0], this->H[1], this->H[2], this->H[3], - this->H[4], this->H[5], this->H[6], this->H[7]}, - {0, 0} - }; - } - inline void hashpp::SHA::SHA2_512_224::ctx_transform(const uint8_t* data) { - uint64_t W[80]; - uint32_t i; - - uint64_t results[8] = { - this->context.state[0], - this->context.state[1], - this->context.state[2], - this->context.state[3], - this->context.state[4], - this->context.state[5], - this->context.state[6], - this->context.state[7] - }; - - for (i = 0; i < 16; i++) { - GU64B(W[i], data, 8 * i); - uint64_t t1, t2; - t1 = (results[7]) + this->SIGMA1((results[4])) + this->F((results[4]), (results[5]), (results[6])) + (this->K[i]) + (W[i]); - t2 = this->SIGMA0((results[0])) + this->G((results[0]), (results[1]), (results[2])); - (results[7]) = (results[6]); - (results[6]) = (results[5]); - (results[5]) = (results[4]); - (results[4]) = (results[3]) + t1; - (results[3]) = (results[2]); - (results[2]) = (results[1]); - (results[1]) = (results[0]); - (results[0]) = t1 + t2; - } - - for (i = 16; i < 80; i++) { - W[i] = this->SIGMA3(W[i - 2]) + W[i - 7] + this->SIGMA2(W[i - 15]) + W[i - 16]; - uint64_t t1, t2; - t1 = (results[7]) + this->SIGMA1((results[4])) + this->F((results[4]), (results[5]), (results[6])) + (this->K[i]) + (W[i]); - t2 = this->SIGMA0((results[0])) + this->G((results[0]), (results[1]), (results[2])); - (results[7]) = (results[6]); - (results[6]) = (results[5]); - (results[5]) = (results[4]); - (results[4]) = (results[3]) + t1; - (results[3]) = (results[2]); - (results[2]) = (results[1]); - (results[1]) = (results[0]); - (results[0]) = t1 + t2; - } - - for (uint32_t z = 0; z < 8; z++) { - this->context.state[z] += results[z]; - } - } - inline void hashpp::SHA::SHA2_512_224::ctx_update(const uint8_t* data, size_t len) { - uint32_t left, fill, rlen = len; - const uint8_t* ptr = data; - - if (len != 0) { - - left = this->context.count[0] & 0x7F; fill = 128 - left; - - this->context.count[0] += len; - if ((this->context.count[0]) < (len)) { - (this->context.count[1])++; - } - - if ((left > 0) && (rlen >= fill)) { - memcpy(this->context.data + left, ptr, fill); - this->ctx_transform(this->context.data); - ptr += fill; - rlen -= fill; - left = 0; - } - - while (rlen >= 128) { - this->ctx_transform(ptr); - ptr += 128; - rlen -= 128; - } - - if (rlen > 0) { - memcpy(this->context.data + left, ptr, rlen); - } - } - } - inline void hashpp::SHA::SHA2_512_224::ctx_final() { - uint32_t block_present = 0; - uint8_t last_padded_block[2 * 128]; - - memset(last_padded_block, 0, sizeof(last_padded_block)); - - block_present = this->context.count[0] % 128; - if (block_present != 0) { - memcpy(last_padded_block, this->context.data, block_present); - } - - last_padded_block[block_present] = 0x80; - - if (block_present > (128 - 1 - (2 * sizeof(uint64_t)))) { - /* We need an additional block */ - PU128B(this->context.count[0], this->context.count[1], last_padded_block, 2 * (128 - sizeof(uint64_t))); - this->ctx_transform(last_padded_block); - this->ctx_transform(last_padded_block + 128); - } - else { - PU128B(this->context.count[0], this->context.count[1], - last_padded_block, - 128 - (2 * sizeof(uint64_t))); - this->ctx_transform(last_padded_block); - } - - PU64B(this->context.state[0], this->context.digest, 0); - PU64B(this->context.state[1], this->context.digest, 8); - PU64B(this->context.state[2], this->context.digest, 16); - PU64B(this->context.state[3], this->context.digest, 24); - } - inline std::string SHA2_512_224::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string SHA2_512_224::HMAC(const std::string& key, const std::string& data) { - uint8_t k[128] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint64_t hashpp::SHA::SHA2_512_224::F(const uint64_t A, const uint64_t B, const uint64_t C) { return ((A & B) ^ (~A & C)); } - constexpr uint64_t hashpp::SHA::SHA2_512_224::G(const uint64_t A, const uint64_t B, const uint64_t C) { return ((A & B) ^ (A & C) ^ (B & C)); } - constexpr uint64_t hashpp::SHA::SHA2_512_224::SIGMA0(const uint64_t A) { return this->rr64(A, 28) ^ this->rr64(A, 34) ^ this->rr64(A, 39); } - constexpr uint64_t hashpp::SHA::SHA2_512_224::SIGMA1(const uint64_t A) { return this->rr64(A, 14) ^ this->rr64(A, 18) ^ this->rr64(A, 41); } - constexpr uint64_t hashpp::SHA::SHA2_512_224::SIGMA2(const uint64_t A) { return this->rr64(A, 1) ^ this->rr64(A, 8) ^ (A >> 7); } - constexpr uint64_t hashpp::SHA::SHA2_512_224::SIGMA3(const uint64_t A) { return this->rr64(A, 19) ^ this->rr64(A, 61) ^ (A >> 6); } - - // SHA2-512-256 - inline void hashpp::SHA::SHA2_512_256::ctx_init() { - this->context = { - {this->H[0], this->H[1], this->H[2], this->H[3], - this->H[4], this->H[5], this->H[6], this->H[7]}, - {0, 0} - }; - } - inline void hashpp::SHA::SHA2_512_256::ctx_transform(const uint8_t* data) { - uint64_t W[80]; - uint32_t i; - - uint64_t results[8] = { - this->context.state[0], - this->context.state[1], - this->context.state[2], - this->context.state[3], - this->context.state[4], - this->context.state[5], - this->context.state[6], - this->context.state[7] - }; - - for (i = 0; i < 16; i++) { - GU64B(W[i], data, 8 * i); - uint64_t t1, t2; - t1 = (results[7]) + this->SIGMA1((results[4])) + this->F((results[4]), (results[5]), (results[6])) + (this->K[i]) + (W[i]); - t2 = this->SIGMA0((results[0])) + this->G((results[0]), (results[1]), (results[2])); - (results[7]) = (results[6]); - (results[6]) = (results[5]); - (results[5]) = (results[4]); - (results[4]) = (results[3]) + t1; - (results[3]) = (results[2]); - (results[2]) = (results[1]); - (results[1]) = (results[0]); - (results[0]) = t1 + t2; - } - - for (i = 16; i < 80; i++) { - W[i] = this->SIGMA3(W[i - 2]) + W[i - 7] + this->SIGMA2(W[i - 15]) + W[i - 16]; - uint64_t t1, t2; - t1 = (results[7]) + this->SIGMA1((results[4])) + this->F((results[4]), (results[5]), (results[6])) + (this->K[i]) + (W[i]); - t2 = this->SIGMA0((results[0])) + this->G((results[0]), (results[1]), (results[2])); - (results[7]) = (results[6]); - (results[6]) = (results[5]); - (results[5]) = (results[4]); - (results[4]) = (results[3]) + t1; - (results[3]) = (results[2]); - (results[2]) = (results[1]); - (results[1]) = (results[0]); - (results[0]) = t1 + t2; - } - - for (uint32_t z = 0; z < 8; z++) { - this->context.state[z] += results[z]; - } - } - inline void hashpp::SHA::SHA2_512_256::ctx_update(const uint8_t* data, size_t len) { - uint32_t left, fill, rlen = len; - const uint8_t* ptr = data; - - if (len != 0) { - - left = this->context.count[0] & 0x7F; fill = 128 - left; - - this->context.count[0] += len; - if ((this->context.count[0]) < (len)) { - (this->context.count[1])++; - } - - if ((left > 0) && (rlen >= fill)) { - memcpy(this->context.data + left, ptr, fill); - this->ctx_transform(this->context.data); - ptr += fill; - rlen -= fill; - left = 0; - } - - while (rlen >= 128) { - this->ctx_transform(ptr); - ptr += 128; - rlen -= 128; - } - - if (rlen > 0) { - memcpy(this->context.data + left, ptr, rlen); - } - } - } - inline void hashpp::SHA::SHA2_512_256::ctx_final() { - uint32_t block_present = 0; - uint8_t last_padded_block[2 * 128]; - - memset(last_padded_block, 0, sizeof(last_padded_block)); - - block_present = this->context.count[0] % 128; - if (block_present != 0) { - memcpy(last_padded_block, this->context.data, block_present); - } - - last_padded_block[block_present] = 0x80; - - if (block_present > (128 - 1 - (2 * sizeof(uint64_t)))) { - PU128B(this->context.count[0], this->context.count[1], last_padded_block, 2 * (128 - sizeof(uint64_t))); - this->ctx_transform(last_padded_block); - this->ctx_transform(last_padded_block + 128); - } - else { - PU128B(this->context.count[0], this->context.count[1], - last_padded_block, - 128 - (2 * sizeof(uint64_t))); - this->ctx_transform(last_padded_block); - } - - PU64B(this->context.state[0], this->context.digest, 0); - PU64B(this->context.state[1], this->context.digest, 8); - PU64B(this->context.state[2], this->context.digest, 16); - PU64B(this->context.state[3], this->context.digest, 24); - } - inline std::string SHA2_512_256::_H(const std::string& a, const std::string& b) { - return this->getHash(a + b); - } - inline std::string SHA2_512_256::HMAC(const std::string& key, const std::string& data) { - uint8_t k[128] = {0}; - - // (1) append zeros to the end of K to create a B byte string - // (e.g., if K is of length 20 bytes and B = 64, then K will be - // appended with 44 zero bytes 0x00) where B is the block size - if (key.length() > this->BLOCK_SIZE) { - // if K is longer than B bytes, reset K to K=H(K) - // where K can either be composed of entirely bytes of H(K) - // (if H(K) == L [where L => digest]) or the first L bytes of - // H(K) (if H(K) != L) followed by the remaining zeros (if H(K) < L) - std::vector k_ = this->fromHex(this->getHash(key)); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - k[i] = k_[i]; - } - } - else { - // if K is shorter than B bytes, append zeros to the end of K - // until K is B bytes long - for (uint32_t i = 0; i < key.length(); ++i) { - k[i] = key[i]; - } - } - - // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with ipad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->ipad[i] ^= k[i]; - } - - // (3) append the stream of data 'data' to the B byte string resulting from step (2) - for (uint32_t i = 0; i < data.length(); ++i) { - this->ipad.push_back(data[i]); - } - - // (4) apply H to the stream generated in step (3) - // see step (6) - - // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1) with opad - for (uint32_t i = 0; i < this->BLOCK_SIZE; ++i) { - this->opad[i] ^= k[i]; - } - - // (6) append the H result from step (4) to the B byte string resulting from step (5) - std::vector h_ = this->fromHex(this->getHash(std::string(this->ipad.begin(), this->ipad.end()))); - for (uint32_t i = 0; i < this->DIGEST_SIZE; ++i) { - this->opad.push_back(h_[i]); - } - - // (7) apply H to the stream generated in step (6) and output the result - return this->getHash(std::string(this->opad.begin(), this->opad.end())); - } - constexpr uint64_t hashpp::SHA::SHA2_512_256::F(const uint64_t A, const uint64_t B, const uint64_t C) { return ((A & B) ^ (~A & C)); } - constexpr uint64_t hashpp::SHA::SHA2_512_256::G(const uint64_t A, const uint64_t B, const uint64_t C) { return ((A & B) ^ (A & C) ^ (B & C)); } - constexpr uint64_t hashpp::SHA::SHA2_512_256::SIGMA0(const uint64_t A) { return this->rr64(A, 28) ^ this->rr64(A, 34) ^ this->rr64(A, 39); } - constexpr uint64_t hashpp::SHA::SHA2_512_256::SIGMA1(const uint64_t A) { return this->rr64(A, 14) ^ this->rr64(A, 18) ^ this->rr64(A, 41); } - constexpr uint64_t hashpp::SHA::SHA2_512_256::SIGMA2(const uint64_t A) { return this->rr64(A, 1) ^ this->rr64(A, 8) ^ (A >> 7); } - constexpr uint64_t hashpp::SHA::SHA2_512_256::SIGMA3(const uint64_t A) { return this->rr64(A, 19) ^ this->rr64(A, 61) ^ (A >> 6); } - } - - - // class used to store hash retrieved from get*Hash - // this class is used as an interface to access a - // hash returned by the above described function(s) - class hash { - public: - hash() noexcept = default; - hash(const hash& hashObj) noexcept : hashStr(hashObj.hashStr) {} - hash(hash&& hashObj) noexcept : hashStr(std::move(hashObj.hashStr)) {} - hash(const std::string& hex) noexcept : hashStr(hex) {} - hash(std::string&& hex) noexcept : hashStr(std::move(hex)) {} - - bool valid() const noexcept { return !this->hashStr.empty(); } - constexpr const std::string& getString() const noexcept { return this->hashStr; }; - - operator std::string() const noexcept { return this->hashStr; } - friend std::ostream& operator<<(std::ostream& _Ostr, const hashpp::hash& object) { - _Ostr << object.getString(); - return _Ostr; - } - - hash& operator=(const hashpp::hash& _rhs) noexcept { - if (this != &_rhs) { - this->hashStr = _rhs.getString(); - } - return *this; - } - - hash& operator=(hashpp::hash&& _rhs) noexcept { - if (this != &_rhs) { - this->hashStr = std::move(_rhs.hashStr); - } - return *this; - } - - bool operator==(const hashpp::hash& _rhs) const noexcept { - return _rhs.getString() == this->getString(); - } - - template , int> = 0> - bool operator==(const _Ty& _rhs) const noexcept { - return _rhs == this->getString(); - } - - private: - std::string hashStr; - }; - - - // class used to store hashes retrieved from get*Hashes - // this class is used to access multiple returned hashes - // of one or more hash algorithms - // - // for instance, we can get several hashes of several algorithms and print only - // selected algorithms like so: - // auto allHashes = hashpp::get::getHashes(ALGORITHMS::MD5, "data1", "data2", "data3", ...); - // for (auto hash : allHashes["MD5"]) { - // std::cout << hash << std::endl; - // } - // - // ... et cetera ... - - class hashCollection { - public: - hashCollection() noexcept = default; - hashCollection(const hashCollection& hc) noexcept : collection(hc.collection) {} - hashCollection(hashCollection&& hc) noexcept : collection(std::move(hc.collection)) {} - hashCollection(const std::vector>>& data) noexcept : collection(data) {} - hashCollection(std::vector>>&& data) noexcept : collection(std::move(data)) {} - - // operator[] overload to access collections of hashpp - // by their specific algorithm - std::vector operator[](const std::string& algoID) const { - return this->getHashesFromID(algoID); - } - - // function used to check if there are any hashes in the collection - // under the requested algorithm - // - // for instance, the below will check if allHashes has hash of type MD5 - // auto allHashes = getHashes(...); if (allHashes.valid("MD5")) { ... } - bool valid(const std::string& algoID) const noexcept { return !this->operator[](algoID).empty(); } - - std::vector>>::const_iterator begin() const noexcept { - return this->collection.begin(); - } - std::vector>>::const_iterator end() const noexcept { - return this->collection.end(); - } - - private: - std::vector>> collection; - std::vector getHashesFromID(const std::string& algoID) const { - for (const std::pair>& idHashCollectionPair : this->collection) { - if (!idHashCollectionPair.first.compare(algoID)) { - return idHashCollectionPair.second; - } - } - - // if no pair in collection contains requested algorithm ID - // just return an empty vector - return std::vector(); - } - }; - - // Ambiguous base container class to simplify original function signatures of getHash(...)-related functions - class Container { - public: // constructors - Container() noexcept = default; - Container(const Container& container) noexcept - : algorithm(container.algorithm), key(container.key), data(container.data) { - } - Container(Container&& container) noexcept - : algorithm(container.algorithm), key(std::move(container.key)), data(std::move(container.data)) { - } - Container( - ALGORITHMS algorithm, - const std::vector& data - ) noexcept : algorithm(algorithm), data(data) { - } - Container( - ALGORITHMS algorithm, - std::vector&& data - ) noexcept : algorithm(algorithm), data(std::move(data)) { - } - Container( - ALGORITHMS algorithm, - const std::vector& data, - const std::string& key - ) noexcept : algorithm(algorithm), data(data), key(key) { - } - Container( - ALGORITHMS algorithm, - std::vector&& data, - const std::string& key - ) noexcept : algorithm(algorithm), data(std::move(data)), key(key) { - } - Container( - ALGORITHMS algorithm, - const std::vector& data, - std::string&& key - ) noexcept : algorithm(algorithm), data(data), key(std::move(key)) { - } - Container( - ALGORITHMS algorithm, - std::vector&& data, - std::string&& key - ) noexcept : algorithm(algorithm), data(std::move(data)), key(std::move(key)) { - } - - template ...>, int> = 0> - Container( - ALGORITHMS algorithm, - const _Ts&... data - ) noexcept : algorithm(algorithm), data({ data... }) { - } - - template ..., std::negation>...>, int> = 0> - Container( - ALGORITHMS algorithm, - _Ts&&... data - ) noexcept : algorithm(algorithm), data({ std::forward<_Ts>(data)... }) { - } - - public: // member functions - constexpr const ALGORITHMS& getAlgorithm() const noexcept { return this->algorithm; } - constexpr const std::string& getKey() const noexcept { return this->key; } - constexpr const std::vector& getData() const noexcept { return this->data; } - void setAlgorithm(ALGORITHMS algorithm) noexcept { this->algorithm = algorithm; } - void setKey(const std::string& key) noexcept { this->key = key; } - - void setData(const std::vector& data) noexcept { this->data = data; } - void setData(std::vector&& data) noexcept { this->data = std::move(data); } - template void setData(const _Ts&... data) noexcept { this->data = { data... }; } - template >...>, int> = 0> - void setData(_Ts&&... data) noexcept { this->data = { std::forward<_Ts>(data)... }; } - - void appendData(const std::vector& data) noexcept { this->data.insert(this->data.end(), data.begin(), data.end()); } - void appendData(std::vector&& data) noexcept { this->data.insert(this->data.end(), std::make_move_iterator(data.begin()), std::make_move_iterator(data.end())); } - template void appendData(const _Ts&... data) noexcept { (this->data.push_back(data), ...); } - template >...>, int> = 0> - void appendData(_Ts&&... data) noexcept { (this->data.push_back(std::forward<_Ts>(data)), ...); } - - Container& operator=(const Container& _rhs) noexcept { - if (this != &_rhs) { - this->algorithm = _rhs.getAlgorithm(); - this->key = _rhs.getKey(); - this->data = _rhs.getData(); - } - return *this; - } - - Container& operator=(Container&& _rhs) noexcept { - if (this != &_rhs) { - this->algorithm = _rhs.algorithm; - this->key = std::move(_rhs.key); - this->data = std::move(_rhs.data); - } - return *this; - } - - private: // member variables - ALGORITHMS algorithm; - std::string key; - - // Holds arbitrary data; how this data is defined is determined - // by the functions that the container containing said data is passed to. - // (e.g., a call to getFilesHashes with a container will treat all data - // in it as paths to files to be hashed, and a call to getHashes with - // a container will treat all data as generic data to be hashed) - std::vector data; - }; - using HMAC_DataContainer = Container; - using DataContainer = Container; - using FilePathsContainer = Container; - - // interface class to allow use of static methods to access - // all algorithm classes and use their functions without - // the need of several instantiations of each class in - // the main source code of the user - // - // i.e., if a user wants to pass data to one or several - // algorithms and get the hash(es), they can do so via: - // hashpp::get::getHash or hashpp::get::getHashes - // - // the retrieval of file hashes is also possible via - // hashpp::get::getFileHash or collectively via - // hashpp::get::getFileHashes - // - // all hashpp::get methods return a hashpp::hash or a - // hashpp::hashCollection object. - // - // refer to the class definitions of both hashpp::hash and - // hashpp::hashCollection for info on how to use them - - class get { - public: - // function to return a resulting hash from selected ALGORITHM and passed data - static hashpp::hash getHash(hashpp::ALGORITHMS algorithm, const std::string& data) { - switch (algorithm) { - case hashpp::ALGORITHMS::MD5: - { - return { hashpp::MD::MD5().getHash(data) }; - } - case hashpp::ALGORITHMS::MD4: - { - return { hashpp::MD::MD4().getHash(data) }; - } - case hashpp::ALGORITHMS::MD2: - { - return { hashpp::MD::MD2().getHash(data) }; - } - case hashpp::ALGORITHMS::SHA1: - { - return { hashpp::SHA::SHA1().getHash(data) }; - } - case hashpp::ALGORITHMS::SHA2_224: - { - return { hashpp::SHA::SHA2_224().getHash(data) }; - } - case hashpp::ALGORITHMS::SHA2_256: - { - return { hashpp::SHA::SHA2_256().getHash(data) }; - } - case hashpp::ALGORITHMS::SHA2_384: - { - return { hashpp::SHA::SHA2_384().getHash(data) }; - } - case hashpp::ALGORITHMS::SHA2_512: - { - return { hashpp::SHA::SHA2_512().getHash(data) }; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - return { hashpp::SHA::SHA2_512_224().getHash(data) }; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - return { hashpp::SHA::SHA2_512_256().getHash(data) }; - } - default: - { - return hashpp::hash(); - } - } - } - - // function to return a resulting HMAC from selected ALGORITHM and passed key-data pair - static hashpp::hash getHMAC(hashpp::ALGORITHMS algorithm, const std::string& key, const std::string& data) { - switch (algorithm) { - case hashpp::ALGORITHMS::MD5: - { - return { hashpp::MD::MD5().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::MD4: - { - return { hashpp::MD::MD4().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::MD2: - { - return { hashpp::MD::MD2().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::SHA1: - { - return { hashpp::SHA::SHA1().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::SHA2_224: - { - return { hashpp::SHA::SHA2_224().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::SHA2_256: - { - return { hashpp::SHA::SHA2_256().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::SHA2_384: - { - return { hashpp::SHA::SHA2_384().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::SHA2_512: - { - return { hashpp::SHA::SHA2_512().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - return { hashpp::SHA::SHA2_512_224().getHMAC(key, data) }; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - return { hashpp::SHA::SHA2_512_256().getHMAC(key, data) }; - } - default: - { - return hashpp::hash(); - } - } - } - - // function to return a collection of resulting hashes from passed data container(s) - static hashpp::hashCollection getHashes(const DataContainer& dataSet) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - switch (dataSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& data : dataSet.getData()) { - vMD5.push_back(hashpp::MD::MD5().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& data : dataSet.getData()) { - vMD4.push_back(hashpp::MD::MD4().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& data : dataSet.getData()) { - vMD2.push_back(hashpp::MD::MD2().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& data : dataSet.getData()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(data)); - } - break; - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - - // function to return a collection of resulting hashes from passed data container(s) - static hashpp::hashCollection getHashes(const std::vector& dataSets) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - for (const DataContainer& dataSet : dataSets) { - switch (dataSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& data : dataSet.getData()) { - vMD5.push_back(hashpp::MD::MD5().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& data : dataSet.getData()) { - vMD4.push_back(hashpp::MD::MD4().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& data : dataSet.getData()) { - vMD2.push_back(hashpp::MD::MD2().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& data : dataSet.getData()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(data)); - } - break; - } - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - - // function to return a collection of resulting hashes from passed data container(s) - static hashpp::hashCollection getHashes(const std::initializer_list& dataSets) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - for (const DataContainer& dataSet : dataSets) { - switch (dataSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& data : dataSet.getData()) { - vMD5.push_back(hashpp::MD::MD5().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& data : dataSet.getData()) { - vMD4.push_back(hashpp::MD::MD4().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& data : dataSet.getData()) { - vMD2.push_back(hashpp::MD::MD2().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& data : dataSet.getData()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& data : dataSet.getData()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(data)); - } - break; - } - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - - // function to return a collection of resulting hashes from selected ALGORITHM and passed data - template ...>, int> = 0> - static hashpp::hashCollection getHashes(hashpp::ALGORITHMS algorithm, const _Ts&... data) { - switch (algorithm) { - case hashpp::ALGORITHMS::MD5: - { - std::vector vMD5; - (vMD5.push_back(hashpp::MD::MD5().getHash(static_cast(data))), ...); - return hashCollection{ {{ "MD5", vMD5 }} }; - } - case hashpp::ALGORITHMS::MD4: - { - std::vector vMD4; - (vMD4.push_back(hashpp::MD::MD4().getHash(static_cast(data))), ...); - return hashCollection{ {{ "MD4", vMD4 }} }; - } - case hashpp::ALGORITHMS::MD2: - { - std::vector vMD2; - (vMD2.push_back(hashpp::MD::MD2().getHash(static_cast(data))), ...); - return hashCollection{ {{ "MD2", vMD2 }} }; - } - case hashpp::ALGORITHMS::SHA1: - { - std::vector vSHA1; - (vSHA1.push_back(hashpp::SHA::SHA1().getHash(static_cast(data))), ...); - return hashCollection{ {{ "SHA1", vSHA1 }} }; - } - case hashpp::ALGORITHMS::SHA2_224: - { - std::vector vSHA2_224; - (vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(static_cast(data))), ...); - return hashCollection{ {{ "SHA2-224", vSHA2_224 }} }; - } - case hashpp::ALGORITHMS::SHA2_256: - { - std::vector vSHA2_256; - (vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(static_cast(data))), ...); - return hashCollection{ {{ "SHA2-256", vSHA2_256 }} }; - } - case hashpp::ALGORITHMS::SHA2_384: - { - std::vector vSHA_384; - (vSHA_384.push_back(hashpp::SHA::SHA2_384().getHash(static_cast(data))), ...); - return hashCollection{ {{ "SHA2-384", vSHA_384 }} }; - } - case hashpp::ALGORITHMS::SHA2_512: - { - std::vector vSHA2_512; - (vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(static_cast(data))), ...); - return hashCollection{ {{ "SHA2-512", vSHA2_512 }} }; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - std::vector vSHA2_512_224; - (vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(static_cast(data))), ...); - return hashCollection{ {{ "SHA2-512-224", vSHA2_512_224 }} }; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - std::vector vSHA2_512_256; - (vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(static_cast(data))), ...); - return hashCollection{ {{ "SHA2-512-256", vSHA2_512_256 }} }; - } - } - } - - // function to return a collection of resulting HMACs from selected ALGORITHMS and passed key-data container(s) - static hashpp::hashCollection getHMACs(const HMAC_DataContainer& keyDataSet) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - switch (keyDataSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& data : keyDataSet.getData()) { - vMD5.push_back(hashpp::MD::MD5().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& data : keyDataSet.getData()) { - vMD4.push_back(hashpp::MD::MD4().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& data : keyDataSet.getData()) { - vMD2.push_back(hashpp::MD::MD2().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - - // function to return a collection of resulting HMACs from selected ALGORITHMS and passed key-data container(s) - static hashpp::hashCollection getHMACs(const std::vector& keyDataSets) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - for (const DataContainer& keyDataSet : keyDataSets) { - switch (keyDataSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& data : keyDataSet.getData()) { - vMD5.push_back(hashpp::MD::MD5().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& data : keyDataSet.getData()) { - vMD4.push_back(hashpp::MD::MD4().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& data : keyDataSet.getData()) { - vMD2.push_back(hashpp::MD::MD2().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - - // function to return a collection of resulting HMACs from selected ALGORITHMS and passed key-data container(s) - static hashpp::hashCollection getHMACs(const std::initializer_list& keyDataSets) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - for (const DataContainer& keyDataSet : keyDataSets) { - switch (keyDataSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& data : keyDataSet.getData()) { - vMD5.push_back(hashpp::MD::MD5().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& data : keyDataSet.getData()) { - vMD4.push_back(hashpp::MD::MD4().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& data : keyDataSet.getData()) { - vMD2.push_back(hashpp::MD::MD2().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& data : keyDataSet.getData()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHMAC(keyDataSet.getKey(), data)); - } - break; - } - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - - // function to return a collection of resulting HMACs from selected ALGORITHM, key, and data - template ...>, int> = 0> - static hashpp::hashCollection getHMACs(hashpp::ALGORITHMS algorithm, const std::string& key, const _Ts&... data) { - switch (algorithm) { - case hashpp::ALGORITHMS::MD5: - { - std::vector vMD5; - (vMD5.push_back(hashpp::MD::MD5().getHMAC(key, data)), ...); - return hashCollection{ {{ "MD5", vMD5 }} }; - } - case hashpp::ALGORITHMS::MD4: - { - std::vector vMD4; - (vMD4.push_back(hashpp::MD::MD4().getHMAC(key, data)), ...); - return hashCollection{ {{ "MD4", vMD4 }} }; - } - case hashpp::ALGORITHMS::MD2: - { - std::vector vMD2; - (vMD2.push_back(hashpp::MD::MD2().getHMAC(key, data)), ...); - return hashCollection{ {{ "MD2", vMD2 }} }; - } - case hashpp::ALGORITHMS::SHA1: - { - std::vector vSHA1; - (vSHA1.push_back(hashpp::SHA::SHA1().getHMAC(key, data)), ...); - return hashCollection{ {{ "SHA1", vSHA1 }} }; - } - case hashpp::ALGORITHMS::SHA2_224: - { - std::vector vSHA2_224; - (vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHMAC(key, data)), ...); - return hashCollection{ {{ "SHA2-224", vSHA2_224 }} }; - } - case hashpp::ALGORITHMS::SHA2_256: - { - std::vector vSHA2_256; - (vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHMAC(key, data)), ...); - return hashCollection{ {{ "SHA2-256", vSHA2_256 }} }; - } - case hashpp::ALGORITHMS::SHA2_384: - { - std::vector vSHA_384; - (vSHA_384.push_back(hashpp::SHA::SHA2_384().getHMAC(key, data)), ...); - return hashCollection{ {{ "SHA2-384", vSHA_384 }} }; - } - case hashpp::ALGORITHMS::SHA2_512: - { - std::vector vSHA2_512; - (vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHMAC(key, data)), ...); - return hashCollection{ {{ "SHA2-512", vSHA2_512 }} }; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - std::vector vSHA2_512_224; - (vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHMAC(key, data)), ...); - return hashCollection{ {{ "SHA2-512-224", vSHA2_512_224 }} }; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - std::vector vSHA2_512_256; - (vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHMAC(key, data)), ...); - return hashCollection{ {{ "SHA2-512-256", vSHA2_512_256 }} }; - } - } - } - - // function to return a resulting hash from selected ALGORITHM and passed file - static hashpp::hash getFileHash(hashpp::ALGORITHMS algorithm, const std::string& path) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - switch (algorithm) { - case hashpp::ALGORITHMS::MD5: - { - return { hashpp::MD::MD5().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::MD4: - { - return { hashpp::MD::MD4().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::MD2: - { - return { hashpp::MD::MD2().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::SHA1: - { - return { hashpp::SHA::SHA1().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::SHA2_224: - { - return { hashpp::SHA::SHA2_224().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::SHA2_256: - { - return { hashpp::SHA::SHA2_256().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::SHA2_384: - { - return { hashpp::SHA::SHA2_384().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::SHA2_512: - { - return { hashpp::SHA::SHA2_512().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - return { hashpp::SHA::SHA2_512_224().getHash(std::filesystem::path(path)) }; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - return { hashpp::SHA::SHA2_512_256().getHash(std::filesystem::path(path)) }; - } - default: - { - return hashpp::hash(); - } - } - } - else { - return hashpp::hash(); - } - } - - // function to return a collection of resulting hashes from selected ALGORITHMS and passed file path container(s) (with recursive directory support) - static hashpp::hashCollection getFilesHashes(const FilePathsContainer& filePathSet) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - switch (filePathSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD5.push_back(hashpp::MD::MD5().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD5.push_back(hashpp::MD::MD5().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD4.push_back(hashpp::MD::MD4().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD4.push_back(hashpp::MD::MD4().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD2.push_back(hashpp::MD::MD2().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD2.push_back(hashpp::MD::MD2().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(item.path())); - } - } - } - } - break; - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - - // function to return a collection of resulting hashes from selected ALGORITHMS and passed file path container(s) (with recursive directory support) - static hashpp::hashCollection getFilesHashes(const std::vector& filePathSets) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - for (const FilePathsContainer& filePathSet : filePathSets) { - switch (filePathSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD5.push_back(hashpp::MD::MD5().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD5.push_back(hashpp::MD::MD5().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD4.push_back(hashpp::MD::MD4().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD4.push_back(hashpp::MD::MD4().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD2.push_back(hashpp::MD::MD2().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD2.push_back(hashpp::MD::MD2().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(item.path())); - } - } - } - } - break; - } - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - - // function to return a collection of resulting hashes from selected ALGORITHMS and passed file path container(s) (with recursive directory support) - static hashpp::hashCollection getFilesHashes(const std::initializer_list& filePathSets) { - std::vector vMD5, vMD4, vMD2, vSHA1, vSHA2_224, vSHA2_256, vSHA2_384, vSHA2_512, vSHA2_512_224, vSHA2_512_256; - - for (const FilePathsContainer& filePathSet : filePathSets) { - switch (filePathSet.getAlgorithm()) { - case hashpp::ALGORITHMS::MD5: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD5.push_back(hashpp::MD::MD5().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD5.push_back(hashpp::MD::MD5().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::MD4: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD4.push_back(hashpp::MD::MD4().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD4.push_back(hashpp::MD::MD4().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::MD2: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vMD2.push_back(hashpp::MD::MD2().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vMD2.push_back(hashpp::MD::MD2().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA1: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA1.push_back(hashpp::SHA::SHA1().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_224: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_224.push_back(hashpp::SHA::SHA2_224().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_256: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_256.push_back(hashpp::SHA::SHA2_256().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_384: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_384.push_back(hashpp::SHA::SHA2_384().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512.push_back(hashpp::SHA::SHA2_512().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_224: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512_224.push_back(hashpp::SHA::SHA2_512_224().getHash(item.path())); - } - } - } - } - break; - } - case hashpp::ALGORITHMS::SHA2_512_256: - { - for (const std::string& path : filePathSet.getData()) { - if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(std::filesystem::path(path))); - } - else if (std::filesystem::exists(path) && std::filesystem::is_directory(path)) { - for (const std::filesystem::directory_entry& item : std::filesystem::recursive_directory_iterator(path)) { - if (item.is_regular_file()) { - vSHA2_512_256.push_back(hashpp::SHA::SHA2_512_256().getHash(item.path())); - } - } - } - } - break; - } - } - } - return hashCollection{ - { - { "MD5", vMD5 }, - { "MD4", vMD4 }, - { "MD2", vMD2 }, - { "SHA1", vSHA1 }, - { "SHA2-224", vSHA2_224 }, - { "SHA2-256", vSHA2_256 }, - { "SHA2-384", vSHA2_384 }, - { "SHA2-512", vSHA2_512 }, - { "SHA2-512-224", vSHA2_512_224 }, - { "SHA2-512-256", vSHA2_512_256 } - } - }; - } - }; - -#if defined(HASHPP_INCLUDE_METRICS) - // well-defined timer class for use in metrics - template - class timer { - public: - void startTimer() { - this->_start = std::chrono::high_resolution_clock::now(); - } - - void stopTimer() { - this->_end = std::chrono::high_resolution_clock::now(); - } - - constexpr _Ty getTime() const { - return std::chrono::duration_cast<_Ty>(this->_end - this->_start); - } - - private: - std::chrono::steady_clock::time_point _start; - std::chrono::steady_clock::time_point _end; - }; - - template - class metrics : private timer<_Ty> { // optional class for algorithm metrics and benchmarking - public: - // Function to check each algorithm for correctness - void checkAlgorithms() const { - for (const hashpp::ALGORITHMS& algorithm : this->algorithms) { - if (hashpp::get::getHash(algorithm, "d").getString() == this->comparisons[static_cast(algorithm)].first) { - std::cout << this->comparisons[static_cast(algorithm)].second << " pass." << std::endl; - } - else { - std::cout << this->comparisons[static_cast(algorithm)].second << " fail." << std::endl; - } - } - } - - // Function to check each algorithm for HMAC correctness - void checkAlgorithms_HMAC() const { - for (const hashpp::ALGORITHMS& algorithm : this->algorithms) { - if (hashpp::get::getHMAC(algorithm, "k", "d").getString() == this->hmac_comparisons[static_cast(algorithm)].first) { - std::cout << this->hmac_comparisons[static_cast(algorithm)].second << " HMAC pass." << std::endl; - } - else { - std::cout << this->hmac_comparisons[static_cast(algorithm)].second << " HMAC fail." << std::endl; - } - } - } - - // Function to measure performance of all hashing algorithms when hashing 10 million - // repetitions of argument 'target.' - void benchmarkAlgorithms(const std::string& target) { - std::cout << "Testing 10m hashing repetitions of '" << target << "'.\n" << std::endl; - for (const hashpp::ALGORITHMS& algorithm : this->algorithms) { - if (algorithm == hashpp::ALGORITHMS::MD2 && !IncludeMD2) { - continue; - } - else { - this->startTimer(); - for (uint32_t i = 0; i < 10000000; i++) { - hashpp::get::getHash(algorithm, target); - } - this->stopTimer(); - std::cout << this->comparisons[static_cast(algorithm)].second << ": " << this->getTime().count() << std::endl; - } - } - } - - // Function to measure performance of all HMAC hashing algorithms when hashing 10 million - // repetitions of argument 'target' with key 'key.' - void benchmarkAlgorithms_HMAC(const std::string& key, const std::string& target) { - std::cout << "Testing 10m HMAC hashing repetitions of '" << target << "' with key '" << key << "'.\n" << std::endl; - for (const hashpp::ALGORITHMS& algorithm : this->algorithms) { - if (algorithm == hashpp::ALGORITHMS::MD2 && !IncludeMD2) { - continue; - } - else { - this->startTimer(); - for (uint32_t i = 0; i < 10000000; i++) { - hashpp::get::getHMAC(algorithm, key, target); - } - this->stopTimer(); - std::cout << this->hmac_comparisons[static_cast(algorithm)].second << ": " << this->getTime().count() << std::endl; - } - } - } - - // Function to measure performance of all hashing algorithms when hashing a given file - void benchmarkAlgorithms_File(const std::string& path) { - std::cout << "Testing algorithm speeds for hashing of file '" << path << "'.\n" << std::endl; - for (const hashpp::ALGORITHMS& algorithm : this->algorithms) { - if (algorithm == hashpp::ALGORITHMS::MD2 && !IncludeMD2) { - continue; - } - else { - this->startTimer(); - hashpp::get::getFileHash(algorithm, path); - this->stopTimer(); - std::cout << this->comparisons[static_cast(algorithm)].second << ": " << this->getTime().count() << std::endl; - } - } - } - - private: - const std::vector algorithms = { - hashpp::ALGORITHMS::MD5, - hashpp::ALGORITHMS::MD4, - hashpp::ALGORITHMS::MD2, - hashpp::ALGORITHMS::SHA1, - hashpp::ALGORITHMS::SHA2_224, - hashpp::ALGORITHMS::SHA2_256, - hashpp::ALGORITHMS::SHA2_384, - hashpp::ALGORITHMS::SHA2_512, - hashpp::ALGORITHMS::SHA2_512_224, - hashpp::ALGORITHMS::SHA2_512_256 - }; - - // All correct hashes of data 'd' for comparison - const std::vector> comparisons = { - { "8277e0910d750195b448797616e091ad", "MD5" }, - { "5d3f7ed29552c4ab4612fb7686bb52bb", "MD4" }, - { "96978c0796ce94f7beb31576946b6bed", "MD2" }, - { "3c363836cf4e16666669a25da280a1865c2d2874", "SHA1" }, - { "06c9f71496e24dec6acc44895648cf9ec40b5cebb7bc4858a3c69f25", "SHA2-224" }, - { "18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4", "SHA2-256" }, - { "8ac10705a78a2dcd15fa577bac70762708597a02e130d8a6192d73dababd2b14502dbeee29d0e22bc341a0c42af6a4fb", "SHA2-384" }, - { "48fb10b15f3d44a09dc82d02b06581e0c0c69478c9fd2cf8f9093659019a1687baecdbb38c9e72b12169dc4148690f87467f9154f5931c5df665c6496cbfd5f5", "SHA2-512" }, - { "a8c9aa3f45f2ada72e3ae9278407b4ade221490596c69b27af611dae", "SHA2-512/224" }, - { "9a895196448c0a9daa9769b48f29db5b41cfe2f6f65943a8ef2b8f446e388f7e", "SHA2-512/256" } - }; - - // All correct hashes of data 'd' with key 'k' for HMAC comparison - const std::vector> hmac_comparisons = { - { "7f330edb3a84f317f7ca433d6038ff9a", "MD5" }, - { "de693e9b565099e8fe8129b3833a702d", "MD4" }, - { "a10a9e7a2bcfa4cd38d0a1cab3f25816", "MD2" }, - { "2b90e41e7c0cc8e2f75c02910c3899cc468ba316", "SHA1" }, - { "c9b7d3a28728c8a0b10dd425247af65e00725f6e5d32131b0c9a4ac1", "SHA2-224" }, - { "e7ea21c3bcb63a4da3ad78503168d36bdca0be622382ea60a108fad4e4966679", "SHA2-256" }, - { "48da203588bac88ca21d843f0dd201e15e33fe08a4db11ff4f07d2b62e2e10dee4e55d49612a658a9e5ac2c0a6b8e945", "SHA2-384" }, - { "75e6621bf12000a13d8dae79fed84aadffbbceaefd36ae061493b34aef6a2988f0fb91b8ba4fef293ed0bd09e6bb7578858b8f2f7f70fe3ca7490d37f655fd38", "SHA2-512" }, - { "7882112b43ad00ad1a01bc1a8df3745aad04e27a999ceb60da32bb18", "SHA2-512/224" }, - { "df48fa6a1e87fc2ccdce7a79028b4cd891ce905ebf411898c9aba975f3a2f8ad", "SHA2-512/256" } - }; - }; -#endif -} - -#endif \ No newline at end of file diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h deleted file mode 100755 index 44ec5295..00000000 --- a/include/rapidjson/allocators.h +++ /dev/null @@ -1,284 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_ALLOCATORS_H_ -#define RAPIDJSON_ALLOCATORS_H_ - -#include "rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Allocator - -/*! \class rapidjson::Allocator - \brief Concept for allocating, resizing and freeing memory block. - - Note that Malloc() and Realloc() are non-static but Free() is static. - - So if an allocator need to support Free(), it needs to put its pointer in - the header of memory block. - -\code -concept Allocator { - static const bool kNeedFree; //!< Whether this allocator needs to call Free(). - - // Allocate a memory block. - // \param size of the memory block in bytes. - // \returns pointer to the memory block. - void* Malloc(size_t size); - - // Resize a memory block. - // \param originalPtr The pointer to current memory block. Null pointer is permitted. - // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) - // \param newSize the new size in bytes. - void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); - - // Free a memory block. - // \param pointer to the memory block. Null pointer is permitted. - static void Free(void *ptr); -}; -\endcode -*/ - - -/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY - \ingroup RAPIDJSON_CONFIG - \brief User-defined kDefaultChunkCapacity definition. - - User can define this as any \c size that is a power of 2. -*/ - -#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY -#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) -#endif - - -/////////////////////////////////////////////////////////////////////////////// -// CrtAllocator - -//! C-runtime library allocator. -/*! This class is just wrapper for standard C library memory routines. - \note implements Allocator concept -*/ -class CrtAllocator { -public: - static const bool kNeedFree = true; - void* Malloc(size_t size) { - if (size) // behavior of malloc(0) is implementation defined. - return RAPIDJSON_MALLOC(size); - else - return NULL; // standardize to returning NULL. - } - void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { - (void)originalSize; - if (newSize == 0) { - RAPIDJSON_FREE(originalPtr); - return NULL; - } - return RAPIDJSON_REALLOC(originalPtr, newSize); - } - static void Free(void *ptr) { RAPIDJSON_FREE(ptr); } -}; - -/////////////////////////////////////////////////////////////////////////////// -// MemoryPoolAllocator - -//! Default memory allocator used by the parser and DOM. -/*! This allocator allocate memory blocks from pre-allocated memory chunks. - - It does not free memory blocks. And Realloc() only allocate new memory. - - The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. - - User may also supply a buffer as the first chunk. - - If the user-buffer is full then additional chunks are allocated by BaseAllocator. - - The user-buffer is not deallocated by this allocator. - - \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. - \note implements Allocator concept -*/ -template -class MemoryPoolAllocator { -public: - static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) - - //! Constructor with chunkSize. - /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. - \param baseAllocator The allocator for allocating memory chunks. - */ - MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : - chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) - { - } - - //! Constructor with user-supplied buffer. - /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. - - The user buffer will not be deallocated when this allocator is destructed. - - \param buffer User supplied buffer. - \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). - \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. - \param baseAllocator The allocator for allocating memory chunks. - */ - MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : - chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) - { - RAPIDJSON_ASSERT(buffer != 0); - RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); - chunkHead_ = reinterpret_cast(buffer); - chunkHead_->capacity = size - sizeof(ChunkHeader); - chunkHead_->size = 0; - chunkHead_->next = 0; - } - - //! Destructor. - /*! This deallocates all memory chunks, excluding the user-supplied buffer. - */ - ~MemoryPoolAllocator() { - Clear(); - RAPIDJSON_DELETE(ownBaseAllocator_); - } - - //! Deallocates all memory chunks, excluding the user-supplied buffer. - void Clear() { - while (chunkHead_ && chunkHead_ != userBuffer_) { - ChunkHeader* next = chunkHead_->next; - baseAllocator_->Free(chunkHead_); - chunkHead_ = next; - } - if (chunkHead_ && chunkHead_ == userBuffer_) - chunkHead_->size = 0; // Clear user buffer - } - - //! Computes the total capacity of allocated memory chunks. - /*! \return total capacity in bytes. - */ - size_t Capacity() const { - size_t capacity = 0; - for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) - capacity += c->capacity; - return capacity; - } - - //! Computes the memory blocks allocated. - /*! \return total used bytes. - */ - size_t Size() const { - size_t size = 0; - for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) - size += c->size; - return size; - } - - //! Allocates a memory block. (concept Allocator) - void* Malloc(size_t size) { - if (!size) - return NULL; - - size = RAPIDJSON_ALIGN(size); - if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) - if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) - return NULL; - - void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; - chunkHead_->size += size; - return buffer; - } - - //! Resizes a memory block (concept Allocator) - void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { - if (originalPtr == 0) - return Malloc(newSize); - - if (newSize == 0) - return NULL; - - originalSize = RAPIDJSON_ALIGN(originalSize); - newSize = RAPIDJSON_ALIGN(newSize); - - // Do not shrink if new size is smaller than original - if (originalSize >= newSize) - return originalPtr; - - // Simply expand it if it is the last allocation and there is sufficient space - if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { - size_t increment = static_cast(newSize - originalSize); - if (chunkHead_->size + increment <= chunkHead_->capacity) { - chunkHead_->size += increment; - return originalPtr; - } - } - - // Realloc process: allocate and copy memory, do not free original buffer. - if (void* newBuffer = Malloc(newSize)) { - if (originalSize) - std::memcpy(newBuffer, originalPtr, originalSize); - return newBuffer; - } - else - return NULL; - } - - //! Frees a memory block (concept Allocator) - static void Free(void *ptr) { (void)ptr; } // Do nothing - -private: - //! Copy constructor is not permitted. - MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; - //! Copy assignment operator is not permitted. - MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; - - //! Creates a new chunk. - /*! \param capacity Capacity of the chunk in bytes. - \return true if success. - */ - bool AddChunk(size_t capacity) { - if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); - if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { - chunk->capacity = capacity; - chunk->size = 0; - chunk->next = chunkHead_; - chunkHead_ = chunk; - return true; - } - else - return false; - } - - static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. - - //! Chunk header for perpending to each chunk. - /*! Chunks are stored as a singly linked list. - */ - struct ChunkHeader { - size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). - size_t size; //!< Current size of allocated memory in bytes. - ChunkHeader *next; //!< Next chunk in the linked list. - }; - - ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. - size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. - void *userBuffer_; //!< User supplied buffer. - BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. - BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. -}; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/cursorstreamwrapper.h b/include/rapidjson/cursorstreamwrapper.h deleted file mode 100755 index fd6513db..00000000 --- a/include/rapidjson/cursorstreamwrapper.h +++ /dev/null @@ -1,78 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ -#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ - -#include "stream.h" - -#if defined(__GNUC__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -#if defined(_MSC_VER) && _MSC_VER <= 1800 -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4702) // unreachable code -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - -RAPIDJSON_NAMESPACE_BEGIN - - -//! Cursor stream wrapper for counting line and column number if error exists. -/*! - \tparam InputStream Any stream that implements Stream Concept -*/ -template > -class CursorStreamWrapper : public GenericStreamWrapper { -public: - typedef typename Encoding::Ch Ch; - - CursorStreamWrapper(InputStream& is): - GenericStreamWrapper(is), line_(1), col_(0) {} - - // counting line and column number - Ch Take() { - Ch ch = this->is_.Take(); - if(ch == '\n') { - line_ ++; - col_ = 0; - } else { - col_ ++; - } - return ch; - } - - //! Get the error line number, if error exists. - size_t GetLine() const { return line_; } - //! Get the error column number, if error exists. - size_t GetColumn() const { return col_; } - -private: - size_t line_; //!< Current Line - size_t col_; //!< Current Column -}; - -#if defined(_MSC_VER) && _MSC_VER <= 1800 -RAPIDJSON_DIAG_POP -#endif - -#if defined(__GNUC__) -RAPIDJSON_DIAG_POP -#endif - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h deleted file mode 100755 index 6ed85160..00000000 --- a/include/rapidjson/document.h +++ /dev/null @@ -1,2732 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_DOCUMENT_H_ -#define RAPIDJSON_DOCUMENT_H_ - -/*! \file document.h */ - -#include "reader.h" -#include "internal/meta.h" -#include "internal/strfunc.h" -#include "memorystream.h" -#include "encodedstream.h" -#include // placement new -#include -#ifdef __cpp_lib_three_way_comparison -#include -#endif - -RAPIDJSON_DIAG_PUSH -#ifdef __clang__ -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(switch-enum) -RAPIDJSON_DIAG_OFF(c++98-compat) -#elif defined(_MSC_VER) -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_OFF(effc++) -#endif // __GNUC__ - -#ifndef RAPIDJSON_NOMEMBERITERATORCLASS -#include // std::random_access_iterator_tag -#endif - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS -#include // std::move -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -// Forward declaration. -template -class GenericValue; - -template -class GenericDocument; - -/*! \def RAPIDJSON_DEFAULT_ALLOCATOR - \ingroup RAPIDJSON_CONFIG - \brief Allows to choose default allocator. - - User can define this to use CrtAllocator or MemoryPoolAllocator. -*/ -#ifndef RAPIDJSON_DEFAULT_ALLOCATOR -#define RAPIDJSON_DEFAULT_ALLOCATOR MemoryPoolAllocator -#endif - -/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR - \ingroup RAPIDJSON_CONFIG - \brief Allows to choose default stack allocator for Document. - - User can define this to use CrtAllocator or MemoryPoolAllocator. -*/ -#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR -#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR CrtAllocator -#endif - -/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY - \ingroup RAPIDJSON_CONFIG - \brief User defined kDefaultObjectCapacity value. - - User can define this as any natural number. -*/ -#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY -// number of objects that rapidjson::Value allocates memory for by default -#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 -#endif - -/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY - \ingroup RAPIDJSON_CONFIG - \brief User defined kDefaultArrayCapacity value. - - User can define this as any natural number. -*/ -#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY -// number of array elements that rapidjson::Value allocates memory for by default -#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 -#endif - -//! Name-value pair in a JSON object value. -/*! - This class was internal to GenericValue. It used to be a inner struct. - But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. - https://code.google.com/p/rapidjson/issues/detail?id=64 -*/ -template -class GenericMember { -public: - GenericValue name; //!< name of member (must be a string) - GenericValue value; //!< value of member. - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move constructor in C++11 - GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT - : name(std::move(rhs.name)), - value(std::move(rhs.value)) - { - } - - //! Move assignment in C++11 - GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { - return *this = static_cast(rhs); - } -#endif - - //! Assignment with move semantics. - /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. - */ - GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { - if (RAPIDJSON_LIKELY(this != &rhs)) { - name = rhs.name; - value = rhs.value; - } - return *this; - } - - // swap() for std::sort() and other potential use in STL. - friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { - a.name.Swap(b.name); - a.value.Swap(b.value); - } - -private: - //! Copy constructor is not permitted. - GenericMember(const GenericMember& rhs); -}; - -/////////////////////////////////////////////////////////////////////////////// -// GenericMemberIterator - -#ifndef RAPIDJSON_NOMEMBERITERATORCLASS - -//! (Constant) member iterator for a JSON object value -/*! - \tparam Const Is this a constant iterator? - \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) - \tparam Allocator Allocator type for allocating memory of object, array and string. - - This class implements a Random Access Iterator for GenericMember elements - of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. - - \note This iterator implementation is mainly intended to avoid implicit - conversions from iterator values to \c NULL, - e.g. from GenericValue::FindMember. - - \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a - pointer-based implementation, if your platform doesn't provide - the C++ header. - - \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator - */ -template -class GenericMemberIterator { - - friend class GenericValue; - template friend class GenericMemberIterator; - - typedef GenericMember PlainType; - typedef typename internal::MaybeAddConst::Type ValueType; - -public: - //! Iterator type itself - typedef GenericMemberIterator Iterator; - //! Constant iterator type - typedef GenericMemberIterator ConstIterator; - //! Non-constant iterator type - typedef GenericMemberIterator NonConstIterator; - - /** \name std::iterator_traits support */ - //@{ - typedef ValueType value_type; - typedef ValueType * pointer; - typedef ValueType & reference; - typedef std::ptrdiff_t difference_type; - typedef std::random_access_iterator_tag iterator_category; - //@} - - //! Pointer to (const) GenericMember - typedef pointer Pointer; - //! Reference to (const) GenericMember - typedef reference Reference; - //! Signed integer type (e.g. \c ptrdiff_t) - typedef difference_type DifferenceType; - - //! Default constructor (singular value) - /*! Creates an iterator pointing to no element. - \note All operations, except for comparisons, are undefined on such values. - */ - GenericMemberIterator() : ptr_() {} - - //! Iterator conversions to more const - /*! - \param it (Non-const) iterator to copy from - - Allows the creation of an iterator from another GenericMemberIterator - that is "less const". Especially, creating a non-constant iterator - from a constant iterator are disabled: - \li const -> non-const (not ok) - \li const -> const (ok) - \li non-const -> const (ok) - \li non-const -> non-const (ok) - - \note If the \c Const template parameter is already \c false, this - constructor effectively defines a regular copy-constructor. - Otherwise, the copy constructor is implicitly defined. - */ - GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} - Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } - - //! @name stepping - //@{ - Iterator& operator++(){ ++ptr_; return *this; } - Iterator& operator--(){ --ptr_; return *this; } - Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } - Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } - //@} - - //! @name increment/decrement - //@{ - Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } - Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } - - Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } - Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } - //@} - - //! @name relations - //@{ - template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } - template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } - template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } - template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } - template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } - template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } - -#ifdef __cpp_lib_three_way_comparison - template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } -#endif - //@} - - //! @name dereference - //@{ - Reference operator*() const { return *ptr_; } - Pointer operator->() const { return ptr_; } - Reference operator[](DifferenceType n) const { return ptr_[n]; } - //@} - - //! Distance - DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } - -private: - //! Internal constructor from plain pointer - explicit GenericMemberIterator(Pointer p) : ptr_(p) {} - - Pointer ptr_; //!< raw pointer -}; - -#else // RAPIDJSON_NOMEMBERITERATORCLASS - -// class-based member iterator implementation disabled, use plain pointers - -template -class GenericMemberIterator; - -//! non-const GenericMemberIterator -template -class GenericMemberIterator { - //! use plain pointer as iterator type - typedef GenericMember* Iterator; -}; -//! const GenericMemberIterator -template -class GenericMemberIterator { - //! use plain const pointer as iterator type - typedef const GenericMember* Iterator; -}; - -#endif // RAPIDJSON_NOMEMBERITERATORCLASS - -/////////////////////////////////////////////////////////////////////////////// -// GenericStringRef - -//! Reference to a constant string (not taking a copy) -/*! - \tparam CharType character type of the string - - This helper class is used to automatically infer constant string - references for string literals, especially from \c const \b (!) - character arrays. - - The main use is for creating JSON string values without copying the - source string via an \ref Allocator. This requires that the referenced - string pointers have a sufficient lifetime, which exceeds the lifetime - of the associated GenericValue. - - \b Example - \code - Value v("foo"); // ok, no need to copy & calculate length - const char foo[] = "foo"; - v.SetString(foo); // ok - - const char* bar = foo; - // Value x(bar); // not ok, can't rely on bar's lifetime - Value x(StringRef(bar)); // lifetime explicitly guaranteed by user - Value y(StringRef(bar, 3)); // ok, explicitly pass length - \endcode - - \see StringRef, GenericValue::SetString -*/ -template -struct GenericStringRef { - typedef CharType Ch; //!< character type of the string - - //! Create string reference from \c const character array -#ifndef __clang__ // -Wdocumentation - /*! - This constructor implicitly creates a constant string reference from - a \c const character array. It has better performance than - \ref StringRef(const CharType*) by inferring the string \ref length - from the array length, and also supports strings containing null - characters. - - \tparam N length of the string, automatically inferred - - \param str Constant character array, lifetime assumed to be longer - than the use of the string in e.g. a GenericValue - - \post \ref s == str - - \note Constant complexity. - \note There is a hidden, private overload to disallow references to - non-const character arrays to be created via this constructor. - By this, e.g. function-scope arrays used to be filled via - \c snprintf are excluded from consideration. - In such cases, the referenced string should be \b copied to the - GenericValue instead. - */ -#endif - template - GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT - : s(str), length(N-1) {} - - //! Explicitly create string reference from \c const character pointer -#ifndef __clang__ // -Wdocumentation - /*! - This constructor can be used to \b explicitly create a reference to - a constant string pointer. - - \see StringRef(const CharType*) - - \param str Constant character pointer, lifetime assumed to be longer - than the use of the string in e.g. a GenericValue - - \post \ref s == str - - \note There is a hidden, private overload to disallow references to - non-const character arrays to be created via this constructor. - By this, e.g. function-scope arrays used to be filled via - \c snprintf are excluded from consideration. - In such cases, the referenced string should be \b copied to the - GenericValue instead. - */ -#endif - explicit GenericStringRef(const CharType* str) - : s(str), length(NotNullStrLen(str)) {} - - //! Create constant string reference from pointer and length -#ifndef __clang__ // -Wdocumentation - /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue - \param len length of the string, excluding the trailing NULL terminator - - \post \ref s == str && \ref length == len - \note Constant complexity. - */ -#endif - GenericStringRef(const CharType* str, SizeType len) - : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } - - GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} - - //! implicit conversion to plain CharType pointer - operator const Ch *() const { return s; } - - const Ch* const s; //!< plain CharType pointer - const SizeType length; //!< length of the string (excluding the trailing NULL terminator) - -private: - SizeType NotNullStrLen(const CharType* str) { - RAPIDJSON_ASSERT(str != 0); - return internal::StrLen(str); - } - - /// Empty string - used when passing in a NULL pointer - static const Ch emptyString[]; - - //! Disallow construction from non-const array - template - GenericStringRef(CharType (&str)[N]) /* = delete */; - //! Copy assignment operator not permitted - immutable type - GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; -}; - -template -const CharType GenericStringRef::emptyString[] = { CharType() }; - -//! Mark a character pointer as constant string -/*! Mark a plain character pointer as a "string literal". This function - can be used to avoid copying a character string to be referenced as a - value in a JSON GenericValue object, if the string's lifetime is known - to be valid long enough. - \tparam CharType Character type of the string - \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue - \return GenericStringRef string reference object - \relatesalso GenericStringRef - - \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember -*/ -template -inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str); -} - -//! Mark a character pointer as constant string -/*! Mark a plain character pointer as a "string literal". This function - can be used to avoid copying a character string to be referenced as a - value in a JSON GenericValue object, if the string's lifetime is known - to be valid long enough. - - This version has better performance with supplied length, and also - supports string containing null characters. - - \tparam CharType character type of the string - \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue - \param length The length of source string. - \return GenericStringRef string reference object - \relatesalso GenericStringRef -*/ -template -inline GenericStringRef StringRef(const CharType* str, size_t length) { - return GenericStringRef(str, SizeType(length)); -} - -#if RAPIDJSON_HAS_STDSTRING -//! Mark a string object as constant string -/*! Mark a string object (e.g. \c std::string) as a "string literal". - This function can be used to avoid copying a string to be referenced as a - value in a JSON GenericValue object, if the string's lifetime is known - to be valid long enough. - - \tparam CharType character type of the string - \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue - \return GenericStringRef string reference object - \relatesalso GenericStringRef - \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. -*/ -template -inline GenericStringRef StringRef(const std::basic_string& str) { - return GenericStringRef(str.data(), SizeType(str.size())); -} -#endif - -/////////////////////////////////////////////////////////////////////////////// -// GenericValue type traits -namespace internal { - -template -struct IsGenericValueImpl : FalseType {}; - -// select candidates according to nested encoding and allocator types -template struct IsGenericValueImpl::Type, typename Void::Type> - : IsBaseOf, T>::Type {}; - -// helper to match arbitrary GenericValue instantiations, including derived classes -template struct IsGenericValue : IsGenericValueImpl::Type {}; - -} // namespace internal - -/////////////////////////////////////////////////////////////////////////////// -// TypeHelper - -namespace internal { - -template -struct TypeHelper {}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsBool(); } - static bool Get(const ValueType& v) { return v.GetBool(); } - static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } - static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsInt(); } - static int Get(const ValueType& v) { return v.GetInt(); } - static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } - static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsUint(); } - static unsigned Get(const ValueType& v) { return v.GetUint(); } - static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } - static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } -}; - -#ifdef _MSC_VER -RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsInt(); } - static long Get(const ValueType& v) { return v.GetInt(); } - static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } - static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } -}; - -RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsUint(); } - static unsigned long Get(const ValueType& v) { return v.GetUint(); } - static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } - static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } -}; -#endif - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsInt64(); } - static int64_t Get(const ValueType& v) { return v.GetInt64(); } - static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } - static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsUint64(); } - static uint64_t Get(const ValueType& v) { return v.GetUint64(); } - static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } - static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsDouble(); } - static double Get(const ValueType& v) { return v.GetDouble(); } - static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } - static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } -}; - -template -struct TypeHelper { - static bool Is(const ValueType& v) { return v.IsFloat(); } - static float Get(const ValueType& v) { return v.GetFloat(); } - static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } - static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } -}; - -template -struct TypeHelper { - typedef const typename ValueType::Ch* StringType; - static bool Is(const ValueType& v) { return v.IsString(); } - static StringType Get(const ValueType& v) { return v.GetString(); } - static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } - static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } -}; - -#if RAPIDJSON_HAS_STDSTRING -template -struct TypeHelper > { - typedef std::basic_string StringType; - static bool Is(const ValueType& v) { return v.IsString(); } - static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } - static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } -}; -#endif - -template -struct TypeHelper { - typedef typename ValueType::Array ArrayType; - static bool Is(const ValueType& v) { return v.IsArray(); } - static ArrayType Get(ValueType& v) { return v.GetArray(); } - static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } - static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } -}; - -template -struct TypeHelper { - typedef typename ValueType::ConstArray ArrayType; - static bool Is(const ValueType& v) { return v.IsArray(); } - static ArrayType Get(const ValueType& v) { return v.GetArray(); } -}; - -template -struct TypeHelper { - typedef typename ValueType::Object ObjectType; - static bool Is(const ValueType& v) { return v.IsObject(); } - static ObjectType Get(ValueType& v) { return v.GetObject(); } - static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } - static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } -}; - -template -struct TypeHelper { - typedef typename ValueType::ConstObject ObjectType; - static bool Is(const ValueType& v) { return v.IsObject(); } - static ObjectType Get(const ValueType& v) { return v.GetObject(); } -}; - -} // namespace internal - -// Forward declarations -template class GenericArray; -template class GenericObject; - -/////////////////////////////////////////////////////////////////////////////// -// GenericValue - -//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. -/*! - A JSON value can be one of 7 types. This class is a variant type supporting - these types. - - Use the Value if UTF8 and default allocator - - \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) - \tparam Allocator Allocator type for allocating memory of object, array and string. -*/ -template -class GenericValue { -public: - //! Name-value pair in an object. - typedef GenericMember Member; - typedef Encoding EncodingType; //!< Encoding type from template parameter. - typedef Allocator AllocatorType; //!< Allocator type from template parameter. - typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. - typedef GenericStringRef StringRefType; //!< Reference to a constant string - typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. - typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. - typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. - typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. - typedef GenericValue ValueType; //!< Value type of itself. - typedef GenericArray Array; - typedef GenericArray ConstArray; - typedef GenericObject Object; - typedef GenericObject ConstObject; - - //!@name Constructors and destructor. - //@{ - - //! Default constructor creates a null value. - GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move constructor in C++11 - GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { - rhs.data_.f.flags = kNullFlag; // give up contents - } -#endif - -private: - //! Copy constructor is not permitted. - GenericValue(const GenericValue& rhs); - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Moving from a GenericDocument is not permitted. - template - GenericValue(GenericDocument&& rhs); - - //! Move assignment from a GenericDocument is not permitted. - template - GenericValue& operator=(GenericDocument&& rhs); -#endif - -public: - - //! Constructor with JSON value type. - /*! This creates a Value of specified type with default content. - \param type Type of the value. - \note Default content for number is zero. - */ - explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { - static const uint16_t defaultFlags[] = { - kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, - kNumberAnyFlag - }; - RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); - data_.f.flags = defaultFlags[type]; - - // Use ShortString to store empty string. - if (type == kStringType) - data_.ss.SetLength(0); - } - - //! Explicit copy constructor (with allocator) - /*! Creates a copy of a Value by using the given Allocator - \tparam SourceAllocator allocator of \c rhs - \param rhs Value to copy from (read-only) - \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). - \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) - \see CopyFrom() - */ - template - GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { - switch (rhs.GetType()) { - case kObjectType: { - SizeType count = rhs.data_.o.size; - Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); - const typename GenericValue::Member* rm = rhs.GetMembersPointer(); - for (SizeType i = 0; i < count; i++) { - new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); - new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); - } - data_.f.flags = kObjectFlag; - data_.o.size = data_.o.capacity = count; - SetMembersPointer(lm); - } - break; - case kArrayType: { - SizeType count = rhs.data_.a.size; - GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); - const GenericValue* re = rhs.GetElementsPointer(); - for (SizeType i = 0; i < count; i++) - new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); - data_.f.flags = kArrayFlag; - data_.a.size = data_.a.capacity = count; - SetElementsPointer(le); - } - break; - case kStringType: - if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - } - else - SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); - break; - default: - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - break; - } - } - - //! Constructor for boolean value. - /*! \param b Boolean value - \note This constructor is limited to \em real boolean values and rejects - implicitly converted types like arbitrary pointers. Use an explicit cast - to \c bool, if you want to construct a boolean JSON value in such cases. - */ -#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen - template - explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 -#else - explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT -#endif - : data_() { - // safe-guard against failing SFINAE - RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); - data_.f.flags = b ? kTrueFlag : kFalseFlag; - } - - //! Constructor for int value. - explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { - data_.n.i64 = i; - data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; - } - - //! Constructor for unsigned value. - explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { - data_.n.u64 = u; - data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); - } - - //! Constructor for int64_t value. - explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { - data_.n.i64 = i64; - data_.f.flags = kNumberInt64Flag; - if (i64 >= 0) { - data_.f.flags |= kNumberUint64Flag; - if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - data_.f.flags |= kUintFlag; - if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - data_.f.flags |= kIntFlag; - } - else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - data_.f.flags |= kIntFlag; - } - - //! Constructor for uint64_t value. - explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { - data_.n.u64 = u64; - data_.f.flags = kNumberUint64Flag; - if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) - data_.f.flags |= kInt64Flag; - if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - data_.f.flags |= kUintFlag; - if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - data_.f.flags |= kIntFlag; - } - - //! Constructor for double value. - explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } - - //! Constructor for float value. - explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } - - //! Constructor for constant string (i.e. do not make a copy of string) - GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } - - //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } - - //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } - - //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } - -#if RAPIDJSON_HAS_STDSTRING - //! Constructor for copy-string from a string object (i.e. do make a copy of string) - /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. - */ - GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } -#endif - - //! Constructor for Array. - /*! - \param a An array obtained by \c GetArray(). - \note \c Array is always pass-by-value. - \note the source array is moved into this value and the sourec array becomes empty. - */ - GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { - a.value_.data_ = Data(); - a.value_.data_.f.flags = kArrayFlag; - } - - //! Constructor for Object. - /*! - \param o An object obtained by \c GetObject(). - \note \c Object is always pass-by-value. - \note the source object is moved into this value and the sourec object becomes empty. - */ - GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { - o.value_.data_ = Data(); - o.value_.data_.f.flags = kObjectFlag; - } - - //! Destructor. - /*! Need to destruct elements of array, members of object, or copy-string. - */ - ~GenericValue() { - if (Allocator::kNeedFree) { // Shortcut by Allocator's trait - switch(data_.f.flags) { - case kArrayFlag: - { - GenericValue* e = GetElementsPointer(); - for (GenericValue* v = e; v != e + data_.a.size; ++v) - v->~GenericValue(); - Allocator::Free(e); - } - break; - - case kObjectFlag: - for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) - m->~Member(); - Allocator::Free(GetMembersPointer()); - break; - - case kCopyStringFlag: - Allocator::Free(const_cast(GetStringPointer())); - break; - - default: - break; // Do nothing for other types. - } - } - } - - //@} - - //!@name Assignment operators - //@{ - - //! Assignment with move semantics. - /*! \param rhs Source of the assignment. It will become a null value after assignment. - */ - GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { - if (RAPIDJSON_LIKELY(this != &rhs)) { - this->~GenericValue(); - RawAssign(rhs); - } - return *this; - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move assignment in C++11 - GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { - return *this = rhs.Move(); - } -#endif - - //! Assignment of constant string reference (no copy) - /*! \param str Constant string reference to be assigned - \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. - \see GenericStringRef, operator=(T) - */ - GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { - GenericValue s(str); - return *this = s; - } - - //! Assignment with primitive types. - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t - \param value The value to be assigned. - - \note The source type \c T explicitly disallows all pointer types, - especially (\c const) \ref Ch*. This helps avoiding implicitly - referencing character strings with insufficient lifetime, use - \ref SetString(const Ch*, Allocator&) (for copying) or - \ref StringRef() (to explicitly mark the pointer as constant) instead. - All other pointer types would implicitly convert to \c bool, - use \ref SetBool() instead. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) - operator=(T value) { - GenericValue v(value); - return *this = v; - } - - //! Deep-copy assignment from Value - /*! Assigns a \b copy of the Value to the current Value object - \tparam SourceAllocator Allocator type of \c rhs - \param rhs Value to copy from (read-only) - \param allocator Allocator to use for copying - \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) - */ - template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { - RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); - this->~GenericValue(); - new (this) GenericValue(rhs, allocator, copyConstStrings); - return *this; - } - - //! Exchange the contents of this value with those of other. - /*! - \param other Another value. - \note Constant complexity. - */ - GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { - GenericValue temp; - temp.RawAssign(*this); - RawAssign(other); - other.RawAssign(temp); - return *this; - } - - //! free-standing swap function helper - /*! - Helper function to enable support for common swap implementation pattern based on \c std::swap: - \code - void swap(MyClass& a, MyClass& b) { - using std::swap; - swap(a.value, b.value); - // ... - } - \endcode - \see Swap() - */ - friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } - - //! Prepare Value for move semantics - /*! \return *this */ - GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } - //@} - - //!@name Equal-to and not-equal-to operators - //@{ - //! Equal-to operator - /*! - \note If an object contains duplicated named member, comparing equality with any object is always \c false. - \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). - */ - template - bool operator==(const GenericValue& rhs) const { - typedef GenericValue RhsType; - if (GetType() != rhs.GetType()) - return false; - - switch (GetType()) { - case kObjectType: // Warning: O(n^2) inner-loop - if (data_.o.size != rhs.data_.o.size) - return false; - for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { - typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); - if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) - return false; - } - return true; - - case kArrayType: - if (data_.a.size != rhs.data_.a.size) - return false; - for (SizeType i = 0; i < data_.a.size; i++) - if ((*this)[i] != rhs[i]) - return false; - return true; - - case kStringType: - return StringEqual(rhs); - - case kNumberType: - if (IsDouble() || rhs.IsDouble()) { - double a = GetDouble(); // May convert from integer to double. - double b = rhs.GetDouble(); // Ditto - return a >= b && a <= b; // Prevent -Wfloat-equal - } - else - return data_.n.u64 == rhs.data_.n.u64; - - default: - return true; - } - } - - //! Equal-to operator with const C-string pointer - bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } - -#if RAPIDJSON_HAS_STDSTRING - //! Equal-to operator with string object - /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. - */ - bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } -#endif - - //! Equal-to operator with primitive types - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false - */ - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } - - //! Not-equal-to operator - /*! \return !(*this == rhs) - */ - template - bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } - - //! Not-equal-to operator with const C-string pointer - bool operator!=(const Ch* rhs) const { return !(*this == rhs); } - - //! Not-equal-to operator with arbitrary types - /*! \return !(*this == rhs) - */ - template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } - - //! Equal-to operator with arbitrary types (symmetric version) - /*! \return (rhs == lhs) - */ - template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } - - //! Not-Equal-to operator with arbitrary types (symmetric version) - /*! \return !(rhs == lhs) - */ - template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } - //@} - - //!@name Type - //@{ - - Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } - bool IsNull() const { return data_.f.flags == kNullFlag; } - bool IsFalse() const { return data_.f.flags == kFalseFlag; } - bool IsTrue() const { return data_.f.flags == kTrueFlag; } - bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } - bool IsObject() const { return data_.f.flags == kObjectFlag; } - bool IsArray() const { return data_.f.flags == kArrayFlag; } - bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } - bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } - bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } - bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } - bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } - bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } - bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } - - // Checks whether a number can be losslessly converted to a double. - bool IsLosslessDouble() const { - if (!IsNumber()) return false; - if (IsUint64()) { - uint64_t u = GetUint64(); - volatile double d = static_cast(u); - return (d >= 0.0) - && (d < static_cast((std::numeric_limits::max)())) - && (u == static_cast(d)); - } - if (IsInt64()) { - int64_t i = GetInt64(); - volatile double d = static_cast(i); - return (d >= static_cast((std::numeric_limits::min)())) - && (d < static_cast((std::numeric_limits::max)())) - && (i == static_cast(d)); - } - return true; // double, int, uint are always lossless - } - - // Checks whether a number is a float (possible lossy). - bool IsFloat() const { - if ((data_.f.flags & kDoubleFlag) == 0) - return false; - double d = GetDouble(); - return d >= -3.4028234e38 && d <= 3.4028234e38; - } - // Checks whether a number can be losslessly converted to a float. - bool IsLosslessFloat() const { - if (!IsNumber()) return false; - double a = GetDouble(); - if (a < static_cast(-(std::numeric_limits::max)()) - || a > static_cast((std::numeric_limits::max)())) - return false; - double b = static_cast(static_cast(a)); - return a >= b && a <= b; // Prevent -Wfloat-equal - } - - //@} - - //!@name Null - //@{ - - GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } - - //@} - - //!@name Bool - //@{ - - bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } - //!< Set boolean value - /*! \post IsBool() == true */ - GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } - - //@} - - //!@name Object - //@{ - - //! Set this value as an empty object. - /*! \post IsObject() == true */ - GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } - - //! Get the number of members in the object. - SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } - - //! Get the capacity of object. - SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } - - //! Check whether the object is empty. - bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } - - //! Get a value from an object associated with the name. - /*! \pre IsObject() == true - \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) - \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. - Since 0.2, if the name is not correct, it will assert. - If user is unsure whether a member exists, user should use HasMember() first. - A better approach is to use FindMember(). - \note Linear time complexity. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { - GenericValue n(StringRef(name)); - return (*this)[n]; - } - template - RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } - - //! Get a value from an object associated with the name. - /*! \pre IsObject() == true - \tparam SourceAllocator Allocator of the \c name value - - \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). - And it can also handle strings with embedded null characters. - - \note Linear time complexity. - */ - template - GenericValue& operator[](const GenericValue& name) { - MemberIterator member = FindMember(name); - if (member != MemberEnd()) - return member->value; - else { - RAPIDJSON_ASSERT(false); // see above note - - // This will generate -Wexit-time-destructors in clang - // static GenericValue NullValue; - // return NullValue; - - // Use static buffer and placement-new to prevent destruction - static char buffer[sizeof(GenericValue)]; - return *new (buffer) GenericValue(); - } - } - template - const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } - -#if RAPIDJSON_HAS_STDSTRING - //! Get a value from an object associated with name (string object). - GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } - const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } -#endif - - //! Const member iterator - /*! \pre IsObject() == true */ - ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } - //! Const \em past-the-end member iterator - /*! \pre IsObject() == true */ - ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } - //! Member iterator - /*! \pre IsObject() == true */ - MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } - //! \em Past-the-end member iterator - /*! \pre IsObject() == true */ - MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } - - //! Request the object to have enough capacity to store members. - /*! \param newCapacity The capacity that the object at least need to have. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \note Linear time complexity. - */ - GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { - RAPIDJSON_ASSERT(IsObject()); - if (newCapacity > data_.o.capacity) { - SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member)))); - data_.o.capacity = newCapacity; - } - return *this; - } - - //! Check whether a member exists in the object. - /*! - \param name Member name to be searched. - \pre IsObject() == true - \return Whether a member with that name exists. - \note It is better to use FindMember() directly if you need the obtain the value as well. - \note Linear time complexity. - */ - bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } - -#if RAPIDJSON_HAS_STDSTRING - //! Check whether a member exists in the object with string object. - /*! - \param name Member name to be searched. - \pre IsObject() == true - \return Whether a member with that name exists. - \note It is better to use FindMember() directly if you need the obtain the value as well. - \note Linear time complexity. - */ - bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } -#endif - - //! Check whether a member exists in the object with GenericValue name. - /*! - This version is faster because it does not need a StrLen(). It can also handle string with null character. - \param name Member name to be searched. - \pre IsObject() == true - \return Whether a member with that name exists. - \note It is better to use FindMember() directly if you need the obtain the value as well. - \note Linear time complexity. - */ - template - bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } - - //! Find member by name. - /*! - \param name Member name to be searched. - \pre IsObject() == true - \return Iterator to member, if it exists. - Otherwise returns \ref MemberEnd(). - - \note Earlier versions of Rapidjson returned a \c NULL pointer, in case - the requested member doesn't exist. For consistency with e.g. - \c std::map, this has been changed to MemberEnd() now. - \note Linear time complexity. - */ - MemberIterator FindMember(const Ch* name) { - GenericValue n(StringRef(name)); - return FindMember(n); - } - - ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } - - //! Find member by name. - /*! - This version is faster because it does not need a StrLen(). It can also handle string with null character. - \param name Member name to be searched. - \pre IsObject() == true - \return Iterator to member, if it exists. - Otherwise returns \ref MemberEnd(). - - \note Earlier versions of Rapidjson returned a \c NULL pointer, in case - the requested member doesn't exist. For consistency with e.g. - \c std::map, this has been changed to MemberEnd() now. - \note Linear time complexity. - */ - template - MemberIterator FindMember(const GenericValue& name) { - RAPIDJSON_ASSERT(IsObject()); - RAPIDJSON_ASSERT(name.IsString()); - MemberIterator member = MemberBegin(); - for ( ; member != MemberEnd(); ++member) - if (name.StringEqual(member->name)) - break; - return member; - } - template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } - -#if RAPIDJSON_HAS_STDSTRING - //! Find member by string object name. - /*! - \param name Member name to be searched. - \pre IsObject() == true - \return Iterator to member, if it exists. - Otherwise returns \ref MemberEnd(). - */ - MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } - ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } -#endif - - //! Add a member (name-value pair) to the object. - /*! \param name A string value as name of member. - \param value Value of any type. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \note The ownership of \c name and \c value will be transferred to this object on success. - \pre IsObject() && name.IsString() - \post name.IsNull() && value.IsNull() - \note Amortized Constant time complexity. - */ - GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { - RAPIDJSON_ASSERT(IsObject()); - RAPIDJSON_ASSERT(name.IsString()); - - ObjectData& o = data_.o; - if (o.size >= o.capacity) - MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator); - Member* members = GetMembersPointer(); - members[o.size].name.RawAssign(name); - members[o.size].value.RawAssign(value); - o.size++; - return *this; - } - - //! Add a constant string value as member (name-value pair) to the object. - /*! \param name A string value as name of member. - \param value constant string reference as value of member. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \pre IsObject() - \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. - \note Amortized Constant time complexity. - */ - GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { - GenericValue v(value); - return AddMember(name, v, allocator); - } - -#if RAPIDJSON_HAS_STDSTRING - //! Add a string object as member (name-value pair) to the object. - /*! \param name A string value as name of member. - \param value constant string reference as value of member. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \pre IsObject() - \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. - \note Amortized Constant time complexity. - */ - GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { - GenericValue v(value, allocator); - return AddMember(name, v, allocator); - } -#endif - - //! Add any primitive value as member (name-value pair) to the object. - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t - \param name A string value as name of member. - \param value Value of primitive type \c T as value of member - \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \pre IsObject() - - \note The source type \c T explicitly disallows all pointer types, - especially (\c const) \ref Ch*. This helps avoiding implicitly - referencing character strings with insufficient lifetime, use - \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref - AddMember(StringRefType, StringRefType, Allocator&). - All other pointer types would implicitly convert to \c bool, - use an explicit cast instead, if needed. - \note Amortized Constant time complexity. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) - AddMember(GenericValue& name, T value, Allocator& allocator) { - GenericValue v(value); - return AddMember(name, v, allocator); - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { - return AddMember(name, value, allocator); - } - GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { - return AddMember(name, value, allocator); - } - GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { - return AddMember(name, value, allocator); - } - GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { - GenericValue n(name); - return AddMember(n, value, allocator); - } -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - - - //! Add a member (name-value pair) to the object. - /*! \param name A constant string reference as name of member. - \param value Value of any type. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \note The ownership of \c value will be transferred to this object on success. - \pre IsObject() - \post value.IsNull() - \note Amortized Constant time complexity. - */ - GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { - GenericValue n(name); - return AddMember(n, value, allocator); - } - - //! Add a constant string value as member (name-value pair) to the object. - /*! \param name A constant string reference as name of member. - \param value constant string reference as value of member. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \pre IsObject() - \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. - \note Amortized Constant time complexity. - */ - GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { - GenericValue v(value); - return AddMember(name, v, allocator); - } - - //! Add any primitive value as member (name-value pair) to the object. - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t - \param name A constant string reference as name of member. - \param value Value of primitive type \c T as value of member - \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \pre IsObject() - - \note The source type \c T explicitly disallows all pointer types, - especially (\c const) \ref Ch*. This helps avoiding implicitly - referencing character strings with insufficient lifetime, use - \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref - AddMember(StringRefType, StringRefType, Allocator&). - All other pointer types would implicitly convert to \c bool, - use an explicit cast instead, if needed. - \note Amortized Constant time complexity. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) - AddMember(StringRefType name, T value, Allocator& allocator) { - GenericValue n(name); - return AddMember(n, value, allocator); - } - - //! Remove all members in the object. - /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. - \note Linear time complexity. - */ - void RemoveAllMembers() { - RAPIDJSON_ASSERT(IsObject()); - for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) - m->~Member(); - data_.o.size = 0; - } - - //! Remove a member in object by its name. - /*! \param name Name of member to be removed. - \return Whether the member existed. - \note This function may reorder the object members. Use \ref - EraseMember(ConstMemberIterator) if you need to preserve the - relative order of the remaining members. - \note Linear time complexity. - */ - bool RemoveMember(const Ch* name) { - GenericValue n(StringRef(name)); - return RemoveMember(n); - } - -#if RAPIDJSON_HAS_STDSTRING - bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } -#endif - - template - bool RemoveMember(const GenericValue& name) { - MemberIterator m = FindMember(name); - if (m != MemberEnd()) { - RemoveMember(m); - return true; - } - else - return false; - } - - //! Remove a member in object by iterator. - /*! \param m member iterator (obtained by FindMember() or MemberBegin()). - \return the new iterator after removal. - \note This function may reorder the object members. Use \ref - EraseMember(ConstMemberIterator) if you need to preserve the - relative order of the remaining members. - \note Constant time complexity. - */ - MemberIterator RemoveMember(MemberIterator m) { - RAPIDJSON_ASSERT(IsObject()); - RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(GetMembersPointer() != 0); - RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); - - MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); - if (data_.o.size > 1 && m != last) - *m = *last; // Move the last one to this place - else - m->~Member(); // Only one left, just destroy - --data_.o.size; - return m; - } - - //! Remove a member from an object by iterator. - /*! \param pos iterator to the member to remove - \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() - \return Iterator following the removed element. - If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. - \note This function preserves the relative order of the remaining object - members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). - \note Linear time complexity. - */ - MemberIterator EraseMember(ConstMemberIterator pos) { - return EraseMember(pos, pos +1); - } - - //! Remove members in the range [first, last) from an object. - /*! \param first iterator to the first member to remove - \param last iterator following the last member to remove - \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() - \return Iterator following the last removed element. - \note This function preserves the relative order of the remaining object - members. - \note Linear time complexity. - */ - MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { - RAPIDJSON_ASSERT(IsObject()); - RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(GetMembersPointer() != 0); - RAPIDJSON_ASSERT(first >= MemberBegin()); - RAPIDJSON_ASSERT(first <= last); - RAPIDJSON_ASSERT(last <= MemberEnd()); - - MemberIterator pos = MemberBegin() + (first - MemberBegin()); - for (MemberIterator itr = pos; itr != last; ++itr) - itr->~Member(); - std::memmove(static_cast(&*pos), &*last, static_cast(MemberEnd() - last) * sizeof(Member)); - data_.o.size -= static_cast(last - first); - return pos; - } - - //! Erase a member in object by its name. - /*! \param name Name of member to be removed. - \return Whether the member existed. - \note Linear time complexity. - */ - bool EraseMember(const Ch* name) { - GenericValue n(StringRef(name)); - return EraseMember(n); - } - -#if RAPIDJSON_HAS_STDSTRING - bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } -#endif - - template - bool EraseMember(const GenericValue& name) { - MemberIterator m = FindMember(name); - if (m != MemberEnd()) { - EraseMember(m); - return true; - } - else - return false; - } - - Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } - ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } - - //@} - - //!@name Array - //@{ - - //! Set this value as an empty array. - /*! \post IsArray == true */ - GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } - - //! Get the number of elements in array. - SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } - - //! Get the capacity of array. - SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } - - //! Check whether the array is empty. - bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } - - //! Remove all elements in the array. - /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. - \note Linear time complexity. - */ - void Clear() { - RAPIDJSON_ASSERT(IsArray()); - GenericValue* e = GetElementsPointer(); - for (GenericValue* v = e; v != e + data_.a.size; ++v) - v->~GenericValue(); - data_.a.size = 0; - } - - //! Get an element from array by index. - /*! \pre IsArray() == true - \param index Zero-based index of element. - \see operator[](T*) - */ - GenericValue& operator[](SizeType index) { - RAPIDJSON_ASSERT(IsArray()); - RAPIDJSON_ASSERT(index < data_.a.size); - return GetElementsPointer()[index]; - } - const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } - - //! Element iterator - /*! \pre IsArray() == true */ - ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } - //! \em Past-the-end element iterator - /*! \pre IsArray() == true */ - ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } - //! Constant element iterator - /*! \pre IsArray() == true */ - ConstValueIterator Begin() const { return const_cast(*this).Begin(); } - //! Constant \em past-the-end element iterator - /*! \pre IsArray() == true */ - ConstValueIterator End() const { return const_cast(*this).End(); } - - //! Request the array to have enough capacity to store elements. - /*! \param newCapacity The capacity that the array at least need to have. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \note Linear time complexity. - */ - GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { - RAPIDJSON_ASSERT(IsArray()); - if (newCapacity > data_.a.capacity) { - SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); - data_.a.capacity = newCapacity; - } - return *this; - } - - //! Append a GenericValue at the end of the array. - /*! \param value Value to be appended. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \pre IsArray() == true - \post value.IsNull() == true - \return The value itself for fluent API. - \note The ownership of \c value will be transferred to this array on success. - \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. - \note Amortized constant time complexity. - */ - GenericValue& PushBack(GenericValue& value, Allocator& allocator) { - RAPIDJSON_ASSERT(IsArray()); - if (data_.a.size >= data_.a.capacity) - Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); - GetElementsPointer()[data_.a.size++].RawAssign(value); - return *this; - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { - return PushBack(value, allocator); - } -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - - //! Append a constant string reference at the end of the array. - /*! \param value Constant string reference to be appended. - \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). - \pre IsArray() == true - \return The value itself for fluent API. - \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. - \note Amortized constant time complexity. - \see GenericStringRef - */ - GenericValue& PushBack(StringRefType value, Allocator& allocator) { - return (*this).template PushBack(value, allocator); - } - - //! Append a primitive value at the end of the array. - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t - \param value Value of primitive type T to be appended. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \pre IsArray() == true - \return The value itself for fluent API. - \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. - - \note The source type \c T explicitly disallows all pointer types, - especially (\c const) \ref Ch*. This helps avoiding implicitly - referencing character strings with insufficient lifetime, use - \ref PushBack(GenericValue&, Allocator&) or \ref - PushBack(StringRefType, Allocator&). - All other pointer types would implicitly convert to \c bool, - use an explicit cast instead, if needed. - \note Amortized constant time complexity. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) - PushBack(T value, Allocator& allocator) { - GenericValue v(value); - return PushBack(v, allocator); - } - - //! Remove the last element in the array. - /*! - \note Constant time complexity. - */ - GenericValue& PopBack() { - RAPIDJSON_ASSERT(IsArray()); - RAPIDJSON_ASSERT(!Empty()); - GetElementsPointer()[--data_.a.size].~GenericValue(); - return *this; - } - - //! Remove an element of array by iterator. - /*! - \param pos iterator to the element to remove - \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() - \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. - \note Linear time complexity. - */ - ValueIterator Erase(ConstValueIterator pos) { - return Erase(pos, pos + 1); - } - - //! Remove elements in the range [first, last) of the array. - /*! - \param first iterator to the first element to remove - \param last iterator following the last element to remove - \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() - \return Iterator following the last removed element. - \note Linear time complexity. - */ - ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { - RAPIDJSON_ASSERT(IsArray()); - RAPIDJSON_ASSERT(data_.a.size > 0); - RAPIDJSON_ASSERT(GetElementsPointer() != 0); - RAPIDJSON_ASSERT(first >= Begin()); - RAPIDJSON_ASSERT(first <= last); - RAPIDJSON_ASSERT(last <= End()); - ValueIterator pos = Begin() + (first - Begin()); - for (ValueIterator itr = pos; itr != last; ++itr) - itr->~GenericValue(); - std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); - data_.a.size -= static_cast(last - first); - return pos; - } - - Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } - ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } - - //@} - - //!@name Number - //@{ - - int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } - unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } - int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } - uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } - - //! Get the value as double type. - /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. - */ - double GetDouble() const { - RAPIDJSON_ASSERT(IsNumber()); - if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. - if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double - if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double - if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) - RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) - } - - //! Get the value as float type. - /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. - */ - float GetFloat() const { - return static_cast(GetDouble()); - } - - GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } - GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } - GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } - GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } - GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } - GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } - - //@} - - //!@name String - //@{ - - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } - - //! Get the length of string. - /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). - */ - SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } - - //! Set this value as a string without copying source string. - /*! This version has better performance with supplied length, and also support string containing null character. - \param s source string pointer. - \param length The length of source string, excluding the trailing null terminator. - \return The value itself for fluent API. - \post IsString() == true && GetString() == s && GetStringLength() == length - \see SetString(StringRefType) - */ - GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } - - //! Set this value as a string without copying source string. - /*! \param s source string reference - \return The value itself for fluent API. - \post IsString() == true && GetString() == s && GetStringLength() == s.length - */ - GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } - - //! Set this value as a string by copying from source string. - /*! This version has better performance with supplied length, and also support string containing null character. - \param s source string. - \param length The length of source string, excluding the trailing null terminator. - \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length - */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } - - //! Set this value as a string by copying from source string. - /*! \param s source string. - \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length - */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } - - //! Set this value as a string by copying from source string. - /*! \param s source string reference - \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length - */ - GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } - -#if RAPIDJSON_HAS_STDSTRING - //! Set this value as a string by copying from source string. - /*! \param s source string. - \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() - \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. - */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } -#endif - - //@} - - //!@name Array - //@{ - - //! Templated version for checking whether this value is type T. - /*! - \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string - */ - template - bool Is() const { return internal::TypeHelper::Is(*this); } - - template - T Get() const { return internal::TypeHelper::Get(*this); } - - template - T Get() { return internal::TypeHelper::Get(*this); } - - template - ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } - - template - ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } - - //@} - - //! Generate events of this value to a Handler. - /*! This function adopts the GoF visitor pattern. - Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. - It can also be used to deep clone this value via GenericDocument, which is also a Handler. - \tparam Handler type of handler. - \param handler An object implementing concept Handler. - */ - template - bool Accept(Handler& handler) const { - switch(GetType()) { - case kNullType: return handler.Null(); - case kFalseType: return handler.Bool(false); - case kTrueType: return handler.Bool(true); - - case kObjectType: - if (RAPIDJSON_UNLIKELY(!handler.StartObject())) - return false; - for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { - RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. - if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) - return false; - if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) - return false; - } - return handler.EndObject(data_.o.size); - - case kArrayType: - if (RAPIDJSON_UNLIKELY(!handler.StartArray())) - return false; - for (const GenericValue* v = Begin(); v != End(); ++v) - if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) - return false; - return handler.EndArray(data_.a.size); - - case kStringType: - return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); - - default: - RAPIDJSON_ASSERT(GetType() == kNumberType); - if (IsDouble()) return handler.Double(data_.n.d); - else if (IsInt()) return handler.Int(data_.n.i.i); - else if (IsUint()) return handler.Uint(data_.n.u.u); - else if (IsInt64()) return handler.Int64(data_.n.i64); - else return handler.Uint64(data_.n.u64); - } - } - -private: - template friend class GenericValue; - template friend class GenericDocument; - - enum { - kBoolFlag = 0x0008, - kNumberFlag = 0x0010, - kIntFlag = 0x0020, - kUintFlag = 0x0040, - kInt64Flag = 0x0080, - kUint64Flag = 0x0100, - kDoubleFlag = 0x0200, - kStringFlag = 0x0400, - kCopyFlag = 0x0800, - kInlineStrFlag = 0x1000, - - // Initial flags of different types. - kNullFlag = kNullType, - kTrueFlag = kTrueType | kBoolFlag, - kFalseFlag = kFalseType | kBoolFlag, - kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, - kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, - kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, - kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, - kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, - kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, - kConstStringFlag = kStringType | kStringFlag, - kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, - kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, - kObjectFlag = kObjectType, - kArrayFlag = kArrayType, - - kTypeMask = 0x07 - }; - - static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; - static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; - - struct Flag { -#if RAPIDJSON_48BITPOINTER_OPTIMIZATION - char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer -#elif RAPIDJSON_64BIT - char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes -#else - char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes -#endif - uint16_t flags; - }; - - struct String { - SizeType length; - SizeType hashcode; //!< reserved - const Ch* str; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars - // (excluding the terminating zero) and store a value to determine the length of the contained - // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string - // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as - // the string terminator as well. For getting the string length back from that value just use - // "MaxSize - str[LenPos]". - // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, - // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). - struct ShortString { - enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; - Ch str[MaxChars]; - - inline static bool Usable(SizeType len) { return (MaxSize >= len); } - inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } - inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } - }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - // By using proper binary layout, retrieval of different integer types do not need conversions. - union Number { -#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN - struct I { - int i; - char padding[4]; - }i; - struct U { - unsigned u; - char padding2[4]; - }u; -#else - struct I { - char padding[4]; - int i; - }i; - struct U { - char padding2[4]; - unsigned u; - }u; -#endif - int64_t i64; - uint64_t u64; - double d; - }; // 8 bytes - - struct ObjectData { - SizeType size; - SizeType capacity; - Member* members; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - struct ArrayData { - SizeType size; - SizeType capacity; - GenericValue* elements; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - union Data { - String s; - ShortString ss; - Number n; - ObjectData o; - ArrayData a; - Flag f; - }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION - - RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } - RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } - RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } - RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } - RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } - RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } - - // Initialize this value as array with initial data, without calling destructor. - void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { - data_.f.flags = kArrayFlag; - if (count) { - GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); - SetElementsPointer(e); - std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); - } - else - SetElementsPointer(0); - data_.a.size = data_.a.capacity = count; - } - - //! Initialize this value as object with initial data, without calling destructor. - void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { - data_.f.flags = kObjectFlag; - if (count) { - Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); - SetMembersPointer(m); - std::memcpy(static_cast(m), members, count * sizeof(Member)); - } - else - SetMembersPointer(0); - data_.o.size = data_.o.capacity = count; - } - - //! Initialize this value as constant string, without calling destructor. - void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { - data_.f.flags = kConstStringFlag; - SetStringPointer(s); - data_.s.length = s.length; - } - - //! Initialize this value as copy string with initial data, without calling destructor. - void SetStringRaw(StringRefType s, Allocator& allocator) { - Ch* str = 0; - if (ShortString::Usable(s.length)) { - data_.f.flags = kShortStringFlag; - data_.ss.SetLength(s.length); - str = data_.ss.str; - } else { - data_.f.flags = kCopyStringFlag; - data_.s.length = s.length; - str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); - SetStringPointer(str); - } - std::memcpy(str, s, s.length * sizeof(Ch)); - str[s.length] = '\0'; - } - - //! Assignment without calling destructor - void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { - data_ = rhs.data_; - // data_.f.flags = rhs.data_.f.flags; - rhs.data_.f.flags = kNullFlag; - } - - template - bool StringEqual(const GenericValue& rhs) const { - RAPIDJSON_ASSERT(IsString()); - RAPIDJSON_ASSERT(rhs.IsString()); - - const SizeType len1 = GetStringLength(); - const SizeType len2 = rhs.GetStringLength(); - if(len1 != len2) { return false; } - - const Ch* const str1 = GetString(); - const Ch* const str2 = rhs.GetString(); - if(str1 == str2) { return true; } // fast path for constant string - - return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); - } - - Data data_; -}; - -//! GenericValue with UTF8 encoding -typedef GenericValue > Value; - -/////////////////////////////////////////////////////////////////////////////// -// GenericDocument - -//! A document for parsing JSON text as DOM. -/*! - \note implements Handler concept - \tparam Encoding Encoding for both parsing and string storage. - \tparam Allocator Allocator for allocating memory for the DOM - \tparam StackAllocator Allocator for allocating memory for stack during parsing. - \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. -*/ -template -class GenericDocument : public GenericValue { -public: - typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. - typedef GenericValue ValueType; //!< Value type of the document. - typedef Allocator AllocatorType; //!< Allocator type from template parameter. - - //! Constructor - /*! Creates an empty document of specified type. - \param type Mandatory type of object to create. - \param allocator Optional allocator for allocating memory. - \param stackCapacity Optional initial capacity of stack in bytes. - \param stackAllocator Optional allocator for allocating memory for stack. - */ - explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : - GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() - { - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - } - - //! Constructor - /*! Creates an empty document which type is Null. - \param allocator Optional allocator for allocating memory. - \param stackCapacity Optional initial capacity of stack in bytes. - \param stackAllocator Optional allocator for allocating memory for stack. - */ - GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : - allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() - { - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move constructor in C++11 - GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT - : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document - allocator_(rhs.allocator_), - ownAllocator_(rhs.ownAllocator_), - stack_(std::move(rhs.stack_)), - parseResult_(rhs.parseResult_) - { - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.parseResult_ = ParseResult(); - } -#endif - - ~GenericDocument() { - Destroy(); - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move assignment in C++11 - GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT - { - // The cast to ValueType is necessary here, because otherwise it would - // attempt to call GenericValue's templated assignment operator. - ValueType::operator=(std::forward(rhs)); - - // Calling the destructor here would prematurely call stack_'s destructor - Destroy(); - - allocator_ = rhs.allocator_; - ownAllocator_ = rhs.ownAllocator_; - stack_ = std::move(rhs.stack_); - parseResult_ = rhs.parseResult_; - - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.parseResult_ = ParseResult(); - - return *this; - } -#endif - - //! Exchange the contents of this document with those of another. - /*! - \param rhs Another document. - \note Constant complexity. - \see GenericValue::Swap - */ - GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { - ValueType::Swap(rhs); - stack_.Swap(rhs.stack_); - internal::Swap(allocator_, rhs.allocator_); - internal::Swap(ownAllocator_, rhs.ownAllocator_); - internal::Swap(parseResult_, rhs.parseResult_); - return *this; - } - - // Allow Swap with ValueType. - // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. - using ValueType::Swap; - - //! free-standing swap function helper - /*! - Helper function to enable support for common swap implementation pattern based on \c std::swap: - \code - void swap(MyClass& a, MyClass& b) { - using std::swap; - swap(a.doc, b.doc); - // ... - } - \endcode - \see Swap() - */ - friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } - - //! Populate this document by a generator which produces SAX events. - /*! \tparam Generator A functor with bool f(Handler) prototype. - \param g Generator functor which sends SAX events to the parameter. - \return The document itself for fluent API. - */ - template - GenericDocument& Populate(Generator& g) { - ClearStackOnExit scope(*this); - if (g(*this)) { - RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object - ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document - } - return *this; - } - - //!@name Parse from stream - //!@{ - - //! Parse JSON text from an input stream (with Encoding conversion) - /*! \tparam parseFlags Combination of \ref ParseFlag. - \tparam SourceEncoding Encoding of input stream - \tparam InputStream Type of input stream, implementing Stream concept - \param is Input stream to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseStream(InputStream& is) { - GenericReader reader( - stack_.HasAllocator() ? &stack_.GetAllocator() : 0); - ClearStackOnExit scope(*this); - parseResult_ = reader.template Parse(is, *this); - if (parseResult_) { - RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object - ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document - } - return *this; - } - - //! Parse JSON text from an input stream - /*! \tparam parseFlags Combination of \ref ParseFlag. - \tparam InputStream Type of input stream, implementing Stream concept - \param is Input stream to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseStream(InputStream& is) { - return ParseStream(is); - } - - //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) - /*! \tparam InputStream Type of input stream, implementing Stream concept - \param is Input stream to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseStream(InputStream& is) { - return ParseStream(is); - } - //!@} - - //!@name Parse in-place from mutable string - //!@{ - - //! Parse JSON text from a mutable string - /*! \tparam parseFlags Combination of \ref ParseFlag. - \param str Mutable zero-terminated string to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseInsitu(Ch* str) { - GenericInsituStringStream s(str); - return ParseStream(s); - } - - //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) - /*! \param str Mutable zero-terminated string to be parsed. - \return The document itself for fluent API. - */ - GenericDocument& ParseInsitu(Ch* str) { - return ParseInsitu(str); - } - //!@} - - //!@name Parse from read-only string - //!@{ - - //! Parse JSON text from a read-only string (with Encoding conversion) - /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). - \tparam SourceEncoding Transcoding from input Encoding - \param str Read-only zero-terminated string to be parsed. - */ - template - GenericDocument& Parse(const typename SourceEncoding::Ch* str) { - RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); - GenericStringStream s(str); - return ParseStream(s); - } - - //! Parse JSON text from a read-only string - /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). - \param str Read-only zero-terminated string to be parsed. - */ - template - GenericDocument& Parse(const Ch* str) { - return Parse(str); - } - - //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) - /*! \param str Read-only zero-terminated string to be parsed. - */ - GenericDocument& Parse(const Ch* str) { - return Parse(str); - } - - template - GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { - RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); - MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); - EncodedInputStream is(ms); - ParseStream(is); - return *this; - } - - template - GenericDocument& Parse(const Ch* str, size_t length) { - return Parse(str, length); - } - - GenericDocument& Parse(const Ch* str, size_t length) { - return Parse(str, length); - } - -#if RAPIDJSON_HAS_STDSTRING - template - GenericDocument& Parse(const std::basic_string& str) { - // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) - return Parse(str.c_str()); - } - - template - GenericDocument& Parse(const std::basic_string& str) { - return Parse(str.c_str()); - } - - GenericDocument& Parse(const std::basic_string& str) { - return Parse(str); - } -#endif // RAPIDJSON_HAS_STDSTRING - - //!@} - - //!@name Handling parse errors - //!@{ - - //! Whether a parse error has occurred in the last parsing. - bool HasParseError() const { return parseResult_.IsError(); } - - //! Get the \ref ParseErrorCode of last parsing. - ParseErrorCode GetParseError() const { return parseResult_.Code(); } - - //! Get the position of last parsing error in input, 0 otherwise. - size_t GetErrorOffset() const { return parseResult_.Offset(); } - - //! Implicit conversion to get the last parse result -#ifndef __clang // -Wdocumentation - /*! \return \ref ParseResult of the last parse operation - - \code - Document doc; - ParseResult ok = doc.Parse(json); - if (!ok) - printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); - \endcode - */ -#endif - operator ParseResult() const { return parseResult_; } - //!@} - - //! Get the allocator of this document. - Allocator& GetAllocator() { - RAPIDJSON_ASSERT(allocator_); - return *allocator_; - } - - //! Get the capacity of stack in bytes. - size_t GetStackCapacity() const { return stack_.GetCapacity(); } - -private: - // clear stack on any exit from ParseStream, e.g. due to exception - struct ClearStackOnExit { - explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} - ~ClearStackOnExit() { d_.ClearStack(); } - private: - ClearStackOnExit(const ClearStackOnExit&); - ClearStackOnExit& operator=(const ClearStackOnExit&); - GenericDocument& d_; - }; - - // callers of the following private Handler functions - // template friend class GenericReader; // for parsing - template friend class GenericValue; // for deep copying - -public: - // Implementation of Handler - bool Null() { new (stack_.template Push()) ValueType(); return true; } - bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } - bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } - bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } - bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } - bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } - bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } - - bool RawNumber(const Ch* str, SizeType length, bool copy) { - if (copy) - new (stack_.template Push()) ValueType(str, length, GetAllocator()); - else - new (stack_.template Push()) ValueType(str, length); - return true; - } - - bool String(const Ch* str, SizeType length, bool copy) { - if (copy) - new (stack_.template Push()) ValueType(str, length, GetAllocator()); - else - new (stack_.template Push()) ValueType(str, length); - return true; - } - - bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } - - bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } - - bool EndObject(SizeType memberCount) { - typename ValueType::Member* members = stack_.template Pop(memberCount); - stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); - return true; - } - - bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } - - bool EndArray(SizeType elementCount) { - ValueType* elements = stack_.template Pop(elementCount); - stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); - return true; - } - -private: - //! Prohibit copying - GenericDocument(const GenericDocument&); - //! Prohibit assignment - GenericDocument& operator=(const GenericDocument&); - - void ClearStack() { - if (Allocator::kNeedFree) - while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) - (stack_.template Pop(1))->~ValueType(); - else - stack_.Clear(); - stack_.ShrinkToFit(); - } - - void Destroy() { - RAPIDJSON_DELETE(ownAllocator_); - } - - static const size_t kDefaultStackCapacity = 1024; - Allocator* allocator_; - Allocator* ownAllocator_; - internal::Stack stack_; - ParseResult parseResult_; -}; - -//! GenericDocument with UTF8 encoding -typedef GenericDocument > Document; - - -//! Helper class for accessing Value of array type. -/*! - Instance of this helper class is obtained by \c GenericValue::GetArray(). - In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. -*/ -template -class GenericArray { -public: - typedef GenericArray ConstArray; - typedef GenericArray Array; - typedef ValueT PlainType; - typedef typename internal::MaybeAddConst::Type ValueType; - typedef ValueType* ValueIterator; // This may be const or non-const iterator - typedef const ValueT* ConstValueIterator; - typedef typename ValueType::AllocatorType AllocatorType; - typedef typename ValueType::StringRefType StringRefType; - - template - friend class GenericValue; - - GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} - GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } - ~GenericArray() {} - - SizeType Size() const { return value_.Size(); } - SizeType Capacity() const { return value_.Capacity(); } - bool Empty() const { return value_.Empty(); } - void Clear() const { value_.Clear(); } - ValueType& operator[](SizeType index) const { return value_[index]; } - ValueIterator Begin() const { return value_.Begin(); } - ValueIterator End() const { return value_.End(); } - GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } - GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } - GenericArray PopBack() const { value_.PopBack(); return *this; } - ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } - ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } - -#if RAPIDJSON_HAS_CXX11_RANGE_FOR - ValueIterator begin() const { return value_.Begin(); } - ValueIterator end() const { return value_.End(); } -#endif - -private: - GenericArray(); - GenericArray(ValueType& value) : value_(value) {} - ValueType& value_; -}; - -//! Helper class for accessing Value of object type. -/*! - Instance of this helper class is obtained by \c GenericValue::GetObject(). - In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. -*/ -template -class GenericObject { -public: - typedef GenericObject ConstObject; - typedef GenericObject Object; - typedef ValueT PlainType; - typedef typename internal::MaybeAddConst::Type ValueType; - typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator - typedef GenericMemberIterator ConstMemberIterator; - typedef typename ValueType::AllocatorType AllocatorType; - typedef typename ValueType::StringRefType StringRefType; - typedef typename ValueType::EncodingType EncodingType; - typedef typename ValueType::Ch Ch; - - template - friend class GenericValue; - - GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} - GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } - ~GenericObject() {} - - SizeType MemberCount() const { return value_.MemberCount(); } - SizeType MemberCapacity() const { return value_.MemberCapacity(); } - bool ObjectEmpty() const { return value_.ObjectEmpty(); } - template ValueType& operator[](T* name) const { return value_[name]; } - template ValueType& operator[](const GenericValue& name) const { return value_[name]; } -#if RAPIDJSON_HAS_STDSTRING - ValueType& operator[](const std::basic_string& name) const { return value_[name]; } -#endif - MemberIterator MemberBegin() const { return value_.MemberBegin(); } - MemberIterator MemberEnd() const { return value_.MemberEnd(); } - GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } - bool HasMember(const Ch* name) const { return value_.HasMember(name); } -#if RAPIDJSON_HAS_STDSTRING - bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } -#endif - template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } - MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } - template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } -#if RAPIDJSON_HAS_STDSTRING - MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } -#endif - GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } -#if RAPIDJSON_HAS_STDSTRING - GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } -#endif - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - void RemoveAllMembers() { value_.RemoveAllMembers(); } - bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } -#if RAPIDJSON_HAS_STDSTRING - bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } -#endif - template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } - MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } - MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } - MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } - bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } -#if RAPIDJSON_HAS_STDSTRING - bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } -#endif - template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } - -#if RAPIDJSON_HAS_CXX11_RANGE_FOR - MemberIterator begin() const { return value_.MemberBegin(); } - MemberIterator end() const { return value_.MemberEnd(); } -#endif - -private: - GenericObject(); - GenericObject(ValueType& value) : value_(value) {} - ValueType& value_; -}; - -RAPIDJSON_NAMESPACE_END -RAPIDJSON_DIAG_POP - -#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h deleted file mode 100755 index cf046b89..00000000 --- a/include/rapidjson/encodedstream.h +++ /dev/null @@ -1,299 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_ENCODEDSTREAM_H_ -#define RAPIDJSON_ENCODEDSTREAM_H_ - -#include "stream.h" -#include "memorystream.h" - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Input byte stream wrapper with a statically bound encoding. -/*! - \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. - \tparam InputByteStream Type of input byte stream. For example, FileReadStream. -*/ -template -class EncodedInputStream { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); -public: - typedef typename Encoding::Ch Ch; - - EncodedInputStream(InputByteStream& is) : is_(is) { - current_ = Encoding::TakeBOM(is_); - } - - Ch Peek() const { return current_; } - Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } - size_t Tell() const { return is_.Tell(); } - - // Not implemented - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - EncodedInputStream(const EncodedInputStream&); - EncodedInputStream& operator=(const EncodedInputStream&); - - InputByteStream& is_; - Ch current_; -}; - -//! Specialized for UTF8 MemoryStream. -template <> -class EncodedInputStream, MemoryStream> { -public: - typedef UTF8<>::Ch Ch; - - EncodedInputStream(MemoryStream& is) : is_(is) { - if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); - if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); - if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); - } - Ch Peek() const { return is_.Peek(); } - Ch Take() { return is_.Take(); } - size_t Tell() const { return is_.Tell(); } - - // Not implemented - void Put(Ch) {} - void Flush() {} - Ch* PutBegin() { return 0; } - size_t PutEnd(Ch*) { return 0; } - - MemoryStream& is_; - -private: - EncodedInputStream(const EncodedInputStream&); - EncodedInputStream& operator=(const EncodedInputStream&); -}; - -//! Output byte stream wrapper with statically bound encoding. -/*! - \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. - \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. -*/ -template -class EncodedOutputStream { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); -public: - typedef typename Encoding::Ch Ch; - - EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { - if (putBOM) - Encoding::PutBOM(os_); - } - - void Put(Ch c) { Encoding::Put(os_, c); } - void Flush() { os_.Flush(); } - - // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} - Ch Take() { RAPIDJSON_ASSERT(false); return 0;} - size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - EncodedOutputStream(const EncodedOutputStream&); - EncodedOutputStream& operator=(const EncodedOutputStream&); - - OutputByteStream& os_; -}; - -#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x - -//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. -/*! - \tparam CharType Type of character for reading. - \tparam InputByteStream type of input byte stream to be wrapped. -*/ -template -class AutoUTFInputStream { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); -public: - typedef CharType Ch; - - //! Constructor. - /*! - \param is input stream to be wrapped. - \param type UTF encoding type if it is not detected from the stream. - */ - AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { - RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); - DetectType(); - static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; - takeFunc_ = f[type_]; - current_ = takeFunc_(*is_); - } - - UTFType GetType() const { return type_; } - bool HasBOM() const { return hasBOM_; } - - Ch Peek() const { return current_; } - Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } - size_t Tell() const { return is_->Tell(); } - - // Not implemented - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - AutoUTFInputStream(const AutoUTFInputStream&); - AutoUTFInputStream& operator=(const AutoUTFInputStream&); - - // Detect encoding type with BOM or RFC 4627 - void DetectType() { - // BOM (Byte Order Mark): - // 00 00 FE FF UTF-32BE - // FF FE 00 00 UTF-32LE - // FE FF UTF-16BE - // FF FE UTF-16LE - // EF BB BF UTF-8 - - const unsigned char* c = reinterpret_cast(is_->Peek4()); - if (!c) - return; - - unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); - hasBOM_ = false; - if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } - else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } - else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } - else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } - else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } - - // RFC 4627: Section 3 - // "Since the first two characters of a JSON text will always be ASCII - // characters [RFC0020], it is possible to determine whether an octet - // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking - // at the pattern of nulls in the first four octets." - // 00 00 00 xx UTF-32BE - // 00 xx 00 xx UTF-16BE - // xx 00 00 00 UTF-32LE - // xx 00 xx 00 UTF-16LE - // xx xx xx xx UTF-8 - - if (!hasBOM_) { - int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); - switch (pattern) { - case 0x08: type_ = kUTF32BE; break; - case 0x0A: type_ = kUTF16BE; break; - case 0x01: type_ = kUTF32LE; break; - case 0x05: type_ = kUTF16LE; break; - case 0x0F: type_ = kUTF8; break; - default: break; // Use type defined by user. - } - } - - // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. - if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); - } - - typedef Ch (*TakeFunc)(InputByteStream& is); - InputByteStream* is_; - UTFType type_; - Ch current_; - TakeFunc takeFunc_; - bool hasBOM_; -}; - -//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. -/*! - \tparam CharType Type of character for writing. - \tparam OutputByteStream type of output byte stream to be wrapped. -*/ -template -class AutoUTFOutputStream { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); -public: - typedef CharType Ch; - - //! Constructor. - /*! - \param os output stream to be wrapped. - \param type UTF encoding type. - \param putBOM Whether to write BOM at the beginning of the stream. - */ - AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { - RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); - - // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. - if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); - - static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; - putFunc_ = f[type_]; - - if (putBOM) - PutBOM(); - } - - UTFType GetType() const { return type_; } - - void Put(Ch c) { putFunc_(*os_, c); } - void Flush() { os_->Flush(); } - - // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} - Ch Take() { RAPIDJSON_ASSERT(false); return 0;} - size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - AutoUTFOutputStream(const AutoUTFOutputStream&); - AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); - - void PutBOM() { - typedef void (*PutBOMFunc)(OutputByteStream&); - static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; - f[type_](*os_); - } - - typedef void (*PutFunc)(OutputByteStream&, Ch); - - OutputByteStream* os_; - UTFType type_; - PutFunc putFunc_; -}; - -#undef RAPIDJSON_ENCODINGS_FUNC - -RAPIDJSON_NAMESPACE_END - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h deleted file mode 100755 index 50ad18bd..00000000 --- a/include/rapidjson/encodings.h +++ /dev/null @@ -1,716 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_ENCODINGS_H_ -#define RAPIDJSON_ENCODINGS_H_ - -#include "rapidjson.h" - -#if defined(_MSC_VER) && !defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data -RAPIDJSON_DIAG_OFF(4702) // unreachable code -#elif defined(__GNUC__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -RAPIDJSON_DIAG_OFF(overflow) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Encoding - -/*! \class rapidjson::Encoding - \brief Concept for encoding of Unicode characters. - -\code -concept Encoding { - typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. - - enum { supportUnicode = 1 }; // or 0 if not supporting unicode - - //! \brief Encode a Unicode codepoint to an output stream. - //! \param os Output stream. - //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. - template - static void Encode(OutputStream& os, unsigned codepoint); - - //! \brief Decode a Unicode codepoint from an input stream. - //! \param is Input stream. - //! \param codepoint Output of the unicode codepoint. - //! \return true if a valid codepoint can be decoded from the stream. - template - static bool Decode(InputStream& is, unsigned* codepoint); - - //! \brief Validate one Unicode codepoint from an encoded stream. - //! \param is Input stream to obtain codepoint. - //! \param os Output for copying one codepoint. - //! \return true if it is valid. - //! \note This function just validating and copying the codepoint without actually decode it. - template - static bool Validate(InputStream& is, OutputStream& os); - - // The following functions are deal with byte streams. - - //! Take a character from input byte stream, skip BOM if exist. - template - static CharType TakeBOM(InputByteStream& is); - - //! Take a character from input byte stream. - template - static Ch Take(InputByteStream& is); - - //! Put BOM to output byte stream. - template - static void PutBOM(OutputByteStream& os); - - //! Put a character to output byte stream. - template - static void Put(OutputByteStream& os, Ch c); -}; -\endcode -*/ - -/////////////////////////////////////////////////////////////////////////////// -// UTF8 - -//! UTF-8 encoding. -/*! http://en.wikipedia.org/wiki/UTF-8 - http://tools.ietf.org/html/rfc3629 - \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. - \note implements Encoding concept -*/ -template -struct UTF8 { - typedef CharType Ch; - - enum { supportUnicode = 1 }; - - template - static void Encode(OutputStream& os, unsigned codepoint) { - if (codepoint <= 0x7F) - os.Put(static_cast(codepoint & 0xFF)); - else if (codepoint <= 0x7FF) { - os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); - os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); - } - else if (codepoint <= 0xFFFF) { - os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); - os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - os.Put(static_cast(0x80 | (codepoint & 0x3F))); - } - else { - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); - os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); - os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - os.Put(static_cast(0x80 | (codepoint & 0x3F))); - } - } - - template - static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - if (codepoint <= 0x7F) - PutUnsafe(os, static_cast(codepoint & 0xFF)); - else if (codepoint <= 0x7FF) { - PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); - PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); - } - else if (codepoint <= 0xFFFF) { - PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); - PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); - } - else { - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); - PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); - PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); - } - } - - template - static bool Decode(InputStream& is, unsigned* codepoint) { -#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) -#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) -#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) - typename InputStream::Ch c = is.Take(); - if (!(c & 0x80)) { - *codepoint = static_cast(c); - return true; - } - - unsigned char type = GetRange(static_cast(c)); - if (type >= 32) { - *codepoint = 0; - } else { - *codepoint = (0xFFu >> type) & static_cast(c); - } - bool result = true; - switch (type) { - case 2: RAPIDJSON_TAIL(); return result; - case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; - case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; - case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; - case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; - case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; - case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; - default: return false; - } -#undef RAPIDJSON_COPY -#undef RAPIDJSON_TRANS -#undef RAPIDJSON_TAIL - } - - template - static bool Validate(InputStream& is, OutputStream& os) { -#define RAPIDJSON_COPY() os.Put(c = is.Take()) -#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) -#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) - Ch c; - RAPIDJSON_COPY(); - if (!(c & 0x80)) - return true; - - bool result = true; - switch (GetRange(static_cast(c))) { - case 2: RAPIDJSON_TAIL(); return result; - case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; - case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; - case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; - case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; - case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; - case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; - default: return false; - } -#undef RAPIDJSON_COPY -#undef RAPIDJSON_TRANS -#undef RAPIDJSON_TAIL - } - - static unsigned char GetRange(unsigned char c) { - // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ - // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. - static const unsigned char type[] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, - }; - return type[c]; - } - - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - typename InputByteStream::Ch c = Take(is); - if (static_cast(c) != 0xEFu) return c; - c = is.Take(); - if (static_cast(c) != 0xBBu) return c; - c = is.Take(); - if (static_cast(c) != 0xBFu) return c; - c = is.Take(); - return c; - } - - template - static Ch Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return static_cast(is.Take()); - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0xEFu)); - os.Put(static_cast(0xBBu)); - os.Put(static_cast(0xBFu)); - } - - template - static void Put(OutputByteStream& os, Ch c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(c)); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// UTF16 - -//! UTF-16 encoding. -/*! http://en.wikipedia.org/wiki/UTF-16 - http://tools.ietf.org/html/rfc2781 - \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. - \note implements Encoding concept - - \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. - For streaming, use UTF16LE and UTF16BE, which handle endianness. -*/ -template -struct UTF16 { - typedef CharType Ch; - RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); - - enum { supportUnicode = 1 }; - - template - static void Encode(OutputStream& os, unsigned codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - if (codepoint <= 0xFFFF) { - RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair - os.Put(static_cast(codepoint)); - } - else { - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - unsigned v = codepoint - 0x10000; - os.Put(static_cast((v >> 10) | 0xD800)); - os.Put(static_cast((v & 0x3FF) | 0xDC00)); - } - } - - - template - static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - if (codepoint <= 0xFFFF) { - RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair - PutUnsafe(os, static_cast(codepoint)); - } - else { - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - unsigned v = codepoint - 0x10000; - PutUnsafe(os, static_cast((v >> 10) | 0xD800)); - PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); - } - } - - template - static bool Decode(InputStream& is, unsigned* codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); - typename InputStream::Ch c = is.Take(); - if (c < 0xD800 || c > 0xDFFF) { - *codepoint = static_cast(c); - return true; - } - else if (c <= 0xDBFF) { - *codepoint = (static_cast(c) & 0x3FF) << 10; - c = is.Take(); - *codepoint |= (static_cast(c) & 0x3FF); - *codepoint += 0x10000; - return c >= 0xDC00 && c <= 0xDFFF; - } - return false; - } - - template - static bool Validate(InputStream& is, OutputStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - typename InputStream::Ch c; - os.Put(static_cast(c = is.Take())); - if (c < 0xD800 || c > 0xDFFF) - return true; - else if (c <= 0xDBFF) { - os.Put(c = is.Take()); - return c >= 0xDC00 && c <= 0xDFFF; - } - return false; - } -}; - -//! UTF-16 little endian encoding. -template -struct UTF16LE : UTF16 { - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = Take(is); - return static_cast(c) == 0xFEFFu ? Take(is) : c; - } - - template - static CharType Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - unsigned c = static_cast(is.Take()); - c |= static_cast(static_cast(is.Take())) << 8; - return static_cast(c); - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0xFFu)); - os.Put(static_cast(0xFEu)); - } - - template - static void Put(OutputByteStream& os, CharType c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(static_cast(c) & 0xFFu)); - os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); - } -}; - -//! UTF-16 big endian encoding. -template -struct UTF16BE : UTF16 { - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = Take(is); - return static_cast(c) == 0xFEFFu ? Take(is) : c; - } - - template - static CharType Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - unsigned c = static_cast(static_cast(is.Take())) << 8; - c |= static_cast(static_cast(is.Take())); - return static_cast(c); - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0xFEu)); - os.Put(static_cast(0xFFu)); - } - - template - static void Put(OutputByteStream& os, CharType c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); - os.Put(static_cast(static_cast(c) & 0xFFu)); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// UTF32 - -//! UTF-32 encoding. -/*! http://en.wikipedia.org/wiki/UTF-32 - \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. - \note implements Encoding concept - - \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. - For streaming, use UTF32LE and UTF32BE, which handle endianness. -*/ -template -struct UTF32 { - typedef CharType Ch; - RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); - - enum { supportUnicode = 1 }; - - template - static void Encode(OutputStream& os, unsigned codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - os.Put(codepoint); - } - - template - static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - PutUnsafe(os, codepoint); - } - - template - static bool Decode(InputStream& is, unsigned* codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); - Ch c = is.Take(); - *codepoint = c; - return c <= 0x10FFFF; - } - - template - static bool Validate(InputStream& is, OutputStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); - Ch c; - os.Put(c = is.Take()); - return c <= 0x10FFFF; - } -}; - -//! UTF-32 little endian enocoding. -template -struct UTF32LE : UTF32 { - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = Take(is); - return static_cast(c) == 0x0000FEFFu ? Take(is) : c; - } - - template - static CharType Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - unsigned c = static_cast(is.Take()); - c |= static_cast(static_cast(is.Take())) << 8; - c |= static_cast(static_cast(is.Take())) << 16; - c |= static_cast(static_cast(is.Take())) << 24; - return static_cast(c); - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0xFFu)); - os.Put(static_cast(0xFEu)); - os.Put(static_cast(0x00u)); - os.Put(static_cast(0x00u)); - } - - template - static void Put(OutputByteStream& os, CharType c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(c & 0xFFu)); - os.Put(static_cast((c >> 8) & 0xFFu)); - os.Put(static_cast((c >> 16) & 0xFFu)); - os.Put(static_cast((c >> 24) & 0xFFu)); - } -}; - -//! UTF-32 big endian encoding. -template -struct UTF32BE : UTF32 { - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = Take(is); - return static_cast(c) == 0x0000FEFFu ? Take(is) : c; - } - - template - static CharType Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - unsigned c = static_cast(static_cast(is.Take())) << 24; - c |= static_cast(static_cast(is.Take())) << 16; - c |= static_cast(static_cast(is.Take())) << 8; - c |= static_cast(static_cast(is.Take())); - return static_cast(c); - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(0x00u)); - os.Put(static_cast(0x00u)); - os.Put(static_cast(0xFEu)); - os.Put(static_cast(0xFFu)); - } - - template - static void Put(OutputByteStream& os, CharType c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast((c >> 24) & 0xFFu)); - os.Put(static_cast((c >> 16) & 0xFFu)); - os.Put(static_cast((c >> 8) & 0xFFu)); - os.Put(static_cast(c & 0xFFu)); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// ASCII - -//! ASCII encoding. -/*! http://en.wikipedia.org/wiki/ASCII - \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. - \note implements Encoding concept -*/ -template -struct ASCII { - typedef CharType Ch; - - enum { supportUnicode = 0 }; - - template - static void Encode(OutputStream& os, unsigned codepoint) { - RAPIDJSON_ASSERT(codepoint <= 0x7F); - os.Put(static_cast(codepoint & 0xFF)); - } - - template - static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - RAPIDJSON_ASSERT(codepoint <= 0x7F); - PutUnsafe(os, static_cast(codepoint & 0xFF)); - } - - template - static bool Decode(InputStream& is, unsigned* codepoint) { - uint8_t c = static_cast(is.Take()); - *codepoint = c; - return c <= 0X7F; - } - - template - static bool Validate(InputStream& is, OutputStream& os) { - uint8_t c = static_cast(is.Take()); - os.Put(static_cast(c)); - return c <= 0x7F; - } - - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - uint8_t c = static_cast(Take(is)); - return static_cast(c); - } - - template - static Ch Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return static_cast(is.Take()); - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - (void)os; - } - - template - static void Put(OutputByteStream& os, Ch c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(c)); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// AutoUTF - -//! Runtime-specified UTF encoding type of a stream. -enum UTFType { - kUTF8 = 0, //!< UTF-8. - kUTF16LE = 1, //!< UTF-16 little endian. - kUTF16BE = 2, //!< UTF-16 big endian. - kUTF32LE = 3, //!< UTF-32 little endian. - kUTF32BE = 4 //!< UTF-32 big endian. -}; - -//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. -/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). -*/ -template -struct AutoUTF { - typedef CharType Ch; - - enum { supportUnicode = 1 }; - -#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x - - template - static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { - typedef void (*EncodeFunc)(OutputStream&, unsigned); - static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; - (*f[os.GetType()])(os, codepoint); - } - - template - static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { - typedef void (*EncodeFunc)(OutputStream&, unsigned); - static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; - (*f[os.GetType()])(os, codepoint); - } - - template - static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { - typedef bool (*DecodeFunc)(InputStream&, unsigned*); - static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; - return (*f[is.GetType()])(is, codepoint); - } - - template - static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { - typedef bool (*ValidateFunc)(InputStream&, OutputStream&); - static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; - return (*f[is.GetType()])(is, os); - } - -#undef RAPIDJSON_ENCODINGS_FUNC -}; - -/////////////////////////////////////////////////////////////////////////////// -// Transcoder - -//! Encoding conversion. -template -struct Transcoder { - //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. - template - static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { - unsigned codepoint; - if (!SourceEncoding::Decode(is, &codepoint)) - return false; - TargetEncoding::Encode(os, codepoint); - return true; - } - - template - static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { - unsigned codepoint; - if (!SourceEncoding::Decode(is, &codepoint)) - return false; - TargetEncoding::EncodeUnsafe(os, codepoint); - return true; - } - - //! Validate one Unicode codepoint from an encoded stream. - template - static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { - return Transcode(is, os); // Since source/target encoding is different, must transcode. - } -}; - -// Forward declaration. -template -inline void PutUnsafe(Stream& stream, typename Stream::Ch c); - -//! Specialization of Transcoder with same source and target encoding. -template -struct Transcoder { - template - static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { - os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. - return true; - } - - template - static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { - PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. - return true; - } - - template - static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { - return Encoding::Validate(is, os); // source/target encoding are the same - } -}; - -RAPIDJSON_NAMESPACE_END - -#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h deleted file mode 100755 index 37a62ebc..00000000 --- a/include/rapidjson/error/en.h +++ /dev/null @@ -1,74 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_ERROR_EN_H_ -#define RAPIDJSON_ERROR_EN_H_ - -#include "error.h" - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(switch-enum) -RAPIDJSON_DIAG_OFF(covered-switch-default) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Maps error code of parsing into error message. -/*! - \ingroup RAPIDJSON_ERRORS - \param parseErrorCode Error code obtained in parsing. - \return the error message. - \note User can make a copy of this function for localization. - Using switch-case is safer for future modification of error codes. -*/ -inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { - switch (parseErrorCode) { - case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); - - case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); - case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); - - case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); - - case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); - case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); - case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); - - case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); - - case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); - case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); - case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); - case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); - case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); - - case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); - case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); - case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); - - case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); - case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); - - default: return RAPIDJSON_ERROR_STRING("Unknown error."); - } -} - -RAPIDJSON_NAMESPACE_END - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h deleted file mode 100755 index 71f6ec4d..00000000 --- a/include/rapidjson/error/error.h +++ /dev/null @@ -1,161 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_ERROR_ERROR_H_ -#define RAPIDJSON_ERROR_ERROR_H_ - -#include "../rapidjson.h" - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - -/*! \file error.h */ - -/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ERROR_CHARTYPE - -//! Character type of error messages. -/*! \ingroup RAPIDJSON_ERRORS - The default character type is \c char. - On Windows, user can define this macro as \c TCHAR for supporting both - unicode/non-unicode settings. -*/ -#ifndef RAPIDJSON_ERROR_CHARTYPE -#define RAPIDJSON_ERROR_CHARTYPE char -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ERROR_STRING - -//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. -/*! \ingroup RAPIDJSON_ERRORS - By default this conversion macro does nothing. - On Windows, user can define this macro as \c _T(x) for supporting both - unicode/non-unicode settings. -*/ -#ifndef RAPIDJSON_ERROR_STRING -#define RAPIDJSON_ERROR_STRING(x) x -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// ParseErrorCode - -//! Error code of parsing. -/*! \ingroup RAPIDJSON_ERRORS - \see GenericReader::Parse, GenericReader::GetParseErrorCode -*/ -enum ParseErrorCode { - kParseErrorNone = 0, //!< No error. - - kParseErrorDocumentEmpty, //!< The document is empty. - kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. - - kParseErrorValueInvalid, //!< Invalid value. - - kParseErrorObjectMissName, //!< Missing a name for object member. - kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. - kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. - - kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. - - kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. - kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. - kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. - kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. - kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. - - kParseErrorNumberTooBig, //!< Number too big to be stored in double. - kParseErrorNumberMissFraction, //!< Miss fraction part in number. - kParseErrorNumberMissExponent, //!< Miss exponent in number. - - kParseErrorTermination, //!< Parsing was terminated. - kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. -}; - -//! Result of parsing (wraps ParseErrorCode) -/*! - \ingroup RAPIDJSON_ERRORS - \code - Document doc; - ParseResult ok = doc.Parse("[42]"); - if (!ok) { - fprintf(stderr, "JSON parse error: %s (%u)", - GetParseError_En(ok.Code()), ok.Offset()); - exit(EXIT_FAILURE); - } - \endcode - \see GenericReader::Parse, GenericDocument::Parse -*/ -struct ParseResult { - //!! Unspecified boolean type - typedef bool (ParseResult::*BooleanType)() const; -public: - //! Default constructor, no error. - ParseResult() : code_(kParseErrorNone), offset_(0) {} - //! Constructor to set an error. - ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} - - //! Get the error code. - ParseErrorCode Code() const { return code_; } - //! Get the error offset, if \ref IsError(), 0 otherwise. - size_t Offset() const { return offset_; } - - //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). - operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } - //! Whether the result is an error. - bool IsError() const { return code_ != kParseErrorNone; } - - bool operator==(const ParseResult& that) const { return code_ == that.code_; } - bool operator==(ParseErrorCode code) const { return code_ == code; } - friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } - - bool operator!=(const ParseResult& that) const { return !(*this == that); } - bool operator!=(ParseErrorCode code) const { return !(*this == code); } - friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } - - //! Reset error code. - void Clear() { Set(kParseErrorNone); } - //! Update error code and offset. - void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } - -private: - ParseErrorCode code_; - size_t offset_; -}; - -//! Function pointer type of GetParseError(). -/*! \ingroup RAPIDJSON_ERRORS - - This is the prototype for \c GetParseError_X(), where \c X is a locale. - User can dynamically change locale in runtime, e.g.: -\code - GetParseErrorFunc GetParseError = GetParseError_En; // or whatever - const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); -\endcode -*/ -typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); - -RAPIDJSON_NAMESPACE_END - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h deleted file mode 100755 index f8bb43cb..00000000 --- a/include/rapidjson/filereadstream.h +++ /dev/null @@ -1,99 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_FILEREADSTREAM_H_ -#define RAPIDJSON_FILEREADSTREAM_H_ - -#include "stream.h" -#include - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(unreachable-code) -RAPIDJSON_DIAG_OFF(missing-noreturn) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! File byte stream for input using fread(). -/*! - \note implements Stream concept -*/ -class FileReadStream { -public: - typedef char Ch; //!< Character type (byte). - - //! Constructor. - /*! - \param fp File pointer opened for read. - \param buffer user-supplied buffer. - \param bufferSize size of buffer in bytes. Must >=4 bytes. - */ - FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { - RAPIDJSON_ASSERT(fp_ != 0); - RAPIDJSON_ASSERT(bufferSize >= 4); - Read(); - } - - Ch Peek() const { return *current_; } - Ch Take() { Ch c = *current_; Read(); return c; } - size_t Tell() const { return count_ + static_cast(current_ - buffer_); } - - // Not implemented - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - // For encoding detection only. - const Ch* Peek4() const { - return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; - } - -private: - void Read() { - if (current_ < bufferLast_) - ++current_; - else if (!eof_) { - count_ += readCount_; - readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); - bufferLast_ = buffer_ + readCount_ - 1; - current_ = buffer_; - - if (readCount_ < bufferSize_) { - buffer_[readCount_] = '\0'; - ++bufferLast_; - eof_ = true; - } - } - } - - std::FILE* fp_; - Ch *buffer_; - size_t bufferSize_; - Ch *bufferLast_; - Ch *current_; - size_t readCount_; - size_t count_; //!< Number of characters read - bool eof_; -}; - -RAPIDJSON_NAMESPACE_END - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filewritestream.h b/include/rapidjson/filewritestream.h deleted file mode 100755 index 5d89588c..00000000 --- a/include/rapidjson/filewritestream.h +++ /dev/null @@ -1,104 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_FILEWRITESTREAM_H_ -#define RAPIDJSON_FILEWRITESTREAM_H_ - -#include "stream.h" -#include - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(unreachable-code) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Wrapper of C file stream for output using fwrite(). -/*! - \note implements Stream concept -*/ -class FileWriteStream { -public: - typedef char Ch; //!< Character type. Only support char. - - FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { - RAPIDJSON_ASSERT(fp_ != 0); - } - - void Put(char c) { - if (current_ >= bufferEnd_) - Flush(); - - *current_++ = c; - } - - void PutN(char c, size_t n) { - size_t avail = static_cast(bufferEnd_ - current_); - while (n > avail) { - std::memset(current_, c, avail); - current_ += avail; - Flush(); - n -= avail; - avail = static_cast(bufferEnd_ - current_); - } - - if (n > 0) { - std::memset(current_, c, n); - current_ += n; - } - } - - void Flush() { - if (current_ != buffer_) { - size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); - if (result < static_cast(current_ - buffer_)) { - // failure deliberately ignored at this time - // added to avoid warn_unused_result build errors - } - current_ = buffer_; - } - } - - // Not implemented - char Peek() const { RAPIDJSON_ASSERT(false); return 0; } - char Take() { RAPIDJSON_ASSERT(false); return 0; } - size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } - char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - // Prohibit copy constructor & assignment operator. - FileWriteStream(const FileWriteStream&); - FileWriteStream& operator=(const FileWriteStream&); - - std::FILE* fp_; - char *buffer_; - char *bufferEnd_; - char *current_; -}; - -//! Implement specialized version of PutN() with memset() for better performance. -template<> -inline void PutN(FileWriteStream& stream, char c, size_t n) { - stream.PutN(c, n); -} - -RAPIDJSON_NAMESPACE_END - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/fwd.h b/include/rapidjson/fwd.h deleted file mode 100755 index d62f77f0..00000000 --- a/include/rapidjson/fwd.h +++ /dev/null @@ -1,151 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_FWD_H_ -#define RAPIDJSON_FWD_H_ - -#include "rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN - -// encodings.h - -template struct UTF8; -template struct UTF16; -template struct UTF16BE; -template struct UTF16LE; -template struct UTF32; -template struct UTF32BE; -template struct UTF32LE; -template struct ASCII; -template struct AutoUTF; - -template -struct Transcoder; - -// allocators.h - -class CrtAllocator; - -template -class MemoryPoolAllocator; - -// stream.h - -template -struct GenericStringStream; - -typedef GenericStringStream > StringStream; - -template -struct GenericInsituStringStream; - -typedef GenericInsituStringStream > InsituStringStream; - -// stringbuffer.h - -template -class GenericStringBuffer; - -typedef GenericStringBuffer, CrtAllocator> StringBuffer; - -// filereadstream.h - -class FileReadStream; - -// filewritestream.h - -class FileWriteStream; - -// memorybuffer.h - -template -struct GenericMemoryBuffer; - -typedef GenericMemoryBuffer MemoryBuffer; - -// memorystream.h - -struct MemoryStream; - -// reader.h - -template -struct BaseReaderHandler; - -template -class GenericReader; - -typedef GenericReader, UTF8, CrtAllocator> Reader; - -// writer.h - -template -class Writer; - -// prettywriter.h - -template -class PrettyWriter; - -// document.h - -template -class GenericMember; - -template -class GenericMemberIterator; - -template -struct GenericStringRef; - -template -class GenericValue; - -typedef GenericValue, MemoryPoolAllocator > Value; - -template -class GenericDocument; - -typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; - -// pointer.h - -template -class GenericPointer; - -typedef GenericPointer Pointer; - -// schema.h - -template -class IGenericRemoteSchemaDocumentProvider; - -template -class GenericSchemaDocument; - -typedef GenericSchemaDocument SchemaDocument; -typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; - -template < - typename SchemaDocumentType, - typename OutputHandler, - typename StateAllocator> -class GenericSchemaValidator; - -typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h deleted file mode 100755 index 12455788..00000000 --- a/include/rapidjson/internal/biginteger.h +++ /dev/null @@ -1,290 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_BIGINTEGER_H_ -#define RAPIDJSON_BIGINTEGER_H_ - -#include "../rapidjson.h" - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) -#include // for _umul128 -#pragma intrinsic(_umul128) -#endif - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -class BigInteger { -public: - typedef uint64_t Type; - - BigInteger(const BigInteger& rhs) : count_(rhs.count_) { - std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); - } - - explicit BigInteger(uint64_t u) : count_(1) { - digits_[0] = u; - } - - BigInteger(const char* decimals, size_t length) : count_(1) { - RAPIDJSON_ASSERT(length > 0); - digits_[0] = 0; - size_t i = 0; - const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 - while (length >= kMaxDigitPerIteration) { - AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); - length -= kMaxDigitPerIteration; - i += kMaxDigitPerIteration; - } - - if (length > 0) - AppendDecimal64(decimals + i, decimals + i + length); - } - - BigInteger& operator=(const BigInteger &rhs) - { - if (this != &rhs) { - count_ = rhs.count_; - std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); - } - return *this; - } - - BigInteger& operator=(uint64_t u) { - digits_[0] = u; - count_ = 1; - return *this; - } - - BigInteger& operator+=(uint64_t u) { - Type backup = digits_[0]; - digits_[0] += u; - for (size_t i = 0; i < count_ - 1; i++) { - if (digits_[i] >= backup) - return *this; // no carry - backup = digits_[i + 1]; - digits_[i + 1] += 1; - } - - // Last carry - if (digits_[count_ - 1] < backup) - PushBack(1); - - return *this; - } - - BigInteger& operator*=(uint64_t u) { - if (u == 0) return *this = 0; - if (u == 1) return *this; - if (*this == 1) return *this = u; - - uint64_t k = 0; - for (size_t i = 0; i < count_; i++) { - uint64_t hi; - digits_[i] = MulAdd64(digits_[i], u, k, &hi); - k = hi; - } - - if (k > 0) - PushBack(k); - - return *this; - } - - BigInteger& operator*=(uint32_t u) { - if (u == 0) return *this = 0; - if (u == 1) return *this; - if (*this == 1) return *this = u; - - uint64_t k = 0; - for (size_t i = 0; i < count_; i++) { - const uint64_t c = digits_[i] >> 32; - const uint64_t d = digits_[i] & 0xFFFFFFFF; - const uint64_t uc = u * c; - const uint64_t ud = u * d; - const uint64_t p0 = ud + k; - const uint64_t p1 = uc + (p0 >> 32); - digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); - k = p1 >> 32; - } - - if (k > 0) - PushBack(k); - - return *this; - } - - BigInteger& operator<<=(size_t shift) { - if (IsZero() || shift == 0) return *this; - - size_t offset = shift / kTypeBit; - size_t interShift = shift % kTypeBit; - RAPIDJSON_ASSERT(count_ + offset <= kCapacity); - - if (interShift == 0) { - std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); - count_ += offset; - } - else { - digits_[count_] = 0; - for (size_t i = count_; i > 0; i--) - digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); - digits_[offset] = digits_[0] << interShift; - count_ += offset; - if (digits_[count_]) - count_++; - } - - std::memset(digits_, 0, offset * sizeof(Type)); - - return *this; - } - - bool operator==(const BigInteger& rhs) const { - return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; - } - - bool operator==(const Type rhs) const { - return count_ == 1 && digits_[0] == rhs; - } - - BigInteger& MultiplyPow5(unsigned exp) { - static const uint32_t kPow5[12] = { - 5, - 5 * 5, - 5 * 5 * 5, - 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 - }; - if (exp == 0) return *this; - for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 - for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 - if (exp > 0) *this *= kPow5[exp - 1]; - return *this; - } - - // Compute absolute difference of this and rhs. - // Assume this != rhs - bool Difference(const BigInteger& rhs, BigInteger* out) const { - int cmp = Compare(rhs); - RAPIDJSON_ASSERT(cmp != 0); - const BigInteger *a, *b; // Makes a > b - bool ret; - if (cmp < 0) { a = &rhs; b = this; ret = true; } - else { a = this; b = &rhs; ret = false; } - - Type borrow = 0; - for (size_t i = 0; i < a->count_; i++) { - Type d = a->digits_[i] - borrow; - if (i < b->count_) - d -= b->digits_[i]; - borrow = (d > a->digits_[i]) ? 1 : 0; - out->digits_[i] = d; - if (d != 0) - out->count_ = i + 1; - } - - return ret; - } - - int Compare(const BigInteger& rhs) const { - if (count_ != rhs.count_) - return count_ < rhs.count_ ? -1 : 1; - - for (size_t i = count_; i-- > 0;) - if (digits_[i] != rhs.digits_[i]) - return digits_[i] < rhs.digits_[i] ? -1 : 1; - - return 0; - } - - size_t GetCount() const { return count_; } - Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } - bool IsZero() const { return count_ == 1 && digits_[0] == 0; } - -private: - void AppendDecimal64(const char* begin, const char* end) { - uint64_t u = ParseUint64(begin, end); - if (IsZero()) - *this = u; - else { - unsigned exp = static_cast(end - begin); - (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u - } - } - - void PushBack(Type digit) { - RAPIDJSON_ASSERT(count_ < kCapacity); - digits_[count_++] = digit; - } - - static uint64_t ParseUint64(const char* begin, const char* end) { - uint64_t r = 0; - for (const char* p = begin; p != end; ++p) { - RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); - r = r * 10u + static_cast(*p - '0'); - } - return r; - } - - // Assume a * b + k < 2^128 - static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { -#if defined(_MSC_VER) && defined(_M_AMD64) - uint64_t low = _umul128(a, b, outHigh) + k; - if (low < k) - (*outHigh)++; - return low; -#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) - __extension__ typedef unsigned __int128 uint128; - uint128 p = static_cast(a) * static_cast(b); - p += k; - *outHigh = static_cast(p >> 64); - return static_cast(p); -#else - const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; - uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; - x1 += (x0 >> 32); // can't give carry - x1 += x2; - if (x1 < x2) - x3 += (static_cast(1) << 32); - uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); - uint64_t hi = x3 + (x1 >> 32); - - lo += k; - if (lo < k) - hi++; - *outHigh = hi; - return lo; -#endif - } - - static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 - static const size_t kCapacity = kBitCount / sizeof(Type); - static const size_t kTypeBit = sizeof(Type) * 8; - - Type digits_[kCapacity]; - size_t count_; -}; - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/include/rapidjson/internal/clzll.h b/include/rapidjson/internal/clzll.h deleted file mode 100755 index 8fc5118a..00000000 --- a/include/rapidjson/internal/clzll.h +++ /dev/null @@ -1,71 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_CLZLL_H_ -#define RAPIDJSON_CLZLL_H_ - -#include "../rapidjson.h" - -#if defined(_MSC_VER) && !defined(UNDER_CE) -#include -#if defined(_WIN64) -#pragma intrinsic(_BitScanReverse64) -#else -#pragma intrinsic(_BitScanReverse) -#endif -#endif - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -inline uint32_t clzll(uint64_t x) { - // Passing 0 to __builtin_clzll is UB in GCC and results in an - // infinite loop in the software implementation. - RAPIDJSON_ASSERT(x != 0); - -#if defined(_MSC_VER) && !defined(UNDER_CE) - unsigned long r = 0; -#if defined(_WIN64) - _BitScanReverse64(&r, x); -#else - // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) - return 63 - (r + 32); - - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); -#endif // _WIN64 - - return 63 - r; -#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) - // __builtin_clzll wrapper - return static_cast(__builtin_clzll(x)); -#else - // naive version - uint32_t r = 0; - while (!(x & (static_cast(1) << 63))) { - x <<= 1; - ++r; - } - - return r; -#endif // _MSC_VER -} - -#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_CLZLL_H_ diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h deleted file mode 100755 index a40797ec..00000000 --- a/include/rapidjson/internal/diyfp.h +++ /dev/null @@ -1,257 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -// This is a C++ header-only implementation of Grisu2 algorithm from the publication: -// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with -// integers." ACM Sigplan Notices 45.6 (2010): 233-243. - -#ifndef RAPIDJSON_DIYFP_H_ -#define RAPIDJSON_DIYFP_H_ - -#include "../rapidjson.h" -#include "clzll.h" -#include - -#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) -#include -#pragma intrinsic(_umul128) -#endif - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - -struct DiyFp { - DiyFp() : f(), e() {} - - DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} - - explicit DiyFp(double d) { - union { - double d; - uint64_t u64; - } u = { d }; - - int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); - uint64_t significand = (u.u64 & kDpSignificandMask); - if (biased_e != 0) { - f = significand + kDpHiddenBit; - e = biased_e - kDpExponentBias; - } - else { - f = significand; - e = kDpMinExponent + 1; - } - } - - DiyFp operator-(const DiyFp& rhs) const { - return DiyFp(f - rhs.f, e); - } - - DiyFp operator*(const DiyFp& rhs) const { -#if defined(_MSC_VER) && defined(_M_AMD64) - uint64_t h; - uint64_t l = _umul128(f, rhs.f, &h); - if (l & (uint64_t(1) << 63)) // rounding - h++; - return DiyFp(h, e + rhs.e + 64); -#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) - __extension__ typedef unsigned __int128 uint128; - uint128 p = static_cast(f) * static_cast(rhs.f); - uint64_t h = static_cast(p >> 64); - uint64_t l = static_cast(p); - if (l & (uint64_t(1) << 63)) // rounding - h++; - return DiyFp(h, e + rhs.e + 64); -#else - const uint64_t M32 = 0xFFFFFFFF; - const uint64_t a = f >> 32; - const uint64_t b = f & M32; - const uint64_t c = rhs.f >> 32; - const uint64_t d = rhs.f & M32; - const uint64_t ac = a * c; - const uint64_t bc = b * c; - const uint64_t ad = a * d; - const uint64_t bd = b * d; - uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); - tmp += 1U << 31; /// mult_round - return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); -#endif - } - - DiyFp Normalize() const { - int s = static_cast(clzll(f)); - return DiyFp(f << s, e - s); - } - - DiyFp NormalizeBoundary() const { - DiyFp res = *this; - while (!(res.f & (kDpHiddenBit << 1))) { - res.f <<= 1; - res.e--; - } - res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); - res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); - return res; - } - - void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { - DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); - DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); - mi.f <<= mi.e - pl.e; - mi.e = pl.e; - *plus = pl; - *minus = mi; - } - - double ToDouble() const { - union { - double d; - uint64_t u64; - }u; - RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); - if (e < kDpDenormalExponent) { - // Underflow. - return 0.0; - } - if (e >= kDpMaxExponent) { - // Overflow. - return std::numeric_limits::infinity(); - } - const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : - static_cast(e + kDpExponentBias); - u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); - return u.d; - } - - static const int kDiySignificandSize = 64; - static const int kDpSignificandSize = 52; - static const int kDpExponentBias = 0x3FF + kDpSignificandSize; - static const int kDpMaxExponent = 0x7FF - kDpExponentBias; - static const int kDpMinExponent = -kDpExponentBias; - static const int kDpDenormalExponent = -kDpExponentBias + 1; - static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); - static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); - static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); - - uint64_t f; - int e; -}; - -inline DiyFp GetCachedPowerByIndex(size_t index) { - // 10^-348, 10^-340, ..., 10^340 - static const uint64_t kCachedPowers_F[] = { - RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), - RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), - RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), - RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), - RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), - RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), - RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), - RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), - RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), - RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), - RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), - RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), - RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), - RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), - RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), - RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), - RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), - RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), - RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), - RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), - RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), - RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), - RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), - RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), - RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), - RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), - RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), - RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), - RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), - RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), - RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), - RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), - RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), - RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), - RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), - RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), - RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), - RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), - RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), - RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), - RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), - RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), - RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), - RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) - }; - static const int16_t kCachedPowers_E[] = { - -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, - -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, - -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, - -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, - -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, - 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, - 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, - 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, - 907, 933, 960, 986, 1013, 1039, 1066 - }; - RAPIDJSON_ASSERT(index < 87); - return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); -} - -inline DiyFp GetCachedPower(int e, int* K) { - - //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; - double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive - int k = static_cast(dk); - if (dk - k > 0.0) - k++; - - unsigned index = static_cast((k >> 3) + 1); - *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table - - return GetCachedPowerByIndex(index); -} - -inline DiyFp GetCachedPower10(int exp, int *outExp) { - RAPIDJSON_ASSERT(exp >= -348); - unsigned index = static_cast(exp + 348) / 8u; - *outExp = -348 + static_cast(index) * 8; - return GetCachedPowerByIndex(index); -} - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -RAPIDJSON_DIAG_OFF(padded) -#endif - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_DIYFP_H_ diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h deleted file mode 100755 index 621402fd..00000000 --- a/include/rapidjson/internal/dtoa.h +++ /dev/null @@ -1,245 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -// This is a C++ header-only implementation of Grisu2 algorithm from the publication: -// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with -// integers." ACM Sigplan Notices 45.6 (2010): 233-243. - -#ifndef RAPIDJSON_DTOA_ -#define RAPIDJSON_DTOA_ - -#include "itoa.h" // GetDigitsLut() -#include "diyfp.h" -#include "ieee754.h" - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 -#endif - -inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { - while (rest < wp_w && delta - rest >= ten_kappa && - (rest + ten_kappa < wp_w || /// closer - wp_w - rest > rest + ten_kappa - wp_w)) { - buffer[len - 1]--; - rest += ten_kappa; - } -} - -inline int CountDecimalDigit32(uint32_t n) { - // Simple pure C++ implementation was faster than __builtin_clz version in this situation. - if (n < 10) return 1; - if (n < 100) return 2; - if (n < 1000) return 3; - if (n < 10000) return 4; - if (n < 100000) return 5; - if (n < 1000000) return 6; - if (n < 10000000) return 7; - if (n < 100000000) return 8; - // Will not reach 10 digits in DigitGen() - //if (n < 1000000000) return 9; - //return 10; - return 9; -} - -inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { - static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); - const DiyFp wp_w = Mp - W; - uint32_t p1 = static_cast(Mp.f >> -one.e); - uint64_t p2 = Mp.f & (one.f - 1); - int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] - *len = 0; - - while (kappa > 0) { - uint32_t d = 0; - switch (kappa) { - case 9: d = p1 / 100000000; p1 %= 100000000; break; - case 8: d = p1 / 10000000; p1 %= 10000000; break; - case 7: d = p1 / 1000000; p1 %= 1000000; break; - case 6: d = p1 / 100000; p1 %= 100000; break; - case 5: d = p1 / 10000; p1 %= 10000; break; - case 4: d = p1 / 1000; p1 %= 1000; break; - case 3: d = p1 / 100; p1 %= 100; break; - case 2: d = p1 / 10; p1 %= 10; break; - case 1: d = p1; p1 = 0; break; - default:; - } - if (d || *len) - buffer[(*len)++] = static_cast('0' + static_cast(d)); - kappa--; - uint64_t tmp = (static_cast(p1) << -one.e) + p2; - if (tmp <= delta) { - *K += kappa; - GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); - return; - } - } - - // kappa = 0 - for (;;) { - p2 *= 10; - delta *= 10; - char d = static_cast(p2 >> -one.e); - if (d || *len) - buffer[(*len)++] = static_cast('0' + d); - p2 &= one.f - 1; - kappa--; - if (p2 < delta) { - *K += kappa; - int index = -kappa; - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); - return; - } - } -} - -inline void Grisu2(double value, char* buffer, int* length, int* K) { - const DiyFp v(value); - DiyFp w_m, w_p; - v.NormalizedBoundaries(&w_m, &w_p); - - const DiyFp c_mk = GetCachedPower(w_p.e, K); - const DiyFp W = v.Normalize() * c_mk; - DiyFp Wp = w_p * c_mk; - DiyFp Wm = w_m * c_mk; - Wm.f++; - Wp.f--; - DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); -} - -inline char* WriteExponent(int K, char* buffer) { - if (K < 0) { - *buffer++ = '-'; - K = -K; - } - - if (K >= 100) { - *buffer++ = static_cast('0' + static_cast(K / 100)); - K %= 100; - const char* d = GetDigitsLut() + K * 2; - *buffer++ = d[0]; - *buffer++ = d[1]; - } - else if (K >= 10) { - const char* d = GetDigitsLut() + K * 2; - *buffer++ = d[0]; - *buffer++ = d[1]; - } - else - *buffer++ = static_cast('0' + static_cast(K)); - - return buffer; -} - -inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { - const int kk = length + k; // 10^(kk-1) <= v < 10^kk - - if (0 <= k && kk <= 21) { - // 1234e7 -> 12340000000 - for (int i = length; i < kk; i++) - buffer[i] = '0'; - buffer[kk] = '.'; - buffer[kk + 1] = '0'; - return &buffer[kk + 2]; - } - else if (0 < kk && kk <= 21) { - // 1234e-2 -> 12.34 - std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); - buffer[kk] = '.'; - if (0 > k + maxDecimalPlaces) { - // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 - // Remove extra trailing zeros (at least one) after truncation. - for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) - if (buffer[i] != '0') - return &buffer[i + 1]; - return &buffer[kk + 2]; // Reserve one zero - } - else - return &buffer[length + 1]; - } - else if (-6 < kk && kk <= 0) { - // 1234e-6 -> 0.001234 - const int offset = 2 - kk; - std::memmove(&buffer[offset], &buffer[0], static_cast(length)); - buffer[0] = '0'; - buffer[1] = '.'; - for (int i = 2; i < offset; i++) - buffer[i] = '0'; - if (length - kk > maxDecimalPlaces) { - // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 - // Remove extra trailing zeros (at least one) after truncation. - for (int i = maxDecimalPlaces + 1; i > 2; i--) - if (buffer[i] != '0') - return &buffer[i + 1]; - return &buffer[3]; // Reserve one zero - } - else - return &buffer[length + offset]; - } - else if (kk < -maxDecimalPlaces) { - // Truncate to zero - buffer[0] = '0'; - buffer[1] = '.'; - buffer[2] = '0'; - return &buffer[3]; - } - else if (length == 1) { - // 1e30 - buffer[1] = 'e'; - return WriteExponent(kk - 1, &buffer[2]); - } - else { - // 1234e30 -> 1.234e33 - std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); - buffer[1] = '.'; - buffer[length + 1] = 'e'; - return WriteExponent(kk - 1, &buffer[0 + length + 2]); - } -} - -inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { - RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); - Double d(value); - if (d.IsZero()) { - if (d.Sign()) - *buffer++ = '-'; // -0.0, Issue #289 - buffer[0] = '0'; - buffer[1] = '.'; - buffer[2] = '0'; - return &buffer[3]; - } - else { - if (value < 0) { - *buffer++ = '-'; - value = -value; - } - int length, K; - Grisu2(value, buffer, &length, &K); - return Prettify(buffer, length, K, maxDecimalPlaces); - } -} - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_DTOA_ diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h deleted file mode 100755 index 68c9e966..00000000 --- a/include/rapidjson/internal/ieee754.h +++ /dev/null @@ -1,78 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_IEEE754_ -#define RAPIDJSON_IEEE754_ - -#include "../rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -class Double { -public: - Double() {} - Double(double d) : d_(d) {} - Double(uint64_t u) : u_(u) {} - - double Value() const { return d_; } - uint64_t Uint64Value() const { return u_; } - - double NextPositiveDouble() const { - RAPIDJSON_ASSERT(!Sign()); - return Double(u_ + 1).Value(); - } - - bool Sign() const { return (u_ & kSignMask) != 0; } - uint64_t Significand() const { return u_ & kSignificandMask; } - int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } - - bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } - bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } - bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } - bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } - bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } - - uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } - int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } - uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } - - static int EffectiveSignificandSize(int order) { - if (order >= -1021) - return 53; - else if (order <= -1074) - return 0; - else - return order + 1074; - } - -private: - static const int kSignificandSize = 52; - static const int kExponentBias = 0x3FF; - static const int kDenormalExponent = 1 - kExponentBias; - static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); - static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); - static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); - static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); - - union { - double d_; - uint64_t u_; - }; -}; - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_IEEE754_ diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h deleted file mode 100755 index 9fe8c932..00000000 --- a/include/rapidjson/internal/itoa.h +++ /dev/null @@ -1,308 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_ITOA_ -#define RAPIDJSON_ITOA_ - -#include "../rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -inline const char* GetDigitsLut() { - static const char cDigitsLut[200] = { - '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', - '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', - '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', - '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', - '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', - '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', - '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', - '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', - '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', - '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' - }; - return cDigitsLut; -} - -inline char* u32toa(uint32_t value, char* buffer) { - RAPIDJSON_ASSERT(buffer != 0); - - const char* cDigitsLut = GetDigitsLut(); - - if (value < 10000) { - const uint32_t d1 = (value / 100) << 1; - const uint32_t d2 = (value % 100) << 1; - - if (value >= 1000) - *buffer++ = cDigitsLut[d1]; - if (value >= 100) - *buffer++ = cDigitsLut[d1 + 1]; - if (value >= 10) - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - } - else if (value < 100000000) { - // value = bbbbcccc - const uint32_t b = value / 10000; - const uint32_t c = value % 10000; - - const uint32_t d1 = (b / 100) << 1; - const uint32_t d2 = (b % 100) << 1; - - const uint32_t d3 = (c / 100) << 1; - const uint32_t d4 = (c % 100) << 1; - - if (value >= 10000000) - *buffer++ = cDigitsLut[d1]; - if (value >= 1000000) - *buffer++ = cDigitsLut[d1 + 1]; - if (value >= 100000) - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - - *buffer++ = cDigitsLut[d3]; - *buffer++ = cDigitsLut[d3 + 1]; - *buffer++ = cDigitsLut[d4]; - *buffer++ = cDigitsLut[d4 + 1]; - } - else { - // value = aabbbbcccc in decimal - - const uint32_t a = value / 100000000; // 1 to 42 - value %= 100000000; - - if (a >= 10) { - const unsigned i = a << 1; - *buffer++ = cDigitsLut[i]; - *buffer++ = cDigitsLut[i + 1]; - } - else - *buffer++ = static_cast('0' + static_cast(a)); - - const uint32_t b = value / 10000; // 0 to 9999 - const uint32_t c = value % 10000; // 0 to 9999 - - const uint32_t d1 = (b / 100) << 1; - const uint32_t d2 = (b % 100) << 1; - - const uint32_t d3 = (c / 100) << 1; - const uint32_t d4 = (c % 100) << 1; - - *buffer++ = cDigitsLut[d1]; - *buffer++ = cDigitsLut[d1 + 1]; - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - *buffer++ = cDigitsLut[d3]; - *buffer++ = cDigitsLut[d3 + 1]; - *buffer++ = cDigitsLut[d4]; - *buffer++ = cDigitsLut[d4 + 1]; - } - return buffer; -} - -inline char* i32toa(int32_t value, char* buffer) { - RAPIDJSON_ASSERT(buffer != 0); - uint32_t u = static_cast(value); - if (value < 0) { - *buffer++ = '-'; - u = ~u + 1; - } - - return u32toa(u, buffer); -} - -inline char* u64toa(uint64_t value, char* buffer) { - RAPIDJSON_ASSERT(buffer != 0); - const char* cDigitsLut = GetDigitsLut(); - const uint64_t kTen8 = 100000000; - const uint64_t kTen9 = kTen8 * 10; - const uint64_t kTen10 = kTen8 * 100; - const uint64_t kTen11 = kTen8 * 1000; - const uint64_t kTen12 = kTen8 * 10000; - const uint64_t kTen13 = kTen8 * 100000; - const uint64_t kTen14 = kTen8 * 1000000; - const uint64_t kTen15 = kTen8 * 10000000; - const uint64_t kTen16 = kTen8 * kTen8; - - if (value < kTen8) { - uint32_t v = static_cast(value); - if (v < 10000) { - const uint32_t d1 = (v / 100) << 1; - const uint32_t d2 = (v % 100) << 1; - - if (v >= 1000) - *buffer++ = cDigitsLut[d1]; - if (v >= 100) - *buffer++ = cDigitsLut[d1 + 1]; - if (v >= 10) - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - } - else { - // value = bbbbcccc - const uint32_t b = v / 10000; - const uint32_t c = v % 10000; - - const uint32_t d1 = (b / 100) << 1; - const uint32_t d2 = (b % 100) << 1; - - const uint32_t d3 = (c / 100) << 1; - const uint32_t d4 = (c % 100) << 1; - - if (value >= 10000000) - *buffer++ = cDigitsLut[d1]; - if (value >= 1000000) - *buffer++ = cDigitsLut[d1 + 1]; - if (value >= 100000) - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - - *buffer++ = cDigitsLut[d3]; - *buffer++ = cDigitsLut[d3 + 1]; - *buffer++ = cDigitsLut[d4]; - *buffer++ = cDigitsLut[d4 + 1]; - } - } - else if (value < kTen16) { - const uint32_t v0 = static_cast(value / kTen8); - const uint32_t v1 = static_cast(value % kTen8); - - const uint32_t b0 = v0 / 10000; - const uint32_t c0 = v0 % 10000; - - const uint32_t d1 = (b0 / 100) << 1; - const uint32_t d2 = (b0 % 100) << 1; - - const uint32_t d3 = (c0 / 100) << 1; - const uint32_t d4 = (c0 % 100) << 1; - - const uint32_t b1 = v1 / 10000; - const uint32_t c1 = v1 % 10000; - - const uint32_t d5 = (b1 / 100) << 1; - const uint32_t d6 = (b1 % 100) << 1; - - const uint32_t d7 = (c1 / 100) << 1; - const uint32_t d8 = (c1 % 100) << 1; - - if (value >= kTen15) - *buffer++ = cDigitsLut[d1]; - if (value >= kTen14) - *buffer++ = cDigitsLut[d1 + 1]; - if (value >= kTen13) - *buffer++ = cDigitsLut[d2]; - if (value >= kTen12) - *buffer++ = cDigitsLut[d2 + 1]; - if (value >= kTen11) - *buffer++ = cDigitsLut[d3]; - if (value >= kTen10) - *buffer++ = cDigitsLut[d3 + 1]; - if (value >= kTen9) - *buffer++ = cDigitsLut[d4]; - - *buffer++ = cDigitsLut[d4 + 1]; - *buffer++ = cDigitsLut[d5]; - *buffer++ = cDigitsLut[d5 + 1]; - *buffer++ = cDigitsLut[d6]; - *buffer++ = cDigitsLut[d6 + 1]; - *buffer++ = cDigitsLut[d7]; - *buffer++ = cDigitsLut[d7 + 1]; - *buffer++ = cDigitsLut[d8]; - *buffer++ = cDigitsLut[d8 + 1]; - } - else { - const uint32_t a = static_cast(value / kTen16); // 1 to 1844 - value %= kTen16; - - if (a < 10) - *buffer++ = static_cast('0' + static_cast(a)); - else if (a < 100) { - const uint32_t i = a << 1; - *buffer++ = cDigitsLut[i]; - *buffer++ = cDigitsLut[i + 1]; - } - else if (a < 1000) { - *buffer++ = static_cast('0' + static_cast(a / 100)); - - const uint32_t i = (a % 100) << 1; - *buffer++ = cDigitsLut[i]; - *buffer++ = cDigitsLut[i + 1]; - } - else { - const uint32_t i = (a / 100) << 1; - const uint32_t j = (a % 100) << 1; - *buffer++ = cDigitsLut[i]; - *buffer++ = cDigitsLut[i + 1]; - *buffer++ = cDigitsLut[j]; - *buffer++ = cDigitsLut[j + 1]; - } - - const uint32_t v0 = static_cast(value / kTen8); - const uint32_t v1 = static_cast(value % kTen8); - - const uint32_t b0 = v0 / 10000; - const uint32_t c0 = v0 % 10000; - - const uint32_t d1 = (b0 / 100) << 1; - const uint32_t d2 = (b0 % 100) << 1; - - const uint32_t d3 = (c0 / 100) << 1; - const uint32_t d4 = (c0 % 100) << 1; - - const uint32_t b1 = v1 / 10000; - const uint32_t c1 = v1 % 10000; - - const uint32_t d5 = (b1 / 100) << 1; - const uint32_t d6 = (b1 % 100) << 1; - - const uint32_t d7 = (c1 / 100) << 1; - const uint32_t d8 = (c1 % 100) << 1; - - *buffer++ = cDigitsLut[d1]; - *buffer++ = cDigitsLut[d1 + 1]; - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - *buffer++ = cDigitsLut[d3]; - *buffer++ = cDigitsLut[d3 + 1]; - *buffer++ = cDigitsLut[d4]; - *buffer++ = cDigitsLut[d4 + 1]; - *buffer++ = cDigitsLut[d5]; - *buffer++ = cDigitsLut[d5 + 1]; - *buffer++ = cDigitsLut[d6]; - *buffer++ = cDigitsLut[d6 + 1]; - *buffer++ = cDigitsLut[d7]; - *buffer++ = cDigitsLut[d7 + 1]; - *buffer++ = cDigitsLut[d8]; - *buffer++ = cDigitsLut[d8 + 1]; - } - - return buffer; -} - -inline char* i64toa(int64_t value, char* buffer) { - RAPIDJSON_ASSERT(buffer != 0); - uint64_t u = static_cast(value); - if (value < 0) { - *buffer++ = '-'; - u = ~u + 1; - } - - return u64toa(u, buffer); -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_ITOA_ diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h deleted file mode 100755 index 27092dc0..00000000 --- a/include/rapidjson/internal/meta.h +++ /dev/null @@ -1,186 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_INTERNAL_META_H_ -#define RAPIDJSON_INTERNAL_META_H_ - -#include "../rapidjson.h" - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -#if defined(_MSC_VER) && !defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(6334) -#endif - -#if RAPIDJSON_HAS_CXX11_TYPETRAITS -#include -#endif - -//@cond RAPIDJSON_INTERNAL -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching -template struct Void { typedef void Type; }; - -/////////////////////////////////////////////////////////////////////////////// -// BoolType, TrueType, FalseType -// -template struct BoolType { - static const bool Value = Cond; - typedef BoolType Type; -}; -typedef BoolType TrueType; -typedef BoolType FalseType; - - -/////////////////////////////////////////////////////////////////////////////// -// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr -// - -template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; -template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; -template struct SelectIfCond : SelectIfImpl::template Apply {}; -template struct SelectIf : SelectIfCond {}; - -template struct AndExprCond : FalseType {}; -template <> struct AndExprCond : TrueType {}; -template struct OrExprCond : TrueType {}; -template <> struct OrExprCond : FalseType {}; - -template struct BoolExpr : SelectIf::Type {}; -template struct NotExpr : SelectIf::Type {}; -template struct AndExpr : AndExprCond::Type {}; -template struct OrExpr : OrExprCond::Type {}; - - -/////////////////////////////////////////////////////////////////////////////// -// AddConst, MaybeAddConst, RemoveConst -template struct AddConst { typedef const T Type; }; -template struct MaybeAddConst : SelectIfCond {}; -template struct RemoveConst { typedef T Type; }; -template struct RemoveConst { typedef T Type; }; - - -/////////////////////////////////////////////////////////////////////////////// -// IsSame, IsConst, IsMoreConst, IsPointer -// -template struct IsSame : FalseType {}; -template struct IsSame : TrueType {}; - -template struct IsConst : FalseType {}; -template struct IsConst : TrueType {}; - -template -struct IsMoreConst - : AndExpr::Type, typename RemoveConst::Type>, - BoolType::Value >= IsConst::Value> >::Type {}; - -template struct IsPointer : FalseType {}; -template struct IsPointer : TrueType {}; - -/////////////////////////////////////////////////////////////////////////////// -// IsBaseOf -// -#if RAPIDJSON_HAS_CXX11_TYPETRAITS - -template struct IsBaseOf - : BoolType< ::std::is_base_of::value> {}; - -#else // simplified version adopted from Boost - -template struct IsBaseOfImpl { - RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); - RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); - - typedef char (&Yes)[1]; - typedef char (&No) [2]; - - template - static Yes Check(const D*, T); - static No Check(const B*, int); - - struct Host { - operator const B*() const; - operator const D*(); - }; - - enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; -}; - -template struct IsBaseOf - : OrExpr, BoolExpr > >::Type {}; - -#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS - - -////////////////////////////////////////////////////////////////////////// -// EnableIf / DisableIf -// -template struct EnableIfCond { typedef T Type; }; -template struct EnableIfCond { /* empty */ }; - -template struct DisableIfCond { typedef T Type; }; -template struct DisableIfCond { /* empty */ }; - -template -struct EnableIf : EnableIfCond {}; - -template -struct DisableIf : DisableIfCond {}; - -// SFINAE helpers -struct SfinaeTag {}; -template struct RemoveSfinaeTag; -template struct RemoveSfinaeTag { typedef T Type; }; - -#define RAPIDJSON_REMOVEFPTR_(type) \ - typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ - < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type - -#define RAPIDJSON_ENABLEIF(cond) \ - typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ - ::Type * = NULL - -#define RAPIDJSON_DISABLEIF(cond) \ - typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ - ::Type * = NULL - -#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ - typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ - ::Type - -#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ - typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ - ::Type - -} // namespace internal -RAPIDJSON_NAMESPACE_END -//@endcond - -#if defined(_MSC_VER) && !defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/include/rapidjson/internal/pow10.h b/include/rapidjson/internal/pow10.h deleted file mode 100755 index eae1a43e..00000000 --- a/include/rapidjson/internal/pow10.h +++ /dev/null @@ -1,55 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_POW10_ -#define RAPIDJSON_POW10_ - -#include "../rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -//! Computes integer powers of 10 in double (10.0^n). -/*! This function uses lookup table for fast and accurate results. - \param n non-negative exponent. Must <= 308. - \return 10.0^n -*/ -inline double Pow10(int n) { - static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes - 1e+0, - 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, - 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, - 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, - 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, - 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, - 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, - 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, - 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, - 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, - 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, - 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, - 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, - 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, - 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, - 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, - 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 - }; - RAPIDJSON_ASSERT(n >= 0 && n <= 308); - return e[n]; -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_POW10_ diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h deleted file mode 100755 index 6446c403..00000000 --- a/include/rapidjson/internal/regex.h +++ /dev/null @@ -1,739 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_INTERNAL_REGEX_H_ -#define RAPIDJSON_INTERNAL_REGEX_H_ - -#include "../allocators.h" -#include "../stream.h" -#include "stack.h" - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(switch-enum) -#elif defined(_MSC_VER) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -#ifndef RAPIDJSON_REGEX_VERBOSE -#define RAPIDJSON_REGEX_VERBOSE 0 -#endif - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -/////////////////////////////////////////////////////////////////////////////// -// DecodedStream - -template -class DecodedStream { -public: - DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } - unsigned Peek() { return codepoint_; } - unsigned Take() { - unsigned c = codepoint_; - if (c) // No further decoding when '\0' - Decode(); - return c; - } - -private: - void Decode() { - if (!Encoding::Decode(ss_, &codepoint_)) - codepoint_ = 0; - } - - SourceStream& ss_; - unsigned codepoint_; -}; - -/////////////////////////////////////////////////////////////////////////////// -// GenericRegex - -static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 -static const SizeType kRegexInvalidRange = ~SizeType(0); - -template -class GenericRegexSearch; - -//! Regular expression engine with subset of ECMAscript grammar. -/*! - Supported regular expression syntax: - - \c ab Concatenation - - \c a|b Alternation - - \c a? Zero or one - - \c a* Zero or more - - \c a+ One or more - - \c a{3} Exactly 3 times - - \c a{3,} At least 3 times - - \c a{3,5} 3 to 5 times - - \c (ab) Grouping - - \c ^a At the beginning - - \c a$ At the end - - \c . Any character - - \c [abc] Character classes - - \c [a-c] Character class range - - \c [a-z0-9_] Character class combination - - \c [^abc] Negated character classes - - \c [^a-c] Negated character class range - - \c [\b] Backspace (U+0008) - - \c \\| \\\\ ... Escape characters - - \c \\f Form feed (U+000C) - - \c \\n Line feed (U+000A) - - \c \\r Carriage return (U+000D) - - \c \\t Tab (U+0009) - - \c \\v Vertical tab (U+000B) - - \note This is a Thompson NFA engine, implemented with reference to - Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", - https://swtch.com/~rsc/regexp/regexp1.html -*/ -template -class GenericRegex { -public: - typedef Encoding EncodingType; - typedef typename Encoding::Ch Ch; - template friend class GenericRegexSearch; - - GenericRegex(const Ch* source, Allocator* allocator = 0) : - ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), - states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), - anchorBegin_(), anchorEnd_() - { - GenericStringStream ss(source); - DecodedStream, Encoding> ds(ss); - Parse(ds); - } - - ~GenericRegex() - { - RAPIDJSON_DELETE(ownAllocator_); - } - - bool IsValid() const { - return root_ != kRegexInvalidState; - } - -private: - enum Operator { - kZeroOrOne, - kZeroOrMore, - kOneOrMore, - kConcatenation, - kAlternation, - kLeftParenthesis - }; - - static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' - static const unsigned kRangeCharacterClass = 0xFFFFFFFE; - static const unsigned kRangeNegationFlag = 0x80000000; - - struct Range { - unsigned start; // - unsigned end; - SizeType next; - }; - - struct State { - SizeType out; //!< Equals to kInvalid for matching state - SizeType out1; //!< Equals to non-kInvalid for split - SizeType rangeStart; - unsigned codepoint; - }; - - struct Frag { - Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} - SizeType start; - SizeType out; //!< link-list of all output states - SizeType minIndex; - }; - - State& GetState(SizeType index) { - RAPIDJSON_ASSERT(index < stateCount_); - return states_.template Bottom()[index]; - } - - const State& GetState(SizeType index) const { - RAPIDJSON_ASSERT(index < stateCount_); - return states_.template Bottom()[index]; - } - - Range& GetRange(SizeType index) { - RAPIDJSON_ASSERT(index < rangeCount_); - return ranges_.template Bottom()[index]; - } - - const Range& GetRange(SizeType index) const { - RAPIDJSON_ASSERT(index < rangeCount_); - return ranges_.template Bottom()[index]; - } - - template - void Parse(DecodedStream& ds) { - Stack operandStack(allocator_, 256); // Frag - Stack operatorStack(allocator_, 256); // Operator - Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) - - *atomCountStack.template Push() = 0; - - unsigned codepoint; - while (ds.Peek() != 0) { - switch (codepoint = ds.Take()) { - case '^': - anchorBegin_ = true; - break; - - case '$': - anchorEnd_ = true; - break; - - case '|': - while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) - if (!Eval(operandStack, *operatorStack.template Pop(1))) - return; - *operatorStack.template Push() = kAlternation; - *atomCountStack.template Top() = 0; - break; - - case '(': - *operatorStack.template Push() = kLeftParenthesis; - *atomCountStack.template Push() = 0; - break; - - case ')': - while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) - if (!Eval(operandStack, *operatorStack.template Pop(1))) - return; - if (operatorStack.Empty()) - return; - operatorStack.template Pop(1); - atomCountStack.template Pop(1); - ImplicitConcatenation(atomCountStack, operatorStack); - break; - - case '?': - if (!Eval(operandStack, kZeroOrOne)) - return; - break; - - case '*': - if (!Eval(operandStack, kZeroOrMore)) - return; - break; - - case '+': - if (!Eval(operandStack, kOneOrMore)) - return; - break; - - case '{': - { - unsigned n, m; - if (!ParseUnsigned(ds, &n)) - return; - - if (ds.Peek() == ',') { - ds.Take(); - if (ds.Peek() == '}') - m = kInfinityQuantifier; - else if (!ParseUnsigned(ds, &m) || m < n) - return; - } - else - m = n; - - if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') - return; - ds.Take(); - } - break; - - case '.': - PushOperand(operandStack, kAnyCharacterClass); - ImplicitConcatenation(atomCountStack, operatorStack); - break; - - case '[': - { - SizeType range; - if (!ParseRange(ds, &range)) - return; - SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); - GetState(s).rangeStart = range; - *operandStack.template Push() = Frag(s, s, s); - } - ImplicitConcatenation(atomCountStack, operatorStack); - break; - - case '\\': // Escape character - if (!CharacterEscape(ds, &codepoint)) - return; // Unsupported escape character - // fall through to default - RAPIDJSON_DELIBERATE_FALLTHROUGH; - - default: // Pattern character - PushOperand(operandStack, codepoint); - ImplicitConcatenation(atomCountStack, operatorStack); - } - } - - while (!operatorStack.Empty()) - if (!Eval(operandStack, *operatorStack.template Pop(1))) - return; - - // Link the operand to matching state. - if (operandStack.GetSize() == sizeof(Frag)) { - Frag* e = operandStack.template Pop(1); - Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); - root_ = e->start; - -#if RAPIDJSON_REGEX_VERBOSE - printf("root: %d\n", root_); - for (SizeType i = 0; i < stateCount_ ; i++) { - State& s = GetState(i); - printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); - } - printf("\n"); -#endif - } - } - - SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { - State* s = states_.template Push(); - s->out = out; - s->out1 = out1; - s->codepoint = codepoint; - s->rangeStart = kRegexInvalidRange; - return stateCount_++; - } - - void PushOperand(Stack& operandStack, unsigned codepoint) { - SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); - *operandStack.template Push() = Frag(s, s, s); - } - - void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { - if (*atomCountStack.template Top()) - *operatorStack.template Push() = kConcatenation; - (*atomCountStack.template Top())++; - } - - SizeType Append(SizeType l1, SizeType l2) { - SizeType old = l1; - while (GetState(l1).out != kRegexInvalidState) - l1 = GetState(l1).out; - GetState(l1).out = l2; - return old; - } - - void Patch(SizeType l, SizeType s) { - for (SizeType next; l != kRegexInvalidState; l = next) { - next = GetState(l).out; - GetState(l).out = s; - } - } - - bool Eval(Stack& operandStack, Operator op) { - switch (op) { - case kConcatenation: - RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); - { - Frag e2 = *operandStack.template Pop(1); - Frag e1 = *operandStack.template Pop(1); - Patch(e1.out, e2.start); - *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); - } - return true; - - case kAlternation: - if (operandStack.GetSize() >= sizeof(Frag) * 2) { - Frag e2 = *operandStack.template Pop(1); - Frag e1 = *operandStack.template Pop(1); - SizeType s = NewState(e1.start, e2.start, 0); - *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); - return true; - } - return false; - - case kZeroOrOne: - if (operandStack.GetSize() >= sizeof(Frag)) { - Frag e = *operandStack.template Pop(1); - SizeType s = NewState(kRegexInvalidState, e.start, 0); - *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); - return true; - } - return false; - - case kZeroOrMore: - if (operandStack.GetSize() >= sizeof(Frag)) { - Frag e = *operandStack.template Pop(1); - SizeType s = NewState(kRegexInvalidState, e.start, 0); - Patch(e.out, s); - *operandStack.template Push() = Frag(s, s, e.minIndex); - return true; - } - return false; - - case kOneOrMore: - if (operandStack.GetSize() >= sizeof(Frag)) { - Frag e = *operandStack.template Pop(1); - SizeType s = NewState(kRegexInvalidState, e.start, 0); - Patch(e.out, s); - *operandStack.template Push() = Frag(e.start, s, e.minIndex); - return true; - } - return false; - - default: - // syntax error (e.g. unclosed kLeftParenthesis) - return false; - } - } - - bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { - RAPIDJSON_ASSERT(n <= m); - RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); - - if (n == 0) { - if (m == 0) // a{0} not support - return false; - else if (m == kInfinityQuantifier) - Eval(operandStack, kZeroOrMore); // a{0,} -> a* - else { - Eval(operandStack, kZeroOrOne); // a{0,5} -> a? - for (unsigned i = 0; i < m - 1; i++) - CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? - for (unsigned i = 0; i < m - 1; i++) - Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? - } - return true; - } - - for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a - CloneTopOperand(operandStack); - - if (m == kInfinityQuantifier) - Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ - else if (m > n) { - CloneTopOperand(operandStack); // a{3,5} -> a a a a - Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? - for (unsigned i = n; i < m - 1; i++) - CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? - for (unsigned i = n; i < m; i++) - Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? - } - - for (unsigned i = 0; i < n - 1; i++) - Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? - - return true; - } - - static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } - - void CloneTopOperand(Stack& operandStack) { - const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation - SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) - State* s = states_.template Push(count); - memcpy(s, &GetState(src.minIndex), count * sizeof(State)); - for (SizeType j = 0; j < count; j++) { - if (s[j].out != kRegexInvalidState) - s[j].out += count; - if (s[j].out1 != kRegexInvalidState) - s[j].out1 += count; - } - *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); - stateCount_ += count; - } - - template - bool ParseUnsigned(DecodedStream& ds, unsigned* u) { - unsigned r = 0; - if (ds.Peek() < '0' || ds.Peek() > '9') - return false; - while (ds.Peek() >= '0' && ds.Peek() <= '9') { - if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 - return false; // overflow - r = r * 10 + (ds.Take() - '0'); - } - *u = r; - return true; - } - - template - bool ParseRange(DecodedStream& ds, SizeType* range) { - bool isBegin = true; - bool negate = false; - int step = 0; - SizeType start = kRegexInvalidRange; - SizeType current = kRegexInvalidRange; - unsigned codepoint; - while ((codepoint = ds.Take()) != 0) { - if (isBegin) { - isBegin = false; - if (codepoint == '^') { - negate = true; - continue; - } - } - - switch (codepoint) { - case ']': - if (start == kRegexInvalidRange) - return false; // Error: nothing inside [] - if (step == 2) { // Add trailing '-' - SizeType r = NewRange('-'); - RAPIDJSON_ASSERT(current != kRegexInvalidRange); - GetRange(current).next = r; - } - if (negate) - GetRange(start).start |= kRangeNegationFlag; - *range = start; - return true; - - case '\\': - if (ds.Peek() == 'b') { - ds.Take(); - codepoint = 0x0008; // Escape backspace character - } - else if (!CharacterEscape(ds, &codepoint)) - return false; - // fall through to default - RAPIDJSON_DELIBERATE_FALLTHROUGH; - - default: - switch (step) { - case 1: - if (codepoint == '-') { - step++; - break; - } - // fall through to step 0 for other characters - RAPIDJSON_DELIBERATE_FALLTHROUGH; - - case 0: - { - SizeType r = NewRange(codepoint); - if (current != kRegexInvalidRange) - GetRange(current).next = r; - if (start == kRegexInvalidRange) - start = r; - current = r; - } - step = 1; - break; - - default: - RAPIDJSON_ASSERT(step == 2); - GetRange(current).end = codepoint; - step = 0; - } - } - } - return false; - } - - SizeType NewRange(unsigned codepoint) { - Range* r = ranges_.template Push(); - r->start = r->end = codepoint; - r->next = kRegexInvalidRange; - return rangeCount_++; - } - - template - bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { - unsigned codepoint; - switch (codepoint = ds.Take()) { - case '^': - case '$': - case '|': - case '(': - case ')': - case '?': - case '*': - case '+': - case '.': - case '[': - case ']': - case '{': - case '}': - case '\\': - *escapedCodepoint = codepoint; return true; - case 'f': *escapedCodepoint = 0x000C; return true; - case 'n': *escapedCodepoint = 0x000A; return true; - case 'r': *escapedCodepoint = 0x000D; return true; - case 't': *escapedCodepoint = 0x0009; return true; - case 'v': *escapedCodepoint = 0x000B; return true; - default: - return false; // Unsupported escape character - } - } - - Allocator* ownAllocator_; - Allocator* allocator_; - Stack states_; - Stack ranges_; - SizeType root_; - SizeType stateCount_; - SizeType rangeCount_; - - static const unsigned kInfinityQuantifier = ~0u; - - // For SearchWithAnchoring() - bool anchorBegin_; - bool anchorEnd_; -}; - -template -class GenericRegexSearch { -public: - typedef typename RegexType::EncodingType Encoding; - typedef typename Encoding::Ch Ch; - - GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : - regex_(regex), allocator_(allocator), ownAllocator_(0), - state0_(allocator, 0), state1_(allocator, 0), stateSet_() - { - RAPIDJSON_ASSERT(regex_.IsValid()); - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); - state0_.template Reserve(regex_.stateCount_); - state1_.template Reserve(regex_.stateCount_); - } - - ~GenericRegexSearch() { - Allocator::Free(stateSet_); - RAPIDJSON_DELETE(ownAllocator_); - } - - template - bool Match(InputStream& is) { - return SearchWithAnchoring(is, true, true); - } - - bool Match(const Ch* s) { - GenericStringStream is(s); - return Match(is); - } - - template - bool Search(InputStream& is) { - return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); - } - - bool Search(const Ch* s) { - GenericStringStream is(s); - return Search(is); - } - -private: - typedef typename RegexType::State State; - typedef typename RegexType::Range Range; - - template - bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { - DecodedStream ds(is); - - state0_.Clear(); - Stack *current = &state0_, *next = &state1_; - const size_t stateSetSize = GetStateSetSize(); - std::memset(stateSet_, 0, stateSetSize); - - bool matched = AddState(*current, regex_.root_); - unsigned codepoint; - while (!current->Empty() && (codepoint = ds.Take()) != 0) { - std::memset(stateSet_, 0, stateSetSize); - next->Clear(); - matched = false; - for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { - const State& sr = regex_.GetState(*s); - if (sr.codepoint == codepoint || - sr.codepoint == RegexType::kAnyCharacterClass || - (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) - { - matched = AddState(*next, sr.out) || matched; - if (!anchorEnd && matched) - return true; - } - if (!anchorBegin) - AddState(*next, regex_.root_); - } - internal::Swap(current, next); - } - - return matched; - } - - size_t GetStateSetSize() const { - return (regex_.stateCount_ + 31) / 32 * 4; - } - - // Return whether the added states is a match state - bool AddState(Stack& l, SizeType index) { - RAPIDJSON_ASSERT(index != kRegexInvalidState); - - const State& s = regex_.GetState(index); - if (s.out1 != kRegexInvalidState) { // Split - bool matched = AddState(l, s.out); - return AddState(l, s.out1) || matched; - } - else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { - stateSet_[index >> 5] |= (1u << (index & 31)); - *l.template PushUnsafe() = index; - } - return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. - } - - bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { - bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; - while (rangeIndex != kRegexInvalidRange) { - const Range& r = regex_.GetRange(rangeIndex); - if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) - return yes; - rangeIndex = r.next; - } - return !yes; - } - - const RegexType& regex_; - Allocator* allocator_; - Allocator* ownAllocator_; - Stack state0_; - Stack state1_; - uint32_t* stateSet_; -}; - -typedef GenericRegex > Regex; -typedef GenericRegexSearch RegexSearch; - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#if defined(__clang__) || defined(_MSC_VER) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h deleted file mode 100755 index 73abd706..00000000 --- a/include/rapidjson/internal/stack.h +++ /dev/null @@ -1,232 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_INTERNAL_STACK_H_ -#define RAPIDJSON_INTERNAL_STACK_H_ - -#include "../allocators.h" -#include "swap.h" -#include - -#if defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -/////////////////////////////////////////////////////////////////////////////// -// Stack - -//! A type-unsafe stack for storing different types of data. -/*! \tparam Allocator Allocator for allocating stack memory. -*/ -template -class Stack { -public: - // Optimization note: Do not allocate memory for stack_ in constructor. - // Do it lazily when first Push() -> Expand() -> Resize(). - Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - Stack(Stack&& rhs) - : allocator_(rhs.allocator_), - ownAllocator_(rhs.ownAllocator_), - stack_(rhs.stack_), - stackTop_(rhs.stackTop_), - stackEnd_(rhs.stackEnd_), - initialCapacity_(rhs.initialCapacity_) - { - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.stack_ = 0; - rhs.stackTop_ = 0; - rhs.stackEnd_ = 0; - rhs.initialCapacity_ = 0; - } -#endif - - ~Stack() { - Destroy(); - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - Stack& operator=(Stack&& rhs) { - if (&rhs != this) - { - Destroy(); - - allocator_ = rhs.allocator_; - ownAllocator_ = rhs.ownAllocator_; - stack_ = rhs.stack_; - stackTop_ = rhs.stackTop_; - stackEnd_ = rhs.stackEnd_; - initialCapacity_ = rhs.initialCapacity_; - - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.stack_ = 0; - rhs.stackTop_ = 0; - rhs.stackEnd_ = 0; - rhs.initialCapacity_ = 0; - } - return *this; - } -#endif - - void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { - internal::Swap(allocator_, rhs.allocator_); - internal::Swap(ownAllocator_, rhs.ownAllocator_); - internal::Swap(stack_, rhs.stack_); - internal::Swap(stackTop_, rhs.stackTop_); - internal::Swap(stackEnd_, rhs.stackEnd_); - internal::Swap(initialCapacity_, rhs.initialCapacity_); - } - - void Clear() { stackTop_ = stack_; } - - void ShrinkToFit() { - if (Empty()) { - // If the stack is empty, completely deallocate the memory. - Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) - stack_ = 0; - stackTop_ = 0; - stackEnd_ = 0; - } - else - Resize(GetSize()); - } - - // Optimization note: try to minimize the size of this function for force inline. - // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. - template - RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { - // Expand the stack if needed - if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) - Expand(count); - } - - template - RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { - Reserve(count); - return PushUnsafe(count); - } - - template - RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { - RAPIDJSON_ASSERT(stackTop_); - RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); - T* ret = reinterpret_cast(stackTop_); - stackTop_ += sizeof(T) * count; - return ret; - } - - template - T* Pop(size_t count) { - RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); - stackTop_ -= count * sizeof(T); - return reinterpret_cast(stackTop_); - } - - template - T* Top() { - RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); - return reinterpret_cast(stackTop_ - sizeof(T)); - } - - template - const T* Top() const { - RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); - return reinterpret_cast(stackTop_ - sizeof(T)); - } - - template - T* End() { return reinterpret_cast(stackTop_); } - - template - const T* End() const { return reinterpret_cast(stackTop_); } - - template - T* Bottom() { return reinterpret_cast(stack_); } - - template - const T* Bottom() const { return reinterpret_cast(stack_); } - - bool HasAllocator() const { - return allocator_ != 0; - } - - Allocator& GetAllocator() { - RAPIDJSON_ASSERT(allocator_); - return *allocator_; - } - - bool Empty() const { return stackTop_ == stack_; } - size_t GetSize() const { return static_cast(stackTop_ - stack_); } - size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } - -private: - template - void Expand(size_t count) { - // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. - size_t newCapacity; - if (stack_ == 0) { - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - newCapacity = initialCapacity_; - } else { - newCapacity = GetCapacity(); - newCapacity += (newCapacity + 1) / 2; - } - size_t newSize = GetSize() + sizeof(T) * count; - if (newCapacity < newSize) - newCapacity = newSize; - - Resize(newCapacity); - } - - void Resize(size_t newCapacity) { - const size_t size = GetSize(); // Backup the current size - stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); - stackTop_ = stack_ + size; - stackEnd_ = stack_ + newCapacity; - } - - void Destroy() { - Allocator::Free(stack_); - RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack - } - - // Prohibit copy constructor & assignment operator. - Stack(const Stack&); - Stack& operator=(const Stack&); - - Allocator* allocator_; - Allocator* ownAllocator_; - char *stack_; - char *stackTop_; - char *stackEnd_; - size_t initialCapacity_; -}; - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#if defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_STACK_H_ diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h deleted file mode 100755 index baecb6cc..00000000 --- a/include/rapidjson/internal/strfunc.h +++ /dev/null @@ -1,69 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ -#define RAPIDJSON_INTERNAL_STRFUNC_H_ - -#include "../stream.h" -#include - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -//! Custom strlen() which works on different character types. -/*! \tparam Ch Character type (e.g. char, wchar_t, short) - \param s Null-terminated input string. - \return Number of characters in the string. - \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. -*/ -template -inline SizeType StrLen(const Ch* s) { - RAPIDJSON_ASSERT(s != 0); - const Ch* p = s; - while (*p) ++p; - return SizeType(p - s); -} - -template <> -inline SizeType StrLen(const char* s) { - return SizeType(std::strlen(s)); -} - -template <> -inline SizeType StrLen(const wchar_t* s) { - return SizeType(std::wcslen(s)); -} - -//! Returns number of code points in a encoded string. -template -bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { - RAPIDJSON_ASSERT(s != 0); - RAPIDJSON_ASSERT(outCount != 0); - GenericStringStream is(s); - const typename Encoding::Ch* end = s + length; - SizeType count = 0; - while (is.src_ < end) { - unsigned codepoint; - if (!Encoding::Decode(is, &codepoint)) - return false; - count++; - } - *outCount = count; - return true; -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h deleted file mode 100755 index d61a67a4..00000000 --- a/include/rapidjson/internal/strtod.h +++ /dev/null @@ -1,290 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_STRTOD_ -#define RAPIDJSON_STRTOD_ - -#include "ieee754.h" -#include "biginteger.h" -#include "diyfp.h" -#include "pow10.h" -#include -#include - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -inline double FastPath(double significand, int exp) { - if (exp < -308) - return 0.0; - else if (exp >= 0) - return significand * internal::Pow10(exp); - else - return significand / internal::Pow10(-exp); -} - -inline double StrtodNormalPrecision(double d, int p) { - if (p < -308) { - // Prevent expSum < -308, making Pow10(p) = 0 - d = FastPath(d, -308); - d = FastPath(d, p + 308); - } - else - d = FastPath(d, p); - return d; -} - -template -inline T Min3(T a, T b, T c) { - T m = a; - if (m > b) m = b; - if (m > c) m = c; - return m; -} - -inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { - const Double db(b); - const uint64_t bInt = db.IntegerSignificand(); - const int bExp = db.IntegerExponent(); - const int hExp = bExp - 1; - - int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; - - // Adjust for decimal exponent - if (dExp >= 0) { - dS_Exp2 += dExp; - dS_Exp5 += dExp; - } - else { - bS_Exp2 -= dExp; - bS_Exp5 -= dExp; - hS_Exp2 -= dExp; - hS_Exp5 -= dExp; - } - - // Adjust for binary exponent - if (bExp >= 0) - bS_Exp2 += bExp; - else { - dS_Exp2 -= bExp; - hS_Exp2 -= bExp; - } - - // Adjust for half ulp exponent - if (hExp >= 0) - hS_Exp2 += hExp; - else { - dS_Exp2 -= hExp; - bS_Exp2 -= hExp; - } - - // Remove common power of two factor from all three scaled values - int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); - dS_Exp2 -= common_Exp2; - bS_Exp2 -= common_Exp2; - hS_Exp2 -= common_Exp2; - - BigInteger dS = d; - dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); - - BigInteger bS(bInt); - bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); - - BigInteger hS(1); - hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); - - BigInteger delta(0); - dS.Difference(bS, &delta); - - return delta.Compare(hS); -} - -inline bool StrtodFast(double d, int p, double* result) { - // Use fast path for string-to-double conversion if possible - // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - if (p > 22 && p < 22 + 16) { - // Fast Path Cases In Disguise - d *= internal::Pow10(p - 22); - p = 22; - } - - if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 - *result = FastPath(d, p); - return true; - } - else - return false; -} - -// Compute an approximation and see if it is within 1/2 ULP -inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) { - uint64_t significand = 0; - int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 - for (; i < dLen; i++) { - if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || - (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) - break; - significand = significand * 10u + static_cast(decimals[i] - '0'); - } - - if (i < dLen && decimals[i] >= '5') // Rounding - significand++; - - int remaining = dLen - i; - const int kUlpShift = 3; - const int kUlp = 1 << kUlpShift; - int64_t error = (remaining == 0) ? 0 : kUlp / 2; - - DiyFp v(significand, 0); - v = v.Normalize(); - error <<= -v.e; - - dExp += remaining; - - int actualExp; - DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); - if (actualExp != dExp) { - static const DiyFp kPow10[] = { - DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 - DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 - DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 - DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 - DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 - DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 - DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 - }; - int adjustment = dExp - actualExp; - RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); - v = v * kPow10[adjustment - 1]; - if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit - error += kUlp / 2; - } - - v = v * cachedPower; - - error += kUlp + (error == 0 ? 0 : 1); - - const int oldExp = v.e; - v = v.Normalize(); - error <<= oldExp - v.e; - - const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); - int precisionSize = 64 - effectiveSignificandSize; - if (precisionSize + kUlpShift >= 64) { - int scaleExp = (precisionSize + kUlpShift) - 63; - v.f >>= scaleExp; - v.e += scaleExp; - error = (error >> scaleExp) + 1 + kUlp; - precisionSize -= scaleExp; - } - - DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); - const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; - const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; - if (precisionBits >= halfWay + static_cast(error)) { - rounded.f++; - if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) - rounded.f >>= 1; - rounded.e++; - } - } - - *result = rounded.ToDouble(); - - return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); -} - -inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) { - RAPIDJSON_ASSERT(dLen >= 0); - const BigInteger dInt(decimals, static_cast(dLen)); - Double a(approx); - int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); - if (cmp < 0) - return a.Value(); // within half ULP - else if (cmp == 0) { - // Round towards even - if (a.Significand() & 1) - return a.NextPositiveDouble(); - else - return a.Value(); - } - else // adjustment - return a.NextPositiveDouble(); -} - -inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { - RAPIDJSON_ASSERT(d >= 0.0); - RAPIDJSON_ASSERT(length >= 1); - - double result = 0.0; - if (StrtodFast(d, p, &result)) - return result; - - RAPIDJSON_ASSERT(length <= INT_MAX); - int dLen = static_cast(length); - - RAPIDJSON_ASSERT(length >= decimalPosition); - RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); - int dExpAdjust = static_cast(length - decimalPosition); - - RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); - int dExp = exp - dExpAdjust; - - // Make sure length+dExp does not overflow - RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); - - // Trim leading zeros - while (dLen > 0 && *decimals == '0') { - dLen--; - decimals++; - } - - // Trim trailing zeros - while (dLen > 0 && decimals[dLen - 1] == '0') { - dLen--; - dExp++; - } - - if (dLen == 0) { // Buffer only contains zeros. - return 0.0; - } - - // Trim right-most digits - const int kMaxDecimalDigit = 767 + 1; - if (dLen > kMaxDecimalDigit) { - dExp += dLen - kMaxDecimalDigit; - dLen = kMaxDecimalDigit; - } - - // If too small, underflow to zero. - // Any x <= 10^-324 is interpreted as zero. - if (dLen + dExp <= -324) - return 0.0; - - // If too large, overflow to infinity. - // Any x >= 10^309 is interpreted as +infinity. - if (dLen + dExp > 309) - return std::numeric_limits::infinity(); - - if (StrtodDiyFp(decimals, dLen, dExp, &result)) - return result; - - // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison - return StrtodBigInteger(result, decimals, dLen, dExp); -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_STRTOD_ diff --git a/include/rapidjson/internal/swap.h b/include/rapidjson/internal/swap.h deleted file mode 100755 index 2cf92f93..00000000 --- a/include/rapidjson/internal/swap.h +++ /dev/null @@ -1,46 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_INTERNAL_SWAP_H_ -#define RAPIDJSON_INTERNAL_SWAP_H_ - -#include "../rapidjson.h" - -#if defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -//! Custom swap() to avoid dependency on C++ header -/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. - \note This has the same semantics as std::swap(). -*/ -template -inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { - T tmp = a; - a = b; - b = tmp; -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#if defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/include/rapidjson/istreamwrapper.h b/include/rapidjson/istreamwrapper.h deleted file mode 100755 index 01437ec0..00000000 --- a/include/rapidjson/istreamwrapper.h +++ /dev/null @@ -1,128 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ -#define RAPIDJSON_ISTREAMWRAPPER_H_ - -#include "stream.h" -#include -#include - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#elif defined(_MSC_VER) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. -/*! - The classes can be wrapped including but not limited to: - - - \c std::istringstream - - \c std::stringstream - - \c std::wistringstream - - \c std::wstringstream - - \c std::ifstream - - \c std::fstream - - \c std::wifstream - - \c std::wfstream - - \tparam StreamType Class derived from \c std::basic_istream. -*/ - -template -class BasicIStreamWrapper { -public: - typedef typename StreamType::char_type Ch; - - //! Constructor. - /*! - \param stream stream opened for read. - */ - BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { - Read(); - } - - //! Constructor. - /*! - \param stream stream opened for read. - \param buffer user-supplied buffer. - \param bufferSize size of buffer in bytes. Must >=4 bytes. - */ - BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { - RAPIDJSON_ASSERT(bufferSize >= 4); - Read(); - } - - Ch Peek() const { return *current_; } - Ch Take() { Ch c = *current_; Read(); return c; } - size_t Tell() const { return count_ + static_cast(current_ - buffer_); } - - // Not implemented - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - // For encoding detection only. - const Ch* Peek4() const { - return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; - } - -private: - BasicIStreamWrapper(); - BasicIStreamWrapper(const BasicIStreamWrapper&); - BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); - - void Read() { - if (current_ < bufferLast_) - ++current_; - else if (!eof_) { - count_ += readCount_; - readCount_ = bufferSize_; - bufferLast_ = buffer_ + readCount_ - 1; - current_ = buffer_; - - if (!stream_.read(buffer_, static_cast(bufferSize_))) { - readCount_ = static_cast(stream_.gcount()); - *(bufferLast_ = buffer_ + readCount_) = '\0'; - eof_ = true; - } - } - } - - StreamType &stream_; - Ch peekBuffer_[4], *buffer_; - size_t bufferSize_; - Ch *bufferLast_; - Ch *current_; - size_t readCount_; - size_t count_; //!< Number of characters read - bool eof_; -}; - -typedef BasicIStreamWrapper IStreamWrapper; -typedef BasicIStreamWrapper WIStreamWrapper; - -#if defined(__clang__) || defined(_MSC_VER) -RAPIDJSON_DIAG_POP -#endif - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/include/rapidjson/memorybuffer.h b/include/rapidjson/memorybuffer.h deleted file mode 100755 index ffbc41ed..00000000 --- a/include/rapidjson/memorybuffer.h +++ /dev/null @@ -1,70 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_MEMORYBUFFER_H_ -#define RAPIDJSON_MEMORYBUFFER_H_ - -#include "stream.h" -#include "internal/stack.h" - -RAPIDJSON_NAMESPACE_BEGIN - -//! Represents an in-memory output byte stream. -/*! - This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. - - It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. - - Differences between MemoryBuffer and StringBuffer: - 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. - 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. - - \tparam Allocator type for allocating memory buffer. - \note implements Stream concept -*/ -template -struct GenericMemoryBuffer { - typedef char Ch; // byte - - GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} - - void Put(Ch c) { *stack_.template Push() = c; } - void Flush() {} - - void Clear() { stack_.Clear(); } - void ShrinkToFit() { stack_.ShrinkToFit(); } - Ch* Push(size_t count) { return stack_.template Push(count); } - void Pop(size_t count) { stack_.template Pop(count); } - - const Ch* GetBuffer() const { - return stack_.template Bottom(); - } - - size_t GetSize() const { return stack_.GetSize(); } - - static const size_t kDefaultCapacity = 256; - mutable internal::Stack stack_; -}; - -typedef GenericMemoryBuffer<> MemoryBuffer; - -//! Implement specialized version of PutN() with memset() for better performance. -template<> -inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { - std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); -} - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/memorystream.h b/include/rapidjson/memorystream.h deleted file mode 100755 index 77af6c99..00000000 --- a/include/rapidjson/memorystream.h +++ /dev/null @@ -1,71 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_MEMORYSTREAM_H_ -#define RAPIDJSON_MEMORYSTREAM_H_ - -#include "stream.h" - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(unreachable-code) -RAPIDJSON_DIAG_OFF(missing-noreturn) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Represents an in-memory input byte stream. -/*! - This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. - - It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. - - Differences between MemoryStream and StringStream: - 1. StringStream has encoding but MemoryStream is a byte stream. - 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. - 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). - \note implements Stream concept -*/ -struct MemoryStream { - typedef char Ch; // byte - - MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} - - Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } - Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } - size_t Tell() const { return static_cast(src_ - begin_); } - - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - // For encoding detection only. - const Ch* Peek4() const { - return Tell() + 4 <= size_ ? src_ : 0; - } - - const Ch* src_; //!< Current read position. - const Ch* begin_; //!< Original head of the string. - const Ch* end_; //!< End of stream. - size_t size_; //!< Size of the stream. -}; - -RAPIDJSON_NAMESPACE_END - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/msinttypes/inttypes.h b/include/rapidjson/msinttypes/inttypes.h deleted file mode 100755 index 18111286..00000000 --- a/include/rapidjson/msinttypes/inttypes.h +++ /dev/null @@ -1,316 +0,0 @@ -// ISO C9x compliant inttypes.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2013 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the product nor the names of its contributors may -// be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -// The above software in this distribution may have been modified by -// THL A29 Limited ("Tencent Modifications"). -// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_INTTYPES_H_ // [ -#define _MSC_INTTYPES_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include "stdint.h" - -// miloyip: VC supports inttypes.h since VC2013 -#if _MSC_VER >= 1800 -#include -#else - -// 7.8 Format conversion of integer types - -typedef struct { - intmax_t quot; - intmax_t rem; -} imaxdiv_t; - -// 7.8.1 Macros for format specifiers - -#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 - -// The fprintf macros for signed integers are: -#define PRId8 "d" -#define PRIi8 "i" -#define PRIdLEAST8 "d" -#define PRIiLEAST8 "i" -#define PRIdFAST8 "d" -#define PRIiFAST8 "i" - -#define PRId16 "hd" -#define PRIi16 "hi" -#define PRIdLEAST16 "hd" -#define PRIiLEAST16 "hi" -#define PRIdFAST16 "hd" -#define PRIiFAST16 "hi" - -#define PRId32 "I32d" -#define PRIi32 "I32i" -#define PRIdLEAST32 "I32d" -#define PRIiLEAST32 "I32i" -#define PRIdFAST32 "I32d" -#define PRIiFAST32 "I32i" - -#define PRId64 "I64d" -#define PRIi64 "I64i" -#define PRIdLEAST64 "I64d" -#define PRIiLEAST64 "I64i" -#define PRIdFAST64 "I64d" -#define PRIiFAST64 "I64i" - -#define PRIdMAX "I64d" -#define PRIiMAX "I64i" - -#define PRIdPTR "Id" -#define PRIiPTR "Ii" - -// The fprintf macros for unsigned integers are: -#define PRIo8 "o" -#define PRIu8 "u" -#define PRIx8 "x" -#define PRIX8 "X" -#define PRIoLEAST8 "o" -#define PRIuLEAST8 "u" -#define PRIxLEAST8 "x" -#define PRIXLEAST8 "X" -#define PRIoFAST8 "o" -#define PRIuFAST8 "u" -#define PRIxFAST8 "x" -#define PRIXFAST8 "X" - -#define PRIo16 "ho" -#define PRIu16 "hu" -#define PRIx16 "hx" -#define PRIX16 "hX" -#define PRIoLEAST16 "ho" -#define PRIuLEAST16 "hu" -#define PRIxLEAST16 "hx" -#define PRIXLEAST16 "hX" -#define PRIoFAST16 "ho" -#define PRIuFAST16 "hu" -#define PRIxFAST16 "hx" -#define PRIXFAST16 "hX" - -#define PRIo32 "I32o" -#define PRIu32 "I32u" -#define PRIx32 "I32x" -#define PRIX32 "I32X" -#define PRIoLEAST32 "I32o" -#define PRIuLEAST32 "I32u" -#define PRIxLEAST32 "I32x" -#define PRIXLEAST32 "I32X" -#define PRIoFAST32 "I32o" -#define PRIuFAST32 "I32u" -#define PRIxFAST32 "I32x" -#define PRIXFAST32 "I32X" - -#define PRIo64 "I64o" -#define PRIu64 "I64u" -#define PRIx64 "I64x" -#define PRIX64 "I64X" -#define PRIoLEAST64 "I64o" -#define PRIuLEAST64 "I64u" -#define PRIxLEAST64 "I64x" -#define PRIXLEAST64 "I64X" -#define PRIoFAST64 "I64o" -#define PRIuFAST64 "I64u" -#define PRIxFAST64 "I64x" -#define PRIXFAST64 "I64X" - -#define PRIoMAX "I64o" -#define PRIuMAX "I64u" -#define PRIxMAX "I64x" -#define PRIXMAX "I64X" - -#define PRIoPTR "Io" -#define PRIuPTR "Iu" -#define PRIxPTR "Ix" -#define PRIXPTR "IX" - -// The fscanf macros for signed integers are: -#define SCNd8 "d" -#define SCNi8 "i" -#define SCNdLEAST8 "d" -#define SCNiLEAST8 "i" -#define SCNdFAST8 "d" -#define SCNiFAST8 "i" - -#define SCNd16 "hd" -#define SCNi16 "hi" -#define SCNdLEAST16 "hd" -#define SCNiLEAST16 "hi" -#define SCNdFAST16 "hd" -#define SCNiFAST16 "hi" - -#define SCNd32 "ld" -#define SCNi32 "li" -#define SCNdLEAST32 "ld" -#define SCNiLEAST32 "li" -#define SCNdFAST32 "ld" -#define SCNiFAST32 "li" - -#define SCNd64 "I64d" -#define SCNi64 "I64i" -#define SCNdLEAST64 "I64d" -#define SCNiLEAST64 "I64i" -#define SCNdFAST64 "I64d" -#define SCNiFAST64 "I64i" - -#define SCNdMAX "I64d" -#define SCNiMAX "I64i" - -#ifdef _WIN64 // [ -# define SCNdPTR "I64d" -# define SCNiPTR "I64i" -#else // _WIN64 ][ -# define SCNdPTR "ld" -# define SCNiPTR "li" -#endif // _WIN64 ] - -// The fscanf macros for unsigned integers are: -#define SCNo8 "o" -#define SCNu8 "u" -#define SCNx8 "x" -#define SCNX8 "X" -#define SCNoLEAST8 "o" -#define SCNuLEAST8 "u" -#define SCNxLEAST8 "x" -#define SCNXLEAST8 "X" -#define SCNoFAST8 "o" -#define SCNuFAST8 "u" -#define SCNxFAST8 "x" -#define SCNXFAST8 "X" - -#define SCNo16 "ho" -#define SCNu16 "hu" -#define SCNx16 "hx" -#define SCNX16 "hX" -#define SCNoLEAST16 "ho" -#define SCNuLEAST16 "hu" -#define SCNxLEAST16 "hx" -#define SCNXLEAST16 "hX" -#define SCNoFAST16 "ho" -#define SCNuFAST16 "hu" -#define SCNxFAST16 "hx" -#define SCNXFAST16 "hX" - -#define SCNo32 "lo" -#define SCNu32 "lu" -#define SCNx32 "lx" -#define SCNX32 "lX" -#define SCNoLEAST32 "lo" -#define SCNuLEAST32 "lu" -#define SCNxLEAST32 "lx" -#define SCNXLEAST32 "lX" -#define SCNoFAST32 "lo" -#define SCNuFAST32 "lu" -#define SCNxFAST32 "lx" -#define SCNXFAST32 "lX" - -#define SCNo64 "I64o" -#define SCNu64 "I64u" -#define SCNx64 "I64x" -#define SCNX64 "I64X" -#define SCNoLEAST64 "I64o" -#define SCNuLEAST64 "I64u" -#define SCNxLEAST64 "I64x" -#define SCNXLEAST64 "I64X" -#define SCNoFAST64 "I64o" -#define SCNuFAST64 "I64u" -#define SCNxFAST64 "I64x" -#define SCNXFAST64 "I64X" - -#define SCNoMAX "I64o" -#define SCNuMAX "I64u" -#define SCNxMAX "I64x" -#define SCNXMAX "I64X" - -#ifdef _WIN64 // [ -# define SCNoPTR "I64o" -# define SCNuPTR "I64u" -# define SCNxPTR "I64x" -# define SCNXPTR "I64X" -#else // _WIN64 ][ -# define SCNoPTR "lo" -# define SCNuPTR "lu" -# define SCNxPTR "lx" -# define SCNXPTR "lX" -#endif // _WIN64 ] - -#endif // __STDC_FORMAT_MACROS ] - -// 7.8.2 Functions for greatest-width integer types - -// 7.8.2.1 The imaxabs function -#define imaxabs _abs64 - -// 7.8.2.2 The imaxdiv function - -// This is modified version of div() function from Microsoft's div.c found -// in %MSVC.NET%\crt\src\div.c -#ifdef STATIC_IMAXDIV // [ -static -#else // STATIC_IMAXDIV ][ -_inline -#endif // STATIC_IMAXDIV ] -imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) -{ - imaxdiv_t result; - - result.quot = numer / denom; - result.rem = numer % denom; - - if (numer < 0 && result.rem > 0) { - // did division wrong; must fix up - ++result.quot; - result.rem -= denom; - } - - return result; -} - -// 7.8.2.3 The strtoimax and strtoumax functions -#define strtoimax _strtoi64 -#define strtoumax _strtoui64 - -// 7.8.2.4 The wcstoimax and wcstoumax functions -#define wcstoimax _wcstoi64 -#define wcstoumax _wcstoui64 - -#endif // _MSC_VER >= 1800 - -#endif // _MSC_INTTYPES_H_ ] diff --git a/include/rapidjson/msinttypes/stdint.h b/include/rapidjson/msinttypes/stdint.h deleted file mode 100755 index 3d4477b9..00000000 --- a/include/rapidjson/msinttypes/stdint.h +++ /dev/null @@ -1,300 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2013 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the product nor the names of its contributors may -// be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -// The above software in this distribution may have been modified by -// THL A29 Limited ("Tencent Modifications"). -// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. -#if _MSC_VER >= 1600 // [ -#include - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -#undef INT8_C -#undef INT16_C -#undef INT32_C -#undef INT64_C -#undef UINT8_C -#undef UINT16_C -#undef UINT32_C -#undef UINT64_C - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -// These #ifndef's are needed to prevent collisions with . -// Check out Issue 9 for the details. -#ifndef INTMAX_C // [ -# define INTMAX_C INT64_C -#endif // INTMAX_C ] -#ifndef UINTMAX_C // [ -# define UINTMAX_C UINT64_C -#endif // UINTMAX_C ] - -#endif // __STDC_CONSTANT_MACROS ] - -#else // ] _MSC_VER >= 1700 [ - -#include - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we have to wrap include with 'extern "C++" {}' -// or compiler would give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#if defined(__cplusplus) && !defined(_M_ARM) -extern "C" { -#endif -# include -#if defined(__cplusplus) && !defined(_M_ARM) -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) - typedef signed char int8_t; - typedef signed short int16_t; - typedef signed int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#else - typedef signed __int8 int8_t; - typedef signed __int16 int16_t; - typedef signed __int32 int32_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef signed __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 signed int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -// These #ifndef's are needed to prevent collisions with . -// Check out Issue 9 for the details. -#ifndef INTMAX_C // [ -# define INTMAX_C INT64_C -#endif // INTMAX_C ] -#ifndef UINTMAX_C // [ -# define UINTMAX_C UINT64_C -#endif // UINTMAX_C ] - -#endif // __STDC_CONSTANT_MACROS ] - -#endif // _MSC_VER >= 1600 ] - -#endif // _MSC_STDINT_H_ ] diff --git a/include/rapidjson/ostreamwrapper.h b/include/rapidjson/ostreamwrapper.h deleted file mode 100755 index 11ed4d33..00000000 --- a/include/rapidjson/ostreamwrapper.h +++ /dev/null @@ -1,81 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ -#define RAPIDJSON_OSTREAMWRAPPER_H_ - -#include "stream.h" -#include - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. -/*! - The classes can be wrapped including but not limited to: - - - \c std::ostringstream - - \c std::stringstream - - \c std::wpstringstream - - \c std::wstringstream - - \c std::ifstream - - \c std::fstream - - \c std::wofstream - - \c std::wfstream - - \tparam StreamType Class derived from \c std::basic_ostream. -*/ - -template -class BasicOStreamWrapper { -public: - typedef typename StreamType::char_type Ch; - BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} - - void Put(Ch c) { - stream_.put(c); - } - - void Flush() { - stream_.flush(); - } - - // Not implemented - char Peek() const { RAPIDJSON_ASSERT(false); return 0; } - char Take() { RAPIDJSON_ASSERT(false); return 0; } - size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } - char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - BasicOStreamWrapper(const BasicOStreamWrapper&); - BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); - - StreamType& stream_; -}; - -typedef BasicOStreamWrapper OStreamWrapper; -typedef BasicOStreamWrapper WOStreamWrapper; - -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h deleted file mode 100755 index 90e5903b..00000000 --- a/include/rapidjson/pointer.h +++ /dev/null @@ -1,1415 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_POINTER_H_ -#define RAPIDJSON_POINTER_H_ - -#include "document.h" -#include "internal/itoa.h" - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(switch-enum) -#elif defined(_MSC_VER) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token - -//! Error code of parsing. -/*! \ingroup RAPIDJSON_ERRORS - \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode -*/ -enum PointerParseErrorCode { - kPointerParseErrorNone = 0, //!< The parse is successful - - kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' - kPointerParseErrorInvalidEscape, //!< Invalid escape - kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment - kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment -}; - -/////////////////////////////////////////////////////////////////////////////// -// GenericPointer - -//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. -/*! - This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" - (https://tools.ietf.org/html/rfc6901). - - A JSON pointer is for identifying a specific value in a JSON document - (GenericDocument). It can simplify coding of DOM tree manipulation, because it - can access multiple-level depth of DOM tree with single API call. - - After it parses a string representation (e.g. "/foo/0" or URI fragment - representation (e.g. "#/foo/0") into its internal representation (tokens), - it can be used to resolve a specific value in multiple documents, or sub-tree - of documents. - - Contrary to GenericValue, Pointer can be copy constructed and copy assigned. - Apart from assignment, a Pointer cannot be modified after construction. - - Although Pointer is very convenient, please aware that constructing Pointer - involves parsing and dynamic memory allocation. A special constructor with user- - supplied tokens eliminates these. - - GenericPointer depends on GenericDocument and GenericValue. - - \tparam ValueType The value type of the DOM tree. E.g. GenericValue > - \tparam Allocator The allocator type for allocating memory for internal representation. - - \note GenericPointer uses same encoding of ValueType. - However, Allocator of GenericPointer is independent of Allocator of Value. -*/ -template -class GenericPointer { -public: - typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value - typedef typename ValueType::Ch Ch; //!< Character type from Value - - //! A token is the basic units of internal representation. - /*! - A JSON pointer string representation "/foo/123" is parsed to two tokens: - "foo" and 123. 123 will be represented in both numeric form and string form. - They are resolved according to the actual value type (object or array). - - For token that are not numbers, or the numeric value is out of bound - (greater than limits of SizeType), they are only treated as string form - (i.e. the token's index will be equal to kPointerInvalidIndex). - - This struct is public so that user can create a Pointer without parsing and - allocation, using a special constructor. - */ - struct Token { - const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. - SizeType length; //!< Length of the name. - SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. - }; - - //!@name Constructors and destructor. - //@{ - - //! Default constructor. - GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} - - //! Constructor that parses a string or URI fragment representation. - /*! - \param source A null-terminated, string or URI fragment representation of JSON pointer. - \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. - */ - explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { - Parse(source, internal::StrLen(source)); - } - -#if RAPIDJSON_HAS_STDSTRING - //! Constructor that parses a string or URI fragment representation. - /*! - \param source A string or URI fragment representation of JSON pointer. - \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. - \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. - */ - explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { - Parse(source.c_str(), source.size()); - } -#endif - - //! Constructor that parses a string or URI fragment representation, with length of the source string. - /*! - \param source A string or URI fragment representation of JSON pointer. - \param length Length of source. - \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. - \note Slightly faster than the overload without length. - */ - GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { - Parse(source, length); - } - - //! Constructor with user-supplied tokens. - /*! - This constructor let user supplies const array of tokens. - This prevents the parsing process and eliminates allocation. - This is preferred for memory constrained environments. - - \param tokens An constant array of tokens representing the JSON pointer. - \param tokenCount Number of tokens. - - \b Example - \code - #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } - #define INDEX(i) { #i, sizeof(#i) - 1, i } - - static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; - static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); - // Equivalent to static const Pointer p("/foo/123"); - - #undef NAME - #undef INDEX - \endcode - */ - GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} - - //! Copy constructor. - GenericPointer(const GenericPointer& rhs) : allocator_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { - *this = rhs; - } - - //! Copy constructor. - GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { - *this = rhs; - } - - //! Destructor. - ~GenericPointer() { - if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. - Allocator::Free(tokens_); - RAPIDJSON_DELETE(ownAllocator_); - } - - //! Assignment operator. - GenericPointer& operator=(const GenericPointer& rhs) { - if (this != &rhs) { - // Do not delete ownAllcator - if (nameBuffer_) - Allocator::Free(tokens_); - - tokenCount_ = rhs.tokenCount_; - parseErrorOffset_ = rhs.parseErrorOffset_; - parseErrorCode_ = rhs.parseErrorCode_; - - if (rhs.nameBuffer_) - CopyFromRaw(rhs); // Normally parsed tokens. - else { - tokens_ = rhs.tokens_; // User supplied const tokens. - nameBuffer_ = 0; - } - } - return *this; - } - - //! Swap the content of this pointer with an other. - /*! - \param other The pointer to swap with. - \note Constant complexity. - */ - GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { - internal::Swap(allocator_, other.allocator_); - internal::Swap(ownAllocator_, other.ownAllocator_); - internal::Swap(nameBuffer_, other.nameBuffer_); - internal::Swap(tokens_, other.tokens_); - internal::Swap(tokenCount_, other.tokenCount_); - internal::Swap(parseErrorOffset_, other.parseErrorOffset_); - internal::Swap(parseErrorCode_, other.parseErrorCode_); - return *this; - } - - //! free-standing swap function helper - /*! - Helper function to enable support for common swap implementation pattern based on \c std::swap: - \code - void swap(MyClass& a, MyClass& b) { - using std::swap; - swap(a.pointer, b.pointer); - // ... - } - \endcode - \see Swap() - */ - friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } - - //@} - - //!@name Append token - //@{ - - //! Append a token and return a new Pointer - /*! - \param token Token to be appended. - \param allocator Allocator for the newly return Pointer. - \return A new Pointer with appended token. - */ - GenericPointer Append(const Token& token, Allocator* allocator = 0) const { - GenericPointer r; - r.allocator_ = allocator; - Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); - std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); - r.tokens_[tokenCount_].name = p; - r.tokens_[tokenCount_].length = token.length; - r.tokens_[tokenCount_].index = token.index; - return r; - } - - //! Append a name token with length, and return a new Pointer - /*! - \param name Name to be appended. - \param length Length of name. - \param allocator Allocator for the newly return Pointer. - \return A new Pointer with appended token. - */ - GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { - Token token = { name, length, kPointerInvalidIndex }; - return Append(token, allocator); - } - - //! Append a name token without length, and return a new Pointer - /*! - \param name Name (const Ch*) to be appended. - \param allocator Allocator for the newly return Pointer. - \return A new Pointer with appended token. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) - Append(T* name, Allocator* allocator = 0) const { - return Append(name, internal::StrLen(name), allocator); - } - -#if RAPIDJSON_HAS_STDSTRING - //! Append a name token, and return a new Pointer - /*! - \param name Name to be appended. - \param allocator Allocator for the newly return Pointer. - \return A new Pointer with appended token. - */ - GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { - return Append(name.c_str(), static_cast(name.size()), allocator); - } -#endif - - //! Append a index token, and return a new Pointer - /*! - \param index Index to be appended. - \param allocator Allocator for the newly return Pointer. - \return A new Pointer with appended token. - */ - GenericPointer Append(SizeType index, Allocator* allocator = 0) const { - char buffer[21]; - char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); - SizeType length = static_cast(end - buffer); - buffer[length] = '\0'; - - if (sizeof(Ch) == 1) { - Token token = { reinterpret_cast(buffer), length, index }; - return Append(token, allocator); - } - else { - Ch name[21]; - for (size_t i = 0; i <= length; i++) - name[i] = static_cast(buffer[i]); - Token token = { name, length, index }; - return Append(token, allocator); - } - } - - //! Append a token by value, and return a new Pointer - /*! - \param token token to be appended. - \param allocator Allocator for the newly return Pointer. - \return A new Pointer with appended token. - */ - GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { - if (token.IsString()) - return Append(token.GetString(), token.GetStringLength(), allocator); - else { - RAPIDJSON_ASSERT(token.IsUint64()); - RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); - return Append(static_cast(token.GetUint64()), allocator); - } - } - - //!@name Handling Parse Error - //@{ - - //! Check whether this is a valid pointer. - bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } - - //! Get the parsing error offset in code unit. - size_t GetParseErrorOffset() const { return parseErrorOffset_; } - - //! Get the parsing error code. - PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } - - //@} - - //! Get the allocator of this pointer. - Allocator& GetAllocator() { return *allocator_; } - - //!@name Tokens - //@{ - - //! Get the token array (const version only). - const Token* GetTokens() const { return tokens_; } - - //! Get the number of tokens. - size_t GetTokenCount() const { return tokenCount_; } - - //@} - - //!@name Equality/inequality operators - //@{ - - //! Equality operator. - /*! - \note When any pointers are invalid, always returns false. - */ - bool operator==(const GenericPointer& rhs) const { - if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) - return false; - - for (size_t i = 0; i < tokenCount_; i++) { - if (tokens_[i].index != rhs.tokens_[i].index || - tokens_[i].length != rhs.tokens_[i].length || - (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) - { - return false; - } - } - - return true; - } - - //! Inequality operator. - /*! - \note When any pointers are invalid, always returns true. - */ - bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } - - //! Less than operator. - /*! - \note Invalid pointers are always greater than valid ones. - */ - bool operator<(const GenericPointer& rhs) const { - if (!IsValid()) - return false; - if (!rhs.IsValid()) - return true; - - if (tokenCount_ != rhs.tokenCount_) - return tokenCount_ < rhs.tokenCount_; - - for (size_t i = 0; i < tokenCount_; i++) { - if (tokens_[i].index != rhs.tokens_[i].index) - return tokens_[i].index < rhs.tokens_[i].index; - - if (tokens_[i].length != rhs.tokens_[i].length) - return tokens_[i].length < rhs.tokens_[i].length; - - if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) - return cmp < 0; - } - - return false; - } - - //@} - - //!@name Stringify - //@{ - - //! Stringify the pointer into string representation. - /*! - \tparam OutputStream Type of output stream. - \param os The output stream. - */ - template - bool Stringify(OutputStream& os) const { - return Stringify(os); - } - - //! Stringify the pointer into URI fragment representation. - /*! - \tparam OutputStream Type of output stream. - \param os The output stream. - */ - template - bool StringifyUriFragment(OutputStream& os) const { - return Stringify(os); - } - - //@} - - //!@name Create value - //@{ - - //! Create a value in a subtree. - /*! - If the value is not exist, it creates all parent values and a JSON Null value. - So it always succeed and return the newly created or existing value. - - Remind that it may change types of parents according to tokens, so it - potentially removes previously stored values. For example, if a document - was an array, and "/foo" is used to create a value, then the document - will be changed to an object, and all existing array elements are lost. - - \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. - \param allocator Allocator for creating the values if the specified value or its parents are not exist. - \param alreadyExist If non-null, it stores whether the resolved value is already exist. - \return The resolved newly created (a JSON Null value), or already exists value. - */ - ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { - RAPIDJSON_ASSERT(IsValid()); - ValueType* v = &root; - bool exist = true; - for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { - if (v->IsArray() && t->name[0] == '-' && t->length == 1) { - v->PushBack(ValueType().Move(), allocator); - v = &((*v)[v->Size() - 1]); - exist = false; - } - else { - if (t->index == kPointerInvalidIndex) { // must be object name - if (!v->IsObject()) - v->SetObject(); // Change to Object - } - else { // object name or array index - if (!v->IsArray() && !v->IsObject()) - v->SetArray(); // Change to Array - } - - if (v->IsArray()) { - if (t->index >= v->Size()) { - v->Reserve(t->index + 1, allocator); - while (t->index >= v->Size()) - v->PushBack(ValueType().Move(), allocator); - exist = false; - } - v = &((*v)[t->index]); - } - else { - typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); - if (m == v->MemberEnd()) { - v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); - m = v->MemberEnd(); - v = &(--m)->value; // Assumes AddMember() appends at the end - exist = false; - } - else - v = &m->value; - } - } - } - - if (alreadyExist) - *alreadyExist = exist; - - return *v; - } - - //! Creates a value in a document. - /*! - \param document A document to be resolved. - \param alreadyExist If non-null, it stores whether the resolved value is already exist. - \return The resolved newly created, or already exists value. - */ - template - ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { - return Create(document, document.GetAllocator(), alreadyExist); - } - - //@} - - //!@name Query value - //@{ - - //! Query a value in a subtree. - /*! - \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. - \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. - \return Pointer to the value if it can be resolved. Otherwise null. - - \note - There are only 3 situations when a value cannot be resolved: - 1. A value in the path is not an array nor object. - 2. An object value does not contain the token. - 3. A token is out of range of an array value. - - Use unresolvedTokenIndex to retrieve the token index. - */ - ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { - RAPIDJSON_ASSERT(IsValid()); - ValueType* v = &root; - for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { - switch (v->GetType()) { - case kObjectType: - { - typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); - if (m == v->MemberEnd()) - break; - v = &m->value; - } - continue; - case kArrayType: - if (t->index == kPointerInvalidIndex || t->index >= v->Size()) - break; - v = &((*v)[t->index]); - continue; - default: - break; - } - - // Error: unresolved token - if (unresolvedTokenIndex) - *unresolvedTokenIndex = static_cast(t - tokens_); - return 0; - } - return v; - } - - //! Query a const value in a const subtree. - /*! - \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. - \return Pointer to the value if it can be resolved. Otherwise null. - */ - const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { - return Get(const_cast(root), unresolvedTokenIndex); - } - - //@} - - //!@name Query a value with default - //@{ - - //! Query a value in a subtree with default value. - /*! - Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. - So that this function always succeed. - - \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. - \param defaultValue Default value to be cloned if the value was not exists. - \param allocator Allocator for creating the values if the specified value or its parents are not exist. - \see Create() - */ - ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { - bool alreadyExist; - ValueType& v = Create(root, allocator, &alreadyExist); - return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); - } - - //! Query a value in a subtree with default null-terminated string. - ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { - bool alreadyExist; - ValueType& v = Create(root, allocator, &alreadyExist); - return alreadyExist ? v : v.SetString(defaultValue, allocator); - } - -#if RAPIDJSON_HAS_STDSTRING - //! Query a value in a subtree with default std::basic_string. - ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { - bool alreadyExist; - ValueType& v = Create(root, allocator, &alreadyExist); - return alreadyExist ? v : v.SetString(defaultValue, allocator); - } -#endif - - //! Query a value in a subtree with default primitive value. - /*! - \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) - GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { - return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); - } - - //! Query a value in a document with default value. - template - ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { - return GetWithDefault(document, defaultValue, document.GetAllocator()); - } - - //! Query a value in a document with default null-terminated string. - template - ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { - return GetWithDefault(document, defaultValue, document.GetAllocator()); - } - -#if RAPIDJSON_HAS_STDSTRING - //! Query a value in a document with default std::basic_string. - template - ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { - return GetWithDefault(document, defaultValue, document.GetAllocator()); - } -#endif - - //! Query a value in a document with default primitive value. - /*! - \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) - GetWithDefault(GenericDocument& document, T defaultValue) const { - return GetWithDefault(document, defaultValue, document.GetAllocator()); - } - - //@} - - //!@name Set a value - //@{ - - //! Set a value in a subtree, with move semantics. - /*! - It creates all parents if they are not exist or types are different to the tokens. - So this function always succeeds but potentially remove existing values. - - \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. - \param value Value to be set. - \param allocator Allocator for creating the values if the specified value or its parents are not exist. - \see Create() - */ - ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { - return Create(root, allocator) = value; - } - - //! Set a value in a subtree, with copy semantics. - ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { - return Create(root, allocator).CopyFrom(value, allocator); - } - - //! Set a null-terminated string in a subtree. - ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { - return Create(root, allocator) = ValueType(value, allocator).Move(); - } - -#if RAPIDJSON_HAS_STDSTRING - //! Set a std::basic_string in a subtree. - ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { - return Create(root, allocator) = ValueType(value, allocator).Move(); - } -#endif - - //! Set a primitive value in a subtree. - /*! - \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) - Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { - return Create(root, allocator) = ValueType(value).Move(); - } - - //! Set a value in a document, with move semantics. - template - ValueType& Set(GenericDocument& document, ValueType& value) const { - return Create(document) = value; - } - - //! Set a value in a document, with copy semantics. - template - ValueType& Set(GenericDocument& document, const ValueType& value) const { - return Create(document).CopyFrom(value, document.GetAllocator()); - } - - //! Set a null-terminated string in a document. - template - ValueType& Set(GenericDocument& document, const Ch* value) const { - return Create(document) = ValueType(value, document.GetAllocator()).Move(); - } - -#if RAPIDJSON_HAS_STDSTRING - //! Sets a std::basic_string in a document. - template - ValueType& Set(GenericDocument& document, const std::basic_string& value) const { - return Create(document) = ValueType(value, document.GetAllocator()).Move(); - } -#endif - - //! Set a primitive value in a document. - /*! - \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) - Set(GenericDocument& document, T value) const { - return Create(document) = value; - } - - //@} - - //!@name Swap a value - //@{ - - //! Swap a value with a value in a subtree. - /*! - It creates all parents if they are not exist or types are different to the tokens. - So this function always succeeds but potentially remove existing values. - - \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. - \param value Value to be swapped. - \param allocator Allocator for creating the values if the specified value or its parents are not exist. - \see Create() - */ - ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { - return Create(root, allocator).Swap(value); - } - - //! Swap a value with a value in a document. - template - ValueType& Swap(GenericDocument& document, ValueType& value) const { - return Create(document).Swap(value); - } - - //@} - - //! Erase a value in a subtree. - /*! - \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. - \return Whether the resolved value is found and erased. - - \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. - */ - bool Erase(ValueType& root) const { - RAPIDJSON_ASSERT(IsValid()); - if (tokenCount_ == 0) // Cannot erase the root - return false; - - ValueType* v = &root; - const Token* last = tokens_ + (tokenCount_ - 1); - for (const Token *t = tokens_; t != last; ++t) { - switch (v->GetType()) { - case kObjectType: - { - typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); - if (m == v->MemberEnd()) - return false; - v = &m->value; - } - break; - case kArrayType: - if (t->index == kPointerInvalidIndex || t->index >= v->Size()) - return false; - v = &((*v)[t->index]); - break; - default: - return false; - } - } - - switch (v->GetType()) { - case kObjectType: - return v->EraseMember(GenericStringRef(last->name, last->length)); - case kArrayType: - if (last->index == kPointerInvalidIndex || last->index >= v->Size()) - return false; - v->Erase(v->Begin() + last->index); - return true; - default: - return false; - } - } - -private: - //! Clone the content from rhs to this. - /*! - \param rhs Source pointer. - \param extraToken Extra tokens to be allocated. - \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. - \return Start of non-occupied name buffer, for storing extra names. - */ - Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { - if (!allocator_) // allocator is independently owned. - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - - size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens - for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) - nameBufferSize += t->length; - - tokenCount_ = rhs.tokenCount_ + extraToken; - tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); - nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); - if (rhs.tokenCount_ > 0) { - std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); - } - if (nameBufferSize > 0) { - std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); - } - - // Adjust pointers to name buffer - std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; - for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) - t->name += diff; - - return nameBuffer_ + nameBufferSize; - } - - //! Check whether a character should be percent-encoded. - /*! - According to RFC 3986 2.3 Unreserved Characters. - \param c The character (code unit) to be tested. - */ - bool NeedPercentEncode(Ch c) const { - return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); - } - - //! Parse a JSON String or its URI fragment representation into tokens. -#ifndef __clang__ // -Wdocumentation - /*! - \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. - \param length Length of the source string. - \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. - */ -#endif - void Parse(const Ch* source, size_t length) { - RAPIDJSON_ASSERT(source != NULL); - RAPIDJSON_ASSERT(nameBuffer_ == 0); - RAPIDJSON_ASSERT(tokens_ == 0); - - // Create own allocator if user did not supply. - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - - // Count number of '/' as tokenCount - tokenCount_ = 0; - for (const Ch* s = source; s != source + length; s++) - if (*s == '/') - tokenCount_++; - - Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); - Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); - size_t i = 0; - - // Detect if it is a URI fragment - bool uriFragment = false; - if (source[i] == '#') { - uriFragment = true; - i++; - } - - if (i != length && source[i] != '/') { - parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; - goto error; - } - - while (i < length) { - RAPIDJSON_ASSERT(source[i] == '/'); - i++; // consumes '/' - - token->name = name; - bool isNumber = true; - - while (i < length && source[i] != '/') { - Ch c = source[i]; - if (uriFragment) { - // Decoding percent-encoding for URI fragment - if (c == '%') { - PercentDecodeStream is(&source[i], source + length); - GenericInsituStringStream os(name); - Ch* begin = os.PutBegin(); - if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { - parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; - goto error; - } - size_t len = os.PutEnd(begin); - i += is.Tell() - 1; - if (len == 1) - c = *name; - else { - name += len; - isNumber = false; - i++; - continue; - } - } - else if (NeedPercentEncode(c)) { - parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; - goto error; - } - } - - i++; - - // Escaping "~0" -> '~', "~1" -> '/' - if (c == '~') { - if (i < length) { - c = source[i]; - if (c == '0') c = '~'; - else if (c == '1') c = '/'; - else { - parseErrorCode_ = kPointerParseErrorInvalidEscape; - goto error; - } - i++; - } - else { - parseErrorCode_ = kPointerParseErrorInvalidEscape; - goto error; - } - } - - // First check for index: all of characters are digit - if (c < '0' || c > '9') - isNumber = false; - - *name++ = c; - } - token->length = static_cast(name - token->name); - if (token->length == 0) - isNumber = false; - *name++ = '\0'; // Null terminator - - // Second check for index: more than one digit cannot have leading zero - if (isNumber && token->length > 1 && token->name[0] == '0') - isNumber = false; - - // String to SizeType conversion - SizeType n = 0; - if (isNumber) { - for (size_t j = 0; j < token->length; j++) { - SizeType m = n * 10 + static_cast(token->name[j] - '0'); - if (m < n) { // overflow detection - isNumber = false; - break; - } - n = m; - } - } - - token->index = isNumber ? n : kPointerInvalidIndex; - token++; - } - - RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer - parseErrorCode_ = kPointerParseErrorNone; - return; - - error: - Allocator::Free(tokens_); - nameBuffer_ = 0; - tokens_ = 0; - tokenCount_ = 0; - parseErrorOffset_ = i; - return; - } - - //! Stringify to string or URI fragment representation. - /*! - \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. - \tparam OutputStream type of output stream. - \param os The output stream. - */ - template - bool Stringify(OutputStream& os) const { - RAPIDJSON_ASSERT(IsValid()); - - if (uriFragment) - os.Put('#'); - - for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { - os.Put('/'); - for (size_t j = 0; j < t->length; j++) { - Ch c = t->name[j]; - if (c == '~') { - os.Put('~'); - os.Put('0'); - } - else if (c == '/') { - os.Put('~'); - os.Put('1'); - } - else if (uriFragment && NeedPercentEncode(c)) { - // Transcode to UTF8 sequence - GenericStringStream source(&t->name[j]); - PercentEncodeStream target(os); - if (!Transcoder >().Validate(source, target)) - return false; - j += source.Tell() - 1; - } - else - os.Put(c); - } - } - return true; - } - - //! A helper stream for decoding a percent-encoded sequence into code unit. - /*! - This stream decodes %XY triplet into code unit (0-255). - If it encounters invalid characters, it sets output code unit as 0 and - mark invalid, and to be checked by IsValid(). - */ - class PercentDecodeStream { - public: - typedef typename ValueType::Ch Ch; - - //! Constructor - /*! - \param source Start of the stream - \param end Past-the-end of the stream. - */ - PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} - - Ch Take() { - if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet - valid_ = false; - return 0; - } - src_++; - Ch c = 0; - for (int j = 0; j < 2; j++) { - c = static_cast(c << 4); - Ch h = *src_; - if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); - else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); - else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); - else { - valid_ = false; - return 0; - } - src_++; - } - return c; - } - - size_t Tell() const { return static_cast(src_ - head_); } - bool IsValid() const { return valid_; } - - private: - const Ch* src_; //!< Current read position. - const Ch* head_; //!< Original head of the string. - const Ch* end_; //!< Past-the-end position. - bool valid_; //!< Whether the parsing is valid. - }; - - //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. - template - class PercentEncodeStream { - public: - PercentEncodeStream(OutputStream& os) : os_(os) {} - void Put(char c) { // UTF-8 must be byte - unsigned char u = static_cast(c); - static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - os_.Put('%'); - os_.Put(static_cast(hexDigits[u >> 4])); - os_.Put(static_cast(hexDigits[u & 15])); - } - private: - OutputStream& os_; - }; - - Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. - Allocator* ownAllocator_; //!< Allocator owned by this Pointer. - Ch* nameBuffer_; //!< A buffer containing all names in tokens. - Token* tokens_; //!< A list of tokens. - size_t tokenCount_; //!< Number of tokens in tokens_. - size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. - PointerParseErrorCode parseErrorCode_; //!< Parsing error code. -}; - -//! GenericPointer for Value (UTF-8, default allocator). -typedef GenericPointer Pointer; - -//!@name Helper functions for GenericPointer -//@{ - -////////////////////////////////////////////////////////////////////////////// - -template -typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { - return pointer.Create(root, a); -} - -template -typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).Create(root, a); -} - -// No allocator parameter - -template -typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { - return pointer.Create(document); -} - -template -typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { - return GenericPointer(source, N - 1).Create(document); -} - -////////////////////////////////////////////////////////////////////////////// - -template -typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { - return pointer.Get(root, unresolvedTokenIndex); -} - -template -const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { - return pointer.Get(root, unresolvedTokenIndex); -} - -template -typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { - return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); -} - -template -const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { - return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); -} - -////////////////////////////////////////////////////////////////////////////// - -template -typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { - return pointer.GetWithDefault(root, defaultValue, a); -} - -template -typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { - return pointer.GetWithDefault(root, defaultValue, a); -} - -#if RAPIDJSON_HAS_STDSTRING -template -typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { - return pointer.GetWithDefault(root, defaultValue, a); -} -#endif - -template -RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) -GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { - return pointer.GetWithDefault(root, defaultValue, a); -} - -template -typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); -} - -template -typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); -} - -#if RAPIDJSON_HAS_STDSTRING -template -typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); -} -#endif - -template -RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) -GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); -} - -// No allocator parameter - -template -typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { - return pointer.GetWithDefault(document, defaultValue); -} - -template -typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { - return pointer.GetWithDefault(document, defaultValue); -} - -#if RAPIDJSON_HAS_STDSTRING -template -typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { - return pointer.GetWithDefault(document, defaultValue); -} -#endif - -template -RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) -GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { - return pointer.GetWithDefault(document, defaultValue); -} - -template -typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { - return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); -} - -template -typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { - return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); -} - -#if RAPIDJSON_HAS_STDSTRING -template -typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { - return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); -} -#endif - -template -RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) -GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { - return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); -} - -////////////////////////////////////////////////////////////////////////////// - -template -typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { - return pointer.Set(root, value, a); -} - -template -typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { - return pointer.Set(root, value, a); -} - -template -typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { - return pointer.Set(root, value, a); -} - -#if RAPIDJSON_HAS_STDSTRING -template -typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { - return pointer.Set(root, value, a); -} -#endif - -template -RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) -SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { - return pointer.Set(root, value, a); -} - -template -typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).Set(root, value, a); -} - -template -typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).Set(root, value, a); -} - -template -typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).Set(root, value, a); -} - -#if RAPIDJSON_HAS_STDSTRING -template -typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).Set(root, value, a); -} -#endif - -template -RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) -SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).Set(root, value, a); -} - -// No allocator parameter - -template -typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { - return pointer.Set(document, value); -} - -template -typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { - return pointer.Set(document, value); -} - -template -typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { - return pointer.Set(document, value); -} - -#if RAPIDJSON_HAS_STDSTRING -template -typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { - return pointer.Set(document, value); -} -#endif - -template -RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) -SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { - return pointer.Set(document, value); -} - -template -typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { - return GenericPointer(source, N - 1).Set(document, value); -} - -template -typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { - return GenericPointer(source, N - 1).Set(document, value); -} - -template -typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { - return GenericPointer(source, N - 1).Set(document, value); -} - -#if RAPIDJSON_HAS_STDSTRING -template -typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { - return GenericPointer(source, N - 1).Set(document, value); -} -#endif - -template -RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) -SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { - return GenericPointer(source, N - 1).Set(document, value); -} - -////////////////////////////////////////////////////////////////////////////// - -template -typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { - return pointer.Swap(root, value, a); -} - -template -typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { - return GenericPointer(source, N - 1).Swap(root, value, a); -} - -template -typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { - return pointer.Swap(document, value); -} - -template -typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { - return GenericPointer(source, N - 1).Swap(document, value); -} - -////////////////////////////////////////////////////////////////////////////// - -template -bool EraseValueByPointer(T& root, const GenericPointer& pointer) { - return pointer.Erase(root); -} - -template -bool EraseValueByPointer(T& root, const CharType(&source)[N]) { - return GenericPointer(source, N - 1).Erase(root); -} - -//@} - -RAPIDJSON_NAMESPACE_END - -#if defined(__clang__) || defined(_MSC_VER) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_POINTER_H_ diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h deleted file mode 100755 index fe45df1d..00000000 --- a/include/rapidjson/prettywriter.h +++ /dev/null @@ -1,277 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_PRETTYWRITER_H_ -#define RAPIDJSON_PRETTYWRITER_H_ - -#include "writer.h" - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -#if defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Combination of PrettyWriter format flags. -/*! \see PrettyWriter::SetFormatOptions - */ -enum PrettyFormatOptions { - kFormatDefault = 0, //!< Default pretty formatting. - kFormatSingleLineArray = 1 //!< Format arrays on a single line. -}; - -//! Writer with indentation and spacing. -/*! - \tparam OutputStream Type of output os. - \tparam SourceEncoding Encoding of source string. - \tparam TargetEncoding Encoding of output stream. - \tparam StackAllocator Type of allocator for allocating memory of stack. -*/ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> -class PrettyWriter : public Writer { -public: - typedef Writer Base; - typedef typename Base::Ch Ch; - - //! Constructor - /*! \param os Output stream. - \param allocator User supplied allocator. If it is null, it will create a private one. - \param levelDepth Initial capacity of stack. - */ - explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} - - - explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - PrettyWriter(PrettyWriter&& rhs) : - Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} -#endif - - //! Set custom indentation. - /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). - \param indentCharCount Number of indent characters for each indentation level. - \note The default indentation is 4 spaces. - */ - PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { - RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); - indentChar_ = indentChar; - indentCharCount_ = indentCharCount; - return *this; - } - - //! Set pretty writer formatting options. - /*! \param options Formatting options. - */ - PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { - formatOptions_ = options; - return *this; - } - - /*! @name Implementation of Handler - \see Handler - */ - //@{ - - bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } - bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } - bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } - bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } - bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } - bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } - bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } - - bool RawNumber(const Ch* str, SizeType length, bool copy = false) { - RAPIDJSON_ASSERT(str != 0); - (void)copy; - PrettyPrefix(kNumberType); - return Base::EndValue(Base::WriteString(str, length)); - } - - bool String(const Ch* str, SizeType length, bool copy = false) { - RAPIDJSON_ASSERT(str != 0); - (void)copy; - PrettyPrefix(kStringType); - return Base::EndValue(Base::WriteString(str, length)); - } - -#if RAPIDJSON_HAS_STDSTRING - bool String(const std::basic_string& str) { - return String(str.data(), SizeType(str.size())); - } -#endif - - bool StartObject() { - PrettyPrefix(kObjectType); - new (Base::level_stack_.template Push()) typename Base::Level(false); - return Base::WriteStartObject(); - } - - bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } - -#if RAPIDJSON_HAS_STDSTRING - bool Key(const std::basic_string& str) { - return Key(str.data(), SizeType(str.size())); - } -#endif - - bool EndObject(SizeType memberCount = 0) { - (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object - RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value - - bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; - - if (!empty) { - Base::os_->Put('\n'); - WriteIndent(); - } - bool ret = Base::EndValue(Base::WriteEndObject()); - (void)ret; - RAPIDJSON_ASSERT(ret == true); - if (Base::level_stack_.Empty()) // end of json text - Base::Flush(); - return true; - } - - bool StartArray() { - PrettyPrefix(kArrayType); - new (Base::level_stack_.template Push()) typename Base::Level(true); - return Base::WriteStartArray(); - } - - bool EndArray(SizeType memberCount = 0) { - (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); - bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; - - if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { - Base::os_->Put('\n'); - WriteIndent(); - } - bool ret = Base::EndValue(Base::WriteEndArray()); - (void)ret; - RAPIDJSON_ASSERT(ret == true); - if (Base::level_stack_.Empty()) // end of json text - Base::Flush(); - return true; - } - - //@} - - /*! @name Convenience extensions */ - //@{ - - //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } - - //@} - - //! Write a raw JSON value. - /*! - For user to write a stringified JSON as a value. - - \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. - \param length Length of the json. - \param type Type of the root of json. - \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. - */ - bool RawValue(const Ch* json, size_t length, Type type) { - RAPIDJSON_ASSERT(json != 0); - PrettyPrefix(type); - return Base::EndValue(Base::WriteRawValue(json, length)); - } - -protected: - void PrettyPrefix(Type type) { - (void)type; - if (Base::level_stack_.GetSize() != 0) { // this value is not at root - typename Base::Level* level = Base::level_stack_.template Top(); - - if (level->inArray) { - if (level->valueCount > 0) { - Base::os_->Put(','); // add comma if it is not the first element in array - if (formatOptions_ & kFormatSingleLineArray) - Base::os_->Put(' '); - } - - if (!(formatOptions_ & kFormatSingleLineArray)) { - Base::os_->Put('\n'); - WriteIndent(); - } - } - else { // in object - if (level->valueCount > 0) { - if (level->valueCount % 2 == 0) { - Base::os_->Put(','); - Base::os_->Put('\n'); - } - else { - Base::os_->Put(':'); - Base::os_->Put(' '); - } - } - else - Base::os_->Put('\n'); - - if (level->valueCount % 2 == 0) - WriteIndent(); - } - if (!level->inArray && level->valueCount % 2 == 0) - RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name - level->valueCount++; - } - else { - RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. - Base::hasRoot_ = true; - } - } - - void WriteIndent() { - size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; - PutN(*Base::os_, static_cast(indentChar_), count); - } - - Ch indentChar_; - unsigned indentCharCount_; - PrettyFormatOptions formatOptions_; - -private: - // Prohibit copy constructor & assignment operator. - PrettyWriter(const PrettyWriter&); - PrettyWriter& operator=(const PrettyWriter&); -}; - -RAPIDJSON_NAMESPACE_END - -#if defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h deleted file mode 100755 index 78aa89a0..00000000 --- a/include/rapidjson/rapidjson.h +++ /dev/null @@ -1,692 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_RAPIDJSON_H_ -#define RAPIDJSON_RAPIDJSON_H_ - -/*!\file rapidjson.h - \brief common definitions and configuration - - \see RAPIDJSON_CONFIG - */ - -/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration - \brief Configuration macros for library features - - Some RapidJSON features are configurable to adapt the library to a wide - variety of platforms, environments and usage scenarios. Most of the - features can be configured in terms of overridden or predefined - preprocessor macros at compile-time. - - Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. - - \note These macros should be given on the compiler command-line - (where applicable) to avoid inconsistent values when compiling - different translation units of a single application. - */ - -#include // malloc(), realloc(), free(), size_t -#include // memset(), memcpy(), memmove(), memcmp() - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_VERSION_STRING -// -// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. -// - -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -// token stringification -#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) -#define RAPIDJSON_DO_STRINGIFY(x) #x - -// token concatenation -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y -//!@endcond - -/*! \def RAPIDJSON_MAJOR_VERSION - \ingroup RAPIDJSON_CONFIG - \brief Major version of RapidJSON in integer. -*/ -/*! \def RAPIDJSON_MINOR_VERSION - \ingroup RAPIDJSON_CONFIG - \brief Minor version of RapidJSON in integer. -*/ -/*! \def RAPIDJSON_PATCH_VERSION - \ingroup RAPIDJSON_CONFIG - \brief Patch version of RapidJSON in integer. -*/ -/*! \def RAPIDJSON_VERSION_STRING - \ingroup RAPIDJSON_CONFIG - \brief Version of RapidJSON in ".." string format. -*/ -#define RAPIDJSON_MAJOR_VERSION 1 -#define RAPIDJSON_MINOR_VERSION 1 -#define RAPIDJSON_PATCH_VERSION 0 -#define RAPIDJSON_VERSION_STRING \ - RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_NAMESPACE_(BEGIN|END) -/*! \def RAPIDJSON_NAMESPACE - \ingroup RAPIDJSON_CONFIG - \brief provide custom rapidjson namespace - - In order to avoid symbol clashes and/or "One Definition Rule" errors - between multiple inclusions of (different versions of) RapidJSON in - a single binary, users can customize the name of the main RapidJSON - namespace. - - In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE - to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple - levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref - RAPIDJSON_NAMESPACE_END need to be defined as well: - - \code - // in some .cpp file - #define RAPIDJSON_NAMESPACE my::rapidjson - #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { - #define RAPIDJSON_NAMESPACE_END } } - #include "rapidjson/..." - \endcode - - \see rapidjson - */ -/*! \def RAPIDJSON_NAMESPACE_BEGIN - \ingroup RAPIDJSON_CONFIG - \brief provide custom rapidjson namespace (opening expression) - \see RAPIDJSON_NAMESPACE -*/ -/*! \def RAPIDJSON_NAMESPACE_END - \ingroup RAPIDJSON_CONFIG - \brief provide custom rapidjson namespace (closing expression) - \see RAPIDJSON_NAMESPACE -*/ -#ifndef RAPIDJSON_NAMESPACE -#define RAPIDJSON_NAMESPACE rapidjson -#endif -#ifndef RAPIDJSON_NAMESPACE_BEGIN -#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { -#endif -#ifndef RAPIDJSON_NAMESPACE_END -#define RAPIDJSON_NAMESPACE_END } -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_HAS_STDSTRING - -#ifndef RAPIDJSON_HAS_STDSTRING -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation -#else -#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default -#endif -/*! \def RAPIDJSON_HAS_STDSTRING - \ingroup RAPIDJSON_CONFIG - \brief Enable RapidJSON support for \c std::string - - By defining this preprocessor symbol to \c 1, several convenience functions for using - \ref rapidjson::GenericValue with \c std::string are enabled, especially - for construction and comparison. - - \hideinitializer -*/ -#endif // !defined(RAPIDJSON_HAS_STDSTRING) - -#if RAPIDJSON_HAS_STDSTRING -#include -#endif // RAPIDJSON_HAS_STDSTRING - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_NO_INT64DEFINE - -/*! \def RAPIDJSON_NO_INT64DEFINE - \ingroup RAPIDJSON_CONFIG - \brief Use external 64-bit integer types. - - RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types - to be available at global scope. - - If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to - prevent RapidJSON from defining its own types. -*/ -#ifndef RAPIDJSON_NO_INT64DEFINE -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 -#include "msinttypes/stdint.h" -#include "msinttypes/inttypes.h" -#else -// Other compilers should have this. -#include -#include -#endif -//!@endcond -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_NO_INT64DEFINE -#endif -#endif // RAPIDJSON_NO_INT64TYPEDEF - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_FORCEINLINE - -#ifndef RAPIDJSON_FORCEINLINE -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#if defined(_MSC_VER) && defined(NDEBUG) -#define RAPIDJSON_FORCEINLINE __forceinline -#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) -#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) -#else -#define RAPIDJSON_FORCEINLINE -#endif -//!@endcond -#endif // RAPIDJSON_FORCEINLINE - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ENDIAN -#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine -#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine - -//! Endianness of the machine. -/*! - \def RAPIDJSON_ENDIAN - \ingroup RAPIDJSON_CONFIG - - GCC 4.6 provided macro for detecting endianness of the target machine. But other - compilers may not have this. User can define RAPIDJSON_ENDIAN to either - \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. - - Default detection implemented with reference to - \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html - \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp -*/ -#ifndef RAPIDJSON_ENDIAN -// Detect with GCC 4.6's macro -# ifdef __BYTE_ORDER__ -# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN -# else -# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. -# endif // __BYTE_ORDER__ -// Detect with GLIBC's endian.h -# elif defined(__GLIBC__) -# include -# if (__BYTE_ORDER == __LITTLE_ENDIAN) -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif (__BYTE_ORDER == __BIG_ENDIAN) -# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN -# else -# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. -# endif // __GLIBC__ -// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro -# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) -# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN -// Detect with architecture macros -# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) -# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN -# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(RAPIDJSON_DOXYGEN_RUNNING) -# define RAPIDJSON_ENDIAN -# else -# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. -# endif -#endif // RAPIDJSON_ENDIAN - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_64BIT - -//! Whether using 64-bit architecture -#ifndef RAPIDJSON_64BIT -#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) -#define RAPIDJSON_64BIT 1 -#else -#define RAPIDJSON_64BIT 0 -#endif -#endif // RAPIDJSON_64BIT - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ALIGN - -//! Data alignment of the machine. -/*! \ingroup RAPIDJSON_CONFIG - \param x pointer to align - - Some machines require strict data alignment. The default is 8 bytes. - User can customize by defining the RAPIDJSON_ALIGN function macro. -*/ -#ifndef RAPIDJSON_ALIGN -#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_UINT64_C2 - -//! Construct a 64-bit literal by a pair of 32-bit integer. -/*! - 64-bit literal with or without ULL suffix is prone to compiler warnings. - UINT64_C() is C macro which cause compilation problems. - Use this macro to define 64-bit constants by a pair of 32-bit integer. -*/ -#ifndef RAPIDJSON_UINT64_C2 -#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_48BITPOINTER_OPTIMIZATION - -//! Use only lower 48-bit address for some pointers. -/*! - \ingroup RAPIDJSON_CONFIG - - This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. - The higher 16-bit can be used for storing other data. - \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. -*/ -#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION -#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) -#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 -#else -#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 -#endif -#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION - -#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 -#if RAPIDJSON_64BIT != 1 -#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 -#endif -#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) -#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) -#else -#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) -#define RAPIDJSON_GETPOINTER(type, p) (p) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD - -/*! \def RAPIDJSON_SIMD - \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2/Neon optimization. - - RapidJSON supports optimized implementations for some parsing operations - based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel - or ARM compatible processors. - - To enable these optimizations, three different symbols can be defined; - \code - // Enable SSE2 optimization. - #define RAPIDJSON_SSE2 - - // Enable SSE4.2 optimization. - #define RAPIDJSON_SSE42 - \endcode - - // Enable ARM Neon optimization. - #define RAPIDJSON_NEON - \endcode - - \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. - - If any of these symbols is defined, RapidJSON defines the macro - \c RAPIDJSON_SIMD to indicate the availability of the optimized code. -*/ -#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) -#define RAPIDJSON_SIMD -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_NO_SIZETYPEDEFINE - -#ifndef RAPIDJSON_NO_SIZETYPEDEFINE -/*! \def RAPIDJSON_NO_SIZETYPEDEFINE - \ingroup RAPIDJSON_CONFIG - \brief User-provided \c SizeType definition. - - In order to avoid using 32-bit size types for indexing strings and arrays, - define this preprocessor symbol and provide the type rapidjson::SizeType - before including RapidJSON: - \code - #define RAPIDJSON_NO_SIZETYPEDEFINE - namespace rapidjson { typedef ::std::size_t SizeType; } - #include "rapidjson/..." - \endcode - - \see rapidjson::SizeType -*/ -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_NO_SIZETYPEDEFINE -#endif -RAPIDJSON_NAMESPACE_BEGIN -//! Size type (for string lengths, array sizes, etc.) -/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, - instead of using \c size_t. Users may override the SizeType by defining - \ref RAPIDJSON_NO_SIZETYPEDEFINE. -*/ -typedef unsigned SizeType; -RAPIDJSON_NAMESPACE_END -#endif - -// always import std::size_t to rapidjson namespace -RAPIDJSON_NAMESPACE_BEGIN -using std::size_t; -RAPIDJSON_NAMESPACE_END - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ASSERT - -//! Assertion. -/*! \ingroup RAPIDJSON_CONFIG - By default, rapidjson uses C \c assert() for internal assertions. - User can override it by defining RAPIDJSON_ASSERT(x) macro. - - \note Parsing errors are handled and can be customized by the - \ref RAPIDJSON_ERRORS APIs. -*/ -#ifndef RAPIDJSON_ASSERT -#include -#define RAPIDJSON_ASSERT(x) assert(x) -#endif // RAPIDJSON_ASSERT - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_STATIC_ASSERT - -// Prefer C++11 static_assert, if available -#ifndef RAPIDJSON_STATIC_ASSERT -#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) -#define RAPIDJSON_STATIC_ASSERT(x) \ - static_assert(x, RAPIDJSON_STRINGIFY(x)) -#endif // C++11 -#endif // RAPIDJSON_STATIC_ASSERT - -// Adopt C++03 implementation from boost -#ifndef RAPIDJSON_STATIC_ASSERT -#ifndef __clang__ -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#endif -RAPIDJSON_NAMESPACE_BEGIN -template struct STATIC_ASSERTION_FAILURE; -template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; -RAPIDJSON_NAMESPACE_END - -#if defined(__GNUC__) || defined(__clang__) -#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) -#else -#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif -#ifndef __clang__ -//!@endcond -#endif - -/*! \def RAPIDJSON_STATIC_ASSERT - \brief (Internal) macro to check for conditions at compile-time - \param x compile-time condition - \hideinitializer - */ -#define RAPIDJSON_STATIC_ASSERT(x) \ - typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ - sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ - RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif // RAPIDJSON_STATIC_ASSERT - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY - -//! Compiler branching hint for expression with high probability to be true. -/*! - \ingroup RAPIDJSON_CONFIG - \param x Boolean expression likely to be true. -*/ -#ifndef RAPIDJSON_LIKELY -#if defined(__GNUC__) || defined(__clang__) -#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) -#else -#define RAPIDJSON_LIKELY(x) (x) -#endif -#endif - -//! Compiler branching hint for expression with low probability to be true. -/*! - \ingroup RAPIDJSON_CONFIG - \param x Boolean expression unlikely to be true. -*/ -#ifndef RAPIDJSON_UNLIKELY -#if defined(__GNUC__) || defined(__clang__) -#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define RAPIDJSON_UNLIKELY(x) (x) -#endif -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Helpers - -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN - -#define RAPIDJSON_MULTILINEMACRO_BEGIN do { -#define RAPIDJSON_MULTILINEMACRO_END \ -} while((void)0, 0) - -// adopted from Boost -#define RAPIDJSON_VERSION_CODE(x,y,z) \ - (((x)*100000) + ((y)*100) + (z)) - -#if defined(__has_builtin) -#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) -#else -#define RAPIDJSON_HAS_BUILTIN(x) 0 -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF - -#if defined(__GNUC__) -#define RAPIDJSON_GNUC \ - RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) -#endif - -#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) - -#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) -#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) -#define RAPIDJSON_DIAG_OFF(x) \ - RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) - -// push/pop support in Clang and GCC>=4.6 -#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) -#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) -#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) -#else // GCC >= 4.2, < 4.6 -#define RAPIDJSON_DIAG_PUSH /* ignored */ -#define RAPIDJSON_DIAG_POP /* ignored */ -#endif - -#elif defined(_MSC_VER) - -// pragma (MSVC specific) -#define RAPIDJSON_PRAGMA(x) __pragma(x) -#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) - -#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) -#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) -#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) - -#else - -#define RAPIDJSON_DIAG_OFF(x) /* ignored */ -#define RAPIDJSON_DIAG_PUSH /* ignored */ -#define RAPIDJSON_DIAG_POP /* ignored */ - -#endif // RAPIDJSON_DIAG_* - -/////////////////////////////////////////////////////////////////////////////// -// C++11 features - -#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS -#if defined(__clang__) -#if __has_feature(cxx_rvalue_references) && \ - (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 -#else -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 -#endif -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) || \ - (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) - -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 -#else -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 -#endif -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - -#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT -#if defined(__clang__) -#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1900) || \ - (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) -#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 -#else -#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 -#endif -#endif -#if RAPIDJSON_HAS_CXX11_NOEXCEPT -#define RAPIDJSON_NOEXCEPT noexcept -#else -#define RAPIDJSON_NOEXCEPT /* noexcept */ -#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT - -// no automatic detection, yet -#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS -#if (defined(_MSC_VER) && _MSC_VER >= 1700) -#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 -#else -#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 -#endif -#endif - -#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR -#if defined(__clang__) -#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) || \ - (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) -#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 -#else -#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 -#endif -#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR - -/////////////////////////////////////////////////////////////////////////////// -// C++17 features - -#if defined(__has_cpp_attribute) -# if __has_cpp_attribute(fallthrough) -# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] -# else -# define RAPIDJSON_DELIBERATE_FALLTHROUGH -# endif -#else -# define RAPIDJSON_DELIBERATE_FALLTHROUGH -#endif - -//!@endcond - -//! Assertion (in non-throwing contexts). - /*! \ingroup RAPIDJSON_CONFIG - Some functions provide a \c noexcept guarantee, if the compiler supports it. - In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to - throw an exception. This macro adds a separate customization point for - such cases. - - Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is - supported, and to \ref RAPIDJSON_ASSERT otherwise. - */ - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_NOEXCEPT_ASSERT - -#ifndef RAPIDJSON_NOEXCEPT_ASSERT -#ifdef RAPIDJSON_ASSERT_THROWS -#if RAPIDJSON_HAS_CXX11_NOEXCEPT -#define RAPIDJSON_NOEXCEPT_ASSERT(x) -#else -#include -#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) -#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT -#else -#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) -#endif // RAPIDJSON_ASSERT_THROWS -#endif // RAPIDJSON_NOEXCEPT_ASSERT - -/////////////////////////////////////////////////////////////////////////////// -// malloc/realloc/free - -#ifndef RAPIDJSON_MALLOC -///! customization point for global \c malloc -#define RAPIDJSON_MALLOC(size) std::malloc(size) -#endif -#ifndef RAPIDJSON_REALLOC -///! customization point for global \c realloc -#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) -#endif -#ifndef RAPIDJSON_FREE -///! customization point for global \c free -#define RAPIDJSON_FREE(ptr) std::free(ptr) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// new/delete - -#ifndef RAPIDJSON_NEW -///! customization point for global \c new -#define RAPIDJSON_NEW(TypeName) new TypeName -#endif -#ifndef RAPIDJSON_DELETE -///! customization point for global \c delete -#define RAPIDJSON_DELETE(x) delete x -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Type - -/*! \namespace rapidjson - \brief main RapidJSON namespace - \see RAPIDJSON_NAMESPACE -*/ -RAPIDJSON_NAMESPACE_BEGIN - -//! Type of JSON value -enum Type { - kNullType = 0, //!< null - kFalseType = 1, //!< false - kTrueType = 2, //!< true - kObjectType = 3, //!< object - kArrayType = 4, //!< array - kStringType = 5, //!< string - kNumberType = 6 //!< number -}; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h deleted file mode 100755 index 09ace4eb..00000000 --- a/include/rapidjson/reader.h +++ /dev/null @@ -1,2244 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_READER_H_ -#define RAPIDJSON_READER_H_ - -/*! \file reader.h */ - -#include "allocators.h" -#include "stream.h" -#include "encodedstream.h" -#include "internal/clzll.h" -#include "internal/meta.h" -#include "internal/stack.h" -#include "internal/strtod.h" -#include - -#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) -#include -#pragma intrinsic(_BitScanForward) -#endif -#ifdef RAPIDJSON_SSE42 -#include -#elif defined(RAPIDJSON_SSE2) -#include -#elif defined(RAPIDJSON_NEON) -#include -#endif - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(old-style-cast) -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(switch-enum) -#elif defined(_MSC_VER) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4702) // unreachable code -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#define RAPIDJSON_NOTHING /* deliberately empty */ -#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN -#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ - RAPIDJSON_MULTILINEMACRO_BEGIN \ - if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ - RAPIDJSON_MULTILINEMACRO_END -#endif -#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) -//!@endcond - -/*! \def RAPIDJSON_PARSE_ERROR_NORETURN - \ingroup RAPIDJSON_ERRORS - \brief Macro to indicate a parse error. - \param parseErrorCode \ref rapidjson::ParseErrorCode of the error - \param offset position of the error in JSON input (\c size_t) - - This macros can be used as a customization point for the internal - error handling mechanism of RapidJSON. - - A common usage model is to throw an exception instead of requiring the - caller to explicitly check the \ref rapidjson::GenericReader::Parse's - return value: - - \code - #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ - throw ParseException(parseErrorCode, #parseErrorCode, offset) - - #include // std::runtime_error - #include "rapidjson/error/error.h" // rapidjson::ParseResult - - struct ParseException : std::runtime_error, rapidjson::ParseResult { - ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) - : std::runtime_error(msg), ParseResult(code, offset) {} - }; - - #include "rapidjson/reader.h" - \endcode - - \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse - */ -#ifndef RAPIDJSON_PARSE_ERROR_NORETURN -#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ - RAPIDJSON_MULTILINEMACRO_BEGIN \ - RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ - SetParseError(parseErrorCode, offset); \ - RAPIDJSON_MULTILINEMACRO_END -#endif - -/*! \def RAPIDJSON_PARSE_ERROR - \ingroup RAPIDJSON_ERRORS - \brief (Internal) macro to indicate and handle a parse error. - \param parseErrorCode \ref rapidjson::ParseErrorCode of the error - \param offset position of the error in JSON input (\c size_t) - - Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. - - \see RAPIDJSON_PARSE_ERROR_NORETURN - \hideinitializer - */ -#ifndef RAPIDJSON_PARSE_ERROR -#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ - RAPIDJSON_MULTILINEMACRO_BEGIN \ - RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ - RAPIDJSON_MULTILINEMACRO_END -#endif - -#include "error/error.h" // ParseErrorCode, ParseResult - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// ParseFlag - -/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS - \ingroup RAPIDJSON_CONFIG - \brief User-defined kParseDefaultFlags definition. - - User can define this as any \c ParseFlag combinations. -*/ -#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS -#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags -#endif - -//! Combination of parseFlags -/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream - */ -enum ParseFlag { - kParseNoFlags = 0, //!< No flags are set. - kParseInsituFlag = 1, //!< In-situ(destructive) parsing. - kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. - kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. - kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. - kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). - kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. - kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. - kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. - kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. - kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. - kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS -}; - -/////////////////////////////////////////////////////////////////////////////// -// Handler - -/*! \class rapidjson::Handler - \brief Concept for receiving events from GenericReader upon parsing. - The functions return true if no error occurs. If they return false, - the event publisher should terminate the process. -\code -concept Handler { - typename Ch; - - bool Null(); - bool Bool(bool b); - bool Int(int i); - bool Uint(unsigned i); - bool Int64(int64_t i); - bool Uint64(uint64_t i); - bool Double(double d); - /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) - bool RawNumber(const Ch* str, SizeType length, bool copy); - bool String(const Ch* str, SizeType length, bool copy); - bool StartObject(); - bool Key(const Ch* str, SizeType length, bool copy); - bool EndObject(SizeType memberCount); - bool StartArray(); - bool EndArray(SizeType elementCount); -}; -\endcode -*/ -/////////////////////////////////////////////////////////////////////////////// -// BaseReaderHandler - -//! Default implementation of Handler. -/*! This can be used as base class of any reader handler. - \note implements Handler concept -*/ -template, typename Derived = void> -struct BaseReaderHandler { - typedef typename Encoding::Ch Ch; - - typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; - - bool Default() { return true; } - bool Null() { return static_cast(*this).Default(); } - bool Bool(bool) { return static_cast(*this).Default(); } - bool Int(int) { return static_cast(*this).Default(); } - bool Uint(unsigned) { return static_cast(*this).Default(); } - bool Int64(int64_t) { return static_cast(*this).Default(); } - bool Uint64(uint64_t) { return static_cast(*this).Default(); } - bool Double(double) { return static_cast(*this).Default(); } - /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) - bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } - bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } - bool StartObject() { return static_cast(*this).Default(); } - bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } - bool EndObject(SizeType) { return static_cast(*this).Default(); } - bool StartArray() { return static_cast(*this).Default(); } - bool EndArray(SizeType) { return static_cast(*this).Default(); } -}; - -/////////////////////////////////////////////////////////////////////////////// -// StreamLocalCopy - -namespace internal { - -template::copyOptimization> -class StreamLocalCopy; - -//! Do copy optimization. -template -class StreamLocalCopy { -public: - StreamLocalCopy(Stream& original) : s(original), original_(original) {} - ~StreamLocalCopy() { original_ = s; } - - Stream s; - -private: - StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; - - Stream& original_; -}; - -//! Keep reference. -template -class StreamLocalCopy { -public: - StreamLocalCopy(Stream& original) : s(original) {} - - Stream& s; - -private: - StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; -}; - -} // namespace internal - -/////////////////////////////////////////////////////////////////////////////// -// SkipWhitespace - -//! Skip the JSON white spaces in a stream. -/*! \param is A input stream for skipping white spaces. - \note This function has SSE2/SSE4.2 specialization. -*/ -template -void SkipWhitespace(InputStream& is) { - internal::StreamLocalCopy copy(is); - InputStream& s(copy.s); - - typename InputStream::Ch c; - while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') - s.Take(); -} - -inline const char* SkipWhitespace(const char* p, const char* end) { - while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) - ++p; - return p; -} - -#ifdef RAPIDJSON_SSE42 -//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. -inline const char *SkipWhitespace_SIMD(const char* p) { - // Fast return for single non-whitespace - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // The rest of string using SIMD - static const char whitespace[16] = " \n\r\t"; - const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); - - for (;; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); - if (r != 16) // some of characters is non-whitespace - return p + r; - } -} - -inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { - // Fast return for single non-whitespace - if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) - ++p; - else - return p; - - // The middle of string using SIMD - static const char whitespace[16] = " \n\r\t"; - const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); - - for (; p <= end - 16; p += 16) { - const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); - const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); - if (r != 16) // some of characters is non-whitespace - return p + r; - } - - return SkipWhitespace(p, end); -} - -#elif defined(RAPIDJSON_SSE2) - -//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. -inline const char *SkipWhitespace_SIMD(const char* p) { - // Fast return for single non-whitespace - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // The rest of string - #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } - static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; - #undef C16 - - const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); - const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); - const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); - const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); - - for (;; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - __m128i x = _mm_cmpeq_epi8(s, w0); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); - unsigned short r = static_cast(~_mm_movemask_epi8(x)); - if (r != 0) { // some of characters may be non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } - } -} - -inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { - // Fast return for single non-whitespace - if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) - ++p; - else - return p; - - // The rest of string - #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } - static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; - #undef C16 - - const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); - const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); - const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); - const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); - - for (; p <= end - 16; p += 16) { - const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); - __m128i x = _mm_cmpeq_epi8(s, w0); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); - unsigned short r = static_cast(~_mm_movemask_epi8(x)); - if (r != 0) { // some of characters may be non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } - } - - return SkipWhitespace(p, end); -} - -#elif defined(RAPIDJSON_NEON) - -//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. -inline const char *SkipWhitespace_SIMD(const char* p) { - // Fast return for single non-whitespace - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - const uint8x16_t w0 = vmovq_n_u8(' '); - const uint8x16_t w1 = vmovq_n_u8('\n'); - const uint8x16_t w2 = vmovq_n_u8('\r'); - const uint8x16_t w3 = vmovq_n_u8('\t'); - - for (;; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, w0); - x = vorrq_u8(x, vceqq_u8(s, w1)); - x = vorrq_u8(x, vceqq_u8(s, w2)); - x = vorrq_u8(x, vceqq_u8(s, w3)); - - x = vmvnq_u8(x); // Negate - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract - uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract - - if (low == 0) { - if (high != 0) { - uint32_t lz = internal::clzll(high); - return p + 8 + (lz >> 3); - } - } else { - uint32_t lz = internal::clzll(low); - return p + (lz >> 3); - } - } -} - -inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { - // Fast return for single non-whitespace - if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) - ++p; - else - return p; - - const uint8x16_t w0 = vmovq_n_u8(' '); - const uint8x16_t w1 = vmovq_n_u8('\n'); - const uint8x16_t w2 = vmovq_n_u8('\r'); - const uint8x16_t w3 = vmovq_n_u8('\t'); - - for (; p <= end - 16; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, w0); - x = vorrq_u8(x, vceqq_u8(s, w1)); - x = vorrq_u8(x, vceqq_u8(s, w2)); - x = vorrq_u8(x, vceqq_u8(s, w3)); - - x = vmvnq_u8(x); // Negate - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract - uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract - - if (low == 0) { - if (high != 0) { - uint32_t lz = internal::clzll(high); - return p + 8 + (lz >> 3); - } - } else { - uint32_t lz = internal::clzll(low); - return p + (lz >> 3); - } - } - - return SkipWhitespace(p, end); -} - -#endif // RAPIDJSON_NEON - -#ifdef RAPIDJSON_SIMD -//! Template function specialization for InsituStringStream -template<> inline void SkipWhitespace(InsituStringStream& is) { - is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); -} - -//! Template function specialization for StringStream -template<> inline void SkipWhitespace(StringStream& is) { - is.src_ = SkipWhitespace_SIMD(is.src_); -} - -template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { - is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); -} -#endif // RAPIDJSON_SIMD - -/////////////////////////////////////////////////////////////////////////////// -// GenericReader - -//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. -/*! GenericReader parses JSON text from a stream, and send events synchronously to an - object implementing Handler concept. - - It needs to allocate a stack for storing a single decoded string during - non-destructive parsing. - - For in-situ parsing, the decoded string is directly written to the source - text string, no temporary buffer is required. - - A GenericReader object can be reused for parsing multiple JSON text. - - \tparam SourceEncoding Encoding of the input stream. - \tparam TargetEncoding Encoding of the parse output. - \tparam StackAllocator Allocator type for stack. -*/ -template -class GenericReader { -public: - typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type - - //! Constructor. - /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) - \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) - */ - GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : - stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} - - //! Parse JSON text. - /*! \tparam parseFlags Combination of \ref ParseFlag. - \tparam InputStream Type of input stream, implementing Stream concept. - \tparam Handler Type of handler, implementing Handler concept. - \param is Input stream to be parsed. - \param handler The handler to receive events. - \return Whether the parsing is successful. - */ - template - ParseResult Parse(InputStream& is, Handler& handler) { - if (parseFlags & kParseIterativeFlag) - return IterativeParse(is, handler); - - parseResult_.Clear(); - - ClearStackOnExit scope(*this); - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - - if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - } - else { - ParseValue(is, handler); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - - if (!(parseFlags & kParseStopWhenDoneFlag)) { - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - - if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - } - } - } - - return parseResult_; - } - - //! Parse JSON text (with \ref kParseDefaultFlags) - /*! \tparam InputStream Type of input stream, implementing Stream concept - \tparam Handler Type of handler, implementing Handler concept. - \param is Input stream to be parsed. - \param handler The handler to receive events. - \return Whether the parsing is successful. - */ - template - ParseResult Parse(InputStream& is, Handler& handler) { - return Parse(is, handler); - } - - //! Initialize JSON text token-by-token parsing - /*! - */ - void IterativeParseInit() { - parseResult_.Clear(); - state_ = IterativeParsingStartState; - } - - //! Parse one token from JSON text - /*! \tparam InputStream Type of input stream, implementing Stream concept - \tparam Handler Type of handler, implementing Handler concept. - \param is Input stream to be parsed. - \param handler The handler to receive events. - \return Whether the parsing is successful. - */ - template - bool IterativeParseNext(InputStream& is, Handler& handler) { - while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { - SkipWhitespaceAndComments(is); - - Token t = Tokenize(is.Peek()); - IterativeParsingState n = Predict(state_, t); - IterativeParsingState d = Transit(state_, t, n, is, handler); - - // If we've finished or hit an error... - if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { - // Report errors. - if (d == IterativeParsingErrorState) { - HandleError(state_, is); - return false; - } - - // Transition to the finish state. - RAPIDJSON_ASSERT(d == IterativeParsingFinishState); - state_ = d; - - // If StopWhenDone is not set... - if (!(parseFlags & kParseStopWhenDoneFlag)) { - // ... and extra non-whitespace data is found... - SkipWhitespaceAndComments(is); - if (is.Peek() != '\0') { - // ... this is considered an error. - HandleError(state_, is); - return false; - } - } - - // Success! We are done! - return true; - } - - // Transition to the new state. - state_ = d; - - // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. - if (!IsIterativeParsingDelimiterState(n)) - return true; - } - - // We reached the end of file. - stack_.Clear(); - - if (state_ != IterativeParsingFinishState) { - HandleError(state_, is); - return false; - } - - return true; - } - - //! Check if token-by-token parsing JSON text is complete - /*! \return Whether the JSON has been fully decoded. - */ - RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { - return IsIterativeParsingCompleteState(state_); - } - - //! Whether a parse error has occurred in the last parsing. - bool HasParseError() const { return parseResult_.IsError(); } - - //! Get the \ref ParseErrorCode of last parsing. - ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } - - //! Get the position of last parsing error in input, 0 otherwise. - size_t GetErrorOffset() const { return parseResult_.Offset(); } - -protected: - void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } - -private: - // Prohibit copy constructor & assignment operator. - GenericReader(const GenericReader&); - GenericReader& operator=(const GenericReader&); - - void ClearStack() { stack_.Clear(); } - - // clear stack on any exit from ParseStream, e.g. due to exception - struct ClearStackOnExit { - explicit ClearStackOnExit(GenericReader& r) : r_(r) {} - ~ClearStackOnExit() { r_.ClearStack(); } - private: - GenericReader& r_; - ClearStackOnExit(const ClearStackOnExit&); - ClearStackOnExit& operator=(const ClearStackOnExit&); - }; - - template - void SkipWhitespaceAndComments(InputStream& is) { - SkipWhitespace(is); - - if (parseFlags & kParseCommentsFlag) { - while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { - if (Consume(is, '*')) { - while (true) { - if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) - RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - else if (Consume(is, '*')) { - if (Consume(is, '/')) - break; - } - else - is.Take(); - } - } - else if (RAPIDJSON_LIKELY(Consume(is, '/'))) - while (is.Peek() != '\0' && is.Take() != '\n') {} - else - RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - - SkipWhitespace(is); - } - } - } - - // Parse object: { string : value, ... } - template - void ParseObject(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == '{'); - is.Take(); // Skip '{' - - if (RAPIDJSON_UNLIKELY(!handler.StartObject())) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - if (Consume(is, '}')) { - if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - return; - } - - for (SizeType memberCount = 0;;) { - if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) - RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); - - ParseString(is, handler, true); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) - RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - ParseValue(is, handler); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - ++memberCount; - - switch (is.Peek()) { - case ',': - is.Take(); - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - break; - case '}': - is.Take(); - if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - return; - default: - RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy - } - - if (parseFlags & kParseTrailingCommasFlag) { - if (is.Peek() == '}') { - if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - is.Take(); - return; - } - } - } - } - - // Parse array: [ value, ... ] - template - void ParseArray(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == '['); - is.Take(); // Skip '[' - - if (RAPIDJSON_UNLIKELY(!handler.StartArray())) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - if (Consume(is, ']')) { - if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - return; - } - - for (SizeType elementCount = 0;;) { - ParseValue(is, handler); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - ++elementCount; - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - if (Consume(is, ',')) { - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - } - else if (Consume(is, ']')) { - if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - return; - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); - - if (parseFlags & kParseTrailingCommasFlag) { - if (is.Peek() == ']') { - if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - is.Take(); - return; - } - } - } - } - - template - void ParseNull(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == 'n'); - is.Take(); - - if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { - if (RAPIDJSON_UNLIKELY(!handler.Null())) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); - } - - template - void ParseTrue(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == 't'); - is.Take(); - - if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { - if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); - } - - template - void ParseFalse(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == 'f'); - is.Take(); - - if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { - if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); - } - - template - RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { - if (RAPIDJSON_LIKELY(is.Peek() == expect)) { - is.Take(); - return true; - } - else - return false; - } - - // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). - template - unsigned ParseHex4(InputStream& is, size_t escapeOffset) { - unsigned codepoint = 0; - for (int i = 0; i < 4; i++) { - Ch c = is.Peek(); - codepoint <<= 4; - codepoint += static_cast(c); - if (c >= '0' && c <= '9') - codepoint -= '0'; - else if (c >= 'A' && c <= 'F') - codepoint -= 'A' - 10; - else if (c >= 'a' && c <= 'f') - codepoint -= 'a' - 10; - else { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); - } - is.Take(); - } - return codepoint; - } - - template - class StackStream { - public: - typedef CharType Ch; - - StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} - RAPIDJSON_FORCEINLINE void Put(Ch c) { - *stack_.template Push() = c; - ++length_; - } - - RAPIDJSON_FORCEINLINE void* Push(SizeType count) { - length_ += count; - return stack_.template Push(count); - } - - size_t Length() const { return length_; } - - Ch* Pop() { - return stack_.template Pop(length_); - } - - private: - StackStream(const StackStream&); - StackStream& operator=(const StackStream&); - - internal::Stack& stack_; - SizeType length_; - }; - - // Parse string and generate String event. Different code paths for kParseInsituFlag. - template - void ParseString(InputStream& is, Handler& handler, bool isKey = false) { - internal::StreamLocalCopy copy(is); - InputStream& s(copy.s); - - RAPIDJSON_ASSERT(s.Peek() == '\"'); - s.Take(); // Skip '\"' - - bool success = false; - if (parseFlags & kParseInsituFlag) { - typename InputStream::Ch *head = s.PutBegin(); - ParseStringToStream(s, s); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - size_t length = s.PutEnd(head) - 1; - RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - const typename TargetEncoding::Ch* const str = reinterpret_cast(head); - success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); - } - else { - StackStream stackStream(stack_); - ParseStringToStream(s, stackStream); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - SizeType length = static_cast(stackStream.Length()) - 1; - const typename TargetEncoding::Ch* const str = stackStream.Pop(); - success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); - } - if (RAPIDJSON_UNLIKELY(!success)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); - } - - // Parse string to an output is - // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. - template - RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - static const char escape[256] = { - Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', - Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, - 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, - 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 - }; -#undef Z16 -//!@endcond - - for (;;) { - // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. - if (!(parseFlags & kParseValidateEncodingFlag)) - ScanCopyUnescapedString(is, os); - - Ch c = is.Peek(); - if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape - size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset - is.Take(); - Ch e = is.Peek(); - if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { - is.Take(); - os.Put(static_cast(escape[static_cast(e)])); - } - else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe - is.Take(); - os.Put('\''); - } - else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode - is.Take(); - unsigned codepoint = ParseHex4(is, escapeOffset); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { - // high surrogate, check if followed by valid low surrogate - if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { - // Handle UTF-16 surrogate pair - if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); - unsigned codepoint2 = ParseHex4(is, escapeOffset); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); - codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; - } - // single low surrogate - else - { - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); - } - } - TEncoding::Encode(os, codepoint); - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); - } - else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote - is.Take(); - os.Put('\0'); // null-terminate the string - return; - } - else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - if (c == '\0') - RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); - else - RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); - } - else { - size_t offset = is.Tell(); - if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? - !Transcoder::Validate(is, os) : - !Transcoder::Transcode(is, os)))) - RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); - } - } - } - - template - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { - // Do nothing for generic version - } - -#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) - // StringStream -> StackStream - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { - const char* p = is.src_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = p; - return; - } - else - os.Put(*p++); - - // The rest of string using SIMD - static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; - static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; - const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); - const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); - const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); - - for (;; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const __m128i t1 = _mm_cmpeq_epi8(s, dq); - const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F - const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); - unsigned short r = static_cast(_mm_movemask_epi8(x)); - if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped - SizeType length; - #ifdef _MSC_VER // Find the index of first escaped - unsigned long offset; - _BitScanForward(&offset, r); - length = offset; - #else - length = static_cast(__builtin_ffs(r) - 1); - #endif - if (length != 0) { - char* q = reinterpret_cast(os.Push(length)); - for (size_t i = 0; i < length; i++) - q[i] = p[i]; - - p += length; - } - break; - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); - } - - is.src_ = p; - } - - // InsituStringStream -> InsituStringStream - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { - RAPIDJSON_ASSERT(&is == &os); - (void)os; - - if (is.src_ == is.dst_) { - SkipUnescapedString(is); - return; - } - - char* p = is.src_; - char *q = is.dst_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = p; - is.dst_ = q; - return; - } - else - *q++ = *p++; - - // The rest of string using SIMD - static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; - static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; - const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); - const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); - const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); - - for (;; p += 16, q += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const __m128i t1 = _mm_cmpeq_epi8(s, dq); - const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F - const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); - unsigned short r = static_cast(_mm_movemask_epi8(x)); - if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped - size_t length; -#ifdef _MSC_VER // Find the index of first escaped - unsigned long offset; - _BitScanForward(&offset, r); - length = offset; -#else - length = static_cast(__builtin_ffs(r) - 1); -#endif - for (const char* pend = p + length; p != pend; ) - *q++ = *p++; - break; - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); - } - - is.src_ = p; - is.dst_ = q; - } - - // When read/write pointers are the same for insitu stream, just skip unescaped characters - static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { - RAPIDJSON_ASSERT(is.src_ == is.dst_); - char* p = is.src_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - for (; p != nextAligned; p++) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = is.dst_ = p; - return; - } - - // The rest of string using SIMD - static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; - static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; - const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); - const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); - const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); - - for (;; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const __m128i t1 = _mm_cmpeq_epi8(s, dq); - const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F - const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); - unsigned short r = static_cast(_mm_movemask_epi8(x)); - if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped - size_t length; -#ifdef _MSC_VER // Find the index of first escaped - unsigned long offset; - _BitScanForward(&offset, r); - length = offset; -#else - length = static_cast(__builtin_ffs(r) - 1); -#endif - p += length; - break; - } - } - - is.src_ = is.dst_ = p; - } -#elif defined(RAPIDJSON_NEON) - // StringStream -> StackStream - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { - const char* p = is.src_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = p; - return; - } - else - os.Put(*p++); - - // The rest of string using SIMD - const uint8x16_t s0 = vmovq_n_u8('"'); - const uint8x16_t s1 = vmovq_n_u8('\\'); - const uint8x16_t s2 = vmovq_n_u8('\b'); - const uint8x16_t s3 = vmovq_n_u8(32); - - for (;; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, s0); - x = vorrq_u8(x, vceqq_u8(s, s1)); - x = vorrq_u8(x, vceqq_u8(s, s2)); - x = vorrq_u8(x, vcltq_u8(s, s3)); - - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract - uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract - - SizeType length = 0; - bool escaped = false; - if (low == 0) { - if (high != 0) { - uint32_t lz = internal::clzll(high); - length = 8 + (lz >> 3); - escaped = true; - } - } else { - uint32_t lz = internal::clzll(low); - length = lz >> 3; - escaped = true; - } - if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped - if (length != 0) { - char* q = reinterpret_cast(os.Push(length)); - for (size_t i = 0; i < length; i++) - q[i] = p[i]; - - p += length; - } - break; - } - vst1q_u8(reinterpret_cast(os.Push(16)), s); - } - - is.src_ = p; - } - - // InsituStringStream -> InsituStringStream - static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { - RAPIDJSON_ASSERT(&is == &os); - (void)os; - - if (is.src_ == is.dst_) { - SkipUnescapedString(is); - return; - } - - char* p = is.src_; - char *q = is.dst_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - while (p != nextAligned) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = p; - is.dst_ = q; - return; - } - else - *q++ = *p++; - - // The rest of string using SIMD - const uint8x16_t s0 = vmovq_n_u8('"'); - const uint8x16_t s1 = vmovq_n_u8('\\'); - const uint8x16_t s2 = vmovq_n_u8('\b'); - const uint8x16_t s3 = vmovq_n_u8(32); - - for (;; p += 16, q += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, s0); - x = vorrq_u8(x, vceqq_u8(s, s1)); - x = vorrq_u8(x, vceqq_u8(s, s2)); - x = vorrq_u8(x, vcltq_u8(s, s3)); - - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract - uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract - - SizeType length = 0; - bool escaped = false; - if (low == 0) { - if (high != 0) { - uint32_t lz = internal::clzll(high); - length = 8 + (lz >> 3); - escaped = true; - } - } else { - uint32_t lz = internal::clzll(low); - length = lz >> 3; - escaped = true; - } - if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped - for (const char* pend = p + length; p != pend; ) { - *q++ = *p++; - } - break; - } - vst1q_u8(reinterpret_cast(q), s); - } - - is.src_ = p; - is.dst_ = q; - } - - // When read/write pointers are the same for insitu stream, just skip unescaped characters - static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { - RAPIDJSON_ASSERT(is.src_ == is.dst_); - char* p = is.src_; - - // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - for (; p != nextAligned; p++) - if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { - is.src_ = is.dst_ = p; - return; - } - - // The rest of string using SIMD - const uint8x16_t s0 = vmovq_n_u8('"'); - const uint8x16_t s1 = vmovq_n_u8('\\'); - const uint8x16_t s2 = vmovq_n_u8('\b'); - const uint8x16_t s3 = vmovq_n_u8(32); - - for (;; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, s0); - x = vorrq_u8(x, vceqq_u8(s, s1)); - x = vorrq_u8(x, vceqq_u8(s, s2)); - x = vorrq_u8(x, vcltq_u8(s, s3)); - - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract - uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract - - if (low == 0) { - if (high != 0) { - uint32_t lz = internal::clzll(high); - p += 8 + (lz >> 3); - break; - } - } else { - uint32_t lz = internal::clzll(low); - p += lz >> 3; - break; - } - } - - is.src_ = is.dst_ = p; - } -#endif // RAPIDJSON_NEON - - template - class NumberStream; - - template - class NumberStream { - public: - typedef typename InputStream::Ch Ch; - - NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } - - RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } - RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } - RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } - RAPIDJSON_FORCEINLINE void Push(char) {} - - size_t Tell() { return is.Tell(); } - size_t Length() { return 0; } - const char* Pop() { return 0; } - - protected: - NumberStream& operator=(const NumberStream&); - - InputStream& is; - }; - - template - class NumberStream : public NumberStream { - typedef NumberStream Base; - public: - NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} - - RAPIDJSON_FORCEINLINE Ch TakePush() { - stackStream.Put(static_cast(Base::is.Peek())); - return Base::is.Take(); - } - - RAPIDJSON_FORCEINLINE void Push(char c) { - stackStream.Put(c); - } - - size_t Length() { return stackStream.Length(); } - - const char* Pop() { - stackStream.Put('\0'); - return stackStream.Pop(); - } - - private: - StackStream stackStream; - }; - - template - class NumberStream : public NumberStream { - typedef NumberStream Base; - public: - NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} - - RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } - }; - - template - void ParseNumber(InputStream& is, Handler& handler) { - internal::StreamLocalCopy copy(is); - NumberStream s(*this, copy.s); - - size_t startOffset = s.Tell(); - double d = 0.0; - bool useNanOrInf = false; - - // Parse minus - bool minus = Consume(s, '-'); - - // Parse int: zero / ( digit1-9 *DIGIT ) - unsigned i = 0; - uint64_t i64 = 0; - bool use64bit = false; - int significandDigit = 0; - if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { - i = 0; - s.TakePush(); - } - else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { - i = static_cast(s.TakePush() - '0'); - - if (minus) - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 - if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { - i64 = i; - use64bit = true; - break; - } - } - i = i * 10 + static_cast(s.TakePush() - '0'); - significandDigit++; - } - else - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 - if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { - i64 = i; - use64bit = true; - break; - } - } - i = i * 10 + static_cast(s.TakePush() - '0'); - significandDigit++; - } - } - // Parse NaN or Infinity here - else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { - if (Consume(s, 'N')) { - if (Consume(s, 'a') && Consume(s, 'N')) { - d = std::numeric_limits::quiet_NaN(); - useNanOrInf = true; - } - } - else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { - if (Consume(s, 'n') && Consume(s, 'f')) { - d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); - useNanOrInf = true; - - if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') - && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); - } - } - } - - if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); - } - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); - - // Parse 64bit int - bool useDouble = false; - if (use64bit) { - if (minus) - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 - if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { - d = static_cast(i64); - useDouble = true; - break; - } - i64 = i64 * 10 + static_cast(s.TakePush() - '0'); - significandDigit++; - } - else - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 - if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { - d = static_cast(i64); - useDouble = true; - break; - } - i64 = i64 * 10 + static_cast(s.TakePush() - '0'); - significandDigit++; - } - } - - // Force double for big integer - if (useDouble) { - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - d = d * 10 + (s.TakePush() - '0'); - } - } - - // Parse frac = decimal-point 1*DIGIT - int expFrac = 0; - size_t decimalPosition; - if (Consume(s, '.')) { - decimalPosition = s.Length(); - - if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); - - if (!useDouble) { -#if RAPIDJSON_64BIT - // Use i64 to store significand in 64-bit architecture - if (!use64bit) - i64 = i; - - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path - break; - else { - i64 = i64 * 10 + static_cast(s.TakePush() - '0'); - --expFrac; - if (i64 != 0) - significandDigit++; - } - } - - d = static_cast(i64); -#else - // Use double to store significand in 32-bit architecture - d = static_cast(use64bit ? i64 : i); -#endif - useDouble = true; - } - - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (significandDigit < 17) { - d = d * 10.0 + (s.TakePush() - '0'); - --expFrac; - if (RAPIDJSON_LIKELY(d > 0.0)) - significandDigit++; - } - else - s.TakePush(); - } - } - else - decimalPosition = s.Length(); // decimal position at the end of integer. - - // Parse exp = e [ minus / plus ] 1*DIGIT - int exp = 0; - if (Consume(s, 'e') || Consume(s, 'E')) { - if (!useDouble) { - d = static_cast(use64bit ? i64 : i); - useDouble = true; - } - - bool expMinus = false; - if (Consume(s, '+')) - ; - else if (Consume(s, '-')) - expMinus = true; - - if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - exp = static_cast(s.Take() - '0'); - if (expMinus) { - // (exp + expFrac) must not underflow int => we're detecting when -exp gets - // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into - // underflow territory): - // - // -(exp * 10 + 9) + expFrac >= INT_MIN - // <=> exp <= (expFrac - INT_MIN - 9) / 10 - RAPIDJSON_ASSERT(expFrac <= 0); - int maxExp = (expFrac + 2147483639) / 10; - - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - exp = exp * 10 + static_cast(s.Take() - '0'); - if (RAPIDJSON_UNLIKELY(exp > maxExp)) { - while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent - s.Take(); - } - } - } - else { // positive exp - int maxExp = 308 - expFrac; - while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - exp = exp * 10 + static_cast(s.Take() - '0'); - if (RAPIDJSON_UNLIKELY(exp > maxExp)) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); - } - } - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); - - if (expMinus) - exp = -exp; - } - - // Finish parsing, call event according to the type of number. - bool cont = true; - - if (parseFlags & kParseNumbersAsStringsFlag) { - if (parseFlags & kParseInsituFlag) { - s.Pop(); // Pop stack no matter if it will be used or not. - typename InputStream::Ch* head = is.PutBegin(); - const size_t length = s.Tell() - startOffset; - RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - // unable to insert the \0 character here, it will erase the comma after this number - const typename TargetEncoding::Ch* const str = reinterpret_cast(head); - cont = handler.RawNumber(str, SizeType(length), false); - } - else { - SizeType numCharsToCopy = static_cast(s.Length()); - StringStream srcStream(s.Pop()); - StackStream dstStream(stack_); - while (numCharsToCopy--) { - Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); - } - dstStream.Put('\0'); - const typename TargetEncoding::Ch* str = dstStream.Pop(); - const SizeType length = static_cast(dstStream.Length()) - 1; - cont = handler.RawNumber(str, SizeType(length), true); - } - } - else { - size_t length = s.Length(); - const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. - - if (useDouble) { - int p = exp + expFrac; - if (parseFlags & kParseFullPrecisionFlag) - d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); - else - d = internal::StrtodNormalPrecision(d, p); - - // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal - if (d > (std::numeric_limits::max)()) { - // Overflow - // TODO: internal::StrtodX should report overflow (or underflow) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); - } - - cont = handler.Double(minus ? -d : d); - } - else if (useNanOrInf) { - cont = handler.Double(d); - } - else { - if (use64bit) { - if (minus) - cont = handler.Int64(static_cast(~i64 + 1)); - else - cont = handler.Uint64(i64); - } - else { - if (minus) - cont = handler.Int(static_cast(~i + 1)); - else - cont = handler.Uint(i); - } - } - } - if (RAPIDJSON_UNLIKELY(!cont)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); - } - - // Parse any JSON value - template - void ParseValue(InputStream& is, Handler& handler) { - switch (is.Peek()) { - case 'n': ParseNull (is, handler); break; - case 't': ParseTrue (is, handler); break; - case 'f': ParseFalse (is, handler); break; - case '"': ParseString(is, handler); break; - case '{': ParseObject(is, handler); break; - case '[': ParseArray (is, handler); break; - default : - ParseNumber(is, handler); - break; - - } - } - - // Iterative Parsing - - // States - enum IterativeParsingState { - IterativeParsingFinishState = 0, // sink states at top - IterativeParsingErrorState, // sink states at top - IterativeParsingStartState, - - // Object states - IterativeParsingObjectInitialState, - IterativeParsingMemberKeyState, - IterativeParsingMemberValueState, - IterativeParsingObjectFinishState, - - // Array states - IterativeParsingArrayInitialState, - IterativeParsingElementState, - IterativeParsingArrayFinishState, - - // Single value state - IterativeParsingValueState, - - // Delimiter states (at bottom) - IterativeParsingElementDelimiterState, - IterativeParsingMemberDelimiterState, - IterativeParsingKeyValueDelimiterState, - - cIterativeParsingStateCount - }; - - // Tokens - enum Token { - LeftBracketToken = 0, - RightBracketToken, - - LeftCurlyBracketToken, - RightCurlyBracketToken, - - CommaToken, - ColonToken, - - StringToken, - FalseToken, - TrueToken, - NullToken, - NumberToken, - - kTokenCount - }; - - RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { - -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#define N NumberToken -#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N - // Maps from ASCII to Token - static const unsigned char tokenMap[256] = { - N16, // 00~0F - N16, // 10~1F - N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F - N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F - N16, // 40~4F - N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F - N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F - N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F - N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF - }; -#undef N -#undef N16 -//!@endcond - - if (sizeof(Ch) == 1 || static_cast(c) < 256) - return static_cast(tokenMap[static_cast(c)]); - else - return NumberToken; - } - - RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { - // current state x one lookahead token -> new state - static const char G[cIterativeParsingStateCount][kTokenCount] = { - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Start - { - IterativeParsingArrayInitialState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingValueState, // String - IterativeParsingValueState, // False - IterativeParsingValueState, // True - IterativeParsingValueState, // Null - IterativeParsingValueState // Number - }, - // ObjectInitial - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // MemberKey - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingKeyValueDelimiterState, // Colon - IterativeParsingErrorState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // MemberValue - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingMemberDelimiterState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingErrorState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // ObjectFinish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // ArrayInitial - { - IterativeParsingArrayInitialState, // Left bracket(push Element state) - IterativeParsingArrayFinishState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push Element state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingElementState, // String - IterativeParsingElementState, // False - IterativeParsingElementState, // True - IterativeParsingElementState, // Null - IterativeParsingElementState // Number - }, - // Element - { - IterativeParsingErrorState, // Left bracket - IterativeParsingArrayFinishState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingElementDelimiterState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingErrorState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // ArrayFinish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Single Value (sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // ElementDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push Element state) - IterativeParsingArrayFinishState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push Element state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingElementState, // String - IterativeParsingElementState, // False - IterativeParsingElementState, // True - IterativeParsingElementState, // Null - IterativeParsingElementState // Number - }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, - }; // End of G - - return static_cast(G[state][token]); - } - - // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). - // May return a new state on state pop. - template - RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { - (void)token; - - switch (dst) { - case IterativeParsingErrorState: - return dst; - - case IterativeParsingObjectInitialState: - case IterativeParsingArrayInitialState: - { - // Push the state(Element or MemeberValue) if we are nested in another array or value of member. - // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. - IterativeParsingState n = src; - if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) - n = IterativeParsingElementState; - else if (src == IterativeParsingKeyValueDelimiterState) - n = IterativeParsingMemberValueState; - // Push current state. - *stack_.template Push(1) = n; - // Initialize and push the member/element count. - *stack_.template Push(1) = 0; - // Call handler - bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); - // On handler short circuits the parsing. - if (!hr) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); - return IterativeParsingErrorState; - } - else { - is.Take(); - return dst; - } - } - - case IterativeParsingMemberKeyState: - ParseString(is, handler, true); - if (HasParseError()) - return IterativeParsingErrorState; - else - return dst; - - case IterativeParsingKeyValueDelimiterState: - RAPIDJSON_ASSERT(token == ColonToken); - is.Take(); - return dst; - - case IterativeParsingMemberValueState: - // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. - ParseValue(is, handler); - if (HasParseError()) { - return IterativeParsingErrorState; - } - return dst; - - case IterativeParsingElementState: - // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. - ParseValue(is, handler); - if (HasParseError()) { - return IterativeParsingErrorState; - } - return dst; - - case IterativeParsingMemberDelimiterState: - case IterativeParsingElementDelimiterState: - is.Take(); - // Update member/element count. - *stack_.template Top() = *stack_.template Top() + 1; - return dst; - - case IterativeParsingObjectFinishState: - { - // Transit from delimiter is only allowed when trailing commas are enabled - if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); - return IterativeParsingErrorState; - } - // Get member count. - SizeType c = *stack_.template Pop(1); - // If the object is not empty, count the last member. - if (src == IterativeParsingMemberValueState) - ++c; - // Restore the state. - IterativeParsingState n = static_cast(*stack_.template Pop(1)); - // Transit to Finish state if this is the topmost scope. - if (n == IterativeParsingStartState) - n = IterativeParsingFinishState; - // Call handler - bool hr = handler.EndObject(c); - // On handler short circuits the parsing. - if (!hr) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); - return IterativeParsingErrorState; - } - else { - is.Take(); - return n; - } - } - - case IterativeParsingArrayFinishState: - { - // Transit from delimiter is only allowed when trailing commas are enabled - if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); - return IterativeParsingErrorState; - } - // Get element count. - SizeType c = *stack_.template Pop(1); - // If the array is not empty, count the last element. - if (src == IterativeParsingElementState) - ++c; - // Restore the state. - IterativeParsingState n = static_cast(*stack_.template Pop(1)); - // Transit to Finish state if this is the topmost scope. - if (n == IterativeParsingStartState) - n = IterativeParsingFinishState; - // Call handler - bool hr = handler.EndArray(c); - // On handler short circuits the parsing. - if (!hr) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); - return IterativeParsingErrorState; - } - else { - is.Take(); - return n; - } - } - - default: - // This branch is for IterativeParsingValueState actually. - // Use `default:` rather than - // `case IterativeParsingValueState:` is for code coverage. - - // The IterativeParsingStartState is not enumerated in this switch-case. - // It is impossible for that case. And it can be caught by following assertion. - - // The IterativeParsingFinishState is not enumerated in this switch-case either. - // It is a "derivative" state which cannot triggered from Predict() directly. - // Therefore it cannot happen here. And it can be caught by following assertion. - RAPIDJSON_ASSERT(dst == IterativeParsingValueState); - - // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. - ParseValue(is, handler); - if (HasParseError()) { - return IterativeParsingErrorState; - } - return IterativeParsingFinishState; - } - } - - template - void HandleError(IterativeParsingState src, InputStream& is) { - if (HasParseError()) { - // Error flag has been set. - return; - } - - switch (src) { - case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; - case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; - case IterativeParsingObjectInitialState: - case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; - case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; - case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; - case IterativeParsingKeyValueDelimiterState: - case IterativeParsingArrayInitialState: - case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; - default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; - } - } - - RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { - return s >= IterativeParsingElementDelimiterState; - } - - RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { - return s <= IterativeParsingErrorState; - } - - template - ParseResult IterativeParse(InputStream& is, Handler& handler) { - parseResult_.Clear(); - ClearStackOnExit scope(*this); - IterativeParsingState state = IterativeParsingStartState; - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - while (is.Peek() != '\0') { - Token t = Tokenize(is.Peek()); - IterativeParsingState n = Predict(state, t); - IterativeParsingState d = Transit(state, t, n, is, handler); - - if (d == IterativeParsingErrorState) { - HandleError(state, is); - break; - } - - state = d; - - // Do not further consume streams if a root JSON has been parsed. - if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) - break; - - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - } - - // Handle the end of file. - if (state != IterativeParsingFinishState) - HandleError(state, is); - - return parseResult_; - } - - static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. - internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. - ParseResult parseResult_; - IterativeParsingState state_; -}; // class GenericReader - -//! Reader with UTF8 encoding and default allocator. -typedef GenericReader, UTF8<> > Reader; - -RAPIDJSON_NAMESPACE_END - -#if defined(__clang__) || defined(_MSC_VER) -RAPIDJSON_DIAG_POP -#endif - - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_READER_H_ diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h deleted file mode 100755 index fc39d06c..00000000 --- a/include/rapidjson/schema.h +++ /dev/null @@ -1,2496 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available-> -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License-> You may obtain a copy of the License at -// -// http://opensource->org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied-> See the License for the -// specific language governing permissions and limitations under the License-> - -#ifndef RAPIDJSON_SCHEMA_H_ -#define RAPIDJSON_SCHEMA_H_ - -#include "document.h" -#include "pointer.h" -#include "stringbuffer.h" -#include // abs, floor - -#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) -#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 -#else -#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 -#endif - -#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) -#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 -#else -#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 -#endif - -#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX -#include "internal/regex.h" -#elif RAPIDJSON_SCHEMA_USE_STDREGEX -#include -#endif - -#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX -#define RAPIDJSON_SCHEMA_HAS_REGEX 1 -#else -#define RAPIDJSON_SCHEMA_HAS_REGEX 0 -#endif - -#ifndef RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_SCHEMA_VERBOSE 0 -#endif - -#if RAPIDJSON_SCHEMA_VERBOSE -#include "stringbuffer.h" -#endif - -RAPIDJSON_DIAG_PUSH - -#if defined(__GNUC__) -RAPIDJSON_DIAG_OFF(effc++) -#endif - -#ifdef __clang__ -RAPIDJSON_DIAG_OFF(weak-vtables) -RAPIDJSON_DIAG_OFF(exit-time-destructors) -RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) -RAPIDJSON_DIAG_OFF(variadic-macros) -#elif defined(_MSC_VER) -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Verbose Utilities - -#if RAPIDJSON_SCHEMA_VERBOSE - -namespace internal { - -inline void PrintInvalidKeyword(const char* keyword) { - printf("Fail keyword: %s\n", keyword); -} - -inline void PrintInvalidKeyword(const wchar_t* keyword) { - wprintf(L"Fail keyword: %ls\n", keyword); -} - -inline void PrintInvalidDocument(const char* document) { - printf("Fail document: %s\n\n", document); -} - -inline void PrintInvalidDocument(const wchar_t* document) { - wprintf(L"Fail document: %ls\n\n", document); -} - -inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { - printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); -} - -inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { - wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); -} - -} // namespace internal - -#endif // RAPIDJSON_SCHEMA_VERBOSE - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_INVALID_KEYWORD_RETURN - -#if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) -#else -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) -#endif - -#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ -RAPIDJSON_MULTILINEMACRO_BEGIN\ - context.invalidKeyword = keyword.GetString();\ - RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ - return false;\ -RAPIDJSON_MULTILINEMACRO_END - -/////////////////////////////////////////////////////////////////////////////// -// Forward declarations - -template -class GenericSchemaDocument; - -namespace internal { - -template -class Schema; - -/////////////////////////////////////////////////////////////////////////////// -// ISchemaValidator - -class ISchemaValidator { -public: - virtual ~ISchemaValidator() {} - virtual bool IsValid() const = 0; -}; - -/////////////////////////////////////////////////////////////////////////////// -// ISchemaStateFactory - -template -class ISchemaStateFactory { -public: - virtual ~ISchemaStateFactory() {} - virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; - virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; - virtual void* CreateHasher() = 0; - virtual uint64_t GetHashCode(void* hasher) = 0; - virtual void DestroryHasher(void* hasher) = 0; - virtual void* MallocState(size_t size) = 0; - virtual void FreeState(void* p) = 0; -}; - -/////////////////////////////////////////////////////////////////////////////// -// IValidationErrorHandler - -template -class IValidationErrorHandler { -public: - typedef typename SchemaType::Ch Ch; - typedef typename SchemaType::SValue SValue; - - virtual ~IValidationErrorHandler() {} - - virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; - virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; - virtual void NotMultipleOf(double actual, const SValue& expected) = 0; - virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; - virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; - virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; - virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; - virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; - virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; - - virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; - virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; - virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; - - virtual void DisallowedItem(SizeType index) = 0; - virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; - virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; - virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; - - virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; - virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; - virtual void StartMissingProperties() = 0; - virtual void AddMissingProperty(const SValue& name) = 0; - virtual bool EndMissingProperties() = 0; - virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; - virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; - - virtual void StartDependencyErrors() = 0; - virtual void StartMissingDependentProperties() = 0; - virtual void AddMissingDependentProperty(const SValue& targetName) = 0; - virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; - virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; - virtual bool EndDependencyErrors() = 0; - - virtual void DisallowedValue() = 0; - virtual void StartDisallowedType() = 0; - virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; - virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; - virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; - virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; - virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; - virtual void Disallowed() = 0; -}; - - -/////////////////////////////////////////////////////////////////////////////// -// Hasher - -// For comparison of compound value -template -class Hasher { -public: - typedef typename Encoding::Ch Ch; - - Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} - - bool Null() { return WriteType(kNullType); } - bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } - bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } - bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } - bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } - bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } - bool Double(double d) { - Number n; - if (d < 0) n.u.i = static_cast(d); - else n.u.u = static_cast(d); - n.d = d; - return WriteNumber(n); - } - - bool RawNumber(const Ch* str, SizeType len, bool) { - WriteBuffer(kNumberType, str, len * sizeof(Ch)); - return true; - } - - bool String(const Ch* str, SizeType len, bool) { - WriteBuffer(kStringType, str, len * sizeof(Ch)); - return true; - } - - bool StartObject() { return true; } - bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } - bool EndObject(SizeType memberCount) { - uint64_t h = Hash(0, kObjectType); - uint64_t* kv = stack_.template Pop(memberCount * 2); - for (SizeType i = 0; i < memberCount; i++) - h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive - *stack_.template Push() = h; - return true; - } - - bool StartArray() { return true; } - bool EndArray(SizeType elementCount) { - uint64_t h = Hash(0, kArrayType); - uint64_t* e = stack_.template Pop(elementCount); - for (SizeType i = 0; i < elementCount; i++) - h = Hash(h, e[i]); // Use hash to achieve element order sensitive - *stack_.template Push() = h; - return true; - } - - bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } - - uint64_t GetHashCode() const { - RAPIDJSON_ASSERT(IsValid()); - return *stack_.template Top(); - } - -private: - static const size_t kDefaultSize = 256; - struct Number { - union U { - uint64_t u; - int64_t i; - }u; - double d; - }; - - bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } - - bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } - - bool WriteBuffer(Type type, const void* data, size_t len) { - // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ - uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); - const unsigned char* d = static_cast(data); - for (size_t i = 0; i < len; i++) - h = Hash(h, d[i]); - *stack_.template Push() = h; - return true; - } - - static uint64_t Hash(uint64_t h, uint64_t d) { - static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); - h ^= d; - h *= kPrime; - return h; - } - - Stack stack_; -}; - -/////////////////////////////////////////////////////////////////////////////// -// SchemaValidationContext - -template -struct SchemaValidationContext { - typedef Schema SchemaType; - typedef ISchemaStateFactory SchemaValidatorFactoryType; - typedef IValidationErrorHandler ErrorHandlerType; - typedef typename SchemaType::ValueType ValueType; - typedef typename ValueType::Ch Ch; - - enum PatternValidatorType { - kPatternValidatorOnly, - kPatternValidatorWithProperty, - kPatternValidatorWithAdditionalProperty - }; - - SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) : - factory(f), - error_handler(eh), - schema(s), - valueSchema(), - invalidKeyword(), - hasher(), - arrayElementHashCodes(), - validators(), - validatorCount(), - patternPropertiesValidators(), - patternPropertiesValidatorCount(), - patternPropertiesSchemas(), - patternPropertiesSchemaCount(), - valuePatternValidatorType(kPatternValidatorOnly), - propertyExist(), - inArray(false), - valueUniqueness(false), - arrayUniqueness(false) - { - } - - ~SchemaValidationContext() { - if (hasher) - factory.DestroryHasher(hasher); - if (validators) { - for (SizeType i = 0; i < validatorCount; i++) - factory.DestroySchemaValidator(validators[i]); - factory.FreeState(validators); - } - if (patternPropertiesValidators) { - for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) - factory.DestroySchemaValidator(patternPropertiesValidators[i]); - factory.FreeState(patternPropertiesValidators); - } - if (patternPropertiesSchemas) - factory.FreeState(patternPropertiesSchemas); - if (propertyExist) - factory.FreeState(propertyExist); - } - - SchemaValidatorFactoryType& factory; - ErrorHandlerType& error_handler; - const SchemaType* schema; - const SchemaType* valueSchema; - const Ch* invalidKeyword; - void* hasher; // Only validator access - void* arrayElementHashCodes; // Only validator access this - ISchemaValidator** validators; - SizeType validatorCount; - ISchemaValidator** patternPropertiesValidators; - SizeType patternPropertiesValidatorCount; - const SchemaType** patternPropertiesSchemas; - SizeType patternPropertiesSchemaCount; - PatternValidatorType valuePatternValidatorType; - PatternValidatorType objectPatternValidatorType; - SizeType arrayElementIndex; - bool* propertyExist; - bool inArray; - bool valueUniqueness; - bool arrayUniqueness; -}; - -/////////////////////////////////////////////////////////////////////////////// -// Schema - -template -class Schema { -public: - typedef typename SchemaDocumentType::ValueType ValueType; - typedef typename SchemaDocumentType::AllocatorType AllocatorType; - typedef typename SchemaDocumentType::PointerType PointerType; - typedef typename ValueType::EncodingType EncodingType; - typedef typename EncodingType::Ch Ch; - typedef SchemaValidationContext Context; - typedef Schema SchemaType; - typedef GenericValue SValue; - typedef IValidationErrorHandler ErrorHandler; - friend class GenericSchemaDocument; - - Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : - allocator_(allocator), - uri_(schemaDocument->GetURI(), *allocator), - pointer_(p, allocator), - typeless_(schemaDocument->GetTypeless()), - enum_(), - enumCount_(), - not_(), - type_((1 << kTotalSchemaType) - 1), // typeless - validatorCount_(), - notValidatorIndex_(), - properties_(), - additionalPropertiesSchema_(), - patternProperties_(), - patternPropertyCount_(), - propertyCount_(), - minProperties_(), - maxProperties_(SizeType(~0)), - additionalProperties_(true), - hasDependencies_(), - hasRequired_(), - hasSchemaDependencies_(), - additionalItemsSchema_(), - itemsList_(), - itemsTuple_(), - itemsTupleCount_(), - minItems_(), - maxItems_(SizeType(~0)), - additionalItems_(true), - uniqueItems_(false), - pattern_(), - minLength_(0), - maxLength_(~SizeType(0)), - exclusiveMinimum_(false), - exclusiveMaximum_(false), - defaultValueLength_(0) - { - typedef typename ValueType::ConstValueIterator ConstValueIterator; - typedef typename ValueType::ConstMemberIterator ConstMemberIterator; - - if (!value.IsObject()) - return; - - if (const ValueType* v = GetMember(value, GetTypeString())) { - type_ = 0; - if (v->IsString()) - AddType(*v); - else if (v->IsArray()) - for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) - AddType(*itr); - } - - if (const ValueType* v = GetMember(value, GetEnumString())) - if (v->IsArray() && v->Size() > 0) { - enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); - for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { - typedef Hasher > EnumHasherType; - char buffer[256u + 24]; - MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); - EnumHasherType h(&hasherAllocator, 256); - itr->Accept(h); - enum_[enumCount_++] = h.GetHashCode(); - } - } - - if (schemaDocument) { - AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); - AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); - AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); - } - - if (const ValueType* v = GetMember(value, GetNotString())) { - schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); - notValidatorIndex_ = validatorCount_; - validatorCount_++; - } - - // Object - - const ValueType* properties = GetMember(value, GetPropertiesString()); - const ValueType* required = GetMember(value, GetRequiredString()); - const ValueType* dependencies = GetMember(value, GetDependenciesString()); - { - // Gather properties from properties/required/dependencies - SValue allProperties(kArrayType); - - if (properties && properties->IsObject()) - for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) - AddUniqueElement(allProperties, itr->name); - - if (required && required->IsArray()) - for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) - if (itr->IsString()) - AddUniqueElement(allProperties, *itr); - - if (dependencies && dependencies->IsObject()) - for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { - AddUniqueElement(allProperties, itr->name); - if (itr->value.IsArray()) - for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) - if (i->IsString()) - AddUniqueElement(allProperties, *i); - } - - if (allProperties.Size() > 0) { - propertyCount_ = allProperties.Size(); - properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); - for (SizeType i = 0; i < propertyCount_; i++) { - new (&properties_[i]) Property(); - properties_[i].name = allProperties[i]; - properties_[i].schema = typeless_; - } - } - } - - if (properties && properties->IsObject()) { - PointerType q = p.Append(GetPropertiesString(), allocator_); - for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { - SizeType index; - if (FindPropertyIndex(itr->name, &index)) - schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); - } - } - - if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { - PointerType q = p.Append(GetPatternPropertiesString(), allocator_); - patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); - patternPropertyCount_ = 0; - - for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { - new (&patternProperties_[patternPropertyCount_]) PatternProperty(); - patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); - schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); - patternPropertyCount_++; - } - } - - if (required && required->IsArray()) - for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) - if (itr->IsString()) { - SizeType index; - if (FindPropertyIndex(*itr, &index)) { - properties_[index].required = true; - hasRequired_ = true; - } - } - - if (dependencies && dependencies->IsObject()) { - PointerType q = p.Append(GetDependenciesString(), allocator_); - hasDependencies_ = true; - for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { - SizeType sourceIndex; - if (FindPropertyIndex(itr->name, &sourceIndex)) { - if (itr->value.IsArray()) { - properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); - std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); - for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { - SizeType targetIndex; - if (FindPropertyIndex(*targetItr, &targetIndex)) - properties_[sourceIndex].dependencies[targetIndex] = true; - } - } - else if (itr->value.IsObject()) { - hasSchemaDependencies_ = true; - schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); - properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; - validatorCount_++; - } - } - } - } - - if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { - if (v->IsBool()) - additionalProperties_ = v->GetBool(); - else if (v->IsObject()) - schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); - } - - AssignIfExist(minProperties_, value, GetMinPropertiesString()); - AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); - - // Array - if (const ValueType* v = GetMember(value, GetItemsString())) { - PointerType q = p.Append(GetItemsString(), allocator_); - if (v->IsObject()) // List validation - schemaDocument->CreateSchema(&itemsList_, q, *v, document); - else if (v->IsArray()) { // Tuple validation - itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); - SizeType index = 0; - for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) - schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); - } - } - - AssignIfExist(minItems_, value, GetMinItemsString()); - AssignIfExist(maxItems_, value, GetMaxItemsString()); - - if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { - if (v->IsBool()) - additionalItems_ = v->GetBool(); - else if (v->IsObject()) - schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); - } - - AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); - - // String - AssignIfExist(minLength_, value, GetMinLengthString()); - AssignIfExist(maxLength_, value, GetMaxLengthString()); - - if (const ValueType* v = GetMember(value, GetPatternString())) - pattern_ = CreatePattern(*v); - - // Number - if (const ValueType* v = GetMember(value, GetMinimumString())) - if (v->IsNumber()) - minimum_.CopyFrom(*v, *allocator_); - - if (const ValueType* v = GetMember(value, GetMaximumString())) - if (v->IsNumber()) - maximum_.CopyFrom(*v, *allocator_); - - AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); - AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); - - if (const ValueType* v = GetMember(value, GetMultipleOfString())) - if (v->IsNumber() && v->GetDouble() > 0.0) - multipleOf_.CopyFrom(*v, *allocator_); - - // Default - if (const ValueType* v = GetMember(value, GetDefaultValueString())) - if (v->IsString()) - defaultValueLength_ = v->GetStringLength(); - - } - - ~Schema() { - AllocatorType::Free(enum_); - if (properties_) { - for (SizeType i = 0; i < propertyCount_; i++) - properties_[i].~Property(); - AllocatorType::Free(properties_); - } - if (patternProperties_) { - for (SizeType i = 0; i < patternPropertyCount_; i++) - patternProperties_[i].~PatternProperty(); - AllocatorType::Free(patternProperties_); - } - AllocatorType::Free(itemsTuple_); -#if RAPIDJSON_SCHEMA_HAS_REGEX - if (pattern_) { - pattern_->~RegexType(); - AllocatorType::Free(pattern_); - } -#endif - } - - const SValue& GetURI() const { - return uri_; - } - - const PointerType& GetPointer() const { - return pointer_; - } - - bool BeginValue(Context& context) const { - if (context.inArray) { - if (uniqueItems_) - context.valueUniqueness = true; - - if (itemsList_) - context.valueSchema = itemsList_; - else if (itemsTuple_) { - if (context.arrayElementIndex < itemsTupleCount_) - context.valueSchema = itemsTuple_[context.arrayElementIndex]; - else if (additionalItemsSchema_) - context.valueSchema = additionalItemsSchema_; - else if (additionalItems_) - context.valueSchema = typeless_; - else { - context.error_handler.DisallowedItem(context.arrayElementIndex); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); - } - } - else - context.valueSchema = typeless_; - - context.arrayElementIndex++; - } - return true; - } - - RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { - if (context.patternPropertiesValidatorCount > 0) { - bool otherValid = false; - SizeType count = context.patternPropertiesValidatorCount; - if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) - otherValid = context.patternPropertiesValidators[--count]->IsValid(); - - bool patternValid = true; - for (SizeType i = 0; i < count; i++) - if (!context.patternPropertiesValidators[i]->IsValid()) { - patternValid = false; - break; - } - - if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { - if (!patternValid) { - context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); - } - } - else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { - if (!patternValid || !otherValid) { - context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); - } - } - else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) - context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); - } - } - - if (enum_) { - const uint64_t h = context.factory.GetHashCode(context.hasher); - for (SizeType i = 0; i < enumCount_; i++) - if (enum_[i] == h) - goto foundEnum; - context.error_handler.DisallowedValue(); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); - foundEnum:; - } - - if (allOf_.schemas) - for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) - if (!context.validators[i]->IsValid()) { - context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); - } - - if (anyOf_.schemas) { - for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) - if (context.validators[i]->IsValid()) - goto foundAny; - context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); - foundAny:; - } - - if (oneOf_.schemas) { - bool oneValid = false; - for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) - if (context.validators[i]->IsValid()) { - if (oneValid) { - context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); - } else - oneValid = true; - } - if (!oneValid) { - context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); - } - } - - if (not_ && context.validators[notValidatorIndex_]->IsValid()) { - context.error_handler.Disallowed(); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); - } - - return true; - } - - bool Null(Context& context) const { - if (!(type_ & (1 << kNullSchemaType))) { - DisallowedType(context, GetNullString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - } - return CreateParallelValidator(context); - } - - bool Bool(Context& context, bool) const { - if (!(type_ & (1 << kBooleanSchemaType))) { - DisallowedType(context, GetBooleanString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - } - return CreateParallelValidator(context); - } - - bool Int(Context& context, int i) const { - if (!CheckInt(context, i)) - return false; - return CreateParallelValidator(context); - } - - bool Uint(Context& context, unsigned u) const { - if (!CheckUint(context, u)) - return false; - return CreateParallelValidator(context); - } - - bool Int64(Context& context, int64_t i) const { - if (!CheckInt(context, i)) - return false; - return CreateParallelValidator(context); - } - - bool Uint64(Context& context, uint64_t u) const { - if (!CheckUint(context, u)) - return false; - return CreateParallelValidator(context); - } - - bool Double(Context& context, double d) const { - if (!(type_ & (1 << kNumberSchemaType))) { - DisallowedType(context, GetNumberString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - } - - if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) - return false; - - if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) - return false; - - if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) - return false; - - return CreateParallelValidator(context); - } - - bool String(Context& context, const Ch* str, SizeType length, bool) const { - if (!(type_ & (1 << kStringSchemaType))) { - DisallowedType(context, GetStringString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - } - - if (minLength_ != 0 || maxLength_ != SizeType(~0)) { - SizeType count; - if (internal::CountStringCodePoint(str, length, &count)) { - if (count < minLength_) { - context.error_handler.TooShort(str, length, minLength_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); - } - if (count > maxLength_) { - context.error_handler.TooLong(str, length, maxLength_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); - } - } - } - - if (pattern_ && !IsPatternMatch(pattern_, str, length)) { - context.error_handler.DoesNotMatch(str, length); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); - } - - return CreateParallelValidator(context); - } - - bool StartObject(Context& context) const { - if (!(type_ & (1 << kObjectSchemaType))) { - DisallowedType(context, GetObjectString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - } - - if (hasDependencies_ || hasRequired_) { - context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); - std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); - } - - if (patternProperties_) { // pre-allocate schema array - SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType - context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); - context.patternPropertiesSchemaCount = 0; - std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); - } - - return CreateParallelValidator(context); - } - - bool Key(Context& context, const Ch* str, SizeType len, bool) const { - if (patternProperties_) { - context.patternPropertiesSchemaCount = 0; - for (SizeType i = 0; i < patternPropertyCount_; i++) - if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { - context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; - context.valueSchema = typeless_; - } - } - - SizeType index = 0; - if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { - if (context.patternPropertiesSchemaCount > 0) { - context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; - context.valueSchema = typeless_; - context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; - } - else - context.valueSchema = properties_[index].schema; - - if (context.propertyExist) - context.propertyExist[index] = true; - - return true; - } - - if (additionalPropertiesSchema_) { - if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { - context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; - context.valueSchema = typeless_; - context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; - } - else - context.valueSchema = additionalPropertiesSchema_; - return true; - } - else if (additionalProperties_) { - context.valueSchema = typeless_; - return true; - } - - if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties - context.error_handler.DisallowedProperty(str, len); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); - } - - return true; - } - - bool EndObject(Context& context, SizeType memberCount) const { - if (hasRequired_) { - context.error_handler.StartMissingProperties(); - for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].required && !context.propertyExist[index]) - if (properties_[index].schema->defaultValueLength_ == 0 ) - context.error_handler.AddMissingProperty(properties_[index].name); - if (context.error_handler.EndMissingProperties()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); - } - - if (memberCount < minProperties_) { - context.error_handler.TooFewProperties(memberCount, minProperties_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); - } - - if (memberCount > maxProperties_) { - context.error_handler.TooManyProperties(memberCount, maxProperties_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); - } - - if (hasDependencies_) { - context.error_handler.StartDependencyErrors(); - for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { - const Property& source = properties_[sourceIndex]; - if (context.propertyExist[sourceIndex]) { - if (source.dependencies) { - context.error_handler.StartMissingDependentProperties(); - for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) - if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) - context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); - context.error_handler.EndMissingDependentProperties(source.name); - } - else if (source.dependenciesSchema) { - ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; - if (!dependenciesValidator->IsValid()) - context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); - } - } - } - if (context.error_handler.EndDependencyErrors()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); - } - - return true; - } - - bool StartArray(Context& context) const { - if (!(type_ & (1 << kArraySchemaType))) { - DisallowedType(context, GetArrayString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - } - - context.arrayElementIndex = 0; - context.inArray = true; - - return CreateParallelValidator(context); - } - - bool EndArray(Context& context, SizeType elementCount) const { - context.inArray = false; - - if (elementCount < minItems_) { - context.error_handler.TooFewItems(elementCount, minItems_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); - } - - if (elementCount > maxItems_) { - context.error_handler.TooManyItems(elementCount, maxItems_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); - } - - return true; - } - - // Generate functions for string literal according to Ch -#define RAPIDJSON_STRING_(name, ...) \ - static const ValueType& Get##name##String() {\ - static const Ch s[] = { __VA_ARGS__, '\0' };\ - static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ - return v;\ - } - - RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') - RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') - RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') - RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') - RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') - RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') - RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') - RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') - RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') - RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') - RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') - RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') - RAPIDJSON_STRING_(Not, 'n', 'o', 't') - RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') - RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') - RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') - RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') - RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') - RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') - RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') - RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') - RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') - RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') - RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') - RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') - RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') - -#undef RAPIDJSON_STRING_ - -private: - enum SchemaValueType { - kNullSchemaType, - kBooleanSchemaType, - kObjectSchemaType, - kArraySchemaType, - kStringSchemaType, - kNumberSchemaType, - kIntegerSchemaType, - kTotalSchemaType - }; - -#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX - typedef internal::GenericRegex RegexType; -#elif RAPIDJSON_SCHEMA_USE_STDREGEX - typedef std::basic_regex RegexType; -#else - typedef char RegexType; -#endif - - struct SchemaArray { - SchemaArray() : schemas(), count() {} - ~SchemaArray() { AllocatorType::Free(schemas); } - const SchemaType** schemas; - SizeType begin; // begin index of context.validators - SizeType count; - }; - - template - void AddUniqueElement(V1& a, const V2& v) { - for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) - if (*itr == v) - return; - V1 c(v, *allocator_); - a.PushBack(c, *allocator_); - } - - static const ValueType* GetMember(const ValueType& value, const ValueType& name) { - typename ValueType::ConstMemberIterator itr = value.FindMember(name); - return itr != value.MemberEnd() ? &(itr->value) : 0; - } - - static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { - if (const ValueType* v = GetMember(value, name)) - if (v->IsBool()) - out = v->GetBool(); - } - - static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { - if (const ValueType* v = GetMember(value, name)) - if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) - out = static_cast(v->GetUint64()); - } - - void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { - if (const ValueType* v = GetMember(value, name)) { - if (v->IsArray() && v->Size() > 0) { - PointerType q = p.Append(name, allocator_); - out.count = v->Size(); - out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); - memset(out.schemas, 0, sizeof(Schema*)* out.count); - for (SizeType i = 0; i < out.count; i++) - schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); - out.begin = validatorCount_; - validatorCount_ += out.count; - } - } - } - -#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX - template - RegexType* CreatePattern(const ValueType& value) { - if (value.IsString()) { - RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); - if (!r->IsValid()) { - r->~RegexType(); - AllocatorType::Free(r); - r = 0; - } - return r; - } - return 0; - } - - static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { - GenericRegexSearch rs(*pattern); - return rs.Search(str); - } -#elif RAPIDJSON_SCHEMA_USE_STDREGEX - template - RegexType* CreatePattern(const ValueType& value) { - if (value.IsString()) { - RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); - try { - return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); - } - catch (const std::regex_error&) { - AllocatorType::Free(r); - } - } - return 0; - } - - static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { - std::match_results r; - return std::regex_search(str, str + length, r, *pattern); - } -#else - template - RegexType* CreatePattern(const ValueType&) { return 0; } - - static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } -#endif // RAPIDJSON_SCHEMA_USE_STDREGEX - - void AddType(const ValueType& type) { - if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; - else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; - else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; - else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; - else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; - else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; - else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); - } - - bool CreateParallelValidator(Context& context) const { - if (enum_ || context.arrayUniqueness) - context.hasher = context.factory.CreateHasher(); - - if (validatorCount_) { - RAPIDJSON_ASSERT(context.validators == 0); - context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); - context.validatorCount = validatorCount_; - - if (allOf_.schemas) - CreateSchemaValidators(context, allOf_); - - if (anyOf_.schemas) - CreateSchemaValidators(context, anyOf_); - - if (oneOf_.schemas) - CreateSchemaValidators(context, oneOf_); - - if (not_) - context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); - - if (hasSchemaDependencies_) { - for (SizeType i = 0; i < propertyCount_; i++) - if (properties_[i].dependenciesSchema) - context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); - } - } - - return true; - } - - void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { - for (SizeType i = 0; i < schemas.count; i++) - context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); - } - - // O(n) - bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { - SizeType len = name.GetStringLength(); - const Ch* str = name.GetString(); - for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].name.GetStringLength() == len && - (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) - { - *outIndex = index; - return true; - } - return false; - } - - bool CheckInt(Context& context, int64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { - DisallowedType(context, GetIntegerString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - } - - if (!minimum_.IsNull()) { - if (minimum_.IsInt64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { - context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); - } - } - else if (minimum_.IsUint64()) { - context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() - } - else if (!CheckDoubleMinimum(context, static_cast(i))) - return false; - } - - if (!maximum_.IsNull()) { - if (maximum_.IsInt64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { - context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); - } - } - else if (maximum_.IsUint64()) { } - /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() - else if (!CheckDoubleMaximum(context, static_cast(i))) - return false; - } - - if (!multipleOf_.IsNull()) { - if (multipleOf_.IsUint64()) { - if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { - context.error_handler.NotMultipleOf(i, multipleOf_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); - } - } - else if (!CheckDoubleMultipleOf(context, static_cast(i))) - return false; - } - - return true; - } - - bool CheckUint(Context& context, uint64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { - DisallowedType(context, GetIntegerString()); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - } - - if (!minimum_.IsNull()) { - if (minimum_.IsUint64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { - context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); - } - } - else if (minimum_.IsInt64()) - /* do nothing */; // i >= 0 > minimum.Getint64() - else if (!CheckDoubleMinimum(context, static_cast(i))) - return false; - } - - if (!maximum_.IsNull()) { - if (maximum_.IsUint64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { - context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); - } - } - else if (maximum_.IsInt64()) { - context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ - } - else if (!CheckDoubleMaximum(context, static_cast(i))) - return false; - } - - if (!multipleOf_.IsNull()) { - if (multipleOf_.IsUint64()) { - if (i % multipleOf_.GetUint64() != 0) { - context.error_handler.NotMultipleOf(i, multipleOf_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); - } - } - else if (!CheckDoubleMultipleOf(context, static_cast(i))) - return false; - } - - return true; - } - - bool CheckDoubleMinimum(Context& context, double d) const { - if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { - context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); - } - return true; - } - - bool CheckDoubleMaximum(Context& context, double d) const { - if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { - context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); - } - return true; - } - - bool CheckDoubleMultipleOf(Context& context, double d) const { - double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); - double q = std::floor(a / b); - double r = a - q * b; - if (r > 0.0) { - context.error_handler.NotMultipleOf(d, multipleOf_); - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); - } - return true; - } - - void DisallowedType(Context& context, const ValueType& actualType) const { - ErrorHandler& eh = context.error_handler; - eh.StartDisallowedType(); - - if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); - if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); - if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); - if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); - if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); - - if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); - else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); - - eh.EndDisallowedType(actualType); - } - - struct Property { - Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} - ~Property() { AllocatorType::Free(dependencies); } - SValue name; - const SchemaType* schema; - const SchemaType* dependenciesSchema; - SizeType dependenciesValidatorIndex; - bool* dependencies; - bool required; - }; - - struct PatternProperty { - PatternProperty() : schema(), pattern() {} - ~PatternProperty() { - if (pattern) { - pattern->~RegexType(); - AllocatorType::Free(pattern); - } - } - const SchemaType* schema; - RegexType* pattern; - }; - - AllocatorType* allocator_; - SValue uri_; - PointerType pointer_; - const SchemaType* typeless_; - uint64_t* enum_; - SizeType enumCount_; - SchemaArray allOf_; - SchemaArray anyOf_; - SchemaArray oneOf_; - const SchemaType* not_; - unsigned type_; // bitmask of kSchemaType - SizeType validatorCount_; - SizeType notValidatorIndex_; - - Property* properties_; - const SchemaType* additionalPropertiesSchema_; - PatternProperty* patternProperties_; - SizeType patternPropertyCount_; - SizeType propertyCount_; - SizeType minProperties_; - SizeType maxProperties_; - bool additionalProperties_; - bool hasDependencies_; - bool hasRequired_; - bool hasSchemaDependencies_; - - const SchemaType* additionalItemsSchema_; - const SchemaType* itemsList_; - const SchemaType** itemsTuple_; - SizeType itemsTupleCount_; - SizeType minItems_; - SizeType maxItems_; - bool additionalItems_; - bool uniqueItems_; - - RegexType* pattern_; - SizeType minLength_; - SizeType maxLength_; - - SValue minimum_; - SValue maximum_; - SValue multipleOf_; - bool exclusiveMinimum_; - bool exclusiveMaximum_; - - SizeType defaultValueLength_; -}; - -template -struct TokenHelper { - RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { - *documentStack.template Push() = '/'; - char buffer[21]; - size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); - for (size_t i = 0; i < length; i++) - *documentStack.template Push() = static_cast(buffer[i]); - } -}; - -// Partial specialized version for char to prevent buffer copying. -template -struct TokenHelper { - RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { - if (sizeof(SizeType) == 4) { - char *buffer = documentStack.template Push(1 + 10); // '/' + uint - *buffer++ = '/'; - const char* end = internal::u32toa(index, buffer); - documentStack.template Pop(static_cast(10 - (end - buffer))); - } - else { - char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 - *buffer++ = '/'; - const char* end = internal::u64toa(index, buffer); - documentStack.template Pop(static_cast(20 - (end - buffer))); - } - } -}; - -} // namespace internal - -/////////////////////////////////////////////////////////////////////////////// -// IGenericRemoteSchemaDocumentProvider - -template -class IGenericRemoteSchemaDocumentProvider { -public: - typedef typename SchemaDocumentType::Ch Ch; - - virtual ~IGenericRemoteSchemaDocumentProvider() {} - virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; -}; - -/////////////////////////////////////////////////////////////////////////////// -// GenericSchemaDocument - -//! JSON schema document. -/*! - A JSON schema document is a compiled version of a JSON schema. - It is basically a tree of internal::Schema. - - \note This is an immutable class (i.e. its instance cannot be modified after construction). - \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. - \tparam Allocator Allocator type for allocating memory of this document. -*/ -template -class GenericSchemaDocument { -public: - typedef ValueT ValueType; - typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; - typedef Allocator AllocatorType; - typedef typename ValueType::EncodingType EncodingType; - typedef typename EncodingType::Ch Ch; - typedef internal::Schema SchemaType; - typedef GenericPointer PointerType; - typedef GenericValue URIType; - friend class internal::Schema; - template - friend class GenericSchemaValidator; - - //! Constructor. - /*! - Compile a JSON document into schema document. - - \param document A JSON document as source. - \param uri The base URI of this schema document for purposes of violation reporting. - \param uriLength Length of \c name, in code points. - \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. - \param allocator An optional allocator instance for allocating memory. Can be null. - */ - explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, - IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : - remoteProvider_(remoteProvider), - allocator_(allocator), - ownAllocator_(), - root_(), - typeless_(), - schemaMap_(allocator, kInitialSchemaMapSize), - schemaRef_(allocator, kInitialSchemaRefSize) - { - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); - - Ch noUri[1] = {0}; - uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); - - typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); - new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_); - - // Generate root schema, it will call CreateSchema() to create sub-schemas, - // And call AddRefSchema() if there are $ref. - CreateSchemaRecursive(&root_, PointerType(), document, document); - - // Resolve $ref - while (!schemaRef_.Empty()) { - SchemaRefEntry* refEntry = schemaRef_.template Pop(1); - if (const SchemaType* s = GetSchema(refEntry->target)) { - if (refEntry->schema) - *refEntry->schema = s; - - // Create entry in map if not exist - if (!GetSchema(refEntry->source)) { - new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); - } - } - else if (refEntry->schema) - *refEntry->schema = typeless_; - - refEntry->~SchemaRefEntry(); - } - - RAPIDJSON_ASSERT(root_ != 0); - - schemaRef_.ShrinkToFit(); // Deallocate all memory for ref - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move constructor in C++11 - GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : - remoteProvider_(rhs.remoteProvider_), - allocator_(rhs.allocator_), - ownAllocator_(rhs.ownAllocator_), - root_(rhs.root_), - typeless_(rhs.typeless_), - schemaMap_(std::move(rhs.schemaMap_)), - schemaRef_(std::move(rhs.schemaRef_)), - uri_(std::move(rhs.uri_)) - { - rhs.remoteProvider_ = 0; - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.typeless_ = 0; - } -#endif - - //! Destructor - ~GenericSchemaDocument() { - while (!schemaMap_.Empty()) - schemaMap_.template Pop(1)->~SchemaEntry(); - - if (typeless_) { - typeless_->~SchemaType(); - Allocator::Free(typeless_); - } - - RAPIDJSON_DELETE(ownAllocator_); - } - - const URIType& GetURI() const { return uri_; } - - //! Get the root schema. - const SchemaType& GetRoot() const { return *root_; } - -private: - //! Prohibit copying - GenericSchemaDocument(const GenericSchemaDocument&); - //! Prohibit assignment - GenericSchemaDocument& operator=(const GenericSchemaDocument&); - - struct SchemaRefEntry { - SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} - PointerType source; - PointerType target; - const SchemaType** schema; - }; - - struct SchemaEntry { - SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} - ~SchemaEntry() { - if (owned) { - schema->~SchemaType(); - Allocator::Free(schema); - } - } - PointerType pointer; - SchemaType* schema; - bool owned; - }; - - void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { - if (schema) - *schema = typeless_; - - if (v.GetType() == kObjectType) { - const SchemaType* s = GetSchema(pointer); - if (!s) - CreateSchema(schema, pointer, v, document); - - for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) - CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); - } - else if (v.GetType() == kArrayType) - for (SizeType i = 0; i < v.Size(); i++) - CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); - } - - void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { - RAPIDJSON_ASSERT(pointer.IsValid()); - if (v.IsObject()) { - if (!HandleRefSchema(pointer, schema, v, document)) { - SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); - new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); - if (schema) - *schema = s; - } - } - } - - bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { - static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; - static const ValueType kRefValue(kRefString, 4); - - typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); - if (itr == v.MemberEnd()) - return false; - - if (itr->value.IsString()) { - SizeType len = itr->value.GetStringLength(); - if (len > 0) { - const Ch* s = itr->value.GetString(); - SizeType i = 0; - while (i < len && s[i] != '#') // Find the first # - i++; - - if (i > 0) { // Remote reference, resolve immediately - if (remoteProvider_) { - if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { - PointerType pointer(&s[i], len - i, allocator_); - if (pointer.IsValid()) { - if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { - if (schema) - *schema = sc; - new (schemaMap_.template Push()) SchemaEntry(source, const_cast(sc), false, allocator_); - return true; - } - } - } - } - } - else if (s[i] == '#') { // Local reference, defer resolution - PointerType pointer(&s[i], len - i, allocator_); - if (pointer.IsValid()) { - if (const ValueType* nv = pointer.Get(document)) - if (HandleRefSchema(source, schema, *nv, document)) - return true; - - new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); - return true; - } - } - } - } - return false; - } - - const SchemaType* GetSchema(const PointerType& pointer) const { - for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) - if (pointer == target->pointer) - return target->schema; - return 0; - } - - PointerType GetPointer(const SchemaType* schema) const { - for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) - if (schema == target->schema) - return target->pointer; - return PointerType(); - } - - const SchemaType* GetTypeless() const { return typeless_; } - - static const size_t kInitialSchemaMapSize = 64; - static const size_t kInitialSchemaRefSize = 64; - - IRemoteSchemaDocumentProviderType* remoteProvider_; - Allocator *allocator_; - Allocator *ownAllocator_; - const SchemaType* root_; //!< Root schema. - SchemaType* typeless_; - internal::Stack schemaMap_; // Stores created Pointer -> Schemas - internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref - URIType uri_; -}; - -//! GenericSchemaDocument using Value type. -typedef GenericSchemaDocument SchemaDocument; -//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. -typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; - -/////////////////////////////////////////////////////////////////////////////// -// GenericSchemaValidator - -//! JSON Schema Validator. -/*! - A SAX style JSON schema validator. - It uses a \c GenericSchemaDocument to validate SAX events. - It delegates the incoming SAX events to an output handler. - The default output handler does nothing. - It can be reused multiple times by calling \c Reset(). - - \tparam SchemaDocumentType Type of schema document. - \tparam OutputHandler Type of output handler. Default handler does nothing. - \tparam StateAllocator Allocator for storing the internal validation states. -*/ -template < - typename SchemaDocumentType, - typename OutputHandler = BaseReaderHandler, - typename StateAllocator = CrtAllocator> -class GenericSchemaValidator : - public internal::ISchemaStateFactory, - public internal::ISchemaValidator, - public internal::IValidationErrorHandler -{ -public: - typedef typename SchemaDocumentType::SchemaType SchemaType; - typedef typename SchemaDocumentType::PointerType PointerType; - typedef typename SchemaType::EncodingType EncodingType; - typedef typename SchemaType::SValue SValue; - typedef typename EncodingType::Ch Ch; - typedef GenericStringRef StringRefType; - typedef GenericValue ValueType; - - //! Constructor without output handler. - /*! - \param schemaDocument The schema document to conform to. - \param allocator Optional allocator for storing internal validation states. - \param schemaStackCapacity Optional initial capacity of schema path stack. - \param documentStackCapacity Optional initial capacity of document path stack. - */ - GenericSchemaValidator( - const SchemaDocumentType& schemaDocument, - StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity, - size_t documentStackCapacity = kDefaultDocumentStackCapacity) - : - schemaDocument_(&schemaDocument), - root_(schemaDocument.GetRoot()), - stateAllocator_(allocator), - ownStateAllocator_(0), - schemaStack_(allocator, schemaStackCapacity), - documentStack_(allocator, documentStackCapacity), - outputHandler_(0), - error_(kObjectType), - currentError_(), - missingDependents_(), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(0) -#endif - { - } - - //! Constructor with output handler. - /*! - \param schemaDocument The schema document to conform to. - \param allocator Optional allocator for storing internal validation states. - \param schemaStackCapacity Optional initial capacity of schema path stack. - \param documentStackCapacity Optional initial capacity of document path stack. - */ - GenericSchemaValidator( - const SchemaDocumentType& schemaDocument, - OutputHandler& outputHandler, - StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity, - size_t documentStackCapacity = kDefaultDocumentStackCapacity) - : - schemaDocument_(&schemaDocument), - root_(schemaDocument.GetRoot()), - stateAllocator_(allocator), - ownStateAllocator_(0), - schemaStack_(allocator, schemaStackCapacity), - documentStack_(allocator, documentStackCapacity), - outputHandler_(&outputHandler), - error_(kObjectType), - currentError_(), - missingDependents_(), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(0) -#endif - { - } - - //! Destructor. - ~GenericSchemaValidator() { - Reset(); - RAPIDJSON_DELETE(ownStateAllocator_); - } - - //! Reset the internal states. - void Reset() { - while (!schemaStack_.Empty()) - PopSchema(); - documentStack_.Clear(); - error_.SetObject(); - currentError_.SetNull(); - missingDependents_.SetNull(); - valid_ = true; - } - - //! Checks whether the current state is valid. - // Implementation of ISchemaValidator - virtual bool IsValid() const { return valid_; } - - //! Gets the error object. - ValueType& GetError() { return error_; } - const ValueType& GetError() const { return error_; } - - //! Gets the JSON pointer pointed to the invalid schema. - PointerType GetInvalidSchemaPointer() const { - return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); - } - - //! Gets the keyword of invalid schema. - const Ch* GetInvalidSchemaKeyword() const { - return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; - } - - //! Gets the JSON pointer pointed to the invalid value. - PointerType GetInvalidDocumentPointer() const { - if (documentStack_.Empty()) { - return PointerType(); - } - else { - return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); - } - } - - void NotMultipleOf(int64_t actual, const SValue& expected) { - AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); - } - void NotMultipleOf(uint64_t actual, const SValue& expected) { - AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); - } - void NotMultipleOf(double actual, const SValue& expected) { - AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); - } - void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { - AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, - exclusive ? &SchemaType::GetExclusiveMaximumString : 0); - } - void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { - AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, - exclusive ? &SchemaType::GetExclusiveMaximumString : 0); - } - void AboveMaximum(double actual, const SValue& expected, bool exclusive) { - AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, - exclusive ? &SchemaType::GetExclusiveMaximumString : 0); - } - void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { - AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, - exclusive ? &SchemaType::GetExclusiveMinimumString : 0); - } - void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { - AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, - exclusive ? &SchemaType::GetExclusiveMinimumString : 0); - } - void BelowMinimum(double actual, const SValue& expected, bool exclusive) { - AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, - exclusive ? &SchemaType::GetExclusiveMinimumString : 0); - } - - void TooLong(const Ch* str, SizeType length, SizeType expected) { - AddNumberError(SchemaType::GetMaxLengthString(), - ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); - } - void TooShort(const Ch* str, SizeType length, SizeType expected) { - AddNumberError(SchemaType::GetMinLengthString(), - ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); - } - void DoesNotMatch(const Ch* str, SizeType length) { - currentError_.SetObject(); - currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); - AddCurrentError(SchemaType::GetPatternString()); - } - - void DisallowedItem(SizeType index) { - currentError_.SetObject(); - currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); - AddCurrentError(SchemaType::GetAdditionalItemsString(), true); - } - void TooFewItems(SizeType actualCount, SizeType expectedCount) { - AddNumberError(SchemaType::GetMinItemsString(), - ValueType(actualCount).Move(), SValue(expectedCount).Move()); - } - void TooManyItems(SizeType actualCount, SizeType expectedCount) { - AddNumberError(SchemaType::GetMaxItemsString(), - ValueType(actualCount).Move(), SValue(expectedCount).Move()); - } - void DuplicateItems(SizeType index1, SizeType index2) { - ValueType duplicates(kArrayType); - duplicates.PushBack(index1, GetStateAllocator()); - duplicates.PushBack(index2, GetStateAllocator()); - currentError_.SetObject(); - currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); - AddCurrentError(SchemaType::GetUniqueItemsString(), true); - } - - void TooManyProperties(SizeType actualCount, SizeType expectedCount) { - AddNumberError(SchemaType::GetMaxPropertiesString(), - ValueType(actualCount).Move(), SValue(expectedCount).Move()); - } - void TooFewProperties(SizeType actualCount, SizeType expectedCount) { - AddNumberError(SchemaType::GetMinPropertiesString(), - ValueType(actualCount).Move(), SValue(expectedCount).Move()); - } - void StartMissingProperties() { - currentError_.SetArray(); - } - void AddMissingProperty(const SValue& name) { - currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); - } - bool EndMissingProperties() { - if (currentError_.Empty()) - return false; - ValueType error(kObjectType); - error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); - currentError_ = error; - AddCurrentError(SchemaType::GetRequiredString()); - return true; - } - void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { - for (SizeType i = 0; i < count; ++i) - MergeError(static_cast(subvalidators[i])->GetError()); - } - void DisallowedProperty(const Ch* name, SizeType length) { - currentError_.SetObject(); - currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); - AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true); - } - - void StartDependencyErrors() { - currentError_.SetObject(); - } - void StartMissingDependentProperties() { - missingDependents_.SetArray(); - } - void AddMissingDependentProperty(const SValue& targetName) { - missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); - } - void EndMissingDependentProperties(const SValue& sourceName) { - if (!missingDependents_.Empty()) - currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), - missingDependents_, GetStateAllocator()); - } - void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { - currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), - static_cast(subvalidator)->GetError(), GetStateAllocator()); - } - bool EndDependencyErrors() { - if (currentError_.ObjectEmpty()) - return false; - ValueType error(kObjectType); - error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); - currentError_ = error; - AddCurrentError(SchemaType::GetDependenciesString()); - return true; - } - - void DisallowedValue() { - currentError_.SetObject(); - AddCurrentError(SchemaType::GetEnumString()); - } - void StartDisallowedType() { - currentError_.SetArray(); - } - void AddExpectedType(const typename SchemaType::ValueType& expectedType) { - currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); - } - void EndDisallowedType(const typename SchemaType::ValueType& actualType) { - ValueType error(kObjectType); - error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); - error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); - currentError_ = error; - AddCurrentError(SchemaType::GetTypeString()); - } - void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { - for (SizeType i = 0; i < count; ++i) { - MergeError(static_cast(subvalidators[i])->GetError()); - } - } - void NoneOf(ISchemaValidator** subvalidators, SizeType count) { - AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count); - } - void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { - AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count); - } - void Disallowed() { - currentError_.SetObject(); - AddCurrentError(SchemaType::GetNotString()); - } - -#define RAPIDJSON_STRING_(name, ...) \ - static const StringRefType& Get##name##String() {\ - static const Ch s[] = { __VA_ARGS__, '\0' };\ - static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ - return v;\ - } - - RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') - RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') - RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') - RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') - RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') - RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') - RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') - RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') - -#undef RAPIDJSON_STRING_ - -#if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ -RAPIDJSON_MULTILINEMACRO_BEGIN\ - *documentStack_.template Push() = '\0';\ - documentStack_.template Pop(1);\ - internal::PrintInvalidDocument(documentStack_.template Bottom());\ -RAPIDJSON_MULTILINEMACRO_END -#else -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() -#endif - -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ - if (!valid_) return false; \ - if (!BeginValue() || !CurrentSchema().method arg1) {\ - RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ - return valid_ = false;\ - } - -#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ - for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ - if (context->hasher)\ - static_cast(context->hasher)->method arg2;\ - if (context->validators)\ - for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ - static_cast(context->validators[i_])->method arg2;\ - if (context->patternPropertiesValidators)\ - for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ - static_cast(context->patternPropertiesValidators[i_])->method arg2;\ - } - -#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ - return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) - -#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ - RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ - RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) - - bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } - bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } - bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } - bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } - bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } - bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } - bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } - bool RawNumber(const Ch* str, SizeType length, bool copy) - { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } - bool String(const Ch* str, SizeType length, bool copy) - { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } - - bool StartObject() { - RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); - return valid_ = !outputHandler_ || outputHandler_->StartObject(); - } - - bool Key(const Ch* str, SizeType len, bool copy) { - if (!valid_) return false; - AppendToken(str, len); - if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); - return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); - } - - bool EndObject(SizeType memberCount) { - if (!valid_) return false; - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); - if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; - RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); - } - - bool StartArray() { - RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); - return valid_ = !outputHandler_ || outputHandler_->StartArray(); - } - - bool EndArray(SizeType elementCount) { - if (!valid_) return false; - RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); - if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; - RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); - } - -#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ -#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ -#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ -#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ - - // Implementation of ISchemaStateFactory - virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { - return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), -#if RAPIDJSON_SCHEMA_VERBOSE - depth_ + 1, -#endif - &GetStateAllocator()); - } - - virtual void DestroySchemaValidator(ISchemaValidator* validator) { - GenericSchemaValidator* v = static_cast(validator); - v->~GenericSchemaValidator(); - StateAllocator::Free(v); - } - - virtual void* CreateHasher() { - return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); - } - - virtual uint64_t GetHashCode(void* hasher) { - return static_cast(hasher)->GetHashCode(); - } - - virtual void DestroryHasher(void* hasher) { - HasherType* h = static_cast(hasher); - h->~HasherType(); - StateAllocator::Free(h); - } - - virtual void* MallocState(size_t size) { - return GetStateAllocator().Malloc(size); - } - - virtual void FreeState(void* p) { - StateAllocator::Free(p); - } - -private: - typedef typename SchemaType::Context Context; - typedef GenericValue, StateAllocator> HashCodeArray; - typedef internal::Hasher HasherType; - - GenericSchemaValidator( - const SchemaDocumentType& schemaDocument, - const SchemaType& root, - const char* basePath, size_t basePathSize, -#if RAPIDJSON_SCHEMA_VERBOSE - unsigned depth, -#endif - StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity, - size_t documentStackCapacity = kDefaultDocumentStackCapacity) - : - schemaDocument_(&schemaDocument), - root_(root), - stateAllocator_(allocator), - ownStateAllocator_(0), - schemaStack_(allocator, schemaStackCapacity), - documentStack_(allocator, documentStackCapacity), - outputHandler_(0), - error_(kObjectType), - currentError_(), - missingDependents_(), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(depth) -#endif - { - if (basePath && basePathSize) - memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); - } - - StateAllocator& GetStateAllocator() { - if (!stateAllocator_) - stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); - return *stateAllocator_; - } - - bool BeginValue() { - if (schemaStack_.Empty()) - PushSchema(root_); - else { - if (CurrentContext().inArray) - internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); - - if (!CurrentSchema().BeginValue(CurrentContext())) - return false; - - SizeType count = CurrentContext().patternPropertiesSchemaCount; - const SchemaType** sa = CurrentContext().patternPropertiesSchemas; - typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; - bool valueUniqueness = CurrentContext().valueUniqueness; - RAPIDJSON_ASSERT(CurrentContext().valueSchema); - PushSchema(*CurrentContext().valueSchema); - - if (count > 0) { - CurrentContext().objectPatternValidatorType = patternValidatorType; - ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; - SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; - va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); - for (SizeType i = 0; i < count; i++) - va[validatorCount++] = CreateSchemaValidator(*sa[i]); - } - - CurrentContext().arrayUniqueness = valueUniqueness; - } - return true; - } - - bool EndValue() { - if (!CurrentSchema().EndValue(CurrentContext())) - return false; - -#if RAPIDJSON_SCHEMA_VERBOSE - GenericStringBuffer sb; - schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); - - *documentStack_.template Push() = '\0'; - documentStack_.template Pop(1); - internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); -#endif - - uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; - - PopSchema(); - - if (!schemaStack_.Empty()) { - Context& context = CurrentContext(); - if (context.valueUniqueness) { - HashCodeArray* a = static_cast(context.arrayElementHashCodes); - if (!a) - CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); - for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) - if (itr->GetUint64() == h) { - DuplicateItems(static_cast(itr - a->Begin()), a->Size()); - RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); - } - a->PushBack(h, GetStateAllocator()); - } - } - - // Remove the last token of document pointer - while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') - ; - - return true; - } - - void AppendToken(const Ch* str, SizeType len) { - documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters - *documentStack_.template PushUnsafe() = '/'; - for (SizeType i = 0; i < len; i++) { - if (str[i] == '~') { - *documentStack_.template PushUnsafe() = '~'; - *documentStack_.template PushUnsafe() = '0'; - } - else if (str[i] == '/') { - *documentStack_.template PushUnsafe() = '~'; - *documentStack_.template PushUnsafe() = '1'; - } - else - *documentStack_.template PushUnsafe() = str[i]; - } - } - - RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema); } - - RAPIDJSON_FORCEINLINE void PopSchema() { - Context* c = schemaStack_.template Pop(1); - if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { - a->~HashCodeArray(); - StateAllocator::Free(a); - } - c->~Context(); - } - - void AddErrorLocation(ValueType& result, bool parent) { - GenericStringBuffer sb; - PointerType instancePointer = GetInvalidDocumentPointer(); - ((parent && instancePointer.GetTokenCount() > 0) - ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) - : instancePointer).StringifyUriFragment(sb); - ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), - GetStateAllocator()); - result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); - sb.Clear(); - memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()), - CurrentSchema().GetURI().GetString(), - CurrentSchema().GetURI().GetStringLength() * sizeof(Ch)); - GetInvalidSchemaPointer().StringifyUriFragment(sb); - ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), - GetStateAllocator()); - result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); - } - - void AddError(ValueType& keyword, ValueType& error) { - typename ValueType::MemberIterator member = error_.FindMember(keyword); - if (member == error_.MemberEnd()) - error_.AddMember(keyword, error, GetStateAllocator()); - else { - if (member->value.IsObject()) { - ValueType errors(kArrayType); - errors.PushBack(member->value, GetStateAllocator()); - member->value = errors; - } - member->value.PushBack(error, GetStateAllocator()); - } - } - - void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) { - AddErrorLocation(currentError_, parent); - AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_); - } - - void MergeError(ValueType& other) { - for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { - AddError(it->name, it->value); - } - } - - void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected, - const typename SchemaType::ValueType& (*exclusive)() = 0) { - currentError_.SetObject(); - currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); - currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); - if (exclusive) - currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); - AddCurrentError(keyword); - } - - void AddErrorArray(const typename SchemaType::ValueType& keyword, - ISchemaValidator** subvalidators, SizeType count) { - ValueType errors(kArrayType); - for (SizeType i = 0; i < count; ++i) - errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); - currentError_.SetObject(); - currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); - AddCurrentError(keyword); - } - - const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } - Context& CurrentContext() { return *schemaStack_.template Top(); } - const Context& CurrentContext() const { return *schemaStack_.template Top(); } - - static const size_t kDefaultSchemaStackCapacity = 1024; - static const size_t kDefaultDocumentStackCapacity = 256; - const SchemaDocumentType* schemaDocument_; - const SchemaType& root_; - StateAllocator* stateAllocator_; - StateAllocator* ownStateAllocator_; - internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) - internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) - OutputHandler* outputHandler_; - ValueType error_; - ValueType currentError_; - ValueType missingDependents_; - bool valid_; -#if RAPIDJSON_SCHEMA_VERBOSE - unsigned depth_; -#endif -}; - -typedef GenericSchemaValidator SchemaValidator; - -/////////////////////////////////////////////////////////////////////////////// -// SchemaValidatingReader - -//! A helper class for parsing with validation. -/*! - This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). - - \tparam parseFlags Combination of \ref ParseFlag. - \tparam InputStream Type of input stream, implementing Stream concept. - \tparam SourceEncoding Encoding of the input stream. - \tparam SchemaDocumentType Type of schema document. - \tparam StackAllocator Allocator type for stack. -*/ -template < - unsigned parseFlags, - typename InputStream, - typename SourceEncoding, - typename SchemaDocumentType = SchemaDocument, - typename StackAllocator = CrtAllocator> -class SchemaValidatingReader { -public: - typedef typename SchemaDocumentType::PointerType PointerType; - typedef typename InputStream::Ch Ch; - typedef GenericValue ValueType; - - //! Constructor - /*! - \param is Input stream. - \param sd Schema document. - */ - SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {} - - template - bool operator()(Handler& handler) { - GenericReader reader; - GenericSchemaValidator validator(sd_, handler); - parseResult_ = reader.template Parse(is_, validator); - - isValid_ = validator.IsValid(); - if (isValid_) { - invalidSchemaPointer_ = PointerType(); - invalidSchemaKeyword_ = 0; - invalidDocumentPointer_ = PointerType(); - error_.SetObject(); - } - else { - invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); - invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); - invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); - error_.CopyFrom(validator.GetError(), allocator_); - } - - return parseResult_; - } - - const ParseResult& GetParseResult() const { return parseResult_; } - bool IsValid() const { return isValid_; } - const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } - const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } - const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } - const ValueType& GetError() const { return error_; } - -private: - InputStream& is_; - const SchemaDocumentType& sd_; - - ParseResult parseResult_; - PointerType invalidSchemaPointer_; - const Ch* invalidSchemaKeyword_; - PointerType invalidDocumentPointer_; - StackAllocator allocator_; - ValueType error_; - bool isValid_; -}; - -RAPIDJSON_NAMESPACE_END -RAPIDJSON_DIAG_POP - -#endif // RAPIDJSON_SCHEMA_H_ diff --git a/include/rapidjson/stream.h b/include/rapidjson/stream.h deleted file mode 100755 index 1fd70915..00000000 --- a/include/rapidjson/stream.h +++ /dev/null @@ -1,223 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#include "rapidjson.h" - -#ifndef RAPIDJSON_STREAM_H_ -#define RAPIDJSON_STREAM_H_ - -#include "encodings.h" - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Stream - -/*! \class rapidjson::Stream - \brief Concept for reading and writing characters. - - For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). - - For write-only stream, only need to implement Put() and Flush(). - -\code -concept Stream { - typename Ch; //!< Character type of the stream. - - //! Read the current character from stream without moving the read cursor. - Ch Peek() const; - - //! Read the current character from stream and moving the read cursor to next character. - Ch Take(); - - //! Get the current read cursor. - //! \return Number of characters read from start. - size_t Tell(); - - //! Begin writing operation at the current read pointer. - //! \return The begin writer pointer. - Ch* PutBegin(); - - //! Write a character. - void Put(Ch c); - - //! Flush the buffer. - void Flush(); - - //! End the writing operation. - //! \param begin The begin write pointer returned by PutBegin(). - //! \return Number of characters written. - size_t PutEnd(Ch* begin); -} -\endcode -*/ - -//! Provides additional information for stream. -/*! - By using traits pattern, this type provides a default configuration for stream. - For custom stream, this type can be specialized for other configuration. - See TEST(Reader, CustomStringStream) in readertest.cpp for example. -*/ -template -struct StreamTraits { - //! Whether to make local copy of stream for optimization during parsing. - /*! - By default, for safety, streams do not use local copy optimization. - Stream that can be copied fast should specialize this, like StreamTraits. - */ - enum { copyOptimization = 0 }; -}; - -//! Reserve n characters for writing to a stream. -template -inline void PutReserve(Stream& stream, size_t count) { - (void)stream; - (void)count; -} - -//! Write character to a stream, presuming buffer is reserved. -template -inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { - stream.Put(c); -} - -//! Put N copies of a character to a stream. -template -inline void PutN(Stream& stream, Ch c, size_t n) { - PutReserve(stream, n); - for (size_t i = 0; i < n; i++) - PutUnsafe(stream, c); -} - -/////////////////////////////////////////////////////////////////////////////// -// GenericStreamWrapper - -//! A Stream Wrapper -/*! \tThis string stream is a wrapper for any stream by just forwarding any - \treceived message to the origin stream. - \note implements Stream concept -*/ - -#if defined(_MSC_VER) && _MSC_VER <= 1800 -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4702) // unreachable code -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - -template > -class GenericStreamWrapper { -public: - typedef typename Encoding::Ch Ch; - GenericStreamWrapper(InputStream& is): is_(is) {} - - Ch Peek() const { return is_.Peek(); } - Ch Take() { return is_.Take(); } - size_t Tell() { return is_.Tell(); } - Ch* PutBegin() { return is_.PutBegin(); } - void Put(Ch ch) { is_.Put(ch); } - void Flush() { is_.Flush(); } - size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } - - // wrapper for MemoryStream - const Ch* Peek4() const { return is_.Peek4(); } - - // wrapper for AutoUTFInputStream - UTFType GetType() const { return is_.GetType(); } - bool HasBOM() const { return is_.HasBOM(); } - -protected: - InputStream& is_; -}; - -#if defined(_MSC_VER) && _MSC_VER <= 1800 -RAPIDJSON_DIAG_POP -#endif - -/////////////////////////////////////////////////////////////////////////////// -// StringStream - -//! Read-only string stream. -/*! \note implements Stream concept -*/ -template -struct GenericStringStream { - typedef typename Encoding::Ch Ch; - - GenericStringStream(const Ch *src) : src_(src), head_(src) {} - - Ch Peek() const { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() const { return static_cast(src_ - head_); } - - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - const Ch* src_; //!< Current read position. - const Ch* head_; //!< Original head of the string. -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! String stream with UTF8 encoding. -typedef GenericStringStream > StringStream; - -/////////////////////////////////////////////////////////////////////////////// -// InsituStringStream - -//! A read-write string stream. -/*! This string stream is particularly designed for in-situ parsing. - \note implements Stream concept -*/ -template -struct GenericInsituStringStream { - typedef typename Encoding::Ch Ch; - - GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} - - // Read - Ch Peek() { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() { return static_cast(src_ - head_); } - - // Write - void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } - - Ch* PutBegin() { return dst_ = src_; } - size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } - void Flush() {} - - Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } - void Pop(size_t count) { dst_ -= count; } - - Ch* src_; - Ch* dst_; - Ch* head_; -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! Insitu string stream with UTF8 encoding. -typedef GenericInsituStringStream > InsituStringStream; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_STREAM_H_ diff --git a/include/rapidjson/stringbuffer.h b/include/rapidjson/stringbuffer.h deleted file mode 100755 index 82ad3ca6..00000000 --- a/include/rapidjson/stringbuffer.h +++ /dev/null @@ -1,121 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_STRINGBUFFER_H_ -#define RAPIDJSON_STRINGBUFFER_H_ - -#include "stream.h" -#include "internal/stack.h" - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS -#include // std::move -#endif - -#include "internal/stack.h" - -#if defined(__clang__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(c++98-compat) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Represents an in-memory output stream. -/*! - \tparam Encoding Encoding of the stream. - \tparam Allocator type for allocating memory buffer. - \note implements Stream concept -*/ -template -class GenericStringBuffer { -public: - typedef typename Encoding::Ch Ch; - - GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} - GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { - if (&rhs != this) - stack_ = std::move(rhs.stack_); - return *this; - } -#endif - - void Put(Ch c) { *stack_.template Push() = c; } - void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } - void Flush() {} - - void Clear() { stack_.Clear(); } - void ShrinkToFit() { - // Push and pop a null terminator. This is safe. - *stack_.template Push() = '\0'; - stack_.ShrinkToFit(); - stack_.template Pop(1); - } - - void Reserve(size_t count) { stack_.template Reserve(count); } - Ch* Push(size_t count) { return stack_.template Push(count); } - Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } - void Pop(size_t count) { stack_.template Pop(count); } - - const Ch* GetString() const { - // Push and pop a null terminator. This is safe. - *stack_.template Push() = '\0'; - stack_.template Pop(1); - - return stack_.template Bottom(); - } - - //! Get the size of string in bytes in the string buffer. - size_t GetSize() const { return stack_.GetSize(); } - - //! Get the length of string in Ch in the string buffer. - size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } - - static const size_t kDefaultCapacity = 256; - mutable internal::Stack stack_; - -private: - // Prohibit copy constructor & assignment operator. - GenericStringBuffer(const GenericStringBuffer&); - GenericStringBuffer& operator=(const GenericStringBuffer&); -}; - -//! String buffer with UTF8 encoding -typedef GenericStringBuffer > StringBuffer; - -template -inline void PutReserve(GenericStringBuffer& stream, size_t count) { - stream.Reserve(count); -} - -template -inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { - stream.PutUnsafe(c); -} - -//! Implement specialized version of PutN() with memset() for better performance. -template<> -inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { - std::memset(stream.stack_.Push(n), c, n * sizeof(c)); -} - -RAPIDJSON_NAMESPACE_END - -#if defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h deleted file mode 100755 index 8b389219..00000000 --- a/include/rapidjson/writer.h +++ /dev/null @@ -1,710 +0,0 @@ -// Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// -// Licensed under the MIT License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// http://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. - -#ifndef RAPIDJSON_WRITER_H_ -#define RAPIDJSON_WRITER_H_ - -#include "stream.h" -#include "internal/clzll.h" -#include "internal/meta.h" -#include "internal/stack.h" -#include "internal/strfunc.h" -#include "internal/dtoa.h" -#include "internal/itoa.h" -#include "stringbuffer.h" -#include // placement new - -#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) -#include -#pragma intrinsic(_BitScanForward) -#endif -#ifdef RAPIDJSON_SSE42 -#include -#elif defined(RAPIDJSON_SSE2) -#include -#elif defined(RAPIDJSON_NEON) -#include -#endif - -#ifdef __clang__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(padded) -RAPIDJSON_DIAG_OFF(unreachable-code) -RAPIDJSON_DIAG_OFF(c++98-compat) -#elif defined(_MSC_VER) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// WriteFlag - -/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS - \ingroup RAPIDJSON_CONFIG - \brief User-defined kWriteDefaultFlags definition. - - User can define this as any \c WriteFlag combinations. -*/ -#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS -#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags -#endif - -//! Combination of writeFlags -enum WriteFlag { - kWriteNoFlags = 0, //!< No flags are set. - kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. - kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. - kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS -}; - -//! JSON writer -/*! Writer implements the concept Handler. - It generates JSON text by events to an output os. - - User may programmatically calls the functions of a writer to generate JSON text. - - On the other side, a writer can also be passed to objects that generates events, - - for example Reader::Parse() and Document::Accept(). - - \tparam OutputStream Type of output stream. - \tparam SourceEncoding Encoding of source string. - \tparam TargetEncoding Encoding of output stream. - \tparam StackAllocator Type of allocator for allocating memory of stack. - \note implements Handler concept -*/ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> -class Writer { -public: - typedef typename SourceEncoding::Ch Ch; - - static const int kDefaultMaxDecimalPlaces = 324; - - //! Constructor - /*! \param os Output stream. - \param stackAllocator User supplied allocator. If it is null, it will create a private one. - \param levelDepth Initial capacity of stack. - */ - explicit - Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} - - explicit - Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - Writer(Writer&& rhs) : - os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { - rhs.os_ = 0; - } -#endif - - //! Reset the writer with a new stream. - /*! - This function reset the writer with a new stream and default settings, - in order to make a Writer object reusable for output multiple JSONs. - - \param os New output stream. - \code - Writer writer(os1); - writer.StartObject(); - // ... - writer.EndObject(); - - writer.Reset(os2); - writer.StartObject(); - // ... - writer.EndObject(); - \endcode - */ - void Reset(OutputStream& os) { - os_ = &os; - hasRoot_ = false; - level_stack_.Clear(); - } - - //! Checks whether the output is a complete JSON. - /*! - A complete JSON has a complete root object or array. - */ - bool IsComplete() const { - return hasRoot_ && level_stack_.Empty(); - } - - int GetMaxDecimalPlaces() const { - return maxDecimalPlaces_; - } - - //! Sets the maximum number of decimal places for double output. - /*! - This setting truncates the output with specified number of decimal places. - - For example, - - \code - writer.SetMaxDecimalPlaces(3); - writer.StartArray(); - writer.Double(0.12345); // "0.123" - writer.Double(0.0001); // "0.0" - writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) - writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) - writer.EndArray(); - \endcode - - The default setting does not truncate any decimal places. You can restore to this setting by calling - \code - writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); - \endcode - */ - void SetMaxDecimalPlaces(int maxDecimalPlaces) { - maxDecimalPlaces_ = maxDecimalPlaces; - } - - /*!@name Implementation of Handler - \see Handler - */ - //@{ - - bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } - bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } - bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } - bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } - bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } - bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } - - //! Writes the given \c double value to the stream - /*! - \param d The value to be written. - \return Whether it is succeed. - */ - bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } - - bool RawNumber(const Ch* str, SizeType length, bool copy = false) { - RAPIDJSON_ASSERT(str != 0); - (void)copy; - Prefix(kNumberType); - return EndValue(WriteString(str, length)); - } - - bool String(const Ch* str, SizeType length, bool copy = false) { - RAPIDJSON_ASSERT(str != 0); - (void)copy; - Prefix(kStringType); - return EndValue(WriteString(str, length)); - } - -#if RAPIDJSON_HAS_STDSTRING - bool String(const std::basic_string& str) { - return String(str.data(), SizeType(str.size())); - } -#endif - - bool StartObject() { - Prefix(kObjectType); - new (level_stack_.template Push()) Level(false); - return WriteStartObject(); - } - - bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } - -#if RAPIDJSON_HAS_STDSTRING - bool Key(const std::basic_string& str) - { - return Key(str.data(), SizeType(str.size())); - } -#endif - - bool EndObject(SizeType memberCount = 0) { - (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object - RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value - level_stack_.template Pop(1); - return EndValue(WriteEndObject()); - } - - bool StartArray() { - Prefix(kArrayType); - new (level_stack_.template Push()) Level(true); - return WriteStartArray(); - } - - bool EndArray(SizeType elementCount = 0) { - (void)elementCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); - level_stack_.template Pop(1); - return EndValue(WriteEndArray()); - } - //@} - - /*! @name Convenience extensions */ - //@{ - - //! Simpler but slower overload. - bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } - - //@} - - //! Write a raw JSON value. - /*! - For user to write a stringified JSON as a value. - - \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. - \param length Length of the json. - \param type Type of the root of json. - */ - bool RawValue(const Ch* json, size_t length, Type type) { - RAPIDJSON_ASSERT(json != 0); - Prefix(type); - return EndValue(WriteRawValue(json, length)); - } - - //! Flush the output stream. - /*! - Allows the user to flush the output stream immediately. - */ - void Flush() { - os_->Flush(); - } - - static const size_t kDefaultLevelDepth = 32; - -protected: - //! Information for each nested level - struct Level { - Level(bool inArray_) : valueCount(0), inArray(inArray_) {} - size_t valueCount; //!< number of values in this level - bool inArray; //!< true if in array, otherwise in object - }; - - bool WriteNull() { - PutReserve(*os_, 4); - PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; - } - - bool WriteBool(bool b) { - if (b) { - PutReserve(*os_, 4); - PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); - } - else { - PutReserve(*os_, 5); - PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); - } - return true; - } - - bool WriteInt(int i) { - char buffer[11]; - const char* end = internal::i32toa(i, buffer); - PutReserve(*os_, static_cast(end - buffer)); - for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); - return true; - } - - bool WriteUint(unsigned u) { - char buffer[10]; - const char* end = internal::u32toa(u, buffer); - PutReserve(*os_, static_cast(end - buffer)); - for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); - return true; - } - - bool WriteInt64(int64_t i64) { - char buffer[21]; - const char* end = internal::i64toa(i64, buffer); - PutReserve(*os_, static_cast(end - buffer)); - for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); - return true; - } - - bool WriteUint64(uint64_t u64) { - char buffer[20]; - char* end = internal::u64toa(u64, buffer); - PutReserve(*os_, static_cast(end - buffer)); - for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); - return true; - } - - bool WriteDouble(double d) { - if (internal::Double(d).IsNanOrInf()) { - if (!(writeFlags & kWriteNanAndInfFlag)) - return false; - if (internal::Double(d).IsNan()) { - PutReserve(*os_, 3); - PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); - return true; - } - if (internal::Double(d).Sign()) { - PutReserve(*os_, 9); - PutUnsafe(*os_, '-'); - } - else - PutReserve(*os_, 8); - PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); - PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); - return true; - } - - char buffer[25]; - char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); - PutReserve(*os_, static_cast(end - buffer)); - for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); - return true; - } - - bool WriteString(const Ch* str, SizeType length) { - static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - static const char escape[256] = { -#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - //0 1 2 3 4 5 6 7 8 9 A B C D E F - 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 - 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 - 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 - Z16, Z16, // 30~4F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 - Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF -#undef Z16 - }; - - if (TargetEncoding::supportUnicode) - PutReserve(*os_, 2 + length * 6); // "\uxxxx..." - else - PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." - - PutUnsafe(*os_, '\"'); - GenericStringStream is(str); - while (ScanWriteUnescapedString(is, length)) { - const Ch c = is.Peek(); - if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { - // Unicode escaping - unsigned codepoint; - if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) - return false; - PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, 'u'); - if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { - PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); - PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); - PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); - PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); - } - else { - RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); - // Surrogate pair - unsigned s = codepoint - 0x010000; - unsigned lead = (s >> 10) + 0xD800; - unsigned trail = (s & 0x3FF) + 0xDC00; - PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); - PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); - PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); - PutUnsafe(*os_, hexDigits[(lead ) & 15]); - PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, 'u'); - PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); - PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); - PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); - PutUnsafe(*os_, hexDigits[(trail ) & 15]); - } - } - else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { - is.Take(); - PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, static_cast(escape[static_cast(c)])); - if (escape[static_cast(c)] == 'u') { - PutUnsafe(*os_, '0'); - PutUnsafe(*os_, '0'); - PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); - PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); - } - } - else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? - Transcoder::Validate(is, *os_) : - Transcoder::TranscodeUnsafe(is, *os_)))) - return false; - } - PutUnsafe(*os_, '\"'); - return true; - } - - bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { - return RAPIDJSON_LIKELY(is.Tell() < length); - } - - bool WriteStartObject() { os_->Put('{'); return true; } - bool WriteEndObject() { os_->Put('}'); return true; } - bool WriteStartArray() { os_->Put('['); return true; } - bool WriteEndArray() { os_->Put(']'); return true; } - - bool WriteRawValue(const Ch* json, size_t length) { - PutReserve(*os_, length); - GenericStringStream is(json); - while (RAPIDJSON_LIKELY(is.Tell() < length)) { - RAPIDJSON_ASSERT(is.Peek() != '\0'); - if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? - Transcoder::Validate(is, *os_) : - Transcoder::TranscodeUnsafe(is, *os_)))) - return false; - } - return true; - } - - void Prefix(Type type) { - (void)type; - if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root - Level* level = level_stack_.template Top(); - if (level->valueCount > 0) { - if (level->inArray) - os_->Put(','); // add comma if it is not the first element in array - else // in object - os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); - } - if (!level->inArray && level->valueCount % 2 == 0) - RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name - level->valueCount++; - } - else { - RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. - hasRoot_ = true; - } - } - - // Flush the value if it is the top level one. - bool EndValue(bool ret) { - if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text - Flush(); - return ret; - } - - OutputStream* os_; - internal::Stack level_stack_; - int maxDecimalPlaces_; - bool hasRoot_; - -private: - // Prohibit copy constructor & assignment operator. - Writer(const Writer&); - Writer& operator=(const Writer&); -}; - -// Full specialization for StringStream to prevent memory copying - -template<> -inline bool Writer::WriteInt(int i) { - char *buffer = os_->Push(11); - const char* end = internal::i32toa(i, buffer); - os_->Pop(static_cast(11 - (end - buffer))); - return true; -} - -template<> -inline bool Writer::WriteUint(unsigned u) { - char *buffer = os_->Push(10); - const char* end = internal::u32toa(u, buffer); - os_->Pop(static_cast(10 - (end - buffer))); - return true; -} - -template<> -inline bool Writer::WriteInt64(int64_t i64) { - char *buffer = os_->Push(21); - const char* end = internal::i64toa(i64, buffer); - os_->Pop(static_cast(21 - (end - buffer))); - return true; -} - -template<> -inline bool Writer::WriteUint64(uint64_t u) { - char *buffer = os_->Push(20); - const char* end = internal::u64toa(u, buffer); - os_->Pop(static_cast(20 - (end - buffer))); - return true; -} - -template<> -inline bool Writer::WriteDouble(double d) { - if (internal::Double(d).IsNanOrInf()) { - // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). - if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) - return false; - if (internal::Double(d).IsNan()) { - PutReserve(*os_, 3); - PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); - return true; - } - if (internal::Double(d).Sign()) { - PutReserve(*os_, 9); - PutUnsafe(*os_, '-'); - } - else - PutReserve(*os_, 8); - PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); - PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); - return true; - } - - char *buffer = os_->Push(25); - char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); - os_->Pop(static_cast(25 - (end - buffer))); - return true; -} - -#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) -template<> -inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { - if (length < 16) - return RAPIDJSON_LIKELY(is.Tell() < length); - - if (!RAPIDJSON_LIKELY(is.Tell() < length)) - return false; - - const char* p = is.src_; - const char* end = is.head_ + length; - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); - if (nextAligned > end) - return true; - - while (p != nextAligned) - if (*p < 0x20 || *p == '\"' || *p == '\\') { - is.src_ = p; - return RAPIDJSON_LIKELY(is.Tell() < length); - } - else - os_->PutUnsafe(*p++); - - // The rest of string using SIMD - static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; - static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; - const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); - const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); - const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); - - for (; p != endAligned; p += 16) { - const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const __m128i t1 = _mm_cmpeq_epi8(s, dq); - const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F - const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); - unsigned short r = static_cast(_mm_movemask_epi8(x)); - if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped - SizeType len; -#ifdef _MSC_VER // Find the index of first escaped - unsigned long offset; - _BitScanForward(&offset, r); - len = offset; -#else - len = static_cast(__builtin_ffs(r) - 1); -#endif - char* q = reinterpret_cast(os_->PushUnsafe(len)); - for (size_t i = 0; i < len; i++) - q[i] = p[i]; - - p += len; - break; - } - _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); - } - - is.src_ = p; - return RAPIDJSON_LIKELY(is.Tell() < length); -} -#elif defined(RAPIDJSON_NEON) -template<> -inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { - if (length < 16) - return RAPIDJSON_LIKELY(is.Tell() < length); - - if (!RAPIDJSON_LIKELY(is.Tell() < length)) - return false; - - const char* p = is.src_; - const char* end = is.head_ + length; - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); - const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); - if (nextAligned > end) - return true; - - while (p != nextAligned) - if (*p < 0x20 || *p == '\"' || *p == '\\') { - is.src_ = p; - return RAPIDJSON_LIKELY(is.Tell() < length); - } - else - os_->PutUnsafe(*p++); - - // The rest of string using SIMD - const uint8x16_t s0 = vmovq_n_u8('"'); - const uint8x16_t s1 = vmovq_n_u8('\\'); - const uint8x16_t s2 = vmovq_n_u8('\b'); - const uint8x16_t s3 = vmovq_n_u8(32); - - for (; p != endAligned; p += 16) { - const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); - uint8x16_t x = vceqq_u8(s, s0); - x = vorrq_u8(x, vceqq_u8(s, s1)); - x = vorrq_u8(x, vceqq_u8(s, s2)); - x = vorrq_u8(x, vcltq_u8(s, s3)); - - x = vrev64q_u8(x); // Rev in 64 - uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract - uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract - - SizeType len = 0; - bool escaped = false; - if (low == 0) { - if (high != 0) { - uint32_t lz = internal::clzll(high); - len = 8 + (lz >> 3); - escaped = true; - } - } else { - uint32_t lz = internal::clzll(low); - len = lz >> 3; - escaped = true; - } - if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped - char* q = reinterpret_cast(os_->PushUnsafe(len)); - for (size_t i = 0; i < len; i++) - q[i] = p[i]; - - p += len; - break; - } - vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); - } - - is.src_ = p; - return RAPIDJSON_LIKELY(is.Tell() < length); -} -#endif // RAPIDJSON_NEON - -RAPIDJSON_NAMESPACE_END - -#if defined(_MSC_VER) || defined(__clang__) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/winmain-inl.h b/include/winmain-inl.h deleted file mode 100644 index 5cabc5dc..00000000 --- a/include/winmain-inl.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -namespace { -struct OnApp { - OnApp() { -#ifdef WIN32 - // Initialize Winsock 2.2 - WSADATA wsaData; - int err = WSAStartup(MAKEWORD(2, 2), &wsaData); - - if (err) { - std::cout << "WSAStartup() failed with error: %d" << err; - } -#endif - } - ~OnApp() { -#ifdef WIN32 - WSACleanup(); -#endif - } -} __s_onexit_pause; -} - - diff --git a/include/zip_file.h b/include/zip_file.h deleted file mode 100644 index 239d58c2..00000000 --- a/include/zip_file.h +++ /dev/null @@ -1,5710 +0,0 @@ -// Copyright (c) 2014-2017 Thomas Fussell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE -// -// @license: http://www.opensource.org/licenses/mit-license.php - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing - See "unlicense" statement at the end of this file. - Rich Geldreich , last updated Oct. 13, 2013 - Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt - - Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define - MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). - - * Change History - 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): - - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug - would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() - (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). - - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size - - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. - Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). - - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes - - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed - - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. - - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti - - Merged MZ_FORCEINLINE fix from hdeanclark - - Fix include before config #ifdef, thanks emil.brink - - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can - set it to 1 for real-time compression). - - Merged in some compiler fixes from paulharris's github repro. - - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. - - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. - - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. - - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled - - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch - 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). - 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. - - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. - - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. - - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly - "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. - - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. - - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. - - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). - 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. - level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. - 5/28/11 v1.11 - Added statement from unlicense.org - 5/27/11 v1.10 - Substantial compressor optimizations: - - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). - - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. - - Refactored the compression code for better readability and maintainability. - - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large - drop in throughput on some files). - 5/15/11 v1.09 - Initial stable release. - - * Low-level Deflate/Inflate implementation notes: - - Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or - greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses - approximately as well as zlib. - - Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function - coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory - block large enough to hold the entire file. - - The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. - - * zlib-style API notes: - - miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in - zlib replacement in many apps: - The z_stream struct, optional memory allocation callbacks - deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound - inflateInit/inflateInit2/inflate/inflateEnd - compress, compress2, compressBound, uncompress - CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. - Supports raw deflate streams or standard zlib streams with adler-32 checking. - - Limitations: - The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. - I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but - there are no guarantees that miniz.c pulls this off perfectly. - - * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by - Alex Evans. Supports 1-4 bytes/pixel images. - - * ZIP archive API notes: - - The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to - get the job done with minimal fuss. There are simple API's to retrieve file information, read files from - existing archives, create new archives, append new files to existing archives, or clone archive data from - one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), - or you can specify custom file read/write callbacks. - - - Archive reading: Just call this function to read a single file from a disk archive: - - void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, - size_t *pSize, mz_uint zip_flags); - - For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central - directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - - - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: - - int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); - - The locate operation can optionally check file comments too, which (as one example) can be used to identify - multiple versions of the same file in an archive. This function uses a simple linear search through the central - directory, so it's not very fast. - - Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and - retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - - - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data - to disk and builds an exact image of the central directory in memory. The central directory image is written - all at once at the end of the archive file when the archive is finalized. - - The archive writer can optionally align each file's local header and file data to any power of 2 alignment, - which can be useful when the archive will be read from optical media. Also, the writer supports placing - arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still - readable by any ZIP tool. - - - Archive appending: The simple way to add a single file to an archive is to call this function: - - mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, - const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); - - The archive will be created if it doesn't already exist, otherwise it'll be appended to. - Note the appending is done in-place and is not an atomic operation, so if something goes wrong - during the operation it's possible the archive could be left without a central directory (although the local - file headers and file data will be fine, so the archive will be recoverable). - - For more complex archive modification scenarios: - 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to - preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the - compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and - you're done. This is safe but requires a bunch of temporary disk space or heap memory. - - 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), - append new files as needed, then finalize the archive which will write an updated central directory to the - original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a - possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - - - ZIP archive support limitations: - No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. - Requires streams capable of seeking. - - * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the - below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. - - * Important: For best perf. be sure to customize the below macros for your target platform: - #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 - #define MINIZ_LITTLE_ENDIAN 1 - #define MINIZ_HAS_64BIT_REGISTERS 1 - - * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz - uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files - (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). -*/ - -#ifndef MINIZ_HEADER_INCLUDED -#define MINIZ_HEADER_INCLUDED - -#include - -// Defines to completely disable specific portions of miniz.c: -// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. - -// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. -//#define MINIZ_NO_STDIO - -// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or -// get/set file times, and the C run-time funcs that get/set times won't be called. -// The current downside is the times written to your archives will be from 1979. -//#define MINIZ_NO_TIME - -// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. -//#define MINIZ_NO_ARCHIVE_APIS - -// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. -//#define MINIZ_NO_ARCHIVE_WRITING_APIS - -// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. -//#define MINIZ_NO_ZLIB_APIS - -// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. -//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES - -// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. -// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc -// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user -// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. -//#define MINIZ_NO_MALLOC - -#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) - // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux - #define MINIZ_NO_TIME -#endif - -#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) - #include -#endif - -#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) -// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. -#define MINIZ_X86_OR_X64_CPU 1 -#endif - -#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU -// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. -#define MINIZ_LITTLE_ENDIAN 1 -#endif - -#if MINIZ_X86_OR_X64_CPU -// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 -#endif - -#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) -// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). -#define MINIZ_HAS_64BIT_REGISTERS 1 -#endif - -#ifdef __APPLE__ -#define ftello64 ftello -#define fseeko64 fseeko -#define fopen64 fopen -#define freopen64 freopen -//#define stat64 stat -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -// ------------------- zlib-style API Definitions. - -// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! -typedef unsigned long mz_ulong; - -// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. -void mz_free(void *p); - -#define MZ_ADLER32_INIT (1) -// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. -mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); - -#define MZ_CRC32_INIT (0) -// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. -mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); - -// Compression strategies. -enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; - -// Method -#define MZ_DEFLATED 8 - -#ifndef MINIZ_NO_ZLIB_APIS - -// Heap allocation callbacks. -// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. -typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); -typedef void (*mz_free_func)(void *opaque, void *address); -typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); - -#define MZ_VERSION "9.1.15" -#define MZ_VERNUM 0x91F0 -#define MZ_VER_MAJOR 9 -#define MZ_VER_MINOR 1 -#define MZ_VER_REVISION 15 -#define MZ_VER_SUBREVISION 0 - -// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). -enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; - -// Return status codes. MZ_PARAM_ERROR is non-standard. -enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; - -// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. -enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; - -// Window bits -#define MZ_DEFAULT_WINDOW_BITS 15 - -struct mz_internal_state; - -// Compression/decompression stream struct. -typedef struct mz_stream_s -{ - const unsigned char *next_in; // pointer to next byte to read - unsigned int avail_in; // number of bytes available at next_in - mz_ulong total_in; // total number of bytes consumed so far - - unsigned char *next_out; // pointer to next byte to write - unsigned int avail_out; // number of bytes that can be written to next_out - mz_ulong total_out; // total number of bytes produced so far - - char *msg; // error msg (unused) - struct mz_internal_state *state; // internal state, allocated by zalloc/zfree - - mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) - mz_free_func zfree; // optional heap free function (defaults to free) - void *opaque; // heap alloc function user pointer - - int data_type; // data_type (unused) - mz_ulong adler; // adler32 of the source or uncompressed data - mz_ulong reserved; // not used -} mz_stream; - -typedef mz_stream *mz_streamp; - -// Returns the version string of miniz.c. -const char *mz_version(void); - -// mz_deflateInit() initializes a compressor with default options: -// Parameters: -// pStream must point to an initialized mz_stream struct. -// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. -// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. -// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) -// Return values: -// MZ_OK on success. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_PARAM_ERROR if the input parameters are bogus. -// MZ_MEM_ERROR on out of memory. -int mz_deflateInit(mz_streamp pStream, int level); - -// mz_deflateInit2() is like mz_deflate(), except with more control: -// Additional parameters: -// method must be MZ_DEFLATED -// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) -// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) -int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); - -// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). -int mz_deflateReset(mz_streamp pStream); - -// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. -// Parameters: -// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. -// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. -// Return values: -// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). -// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_PARAM_ERROR if one of the parameters is invalid. -// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) -int mz_deflate(mz_streamp pStream, int flush); - -// mz_deflateEnd() deinitializes a compressor: -// Return values: -// MZ_OK on success. -// MZ_STREAM_ERROR if the stream is bogus. -int mz_deflateEnd(mz_streamp pStream); - -// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. -mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); - -// Single-call compression functions mz_compress() and mz_compress2(): -// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. -int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); -int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); - -// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). -mz_ulong mz_compressBound(mz_ulong source_len); - -// Initializes a decompressor. -int mz_inflateInit(mz_streamp pStream); - -// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: -// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). -int mz_inflateInit2(mz_streamp pStream, int window_bits); - -// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. -// Parameters: -// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. -// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. -// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). -// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. -// Return values: -// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. -// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_DATA_ERROR if the deflate stream is invalid. -// MZ_PARAM_ERROR if one of the parameters is invalid. -// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again -// with more input data, or with more room in the output buffer (except when using single call decompression, described above). -int mz_inflate(mz_streamp pStream, int flush); - -// Deinitializes a decompressor. -int mz_inflateEnd(mz_streamp pStream); - -// Single-call decompression. -// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); - -// Returns a string description of the specified error code, or NULL if the error code is invalid. -const char *mz_error(int err); - -// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. -// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. -#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES - typedef unsigned char Byte; - typedef unsigned int uInt; - typedef mz_ulong uLong; - typedef Byte Bytef; - typedef uInt uIntf; - typedef char charf; - typedef int intf; - typedef void *voidpf; - typedef uLong uLongf; - typedef void *voidp; - typedef void *const voidpc; - #define Z_NULL 0 - #define Z_NO_FLUSH MZ_NO_FLUSH - #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH - #define Z_SYNC_FLUSH MZ_SYNC_FLUSH - #define Z_FULL_FLUSH MZ_FULL_FLUSH - #define Z_FINISH MZ_FINISH - #define Z_BLOCK MZ_BLOCK - #define Z_OK MZ_OK - #define Z_STREAM_END MZ_STREAM_END - #define Z_NEED_DICT MZ_NEED_DICT - #define Z_ERRNO MZ_ERRNO - #define Z_STREAM_ERROR MZ_STREAM_ERROR - #define Z_DATA_ERROR MZ_DATA_ERROR - #define Z_MEM_ERROR MZ_MEM_ERROR - #define Z_BUF_ERROR MZ_BUF_ERROR - #define Z_VERSION_ERROR MZ_VERSION_ERROR - #define Z_PARAM_ERROR MZ_PARAM_ERROR - #define Z_NO_COMPRESSION MZ_NO_COMPRESSION - #define Z_BEST_SPEED MZ_BEST_SPEED - #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION - #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION - #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY - #define Z_FILTERED MZ_FILTERED - #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY - #define Z_RLE MZ_RLE - #define Z_FIXED MZ_FIXED - #define Z_DEFLATED MZ_DEFLATED - #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS - #define alloc_func mz_alloc_func - #define free_func mz_free_func - #define internal_state mz_internal_state - #define z_stream mz_stream - #define deflateInit mz_deflateInit - #define deflateInit2 mz_deflateInit2 - #define deflateReset mz_deflateReset - #define deflate mz_deflate - #define deflateEnd mz_deflateEnd - #define deflateBound mz_deflateBound - #define compress mz_compress - #define compress2 mz_compress2 - #define compressBound mz_compressBound - #define inflateInit mz_inflateInit - #define inflateInit2 mz_inflateInit2 - #define inflate mz_inflate - #define inflateEnd mz_inflateEnd - #define uncompress mz_uncompress - #define crc32 mz_crc32 - #define adler32 mz_adler32 - #define MAX_WBITS 15 - #define MAX_MEM_LEVEL 9 - #define zError mz_error - #define ZLIB_VERSION MZ_VERSION - #define ZLIB_VERNUM MZ_VERNUM - #define ZLIB_VER_MAJOR MZ_VER_MAJOR - #define ZLIB_VER_MINOR MZ_VER_MINOR - #define ZLIB_VER_REVISION MZ_VER_REVISION - #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION - #define zlibVersion mz_version - #define zlib_version mz_version() -#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES - -#endif // MINIZ_NO_ZLIB_APIS - -// ------------------- Types and macros - -typedef unsigned char mz_uint8; -typedef signed short mz_int16; -typedef unsigned short mz_uint16; -typedef unsigned int mz_uint32; -typedef unsigned int mz_uint; -typedef long long mz_int64; -typedef unsigned long long mz_uint64; -typedef int mz_bool; - -#define MZ_FALSE (0) -#define MZ_TRUE (1) - -// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. -#ifdef _MSC_VER - #define MZ_MACRO_END while (0, 0) -#else - #define MZ_MACRO_END while (0) -#endif - -// ------------------- ZIP archive reading/writing - -#ifndef MINIZ_NO_ARCHIVE_APIS - -enum -{ - MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, - MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, - MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 -}; - -typedef struct -{ - mz_uint32 m_file_index; - mz_uint32 m_central_dir_ofs; - mz_uint16 m_version_made_by; - mz_uint16 m_version_needed; - mz_uint16 m_bit_flag; - mz_uint16 m_method; -#ifndef MINIZ_NO_TIME - time_t m_time; -#endif - mz_uint32 m_crc32; - mz_uint64 m_comp_size; - mz_uint64 m_uncomp_size; - mz_uint16 m_internal_attr; - mz_uint32 m_external_attr; - mz_uint64 m_local_header_ofs; - mz_uint32 m_comment_size; - char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; - char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; -} mz_zip_archive_file_stat; - -typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); -typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); - -struct mz_zip_internal_state_tag; -typedef struct mz_zip_internal_state_tag mz_zip_internal_state; - -typedef enum -{ - MZ_ZIP_MODE_INVALID = 0, - MZ_ZIP_MODE_READING = 1, - MZ_ZIP_MODE_WRITING = 2, - MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 -} mz_zip_mode; - -typedef struct mz_zip_archive_tag -{ - mz_uint64 m_archive_size; - mz_uint64 m_central_directory_file_ofs; - mz_uint m_total_files; - mz_zip_mode m_zip_mode; - - mz_uint m_file_offset_alignment; - - mz_alloc_func m_pAlloc; - mz_free_func m_pFree; - mz_realloc_func m_pRealloc; - void *m_pAlloc_opaque; - - mz_file_read_func m_pRead; - mz_file_write_func m_pWrite; - void *m_pIO_opaque; - - mz_zip_internal_state *m_pState; - -} mz_zip_archive; - -typedef enum -{ - MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, - MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, - MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, - MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 -} mz_zip_flags; - -// ZIP archive reading - -// Inits a ZIP archive reader. -// These functions read and validate the archive's central directory. -mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); -mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); -#endif - -// Returns the total number of files in the archive. -mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); - -// Returns detailed information about an archive file entry. -mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); - -// Determines if an archive file entry is a directory entry. -mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); -mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); - -// Retrieves the filename of an archive file entry. -// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. -mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); - -// Attempts to locates a file in the archive's central directory. -// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH -// Returns -1 if the file cannot be found. -int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); - -// Extracts a archive file to a memory buffer using no memory allocation. -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); -mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); - -// Extracts a archive file to a memory buffer. -mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); - -// Extracts a archive file to a dynamically allocated heap buffer. -void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); -void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); - -// Extracts a archive file using a callback function to output the file's data. -mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); - -#ifndef MINIZ_NO_STDIO -// Extracts a archive file to a disk file and sets its last accessed and modified times. -// This function only extracts files, not archive directory records. -mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); -#endif - -// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. -mz_bool mz_zip_reader_end(mz_zip_archive *pZip); - -// ZIP archive writing - -#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -// Inits a ZIP archive writer. -mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); -mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); -#endif - -// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. -// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. -// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). -// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. -// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before -// the archive is finalized the file's central directory will be hosed. -mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); - -// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. -// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); -mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); - -#ifndef MINIZ_NO_STDIO -// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); -#endif - -// Adds a file to an archive by fully cloning the data from another archive. -// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. -mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); - -// Finalizes the archive by writing the central directory records followed by the end of central directory record. -// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). -// An archive must be manually finalized by calling this function for it to be valid. -mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); -mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); - -// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. -// Note for the archive to be valid, it must have been finalized before ending. -mz_bool mz_zip_writer_end(mz_zip_archive *pZip); - -// Misc. high-level helper functions: - -// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); - -// Reads a single file from an archive into a heap block. -// Returns NULL on failure. -void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); - -#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -#endif // #ifndef MINIZ_NO_ARCHIVE_APIS - -// ------------------- Low-level Decompression API Definitions - -// Decompression flags used by tinfl_decompress(). -// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. -// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. -// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). -// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. -enum -{ - TINFL_FLAG_PARSE_ZLIB_HEADER = 1, - TINFL_FLAG_HAS_MORE_INPUT = 2, - TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, - TINFL_FLAG_COMPUTE_ADLER32 = 8 -}; - -// High level decompression functions: -// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. -// On return: -// Function returns a pointer to the decompressed data, or NULL on failure. -// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must call mz_free() on the returned block when it's no longer needed. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); - -// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. -// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. -#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); - -// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. -// Returns 1 on success or 0 on failure. -typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; - -// Max size of LZ dictionary. -#define TINFL_LZ_DICT_SIZE 32768 - -// Return status. -typedef enum -{ - TINFL_STATUS_BAD_PARAM = -3, - TINFL_STATUS_ADLER32_MISMATCH = -2, - TINFL_STATUS_FAILED = -1, - TINFL_STATUS_DONE = 0, - TINFL_STATUS_NEEDS_MORE_INPUT = 1, - TINFL_STATUS_HAS_MORE_OUTPUT = 2 -} tinfl_status; - -// Initializes the decompressor to its initial state. -#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END -#define tinfl_get_adler32(r) (r)->m_check_adler32 - -// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. -// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); - -// Internal/private bits follow. -enum -{ - TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, - TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS -}; - -typedef struct -{ - mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; -} tinfl_huff_table; - -#if MINIZ_HAS_64BIT_REGISTERS - #define TINFL_USE_64BIT_BITBUF 1 -#endif - -#if TINFL_USE_64BIT_BITBUF - typedef mz_uint64 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (64) -#else - typedef mz_uint32 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (32) -#endif - -struct tinfl_decompressor_tag -{ - mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; - tinfl_bit_buf_t m_bit_buf; - size_t m_dist_from_out_buf_start; - tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; - mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; -}; - -// ------------------- Low-level Compression API Definitions - -// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). -#define TDEFL_LESS_MEMORY 0 - -// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): -// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). -enum -{ - TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF -}; - -// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. -// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). -// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. -// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). -// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) -// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. -// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. -// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. -// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). -enum -{ - TDEFL_WRITE_ZLIB_HEADER = 0x01000, - TDEFL_COMPUTE_ADLER32 = 0x02000, - TDEFL_GREEDY_PARSING_FLAG = 0x04000, - TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, - TDEFL_RLE_MATCHES = 0x10000, - TDEFL_FILTER_MATCHES = 0x20000, - TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, - TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 -}; - -// High level compression functions: -// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of source block to compress. -// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. -// On return: -// Function returns a pointer to the compressed data, or NULL on failure. -// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must free() the returned block when it's no longer needed. -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); - -// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. -// Returns 0 on failure. -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); - -// Compresses an image to a compressed PNG file in memory. -// On entry: -// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. -// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. -// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL -// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). -// On return: -// Function returns a pointer to the compressed data, or NULL on failure. -// *pLen_out will be set to the size of the PNG image file. -// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); - -// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. -typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); - -// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; - -// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). -#if TDEFL_LESS_MEMORY -enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; -#else -enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; -#endif - -// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. -typedef enum -{ - TDEFL_STATUS_BAD_PARAM = -2, - TDEFL_STATUS_PUT_BUF_FAILED = -1, - TDEFL_STATUS_OKAY = 0, - TDEFL_STATUS_DONE = 1, -} tdefl_status; - -// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums -typedef enum -{ - TDEFL_NO_FLUSH = 0, - TDEFL_SYNC_FLUSH = 2, - TDEFL_FULL_FLUSH = 3, - TDEFL_FINISH = 4 -} tdefl_flush; - -// tdefl's compression state structure. -typedef struct -{ - tdefl_put_buf_func_ptr m_pPut_buf_func; - void *m_pPut_buf_user; - mz_uint m_flags, m_max_probes[2]; - int m_greedy_parsing; - mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; - mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; - mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; - mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; - tdefl_status m_prev_return_status; - const void *m_pIn_buf; - void *m_pOut_buf; - size_t *m_pIn_buf_size, *m_pOut_buf_size; - tdefl_flush m_flush; - const mz_uint8 *m_pSrc; - size_t m_src_buf_left, m_out_buf_ofs; - mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; - mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; - mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; - mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; - mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; -} tdefl_compressor; - -// Initializes the compressor. -// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. -// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. -// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. -// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); - -// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. -// tdefl_compress_buffer() always consumes the entire input buffer. -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); - -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); -mz_uint32 tdefl_get_adler32(tdefl_compressor *d); - -// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. -#ifndef MINIZ_NO_ZLIB_APIS -// Create tdefl_compress() flags given zlib-style compression parameters. -// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) -// window_bits may be -15 (raw deflate) or 15 (zlib) -// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); -#endif // #ifndef MINIZ_NO_ZLIB_APIS - -#ifdef __cplusplus -} -#endif - -#endif // MINIZ_HEADER_INCLUDED - -// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) - -#ifndef MINIZ_HEADER_FILE_ONLY - -typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; -typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; -typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; - -#include -#include - -#define MZ_ASSERT(x) assert(x) - -#ifdef MINIZ_NO_MALLOC - #define MZ_MALLOC(x) NULL - #define MZ_FREE(x) (void)x, ((void)0) - #define MZ_REALLOC(p, x) NULL -#else - #define MZ_MALLOC(x) malloc(x) - #define MZ_FREE(x) free(x) - #define MZ_REALLOC(p, x) realloc(p, x) -#endif - -#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) -#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) -#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) - #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) -#else - #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) - #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) -#endif - -#ifdef _MSC_VER - #define MZ_FORCEINLINE __forceinline -#elif defined(__GNUC__) - #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) -#else - #define MZ_FORCEINLINE inline -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -// ------------------- zlib-style API's - -mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) -{ - mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; - if (!ptr) return MZ_ADLER32_INIT; - while (buf_len) { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - return (s2 << 16) + s1; -} - -// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ -mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) -{ - static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; - mz_uint32 crcu32 = (mz_uint32)crc; - if (!ptr) return MZ_CRC32_INIT; - crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } - return ~crcu32; -} - -void mz_free(void *p) -{ - MZ_FREE(p); -} - -#ifndef MINIZ_NO_ZLIB_APIS - -static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } -static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } -static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } - -const char *mz_version(void) -{ - return MZ_VERSION; -} - -int mz_deflateInit(mz_streamp pStream, int level) -{ - return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); -} - -int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) -{ - tdefl_compressor *pComp; - mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); - - if (!pStream) return MZ_STREAM_ERROR; - if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; - - pStream->data_type = 0; - pStream->adler = MZ_ADLER32_INIT; - pStream->msg = NULL; - pStream->reserved = 0; - pStream->total_in = 0; - pStream->total_out = 0; - if (!pStream->zalloc) pStream->zalloc = def_alloc_func; - if (!pStream->zfree) pStream->zfree = def_free_func; - - pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); - if (!pComp) - return MZ_MEM_ERROR; - - pStream->state = (struct mz_internal_state *)pComp; - - if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) - { - mz_deflateEnd(pStream); - return MZ_PARAM_ERROR; - } - - return MZ_OK; -} - -int mz_deflateReset(mz_streamp pStream) -{ - if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; - pStream->total_in = pStream->total_out = 0; - tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); - return MZ_OK; -} - -int mz_deflate(mz_streamp pStream, int flush) -{ - size_t in_bytes, out_bytes; - mz_ulong orig_total_in, orig_total_out; - int mz_status = MZ_OK; - - if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; - if (!pStream->avail_out) return MZ_BUF_ERROR; - - if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; - - if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) - return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; - - orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; - for ( ; ; ) - { - tdefl_status defl_status; - in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; - - defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; - pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); - - pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; - pStream->total_out += (mz_uint)out_bytes; - - if (defl_status < 0) - { - mz_status = MZ_STREAM_ERROR; - break; - } - else if (defl_status == TDEFL_STATUS_DONE) - { - mz_status = MZ_STREAM_END; - break; - } - else if (!pStream->avail_out) - break; - else if ((!pStream->avail_in) && (flush != MZ_FINISH)) - { - if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) - break; - return MZ_BUF_ERROR; // Can't make forward progress without some input. - } - } - return mz_status; -} - -int mz_deflateEnd(mz_streamp pStream) -{ - if (!pStream) return MZ_STREAM_ERROR; - if (pStream->state) - { - pStream->zfree(pStream->opaque, pStream->state); - pStream->state = NULL; - } - return MZ_OK; -} - -mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) -{ - (void)pStream; - // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) - return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); -} - -int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) -{ - int status; - mz_stream stream; - memset(&stream, 0, sizeof(stream)); - - // In case mz_ulong is 64-bits (argh I hate longs). - if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; - - stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; - stream.next_out = pDest; - stream.avail_out = (mz_uint32)*pDest_len; - - status = mz_deflateInit(&stream, level); - if (status != MZ_OK) return status; - - status = mz_deflate(&stream, MZ_FINISH); - if (status != MZ_STREAM_END) - { - mz_deflateEnd(&stream); - return (status == MZ_OK) ? MZ_BUF_ERROR : status; - } - - *pDest_len = stream.total_out; - return mz_deflateEnd(&stream); -} - -int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) -{ - return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); -} - -mz_ulong mz_compressBound(mz_ulong source_len) -{ - return mz_deflateBound(NULL, source_len); -} - -typedef struct -{ - tinfl_decompressor m_decomp; - mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; - mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; - tinfl_status m_last_status; -} inflate_state; - -int mz_inflateInit2(mz_streamp pStream, int window_bits) -{ - inflate_state *pDecomp; - if (!pStream) return MZ_STREAM_ERROR; - if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; - - pStream->data_type = 0; - pStream->adler = 0; - pStream->msg = NULL; - pStream->total_in = 0; - pStream->total_out = 0; - pStream->reserved = 0; - if (!pStream->zalloc) pStream->zalloc = def_alloc_func; - if (!pStream->zfree) pStream->zfree = def_free_func; - - pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); - if (!pDecomp) return MZ_MEM_ERROR; - - pStream->state = (struct mz_internal_state *)pDecomp; - - tinfl_init(&pDecomp->m_decomp); - pDecomp->m_dict_ofs = 0; - pDecomp->m_dict_avail = 0; - pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; - pDecomp->m_first_call = 1; - pDecomp->m_has_flushed = 0; - pDecomp->m_window_bits = window_bits; - - return MZ_OK; -} - -int mz_inflateInit(mz_streamp pStream) -{ - return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); -} - -int mz_inflate(mz_streamp pStream, int flush) -{ - inflate_state* pState; - mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; - size_t in_bytes, out_bytes, orig_avail_in; - tinfl_status status; - - if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; - if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; - if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; - - pState = (inflate_state*)pStream->state; - if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; - orig_avail_in = pStream->avail_in; - - first_call = pState->m_first_call; pState->m_first_call = 0; - if (pState->m_last_status < 0) return MZ_DATA_ERROR; - - if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; - pState->m_has_flushed |= (flush == MZ_FINISH); - - if ((flush == MZ_FINISH) && (first_call)) - { - // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. - decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; - in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; - status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); - pState->m_last_status = status; - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; - pStream->adler = tinfl_get_adler32(&pState->m_decomp); - pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; - - if (status < 0) - return MZ_DATA_ERROR; - else if (status != TINFL_STATUS_DONE) - { - pState->m_last_status = TINFL_STATUS_FAILED; - return MZ_BUF_ERROR; - } - return MZ_STREAM_END; - } - // flush != MZ_FINISH then we must assume there's more input. - if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; - - if (pState->m_dict_avail) - { - n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); - memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); - pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; - pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); - return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; - } - - for ( ; ; ) - { - in_bytes = pStream->avail_in; - out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; - - status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); - pState->m_last_status = status; - - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; - pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); - - pState->m_dict_avail = (mz_uint)out_bytes; - - n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); - memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); - pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; - pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); - - if (status < 0) - return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). - else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) - return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. - else if (flush == MZ_FINISH) - { - // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. - if (status == TINFL_STATUS_DONE) - return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; - // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. - else if (!pStream->avail_out) - return MZ_BUF_ERROR; - } - else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) - break; - } - - return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; -} - -int mz_inflateEnd(mz_streamp pStream) -{ - if (!pStream) - return MZ_STREAM_ERROR; - if (pStream->state) - { - pStream->zfree(pStream->opaque, pStream->state); - pStream->state = NULL; - } - return MZ_OK; -} - -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) -{ - mz_stream stream; - int status; - memset(&stream, 0, sizeof(stream)); - - // In case mz_ulong is 64-bits (argh I hate longs). - if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; - - stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; - stream.next_out = pDest; - stream.avail_out = (mz_uint32)*pDest_len; - - status = mz_inflateInit(&stream); - if (status != MZ_OK) - return status; - - status = mz_inflate(&stream, MZ_FINISH); - if (status != MZ_STREAM_END) - { - mz_inflateEnd(&stream); - return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; - } - *pDest_len = stream.total_out; - - return mz_inflateEnd(&stream); -} - -const char *mz_error(int err) -{ - static struct { int m_err; const char *m_pDesc; } s_error_descs[] = - { - { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, - { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } - }; - mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; - return NULL; -} - -#endif //MINIZ_NO_ZLIB_APIS - -// ------------------- Low-level Decompression (completely independent from all compression API's) - -#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) -#define TINFL_MEMSET(p, c, l) memset(p, c, l) - -#define TINFL_CR_BEGIN switch(r->m_state) { case 0: -#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END -#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END -#define TINFL_CR_FINISH } - -// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never -// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. -#define TINFL_GET_BYTE(state_index, c) do { \ - if (pIn_buf_cur >= pIn_buf_end) { \ - for ( ; ; ) { \ - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ - TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ - if (pIn_buf_cur < pIn_buf_end) { \ - c = *pIn_buf_cur++; \ - break; \ - } \ - } else { \ - c = 0; \ - break; \ - } \ - } \ - } else c = *pIn_buf_cur++; } MZ_MACRO_END - -#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) -#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END -#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END - -// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. -// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a -// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the -// bit buffer contains >=15 bits (deflate's max. Huffman code size). -#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ - do { \ - temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ - if (temp >= 0) { \ - code_len = temp >> 9; \ - if ((code_len) && (num_bits >= code_len)) \ - break; \ - } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ - code_len = TINFL_FAST_LOOKUP_BITS; \ - do { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ - } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ - } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ - } while (num_bits < 15); - -// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read -// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully -// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. -// The slow path is only executed at the very end of the input buffer. -#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ - int temp; mz_uint code_len, c; \ - if (num_bits < 15) { \ - if ((pIn_buf_end - pIn_buf_cur) < 2) { \ - TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ - } else { \ - bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ - } \ - } \ - if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ - code_len = temp >> 9, temp &= 511; \ - else { \ - code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ - } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4334) -#endif - -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) -{ - static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; - static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - static const int s_min_table_sizes[3] = { 257, 1, 4 }; - - tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; - const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; - size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; - - // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). - if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } - - num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; - TINFL_CR_BEGIN - - bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); - counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); - if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); - if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } - } - - do - { - TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; - if (r->m_type == 0) - { - TINFL_SKIP_BITS(5, num_bits & 7); - for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } - if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } - while ((counter) && (num_bits)) - { - TINFL_GET_BITS(51, dist, 8); - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)dist; - counter--; - } - while (counter) - { - size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } - while (pIn_buf_cur >= pIn_buf_end) - { - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) - { - TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); - } - else - { - TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); - } - } - n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); - TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; - } - } - else if (r->m_type == 3) - { - TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); - } - else - { - if (r->m_type == 1) - { - mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; - r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); - for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; - } - else - { - for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } - r->m_table_sizes[2] = 19; - } - for ( ; (int)r->m_type >= 0; r->m_type--) - { - int tree_next, tree_cur; tinfl_huff_table *pTable; - mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); - for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; - used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; - for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } - if ((65536 != total) && (used_syms > 1)) - { - TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); - } - for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) - { - mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; - cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); - if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } - rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); - for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) - { - tree_cur -= ((rev_code >>= 1) & 1); - if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; - } - tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; - } - if (r->m_type == 2) - { - for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) - { - mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } - if ((dist == 16) && (!counter)) - { - TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); - } - num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; - TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; - } - if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) - { - TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); - } - TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); - } - } - for ( ; ; ) - { - mz_uint8 *pSrc; - for ( ; ; ) - { - if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) - { - TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); - if (counter >= 256) - break; - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)counter; - } - else - { - int sym2; mz_uint code_len; -#if TINFL_USE_64BIT_BITBUF - if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } -#else - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - counter = sym2; bit_buf >>= code_len; num_bits -= code_len; - if (counter & 256) - break; - -#if !TINFL_USE_64BIT_BITBUF - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - bit_buf >>= code_len; num_bits -= code_len; - - pOut_buf_cur[0] = (mz_uint8)counter; - if (sym2 & 256) - { - pOut_buf_cur++; - counter = sym2; - break; - } - pOut_buf_cur[1] = (mz_uint8)sym2; - pOut_buf_cur += 2; - } - } - if ((counter &= 511) == 256) break; - - num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } - - TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); - num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } - - dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; - if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - { - TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); - } - - pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); - - if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) - { - while (counter--) - { - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; - } - continue; - } -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - else if ((counter >= 9) && (counter <= dist)) - { - const mz_uint8 *pSrc_end = pSrc + (counter & ~7); - do - { - ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; - ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; - pOut_buf_cur += 8; - } while ((pSrc += 8) < pSrc_end); - if ((counter &= 7) < 3) - { - if (counter) - { - pOut_buf_cur[0] = pSrc[0]; - if (counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - continue; - } - } -#endif - do - { - pOut_buf_cur[0] = pSrc[0]; - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur[2] = pSrc[2]; - pOut_buf_cur += 3; pSrc += 3; - } while ((int)(counter -= 3) > 2); - if ((int)counter > 0) - { - pOut_buf_cur[0] = pSrc[0]; - if ((int)counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - } - } - } while (!(r->m_final & 1)); - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } - } - TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); - TINFL_CR_FINISH - -common_exit: - r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; - *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; - if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) - { - const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; - mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; - while (buf_len) - { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) - { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; - } - return status; -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -// Higher level helper functions. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; - *pOut_len = 0; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, - (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - src_buf_ofs += src_buf_size; - *pOut_len += dst_buf_size; - if (status == TINFL_STATUS_DONE) break; - new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; - pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); - if (!pNew_buf) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; - } - return pBuf; -} - -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); - status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; -} - -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - int result = 0; - tinfl_decompressor decomp; - mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; - if (!pDict) - return TINFL_STATUS_FAILED; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, - (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); - in_buf_ofs += in_buf_size; - if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) - break; - if (status != TINFL_STATUS_HAS_MORE_OUTPUT) - { - result = (status == TINFL_STATUS_DONE); - break; - } - dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); - } - MZ_FREE(pDict); - *pIn_buf_size = in_buf_ofs; - return result; -} - -// ------------------- Low-level Compression (independent from all decompression API's) - -// Purposely making these tables static for faster init and thread safety. -static const mz_uint16 s_tdefl_len_sym[256] = { - 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, - 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, - 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, - 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, - 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, - 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, - 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, - 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; - -static const mz_uint8 s_tdefl_len_extra[256] = { - 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; - -static const mz_uint8 s_tdefl_small_dist_sym[512] = { - 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, - 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, - 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; - -static const mz_uint8 s_tdefl_small_dist_extra[512] = { - 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7 }; - -static const mz_uint8 s_tdefl_large_dist_sym[128] = { - 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, - 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; - -static const mz_uint8 s_tdefl_large_dist_extra[128] = { - 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, - 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, - 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; - -// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. -typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; -static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) -{ - mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); - for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } - while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; - for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) - { - const mz_uint32* pHist = &hist[pass << 8]; - mz_uint offsets[256], cur_ofs = 0; - for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } - for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; - { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } - } - return pCur_syms; -} - -// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. -static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) -{ - int root, leaf, next, avbl, used, dpth; - if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } - A[0].m_key += A[1].m_key; root = 0; leaf = 2; - for (next=1; next < n-1; next++) - { - if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; - avbl = 1; used = dpth = 0; root = n-2; next = n-1; - while (avbl>0) - { - while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } - while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } - avbl = 2*used; dpth++; used = 0; - } -} - -// Limits canonical Huffman code table's max code size. -enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; -static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) -{ - int i; mz_uint32 total = 0; if (code_list_len <= 1) return; - for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; - for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); - while (total != (1UL << max_code_size)) - { - pNum_codes[max_code_size]--; - for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } - total--; - } -} - -static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) -{ - int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); - if (static_table) - { - for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; - } - else - { - tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; - int num_used_syms = 0; - const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; - for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } - - pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); - - for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; - - tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); - - MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); - for (i = 1, j = num_used_syms; i <= code_size_limit; i++) - for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); - } - - next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); - - for (i = 0; i < table_len; i++) - { - mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; - code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); - d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; - } -} - -#define TDEFL_PUT_BITS(b, l) do { \ - mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ - d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ - while (d->m_bits_in >= 8) { \ - if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ - *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ - d->m_bit_buffer >>= 8; \ - d->m_bits_in -= 8; \ - } \ -} MZ_MACRO_END - -#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ - if (rle_repeat_count < 3) { \ - d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ - while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ - } else { \ - d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ -} rle_repeat_count = 0; } } - -#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ - if (rle_z_count < 3) { \ - d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ - } else if (rle_z_count <= 10) { \ - d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ - } else { \ - d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ -} rle_z_count = 0; } } - -static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - -static void tdefl_start_dynamic_block(tdefl_compressor *d) -{ - int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; - mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; - - d->m_huff_count[0][256] = 1; - - tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); - tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); - - for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; - for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; - - memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); - memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); - total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; - - memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); - for (i = 0; i < total_code_sizes_to_pack; i++) - { - mz_uint8 code_size = code_sizes_to_pack[i]; - if (!code_size) - { - TDEFL_RLE_PREV_CODE_SIZE(); - if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } - } - else - { - TDEFL_RLE_ZERO_CODE_SIZE(); - if (code_size != prev_code_size) - { - TDEFL_RLE_PREV_CODE_SIZE(); - d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; - } - else if (++rle_repeat_count == 6) - { - TDEFL_RLE_PREV_CODE_SIZE(); - } - } - prev_code_size = code_size; - } - if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } - - tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); - - TDEFL_PUT_BITS(2, 2); - - TDEFL_PUT_BITS(num_lit_codes - 257, 5); - TDEFL_PUT_BITS(num_dist_codes - 1, 5); - - for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; - num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); - for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); - - for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) - { - mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); - TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); - if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); - } -} - -static void tdefl_start_static_block(tdefl_compressor *d) -{ - mz_uint i; - mz_uint8 *p = &d->m_huff_code_sizes[0][0]; - - for (i = 0; i <= 143; ++i) *p++ = 8; - for ( ; i <= 255; ++i) *p++ = 9; - for ( ; i <= 279; ++i) *p++ = 7; - for ( ; i <= 287; ++i) *p++ = 8; - - memset(d->m_huff_code_sizes[1], 5, 32); - - tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); - tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); - - TDEFL_PUT_BITS(1, 2); -} - -static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS -static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) -{ - mz_uint flags; - mz_uint8 *pLZ_codes; - mz_uint8 *pOutput_buf = d->m_pOutput_buf; - mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; - mz_uint64 bit_buffer = d->m_bit_buffer; - mz_uint bits_in = d->m_bits_in; - -#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } - - flags = 1; - for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) - { - if (flags == 1) - flags = *pLZ_codes++ | 0x100; - - if (flags & 1) - { - mz_uint s0, s1, n0, n1, sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; - - MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - - // This sequence coaxes MSVC into using cmov's vs. jmp's. - s0 = s_tdefl_small_dist_sym[match_dist & 511]; - n0 = s_tdefl_small_dist_extra[match_dist & 511]; - s1 = s_tdefl_large_dist_sym[match_dist >> 8]; - n1 = s_tdefl_large_dist_extra[match_dist >> 8]; - sym = (match_dist < 512) ? s0 : s1; - num_extra_bits = (match_dist < 512) ? n0 : n1; - - MZ_ASSERT(d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); - } - else - { - mz_uint lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - - if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) - { - flags >>= 1; - lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - - if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) - { - flags >>= 1; - lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - } - } - } - - if (pOutput_buf >= d->m_pOutput_buf_end) - return MZ_FALSE; - - *(mz_uint64*)pOutput_buf = bit_buffer; - pOutput_buf += (bits_in >> 3); - bit_buffer >>= (bits_in & ~7); - bits_in &= 7; - } - -#undef TDEFL_PUT_BITS_FAST - - d->m_pOutput_buf = pOutput_buf; - d->m_bits_in = 0; - d->m_bit_buffer = 0; - - while (bits_in) - { - mz_uint32 n = MZ_MIN(bits_in, 16); - TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); - bit_buffer >>= n; - bits_in -= n; - } - - TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); - - return (d->m_pOutput_buf < d->m_pOutput_buf_end); -} -#else -static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) -{ - mz_uint flags; - mz_uint8 *pLZ_codes; - - flags = 1; - for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) - { - if (flags == 1) - flags = *pLZ_codes++ | 0x100; - if (flags & 1) - { - mz_uint sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; - - MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - - if (match_dist < 512) - { - sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; - } - else - { - sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; - } - MZ_ASSERT(d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); - } - else - { - mz_uint lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - } - } - - TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); - - return (d->m_pOutput_buf < d->m_pOutput_buf_end); -} -#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS - -static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) -{ - if (static_block) - tdefl_start_static_block(d); - else - tdefl_start_dynamic_block(d); - return tdefl_compress_lz_codes(d); -} - -static int tdefl_flush_block(tdefl_compressor *d, int flush) -{ - mz_uint saved_bit_buf, saved_bits_in; - mz_uint8 *pSaved_output_buf; - mz_bool comp_block_succeeded = MZ_FALSE; - int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; - mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; - - d->m_pOutput_buf = pOutput_buf_start; - d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; - - MZ_ASSERT(!d->m_output_flush_remaining); - d->m_output_flush_ofs = 0; - d->m_output_flush_remaining = 0; - - *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); - d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); - - if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) - { - TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); - } - - TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); - - pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; - - if (!use_raw_block) - comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); - - // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. - if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && - ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) - { - mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; - TDEFL_PUT_BITS(0, 2); - if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) - { - TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); - } - for (i = 0; i < d->m_total_lz_bytes; ++i) - { - TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); - } - } - // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. - else if (!comp_block_succeeded) - { - d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; - tdefl_compress_block(d, MZ_TRUE); - } - - if (flush) - { - if (flush == TDEFL_FINISH) - { - if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } - } - else - { - mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } - } - } - - MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); - - memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); - memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); - - d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; - - if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) - { - if (d->m_pPut_buf_func) - { - *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; - if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) - return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); - } - else if (pOutput_buf_start == d->m_output_buf) - { - int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); - memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); - d->m_out_buf_ofs += bytes_to_copy; - if ((n -= bytes_to_copy) != 0) - { - d->m_output_flush_ofs = bytes_to_copy; - d->m_output_flush_remaining = n; - } - } - else - { - d->m_out_buf_ofs += n; - } - } - - return d->m_output_flush_remaining; -} - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES -#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) -static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) -{ - mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; - mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; - const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; - mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); - MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; - for ( ; ; ) - { - for ( ; ; ) - { - if (--num_probes_left == 0) return; - #define TDEFL_PROBE \ - next_probe_pos = d->m_next[probe_pos]; \ - if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ - probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ - if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; - TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; - } - if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; - do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && - (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); - if (!probe_len) - { - *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; - } - else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) - { - *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; - c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); - } - } -} -#else -static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) -{ - mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; - mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; - const mz_uint8 *s = d->m_dict + pos, *p, *q; - mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; - MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; - for ( ; ; ) - { - for ( ; ; ) - { - if (--num_probes_left == 0) return; - #define TDEFL_PROBE \ - next_probe_pos = d->m_next[probe_pos]; \ - if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ - probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ - if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; - TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; - } - if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; - if (probe_len > match_len) - { - *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; - c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; - } - } -} -#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN -static mz_bool tdefl_compress_fast(tdefl_compressor *d) -{ - // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. - mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; - mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; - mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - - while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) - { - const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; - mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; - mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); - d->m_src_buf_left -= num_bytes_to_process; - lookahead_size += num_bytes_to_process; - - while (num_bytes_to_process) - { - mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); - memcpy(d->m_dict + dst_pos, d->m_pSrc, n); - if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) - memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); - d->m_pSrc += n; - dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; - num_bytes_to_process -= n; - } - - dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); - if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; - - while (lookahead_size >= 4) - { - mz_uint cur_match_dist, cur_match_len = 1; - mz_uint8 *pCur_dict = d->m_dict + cur_pos; - mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; - mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; - mz_uint probe_pos = d->m_hash[hash]; - d->m_hash[hash] = (mz_uint16)lookahead_pos; - - if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) - { - const mz_uint16 *p = (const mz_uint16 *)pCur_dict; - const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); - mz_uint32 probe_len = 32; - do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && - (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); - cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); - if (!probe_len) - cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; - - if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) - { - cur_match_len = 1; - *pLZ_code_buf++ = (mz_uint8)first_trigram; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - d->m_huff_count[0][(mz_uint8)first_trigram]++; - } - else - { - mz_uint32 s0, s1; - cur_match_len = MZ_MIN(cur_match_len, lookahead_size); - - MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); - - cur_match_dist--; - - pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); - *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; - pLZ_code_buf += 3; - *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); - - s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; - s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; - d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; - - d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; - } - } - else - { - *pLZ_code_buf++ = (mz_uint8)first_trigram; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - d->m_huff_count[0][(mz_uint8)first_trigram]++; - } - - if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } - - total_lz_bytes += cur_match_len; - lookahead_pos += cur_match_len; - dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); - cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; - MZ_ASSERT(lookahead_size >= cur_match_len); - lookahead_size -= cur_match_len; - - if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) - { - int n; - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; - } - } - - while (lookahead_size) - { - mz_uint8 lit = d->m_dict[cur_pos]; - - total_lz_bytes++; - *pLZ_code_buf++ = lit; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } - - d->m_huff_count[0][lit]++; - - lookahead_pos++; - dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); - cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; - lookahead_size--; - - if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) - { - int n; - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; - } - } - } - - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - return MZ_TRUE; -} -#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - -static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) -{ - d->m_total_lz_bytes++; - *d->m_pLZ_code_buf++ = lit; - *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } - d->m_huff_count[0][lit]++; -} - -static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) -{ - mz_uint32 s0, s1; - - MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); - - d->m_total_lz_bytes += match_len; - - d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); - - match_dist -= 1; - d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); - d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; - - *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } - - s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; - d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; - - if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; -} - -static mz_bool tdefl_compress_normal(tdefl_compressor *d) -{ - const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; - tdefl_flush flush = d->m_flush; - - while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) - { - mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; - // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. - if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) - { - mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; - mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; - mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); - const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; - src_buf_left -= num_bytes_to_process; - d->m_lookahead_size += num_bytes_to_process; - while (pSrc != pSrc_end) - { - mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; - hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); - d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); - dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; - } - } - else - { - while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) - { - mz_uint8 c = *pSrc++; - mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; - src_buf_left--; - d->m_dict[dst_pos] = c; - if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) - d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; - if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) - { - mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; - mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); - d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); - } - } - } - d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); - if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) - break; - - // Simple lazy/greedy parsing state machine. - len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) - { - if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) - { - mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; - cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } - if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; - } - } - else - { - tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); - } - if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) - { - cur_match_dist = cur_match_len = 0; - } - if (d->m_saved_match_len) - { - if (cur_match_len > d->m_saved_match_len) - { - tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); - if (cur_match_len >= 128) - { - tdefl_record_match(d, cur_match_len, cur_match_dist); - d->m_saved_match_len = 0; len_to_move = cur_match_len; - } - else - { - d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; - } - } - else - { - tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); - len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; - } - } - else if (!cur_match_dist) - tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); - else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) - { - tdefl_record_match(d, cur_match_len, cur_match_dist); - len_to_move = cur_match_len; - } - else - { - d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; - } - // Move the lookahead forward by len_to_move bytes. - d->m_lookahead_pos += len_to_move; - MZ_ASSERT(d->m_lookahead_size >= len_to_move); - d->m_lookahead_size -= len_to_move; - d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); - // Check if it's time to flush the current LZ codes to the internal output buffer. - if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || - ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) - { - int n; - d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - } - } - - d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; - return MZ_TRUE; -} - -static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) -{ - if (d->m_pIn_buf_size) - { - *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; - } - - if (d->m_pOut_buf_size) - { - size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); - memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); - d->m_output_flush_ofs += (mz_uint)n; - d->m_output_flush_remaining -= (mz_uint)n; - d->m_out_buf_ofs += n; - - *d->m_pOut_buf_size = d->m_out_buf_ofs; - } - - return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; -} - -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) -{ - if (!d) - { - if (pIn_buf_size) *pIn_buf_size = 0; - if (pOut_buf_size) *pOut_buf_size = 0; - return TDEFL_STATUS_BAD_PARAM; - } - - d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; - d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; - d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; - d->m_out_buf_ofs = 0; - d->m_flush = flush; - - if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || - (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) - { - if (pIn_buf_size) *pIn_buf_size = 0; - if (pOut_buf_size) *pOut_buf_size = 0; - return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); - } - d->m_wants_to_finish |= (flush == TDEFL_FINISH); - - if ((d->m_output_flush_remaining) || (d->m_finished)) - return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && - ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && - ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) - { - if (!tdefl_compress_fast(d)) - return d->m_prev_return_status; - } - else -#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - { - if (!tdefl_compress_normal(d)) - return d->m_prev_return_status; - } - - if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) - d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); - - if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) - { - if (tdefl_flush_block(d, flush) < 0) - return d->m_prev_return_status; - d->m_finished = (flush == TDEFL_FINISH); - if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } - } - - return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); -} - -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) -{ - MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); -} - -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; - d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; - d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; - if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); - d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; - d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; - d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; - d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; - d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; - d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; - d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; - d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; - memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); - memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); - return TDEFL_STATUS_OKAY; -} - -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) -{ - return d->m_prev_return_status; -} - -mz_uint32 tdefl_get_adler32(tdefl_compressor *d) -{ - return d->m_adler32; -} - -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; - pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; - succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); - succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); - MZ_FREE(pComp); return succeeded; -} - -typedef struct -{ - size_t m_size, m_capacity; - mz_uint8 *m_pBuf; - mz_bool m_expandable; -} tdefl_output_buffer; - -static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) -{ - tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; - size_t new_size = p->m_size + len; - if (new_size > p->m_capacity) - { - size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; - do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); - pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; - p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; - } - memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; - return MZ_TRUE; -} - -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); - if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; - out_buf.m_expandable = MZ_TRUE; - if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; - *pOut_len = out_buf.m_size; return out_buf.m_pBuf; -} - -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); - if (!pOut_buf) return 0; - out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; - if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; - return out_buf.m_size; -} - -#ifndef MINIZ_NO_ZLIB_APIS -static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; - -// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) -{ - mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); - if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; - - if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; - else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; - else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; - else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; - else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; - - return comp_flags; -} -#endif //MINIZ_NO_ZLIB_APIS - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) -#endif - -// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at -// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. -// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) -{ - // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. - static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; - tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; - if (!pComp) return NULL; - MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } - // write dummy header - for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); - // compress image data - tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); - for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } - if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } - // write real header - *pLen_out = out_buf.m_size-41; - { - static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; - mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, - 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, - (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; - c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); - memcpy(out_buf.m_pBuf, pnghdr, 41); - } - // write footer (IDAT CRC-32, followed by IEND chunk) - if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } - c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); - // compute final size of file, grab compressed data buffer and return - *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; -} -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) -{ - // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) - return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); -} - -#ifdef _MSC_VER -#pragma warning (pop) -#endif - -// ------------------- .ZIP archive reading - -#ifndef MINIZ_NO_ARCHIVE_APIS - -#ifdef MINIZ_NO_STDIO - #define MZ_FILE void * -#else - #include - #include - - #if defined(_MSC_VER) || defined(__MINGW64__) - static FILE *mz_fopen(const char *pFilename, const char *pMode) - { - FILE* pFile = NULL; - fopen_s(&pFile, pFilename, pMode); - return pFile; - } - static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) - { - FILE* pFile = NULL; - if (freopen_s(&pFile, pPath, pMode, pStream)) - return NULL; - return pFile; - } - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN mz_fopen - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 _ftelli64 - #define MZ_FSEEK64 _fseeki64 - #define MZ_FILE_STAT_STRUCT _stat - #define MZ_FILE_STAT _stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN mz_freopen - #define MZ_DELETE_FILE remove - #elif defined(__MINGW32__) - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello64 - #define MZ_FSEEK64 fseeko64 - #define MZ_FILE_STAT_STRUCT _stat - #define MZ_FILE_STAT _stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #elif defined(__TINYC__) - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftell - #define MZ_FSEEK64 fseek - #define MZ_FILE_STAT_STRUCT stat - #define MZ_FILE_STAT stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #elif defined(__GNUC__) && _LARGEFILE64_SOURCE - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen64(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello64 - #define MZ_FSEEK64 fseeko64 - #define MZ_FILE_STAT_STRUCT stat64 - #define MZ_FILE_STAT stat64 - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) - #define MZ_DELETE_FILE remove - #else - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello - #define MZ_FSEEK64 fseeko - #define MZ_FILE_STAT_STRUCT stat - #define MZ_FILE_STAT stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #endif // #ifdef _MSC_VER -#endif // #ifdef MINIZ_NO_STDIO - -#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) - -// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. -enum -{ - // ZIP archive identifiers and record sizes - MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, - MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, - // Central directory header record offsets - MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, - MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, - MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, - MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, - // Local directory header offsets - MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, - MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, - MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, - // End of central directory offsets - MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, - MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, -}; - -typedef struct -{ - void *m_p; - size_t m_size, m_capacity; - mz_uint m_element_size; -} mz_zip_array; - -struct mz_zip_internal_state_tag -{ - mz_zip_array m_central_dir; - mz_zip_array m_central_dir_offsets; - mz_zip_array m_sorted_central_dir_offsets; - MZ_FILE *m_pFile; - void *m_pMem; - size_t m_mem_size; - size_t m_mem_capacity; -}; - -#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size -#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] - -static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) -{ - pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); - memset(pArray, 0, sizeof(mz_zip_array)); -} - -static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) -{ - void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; - if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } - if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; - pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) -{ - if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) -{ - if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } - pArray->m_size = new_size; - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) -{ - return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) -{ - size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; - memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); - return MZ_TRUE; -} - -#ifndef MINIZ_NO_TIME -static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) -{ - struct tm tm; - memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; - tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; - tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; - return mktime(&tm); -} - -static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) -{ -#ifdef _MSC_VER - struct tm tm_struct; - struct tm *tm = &tm_struct; - errno_t err = localtime_s(tm, &time); - if (err) - { - *pDOS_date = 0; *pDOS_time = 0; - return; - } -#else - struct tm *tm = localtime(&time); -#endif - *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); - *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); -} -#endif - -#ifndef MINIZ_NO_STDIO -static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) -{ -#ifdef MINIZ_NO_TIME - (void)pFilename; *pDOS_date = *pDOS_time = 0; -#else - struct MZ_FILE_STAT_STRUCT file_stat; - // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. - if (MZ_FILE_STAT(pFilename, &file_stat) != 0) - return MZ_FALSE; - mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); -#endif // #ifdef MINIZ_NO_TIME - return MZ_TRUE; -} - -#ifndef MINIZ_NO_TIME -static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) -{ - struct utimbuf t; t.actime = access_time; t.modtime = modified_time; - return !utime(pFilename, &t); -} -#endif // #ifndef MINIZ_NO_TIME -#endif // #ifndef MINIZ_NO_STDIO - -static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) -{ - (void)flags; - if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) - return MZ_FALSE; - - if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; - if (!pZip->m_pFree) pZip->m_pFree = def_free_func; - if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; - - pZip->m_zip_mode = MZ_ZIP_MODE_READING; - pZip->m_archive_size = 0; - pZip->m_central_directory_file_ofs = 0; - pZip->m_total_files = 0; - - if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) - return MZ_FALSE; - memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) -{ - const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; - const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); - mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); - mz_uint8 l = 0, r = 0; - pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - pE = pL + MZ_MIN(l_len, r_len); - while (pL < pE) - { - if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) - break; - pL++; pR++; - } - return (pL == pE) ? (l_len < r_len) : (l < r); -} - -#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END - -// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) -static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState = pZip->m_pState; - const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; - const mz_zip_array *pCentral_dir = &pState->m_central_dir; - mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const int size = pZip->m_total_files; - int start = (size - 2) >> 1, end; - while (start >= 0) - { - int child, root = start; - for ( ; ; ) - { - if ((child = (root << 1) + 1) >= size) - break; - child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); - if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) - break; - MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; - } - start--; - } - - end = size - 1; - while (end > 0) - { - int child, root = 0; - MZ_SWAP_UINT32(pIndices[end], pIndices[0]); - for ( ; ; ) - { - if ((child = (root << 1) + 1) >= end) - break; - child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); - if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) - break; - MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; - } - end--; - } -} - -static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) -{ - mz_uint cdir_size, num_this_disk, cdir_disk_index; - mz_uint64 cdir_ofs; - mz_int64 cur_file_ofs; - const mz_uint8 *p; - mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; - mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); - // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. - if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - // Find the end of central directory record by scanning the file from the end towards the beginning. - cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); - for ( ; ; ) - { - int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) - return MZ_FALSE; - for (i = n - 4; i >= 0; --i) - if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) - break; - if (i >= 0) - { - cur_file_ofs += i; - break; - } - if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) - return MZ_FALSE; - cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); - } - // Read and verify the end of central directory record. - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || - ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) - return MZ_FALSE; - - num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); - cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); - if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) - return MZ_FALSE; - - if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - - cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); - if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) - return MZ_FALSE; - - pZip->m_central_directory_file_ofs = cdir_ofs; - - if (pZip->m_total_files) - { - mz_uint i, n; - - // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. - if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || - (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) - return MZ_FALSE; - - if (sort_central_dir) - { - if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) - return MZ_FALSE; - } - - if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) - return MZ_FALSE; - - // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). - p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; - for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) - { - mz_uint total_header_size, comp_size, decomp_size, disk_index; - if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) - return MZ_FALSE; - MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); - if (sort_central_dir) - MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) - return MZ_FALSE; - disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); - if ((disk_index != num_this_disk) && (disk_index != 1)) - return MZ_FALSE; - if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) - return MZ_FALSE; - if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) - return MZ_FALSE; - n -= total_header_size; p += total_header_size; - } - } - - if (sort_central_dir) - mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); - - return MZ_TRUE; -} - -mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) -{ - if ((!pZip) || (!pZip->m_pRead)) - return MZ_FALSE; - if (!mz_zip_reader_init_internal(pZip, flags)) - return MZ_FALSE; - pZip->m_archive_size = size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} - -static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); - memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); - return s; -} - -mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) -{ - if (!mz_zip_reader_init_internal(pZip, flags)) - return MZ_FALSE; - pZip->m_archive_size = size; - pZip->m_pRead = mz_zip_mem_read_func; - pZip->m_pIO_opaque = pZip; -#ifdef __cplusplus - pZip->m_pState->m_pMem = const_cast(pMem); -#else - pZip->m_pState->m_pMem = (void *)pMem; -#endif - pZip->m_pState->m_mem_size = size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) - return 0; - return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); -} - -mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) -{ - mz_uint64 file_size; - MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); - if (!pFile) - return MZ_FALSE; - if (MZ_FSEEK64(pFile, 0, SEEK_END)) - { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - file_size = MZ_FTELL64(pFile); - if (!mz_zip_reader_init_internal(pZip, flags)) - { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - pZip->m_pRead = mz_zip_file_read_func; - pZip->m_pIO_opaque = pZip; - pZip->m_pState->m_pFile = pFile; - pZip->m_archive_size = file_size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) -{ - return pZip ? pZip->m_total_files : 0; -} - -static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) -{ - if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return NULL; - return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); -} - -mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) -{ - mz_uint m_bit_flag; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) - return MZ_FALSE; - m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); - return (m_bit_flag & 1); -} - -mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) -{ - mz_uint filename_len, external_attr; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) - return MZ_FALSE; - - // First see if the filename ends with a '/' character. - filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); - if (filename_len) - { - if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') - return MZ_TRUE; - } - - // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. - // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. - // FIXME: Remove this check? Is it necessary - we already check the filename. - external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); - if ((external_attr & 0x10) != 0) - return MZ_TRUE; - - return MZ_FALSE; -} - -mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) -{ - mz_uint n; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if ((!p) || (!pStat)) - return MZ_FALSE; - - // Unpack the central directory record. - pStat->m_file_index = file_index; - pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); - pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); - pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); - pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); - pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); -#ifndef MINIZ_NO_TIME - pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); -#endif - pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); - pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); - pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); - pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); - - // Copy as much of the filename and comment as possible. - n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); - memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; - - n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); - pStat->m_comment_size = n; - memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; - - return MZ_TRUE; -} - -mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) -{ - mz_uint n; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } - n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); - if (filename_buf_size) - { - n = MZ_MIN(n, filename_buf_size - 1); - memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); - pFilename[n] = '\0'; - } - return n + 1; -} - -static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) -{ - mz_uint i; - if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) - return 0 == memcmp(pA, pB, len); - for (i = 0; i < len; ++i) - if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) - return MZ_FALSE; - return MZ_TRUE; -} - -static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) -{ - const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; - mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); - mz_uint8 l = 0, r = 0; - pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - pE = pL + MZ_MIN(l_len, r_len); - while (pL < pE) - { - if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) - break; - pL++; pR++; - } - return (pL == pE) ? (int)(l_len - r_len) : (l - r); -} - -static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) -{ - mz_zip_internal_state *pState = pZip->m_pState; - const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; - const mz_zip_array *pCentral_dir = &pState->m_central_dir; - mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const int size = pZip->m_total_files; - const mz_uint filename_len = (mz_uint)strlen(pFilename); - int l = 0, h = size - 1; - while (l <= h) - { - int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); - if (!comp) - return file_index; - else if (comp < 0) - l = m + 1; - else - h = m - 1; - } - return -1; -} - -int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) -{ - mz_uint file_index; size_t name_len, comment_len; - if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return -1; - if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) - return mz_zip_reader_locate_file_binary_search(pZip, pName); - name_len = strlen(pName); if (name_len > 0xFFFF) return -1; - comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; - for (file_index = 0; file_index < pZip->m_total_files; file_index++) - { - const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); - mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); - const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - if (filename_len < name_len) - continue; - if (comment_len) - { - mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); - const char *pFile_comment = pFilename + filename_len + file_extra_len; - if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) - continue; - } - if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) - { - int ofs = filename_len - 1; - do - { - if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) - break; - } while (--ofs >= 0); - ofs++; - pFilename += ofs; filename_len -= ofs; - } - if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) - return file_index; - } - return -1; -} - -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - int status = TINFL_STATUS_DONE; - mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; - mz_zip_archive_file_stat file_stat; - void *pRead_buf; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - tinfl_decompressor inflator; - - if ((buf_size) && (!pBuf)) - return MZ_FALSE; - - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - - // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) - if (!file_stat.m_comp_size) - return MZ_TRUE; - - // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). - // I'm torn how to handle this case - should it fail instead? - if (mz_zip_reader_is_file_a_directory(pZip, file_index)) - return MZ_TRUE; - - // Encryption and patch files are not supported. - if (file_stat.m_bit_flag & (1 | 32)) - return MZ_FALSE; - - // This function only supports stored and deflate. - if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) - return MZ_FALSE; - - // Ensure supplied output buffer is large enough. - needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; - if (buf_size < needed_size) - return MZ_FALSE; - - // Read and parse the local directory entry. - cur_file_ofs = file_stat.m_local_header_ofs; - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) - return MZ_FALSE; - - if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) - { - // The file is stored or the caller has requested the compressed data. - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) - return MZ_FALSE; - return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); - } - - // Decompress the file either directly from memory or from a file input buffer. - tinfl_init(&inflator); - - if (pZip->m_pState->m_pMem) - { - // Read directly from the archive in memory. - pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; - read_buf_size = read_buf_avail = file_stat.m_comp_size; - comp_remaining = 0; - } - else if (pUser_read_buf) - { - // Use a user provided read buffer. - if (!user_read_buf_size) - return MZ_FALSE; - pRead_buf = (mz_uint8 *)pUser_read_buf; - read_buf_size = user_read_buf_size; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - else - { - // Temporarily allocate a read buffer. - read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) -#endif - return MZ_FALSE; - if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) - return MZ_FALSE; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - - do - { - size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); - if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - read_buf_ofs = 0; - } - in_buf_size = (size_t)read_buf_avail; - status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); - read_buf_avail -= in_buf_size; - read_buf_ofs += in_buf_size; - out_buf_ofs += out_buf_size; - } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); - - if (status == TINFL_STATUS_DONE) - { - // Make sure the entire file was decompressed, and check its CRC. - if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) - status = TINFL_STATUS_FAILED; - } - - if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - - return status == TINFL_STATUS_DONE; -} - -mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); -} - -mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) -{ - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); -} - -mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) -{ - return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); -} - -void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) -{ - mz_uint64 comp_size, uncomp_size, alloc_size; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - void *pBuf; - - if (pSize) - *pSize = 0; - if (!p) - return NULL; - - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - - alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#endif - return NULL; - if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) - return NULL; - - if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return NULL; - } - - if (pSize) *pSize = (size_t)alloc_size; - return pBuf; -} - -void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - { - if (pSize) *pSize = 0; - return MZ_FALSE; - } - return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); -} - -mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) -{ - int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; - mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; - mz_zip_archive_file_stat file_stat; - void *pRead_buf = NULL; void *pWrite_buf = NULL; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - - // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) - if (!file_stat.m_comp_size) - return MZ_TRUE; - - // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). - // I'm torn how to handle this case - should it fail instead? - if (mz_zip_reader_is_file_a_directory(pZip, file_index)) - return MZ_TRUE; - - // Encryption and patch files are not supported. - if (file_stat.m_bit_flag & (1 | 32)) - return MZ_FALSE; - - // This function only supports stored and deflate. - if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) - return MZ_FALSE; - - // Read and parse the local directory entry. - cur_file_ofs = file_stat.m_local_header_ofs; - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) - return MZ_FALSE; - - // Decompress the file either directly from memory or from a file input buffer. - if (pZip->m_pState->m_pMem) - { - pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; - read_buf_size = read_buf_avail = file_stat.m_comp_size; - comp_remaining = 0; - } - else - { - read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); - if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) - return MZ_FALSE; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - - if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) - { - // The file is stored or the caller has requested the compressed data. - if (pZip->m_pState->m_pMem) - { -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#endif - return MZ_FALSE; - if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) - status = TINFL_STATUS_FAILED; - else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); - cur_file_ofs += file_stat.m_comp_size; - out_buf_ofs += file_stat.m_comp_size; - comp_remaining = 0; - } - else - { - while (comp_remaining) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - - if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); - - if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - out_buf_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - } - } - } - else - { - tinfl_decompressor inflator; - tinfl_init(&inflator); - - if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) - status = TINFL_STATUS_FAILED; - else - { - do - { - mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); - size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); - if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - read_buf_ofs = 0; - } - - in_buf_size = (size_t)read_buf_avail; - status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); - read_buf_avail -= in_buf_size; - read_buf_ofs += in_buf_size; - - if (out_buf_size) - { - if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) - { - status = TINFL_STATUS_FAILED; - break; - } - file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); - if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) - { - status = TINFL_STATUS_FAILED; - break; - } - } - } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); - } - } - - if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) - { - // Make sure the entire file was decompressed, and check its CRC. - if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) - status = TINFL_STATUS_FAILED; - } - - if (!pZip->m_pState->m_pMem) - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - if (pWrite_buf) - pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); - - return status == TINFL_STATUS_DONE; -} - -mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) -{ - (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); -} - -mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) -{ - mz_bool status; - mz_zip_archive_file_stat file_stat; - MZ_FILE *pFile; - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - pFile = MZ_FOPEN(pDst_filename, "wb"); - if (!pFile) - return MZ_FALSE; - status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); - if (MZ_FCLOSE(pFile) == EOF) - return MZ_FALSE; -#ifndef MINIZ_NO_TIME - if (status) - mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); -#endif - return status; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_reader_end(mz_zip_archive *pZip) -{ - if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return MZ_FALSE; - - if (pZip->m_pState) - { - mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; - mz_zip_array_clear(pZip, &pState->m_central_dir); - mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); - mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); - -#ifndef MINIZ_NO_STDIO - if (pState->m_pFile) - { - MZ_FCLOSE(pState->m_pFile); - pState->m_pFile = NULL; - } -#endif // #ifndef MINIZ_NO_STDIO - - pZip->m_pFree(pZip->m_pAlloc_opaque, pState); - } - pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; - - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); -} -#endif - -// ------------------- .ZIP archive writing - -#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } -static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } -#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) -#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) - -mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) -{ - if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) - return MZ_FALSE; - - if (pZip->m_file_offset_alignment) - { - // Ensure user specified file offset alignment is a power of 2. - if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) - return MZ_FALSE; - } - - if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; - if (!pZip->m_pFree) pZip->m_pFree = def_free_func; - if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; - - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - pZip->m_archive_size = existing_size; - pZip->m_central_directory_file_ofs = 0; - pZip->m_total_files = 0; - - if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) - return MZ_FALSE; - memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); - return MZ_TRUE; -} - -static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_zip_internal_state *pState = pZip->m_pState; - mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); -#ifdef _MSC_VER - if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) -#else - if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) -#endif - return 0; - if (new_size > pState->m_mem_capacity) - { - void *pNew_block; - size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; - if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) - return 0; - pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; - } - memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); - pState->m_mem_size = (size_t)new_size; - return n; -} - -mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) -{ - pZip->m_pWrite = mz_zip_heap_write_func; - pZip->m_pIO_opaque = pZip; - if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) - return MZ_FALSE; - if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) - { - if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - pZip->m_pState->m_mem_capacity = initial_allocation_size; - } - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) - return 0; - return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); -} - -mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) -{ - MZ_FILE *pFile; - pZip->m_pWrite = mz_zip_file_write_func; - pZip->m_pIO_opaque = pZip; - if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) - return MZ_FALSE; - if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - pZip->m_pState->m_pFile = pFile; - if (size_to_reserve_at_beginning) - { - mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); - do - { - size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - cur_ofs += n; size_to_reserve_at_beginning -= n; - } while (size_to_reserve_at_beginning); - } - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) -{ - mz_zip_internal_state *pState; - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return MZ_FALSE; - // No sense in trying to write to an archive that's already at the support max size - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - pState = pZip->m_pState; - - if (pState->m_pFile) - { -#ifdef MINIZ_NO_STDIO - pFilename; return MZ_FALSE; -#else - // Archive is being read from stdio - try to reopen as writable. - if (pZip->m_pIO_opaque != pZip) - return MZ_FALSE; - if (!pFilename) - return MZ_FALSE; - pZip->m_pWrite = mz_zip_file_write_func; - if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) - { - // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. - mz_zip_reader_end(pZip); - return MZ_FALSE; - } -#endif // #ifdef MINIZ_NO_STDIO - } - else if (pState->m_pMem) - { - // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. - if (pZip->m_pIO_opaque != pZip) - return MZ_FALSE; - pState->m_mem_capacity = pState->m_mem_size; - pZip->m_pWrite = mz_zip_heap_write_func; - } - // Archive is being read via a user provided read function - make sure the user has specified a write function too. - else if (!pZip->m_pWrite) - return MZ_FALSE; - - // Start writing new files at the archive's current central directory location. - pZip->m_archive_size = pZip->m_central_directory_file_ofs; - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - pZip->m_central_directory_file_ofs = 0; - - return MZ_TRUE; -} - -mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) -{ - return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); -} - -typedef struct -{ - mz_zip_archive *m_pZip; - mz_uint64 m_cur_archive_file_ofs; - mz_uint64 m_comp_size; -} mz_zip_writer_add_state; - -static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) -{ - mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; - if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) - return MZ_FALSE; - pState->m_cur_archive_file_ofs += len; - pState->m_comp_size += len; - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) -{ - (void)pZip; - memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) -{ - (void)pZip; - memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) -{ - mz_zip_internal_state *pState = pZip->m_pState; - mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; - size_t orig_central_dir_size = pState->m_central_dir.m_size; - mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - - // No zip64 support yet - if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) - return MZ_FALSE; - - if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) - { - // Try to push the central directory array back into its original state. - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) -{ - // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. - if (*pArchive_name == '/') - return MZ_FALSE; - while (*pArchive_name) - { - if ((*pArchive_name == '\\') || (*pArchive_name == ':')) - return MZ_FALSE; - pArchive_name++; - } - return MZ_TRUE; -} - -static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) -{ - mz_uint32 n; - if (!pZip->m_file_offset_alignment) - return 0; - n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); - return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); -} - -static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) -{ - char buf[4096]; - memset(buf, 0, MZ_MIN(sizeof(buf), n)); - while (n) - { - mz_uint32 s = MZ_MIN(sizeof(buf), n); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) - return MZ_FALSE; - cur_file_ofs += s; n -= s; - } - return MZ_TRUE; -} - -mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) -{ - mz_uint16 method = 0, dos_time = 0, dos_date = 0; - mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; - mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; - size_t archive_name_size; - mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - tdefl_compressor *pComp = NULL; - mz_bool store_data_uncompressed; - mz_zip_internal_state *pState; - - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - level = level_and_flags & 0xF; - store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - - pState = pZip->m_pState; - - if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) - return MZ_FALSE; - // No zip64 support yet - if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - -#ifndef MINIZ_NO_TIME - { - time_t cur_time; time(&cur_time); - mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); - } -#endif // #ifndef MINIZ_NO_TIME - - archive_name_size = strlen(pArchive_name); - if (archive_name_size > 0xFFFF) - return MZ_FALSE; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) - { - // Set DOS Subdirectory attribute bit. - ext_attributes |= 0x10; - // Subdirectories cannot contain data. - if ((buf_size) || (uncomp_size)) - return MZ_FALSE; - } - - // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) - if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) - return MZ_FALSE; - - if ((!store_data_uncompressed) && (buf_size)) - { - if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) - return MZ_FALSE; - } - - if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - local_dir_header_ofs += num_alignment_padding_bytes; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); - - MZ_CLEAR_OBJ(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - cur_archive_file_ofs += archive_name_size; - - if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - { - uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); - uncomp_size = buf_size; - if (uncomp_size <= 3) - { - level = 0; - store_data_uncompressed = MZ_TRUE; - } - } - - if (store_data_uncompressed) - { - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - - cur_archive_file_ofs += buf_size; - comp_size = buf_size; - - if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) - method = MZ_DEFLATED; - } - else if (buf_size) - { - mz_zip_writer_add_state state; - - state.m_pZip = pZip; - state.m_cur_archive_file_ofs = cur_archive_file_ofs; - state.m_comp_size = 0; - - if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || - (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - - comp_size = state.m_comp_size; - cur_archive_file_ofs = state.m_cur_archive_file_ofs; - - method = MZ_DEFLATED; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - pComp = NULL; - - // no zip64 support yet - if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) - return MZ_FALSE; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return MZ_FALSE; - - if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) - return MZ_FALSE; - - pZip->m_total_files++; - pZip->m_archive_size = cur_archive_file_ofs; - - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) -{ - mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; - mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; - mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; - size_t archive_name_size; - mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - MZ_FILE *pSrc_file = NULL; - - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - level = level_and_flags & 0xF; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - - archive_name_size = strlen(pArchive_name); - if (archive_name_size > 0xFFFF) - return MZ_FALSE; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) - return MZ_FALSE; - - pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); - if (!pSrc_file) - return MZ_FALSE; - MZ_FSEEK64(pSrc_file, 0, SEEK_END); - uncomp_size = MZ_FTELL64(pSrc_file); - MZ_FSEEK64(pSrc_file, 0, SEEK_SET); - - if (uncomp_size > 0xFFFFFFFF) - { - // No zip64 support yet - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - if (uncomp_size <= 3) - level = 0; - - if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - local_dir_header_ofs += num_alignment_padding_bytes; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); - - MZ_CLEAR_OBJ(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - cur_archive_file_ofs += archive_name_size; - - if (uncomp_size) - { - mz_uint64 uncomp_remaining = uncomp_size; - void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); - if (!pRead_buf) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - if (!level) - { - while (uncomp_remaining) - { - mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); - if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); - uncomp_remaining -= n; - cur_archive_file_ofs += n; - } - comp_size = uncomp_size; - } - else - { - mz_bool result = MZ_FALSE; - mz_zip_writer_add_state state; - tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); - if (!pComp) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - state.m_pZip = pZip; - state.m_cur_archive_file_ofs = cur_archive_file_ofs; - state.m_comp_size = 0; - - if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - for ( ; ; ) - { - size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); - tdefl_status status; - - if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) - break; - - uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); - uncomp_remaining -= in_buf_size; - - status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); - if (status == TDEFL_STATUS_DONE) - { - result = MZ_TRUE; - break; - } - else if (status != TDEFL_STATUS_OKAY) - break; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - - if (!result) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - comp_size = state.m_comp_size; - cur_archive_file_ofs = state.m_cur_archive_file_ofs; - - method = MZ_DEFLATED; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - } - - MZ_FCLOSE(pSrc_file); pSrc_file = NULL; - - // no zip64 support yet - if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) - return MZ_FALSE; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return MZ_FALSE; - - if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) - return MZ_FALSE; - - pZip->m_total_files++; - pZip->m_archive_size = cur_archive_file_ofs; - - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) -{ - mz_uint n, bit_flags, num_alignment_padding_bytes; - mz_uint64 comp_bytes_remaining, local_dir_header_ofs; - mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - size_t orig_central_dir_size; - mz_zip_internal_state *pState; - void *pBuf; const mz_uint8 *pSrc_central_header; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) - return MZ_FALSE; - if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) - return MZ_FALSE; - pState = pZip->m_pState; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); - cur_dst_file_ofs = pZip->m_archive_size; - - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - - if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) - return MZ_FALSE; - cur_dst_file_ofs += num_alignment_padding_bytes; - local_dir_header_ofs = cur_dst_file_ofs; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - - n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - - if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) - return MZ_FALSE; - - while (comp_bytes_remaining) - { - n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - cur_src_file_ofs += n; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - cur_dst_file_ofs += n; - - comp_bytes_remaining -= n; - } - - bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); - if (bit_flags & 8) - { - // Copy data descriptor - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - - n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - - cur_src_file_ofs += n; - cur_dst_file_ofs += n; - } - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - - // no zip64 support yet - if (cur_dst_file_ofs > 0xFFFFFFFF) - return MZ_FALSE; - - orig_central_dir_size = pState->m_central_dir.m_size; - - memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) - return MZ_FALSE; - - n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) - { - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - if (pState->m_central_dir.m_size > 0xFFFFFFFF) - return MZ_FALSE; - n = (mz_uint32)orig_central_dir_size; - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) - { - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - pZip->m_total_files++; - pZip->m_archive_size = cur_dst_file_ofs; - - return MZ_TRUE; -} - -mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState; - mz_uint64 central_dir_ofs, central_dir_size; - mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) - return MZ_FALSE; - - pState = pZip->m_pState; - - // no zip64 support yet - if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - central_dir_ofs = 0; - central_dir_size = 0; - if (pZip->m_total_files) - { - // Write central directory - central_dir_ofs = pZip->m_archive_size; - central_dir_size = pState->m_central_dir.m_size; - pZip->m_central_directory_file_ofs = central_dir_ofs; - if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) - return MZ_FALSE; - pZip->m_archive_size += central_dir_size; - } - - // Write end of central directory record - MZ_CLEAR_OBJ(hdr); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); - MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); - - if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) - return MZ_FALSE; -#ifndef MINIZ_NO_STDIO - if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) - return MZ_FALSE; -#endif // #ifndef MINIZ_NO_STDIO - - pZip->m_archive_size += sizeof(hdr); - - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; - return MZ_TRUE; -} - -mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) -{ - if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) - return MZ_FALSE; - if (pZip->m_pWrite != mz_zip_heap_write_func) - return MZ_FALSE; - if (!mz_zip_writer_finalize_archive(pZip)) - return MZ_FALSE; - - *pBuf = pZip->m_pState->m_pMem; - *pSize = pZip->m_pState->m_mem_size; - pZip->m_pState->m_pMem = NULL; - pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; - return MZ_TRUE; -} - -mz_bool mz_zip_writer_end(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState; - mz_bool status = MZ_TRUE; - if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) - return MZ_FALSE; - - pState = pZip->m_pState; - pZip->m_pState = NULL; - mz_zip_array_clear(pZip, &pState->m_central_dir); - mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); - mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); - -#ifndef MINIZ_NO_STDIO - if (pState->m_pFile) - { - MZ_FCLOSE(pState->m_pFile); - pState->m_pFile = NULL; - } -#endif // #ifndef MINIZ_NO_STDIO - - if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); - pState->m_pMem = NULL; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pState); - pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; - return status; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) -{ - mz_bool status, created_new_archive = MZ_FALSE; - mz_zip_archive zip_archive; - struct MZ_FILE_STAT_STRUCT file_stat; - MZ_CLEAR_OBJ(zip_archive); - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) - { - // Create a new archive. - if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) - return MZ_FALSE; - created_new_archive = MZ_TRUE; - } - else - { - // Append to an existing archive. - if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) - return MZ_FALSE; - if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) - { - mz_zip_reader_end(&zip_archive); - return MZ_FALSE; - } - } - status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); - // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) - if (!mz_zip_writer_finalize_archive(&zip_archive)) - status = MZ_FALSE; - if (!mz_zip_writer_end(&zip_archive)) - status = MZ_FALSE; - if ((!status) && (created_new_archive)) - { - // It's a new archive and something went wrong, so just delete it. - int ignoredStatus = MZ_DELETE_FILE(pZip_filename); - (void)ignoredStatus; - } - return status; -} - -void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) -{ - int file_index; - mz_zip_archive zip_archive; - void *p = NULL; - - if (pSize) - *pSize = 0; - - if ((!pZip_filename) || (!pArchive_name)) - return NULL; - - MZ_CLEAR_OBJ(zip_archive); - if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) - return NULL; - - if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) - p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); - - mz_zip_reader_end(&zip_archive); - return p; -} - -#endif // #ifndef MINIZ_NO_STDIO - -#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -#endif // #ifndef MINIZ_NO_ARCHIVE_APIS - -#ifdef __cplusplus -} -#endif - -#endif // MINIZ_HEADER_FILE_ONLY - -/* - This is free and unencumbered software released into the public domain. - - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - For more information, please refer to -*/ - -namespace miniz_cpp { -namespace detail { - -#ifdef _WIN32 -char directory_separator = '\\'; -char alt_directory_separator = '/'; -#else -char directory_separator = '/'; -char alt_directory_separator = '\\'; -#endif - -std::string join_path(const std::vector &parts) -{ - std::string joined; - std::size_t i = 0; - for(auto part : parts) - { - joined.append(part); - - if(i++ != parts.size() - 1) - { - joined.append(1, '/'); - } - } - return joined; -} - -std::vector split_path(const std::string &path, char delim = directory_separator) -{ - std::vector split; - std::string::size_type previous_index = 0; - auto separator_index = path.find(delim); - - while(separator_index != std::string::npos) - { - auto part = path.substr(previous_index, separator_index - previous_index); - if(part != "..") - { - split.push_back(part); - } - else - { - split.pop_back(); - } - previous_index = separator_index + 1; - separator_index = path.find(delim, previous_index); - } - - split.push_back(path.substr(previous_index)); - - if(split.size() == 1 && delim == directory_separator) - { - auto alternative = split_path(path, alt_directory_separator); - if(alternative.size() > 1) - { - return alternative; - } - } - - return split; -} - -uint32_t crc32buf(const char *buf, std::size_t len) -{ - uint32_t oldcrc32 = 0xFFFFFFFF; - - uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d - }; - -#define UPDC32(octet,crc) (crc_32_tab[((crc)\ -^ static_cast(octet)) & 0xff] ^ ((crc) >> 8)) - - for ( ; len; --len, ++buf) - { - oldcrc32 = UPDC32(*buf, oldcrc32); - } - - return ~oldcrc32; -} - -tm safe_localtime(const time_t &t) -{ -#ifdef _WIN32 - tm time; - localtime_s(&time, &t); - return time; -#else - tm *time = localtime(&t); - assert(time != nullptr); - return *time; -#endif -} - -std::size_t write_callback(void *opaque, std::uint64_t file_ofs, const void *pBuf, std::size_t n) -{ - auto buffer = static_cast *>(opaque); - - if(file_ofs + n > buffer->size()) - { - auto new_size = static_cast::size_type>(file_ofs + n); - buffer->resize(new_size); - } - - for(std::size_t i = 0; i < n; i++) - { - (*buffer)[static_cast(file_ofs + i)] = (static_cast(pBuf))[i]; - } - - return n; -} - -} // namespace detail - -struct zip_info -{ - std::string filename; - - struct - { - int year = 1980; - int month = 0; - int day = 0; - int hours = 0; - int minutes = 0; - int seconds = 0; - } date_time; - - std::string comment; - std::string extra; - uint16_t create_system = 0; - uint16_t create_version = 0; - uint16_t extract_version = 0; - uint16_t flag_bits = 0; - std::size_t volume = 0; - uint32_t internal_attr = 0; - uint32_t external_attr = 0; - std::size_t header_offset = 0; - uint32_t crc = 0; - std::size_t compress_size = 0; - std::size_t file_size = 0; -}; - -class zip_file -{ -public: - zip_file() : archive_(new mz_zip_archive()) - { - reset(); - } - - zip_file(const std::string &filename) : zip_file() - { - load(filename); - } - - zip_file(std::istream &stream) : zip_file() - { - load(stream); - } - - zip_file(const std::vector &bytes) : zip_file() - { - load(bytes); - } - - ~zip_file() - { - reset(); - } - - void load(std::istream &stream) - { - reset(); - buffer_.assign(std::istreambuf_iterator(stream), std::istreambuf_iterator()); - remove_comment(); - start_read(); - } - - void load(const std::string &filename) - { - filename_ = filename; - std::ifstream stream(filename, std::ios::binary); - load(stream); - } - - void load(const std::vector &bytes) - { - reset(); - buffer_.assign(bytes.begin(), bytes.end()); - remove_comment(); - start_read(); - } - - void save(const std::string &filename) - { - filename_ = filename; - std::ofstream stream(filename, std::ios::binary); - save(stream); - } - - void save(std::ostream &stream) - { - if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING) - { - mz_zip_writer_finalize_archive(archive_.get()); - } - - if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) - { - mz_zip_writer_end(archive_.get()); - } - - if(archive_->m_zip_mode == MZ_ZIP_MODE_INVALID) - { - start_read(); - } - - append_comment(); - stream.write(buffer_.data(), static_cast(buffer_.size())); - } - - void save(std::vector &bytes) - { - if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING) - { - mz_zip_writer_finalize_archive(archive_.get()); - } - - if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) - { - mz_zip_writer_end(archive_.get()); - } - - if(archive_->m_zip_mode == MZ_ZIP_MODE_INVALID) - { - start_read(); - } - - append_comment(); - bytes.assign(buffer_.begin(), buffer_.end()); - } - - void reset() - { - switch(archive_->m_zip_mode) - { - case MZ_ZIP_MODE_READING: - mz_zip_reader_end(archive_.get()); - break; - case MZ_ZIP_MODE_WRITING: - mz_zip_writer_finalize_archive(archive_.get()); - mz_zip_writer_end(archive_.get()); - break; - case MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED: - mz_zip_writer_end(archive_.get()); - break; - case MZ_ZIP_MODE_INVALID: - break; - } - - if(archive_->m_zip_mode != MZ_ZIP_MODE_INVALID) - { - throw std::runtime_error(""); - } - - buffer_.clear(); - comment.clear(); - - start_write(); - mz_zip_writer_finalize_archive(archive_.get()); - mz_zip_writer_end(archive_.get()); - } - - bool has_file(const std::string &name) - { - if(archive_->m_zip_mode != MZ_ZIP_MODE_READING) - { - start_read(); - } - - int index = mz_zip_reader_locate_file(archive_.get(), name.c_str(), nullptr, 0); - - return index != -1; - } - - bool has_file(const zip_info &name) - { - return has_file(name.filename); - } - - zip_info getinfo(const std::string &name) - { - if(archive_->m_zip_mode != MZ_ZIP_MODE_READING) - { - start_read(); - } - - int index = mz_zip_reader_locate_file(archive_.get(), name.c_str(), nullptr, 0); - - if(index == -1) - { - throw std::runtime_error("not found"); - } - - return getinfo(index); - } - - std::vector infolist() - { - if(archive_->m_zip_mode != MZ_ZIP_MODE_READING) - { - start_read(); - } - - std::vector info; - - for(std::size_t i = 0; i < mz_zip_reader_get_num_files(archive_.get()); i++) - { - info.push_back(getinfo(static_cast(i))); - } - - return info; - } - - std::vector namelist() - { - std::vector names; - - for(auto &info : infolist()) - { - names.push_back(info.filename); - } - - return names; - } - - std::ostream &open(const std::string &name) - { - return open(getinfo(name)); - } - - std::ostream &open(const zip_info &name) - { - auto data = read(name); - std::string data_string(data.begin(), data.end()); - open_stream_ << data_string; - return open_stream_; - } - - void extract(const std::string &member, const std::string &path) - { - std::fstream stream(detail::join_path({path, member}), std::ios::binary | std::ios::out); - stream << open(member).rdbuf(); - } - - void extract(const zip_info &member, const std::string &path) - { - std::fstream stream(detail::join_path({path, member.filename}), std::ios::binary | std::ios::out); - stream << open(member).rdbuf(); - } - - void extractall(const std::string &path) - { - extractall(path, infolist()); - } - - void extractall(const std::string &path, const std::vector &members) - { - for(auto &member : members) - { - extract(member, path); - } - } - - void extractall(const std::string &path, const std::vector &members) - { - for(auto &member : members) - { - extract(member, path); - } - } - - void printdir() - { - printdir(std::cout); - } - - void printdir(std::ostream &stream) - { - stream << " Length " << " " << " " << "Date" << " " << " " << "Time " << " " << "Name" << std::endl; - stream << "--------- ---------- ----- ----" << std::endl; - - std::size_t sum_length = 0; - std::size_t file_count = 0; - - for(auto &member : infolist()) - { - sum_length += member.file_size; - file_count++; - - std::string length_string = std::to_string(member.file_size); - while(length_string.length() < 9) - { - length_string = " " + length_string; - } - stream << length_string; - - stream << " "; - stream << (member.date_time.month < 10 ? "0" : "") << member.date_time.month; - stream << "/"; - stream << (member.date_time.day < 10 ? "0" : "") << member.date_time.day; - stream << "/"; - stream << member.date_time.year; - stream << " "; - stream << (member.date_time.hours < 10 ? "0" : "") << member.date_time.hours; - stream << ":"; - stream << (member.date_time.minutes < 10 ? "0" : "") << member.date_time.minutes; - stream << " "; - stream << member.filename; - stream << std::endl; - } - - stream << "--------- -------" << std::endl; - - std::string length_string = std::to_string(sum_length); - while(length_string.length() < 9) - { - length_string = " " + length_string; - } - stream << length_string << " " << file_count << " " << (file_count == 1 ? "file" : "files"); - stream << std::endl; - } - - std::string read(const zip_info &info) - { - std::size_t size; - char *data = static_cast(mz_zip_reader_extract_file_to_heap(archive_.get(), info.filename.c_str(), &size, 0)); - if(data == nullptr) - { - throw std::runtime_error("file couldn't be read"); - } - std::string extracted(data, data + size); - mz_free(data); - return extracted; - } - - std::string read(const std::string &name) - { - return read(getinfo(name)); - } - - std::pair testzip() - { - if(archive_->m_zip_mode == MZ_ZIP_MODE_INVALID) - { - throw std::runtime_error("not open"); - } - - for(auto &file : infolist()) - { - auto content = read(file); - auto crc = detail::crc32buf(content.c_str(), content.size()); - - if(crc != file.crc) - { - return {false, file.filename}; - } - } - - return {true, ""}; - } - - void write(const std::string &filename) - { - auto split = detail::split_path(filename); - if(split.size() > 1) - { - split.erase(split.begin()); - } - auto arcname = detail::join_path(split); - write(filename, arcname); - } - - void write(const std::string &filename, const std::string &arcname) - { - std::fstream file(filename, std::ios::binary | std::ios::in); - std::stringstream ss; - ss << file.rdbuf(); - std::string bytes = ss.str(); - - writestr(arcname, bytes); - } - - void writestr(const std::string &arcname, const std::string &bytes) - { - if(archive_->m_zip_mode != MZ_ZIP_MODE_WRITING) - { - start_write(); - } - - if(!mz_zip_writer_add_mem(archive_.get(), arcname.c_str(), bytes.data(), bytes.size(), MZ_BEST_COMPRESSION)) - { - throw std::runtime_error("write error"); - } - } - - void writestr(const zip_info &info, const std::string &bytes) - { - if(info.filename.empty() || info.date_time.year < 1980) - { - throw std::runtime_error("must specify a filename and valid date (year >= 1980"); - } - - if(archive_->m_zip_mode != MZ_ZIP_MODE_WRITING) - { - start_write(); - } - - auto crc = detail::crc32buf(bytes.c_str(), bytes.size()); - - if(!mz_zip_writer_add_mem_ex(archive_.get(), info.filename.c_str(), bytes.data(), bytes.size(), info.comment.c_str(), static_cast(info.comment.size()), MZ_BEST_COMPRESSION, 0, crc)) - { - throw std::runtime_error("write error"); - } - } - - std::string get_filename() const { return filename_; } - - std::string comment; - -private: - void start_read() - { - if(archive_->m_zip_mode == MZ_ZIP_MODE_READING) return; - - if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING) - { - mz_zip_writer_finalize_archive(archive_.get()); - } - - if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) - { - mz_zip_writer_end(archive_.get()); - } - - if(!mz_zip_reader_init_mem(archive_.get(), buffer_.data(), buffer_.size(), 0)) - { - throw std::runtime_error("bad zip"); - } - } - - void start_write() - { - if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING) return; - - switch(archive_->m_zip_mode) - { - case MZ_ZIP_MODE_READING: - { - mz_zip_archive archive_copy; - std::memset(&archive_copy, 0, sizeof(mz_zip_archive)); - std::vector buffer_copy(buffer_.begin(), buffer_.end()); - - if(!mz_zip_reader_init_mem(&archive_copy, buffer_copy.data(), buffer_copy.size(), 0)) - { - throw std::runtime_error("bad zip"); - } - - mz_zip_reader_end(archive_.get()); - - archive_->m_pWrite = reinterpret_cast(&detail::write_callback); - archive_->m_pIO_opaque = &buffer_; - buffer_ = std::vector(); - - if(!mz_zip_writer_init(archive_.get(), 0)) - { - throw std::runtime_error("bad zip"); - } - - for(unsigned int i = 0; i < static_cast(archive_copy.m_total_files); i++) - { - if(!mz_zip_writer_add_from_zip_reader(archive_.get(), &archive_copy, i)) - { - throw std::runtime_error("fail"); - } - } - - mz_zip_reader_end(&archive_copy); - return; - } - case MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED: - mz_zip_writer_end(archive_.get()); - break; - case MZ_ZIP_MODE_INVALID: - case MZ_ZIP_MODE_WRITING: - break; - } - - archive_->m_pWrite = reinterpret_cast(&detail::write_callback); - archive_->m_pIO_opaque = &buffer_; - - if(!mz_zip_writer_init(archive_.get(), 0)) - { - throw std::runtime_error("bad zip"); - } - } - - void append_comment() - { - if(!comment.empty()) - { - auto comment_length = std::min(static_cast(comment.length()), std::numeric_limits::max()); - buffer_[buffer_.size() - 2] = static_cast(comment_length); - buffer_[buffer_.size() - 1] = static_cast(comment_length >> 8); - std::copy(comment.begin(), comment.end(), std::back_inserter(buffer_)); - } - } - - void remove_comment() - { - if(buffer_.empty()) return; - - std::size_t position = buffer_.size() - 1; - - for(; position >= 3; position--) - { - if(buffer_[position - 3] == 'P' - && buffer_[position - 2] == 'K' - && buffer_[position - 1] == '\x05' - && buffer_[position] == '\x06') - { - position = position + 17; - break; - } - } - - if(position == 3) - { - throw std::runtime_error("didn't find end of central directory signature"); - } - - uint16_t length = static_cast(buffer_[position + 1]); - length = static_cast(length << 8) + static_cast(buffer_[position]); - position += 2; - - if(length != 0) - { - comment = std::string(buffer_.data() + position, buffer_.data() + position + length); - buffer_.resize(buffer_.size() - length); - buffer_[buffer_.size() - 1] = 0; - buffer_[buffer_.size() - 2] = 0; - } - } - - zip_info getinfo(int index) - { - if(archive_->m_zip_mode != MZ_ZIP_MODE_READING) - { - start_read(); - } - - mz_zip_archive_file_stat stat; - mz_zip_reader_file_stat(archive_.get(), static_cast(index), &stat); - - zip_info result; - - result.filename = std::string(stat.m_filename, stat.m_filename + std::strlen(stat.m_filename)); - result.comment = std::string(stat.m_comment, stat.m_comment + stat.m_comment_size); - result.compress_size = static_cast(stat.m_comp_size); - result.file_size = static_cast(stat.m_uncomp_size); - result.header_offset = static_cast(stat.m_local_header_ofs); - result.crc = stat.m_crc32; - auto time = detail::safe_localtime(stat.m_time); - result.date_time.year = 1900 + time.tm_year; - result.date_time.month = 1 + time.tm_mon; - result.date_time.day = time.tm_mday; - result.date_time.hours = time.tm_hour; - result.date_time.minutes = time.tm_min; - result.date_time.seconds = time.tm_sec; - result.flag_bits = stat.m_bit_flag; - result.internal_attr = stat.m_internal_attr; - result.external_attr = stat.m_external_attr; - result.extract_version = stat.m_version_needed; - result.create_version = stat.m_version_made_by; - result.volume = stat.m_file_index; - result.create_system = stat.m_method; - - return result; - } - - std::unique_ptr archive_; - std::vector buffer_; - std::stringstream open_stream_; - std::string filename_; -}; - -} // namespace miniz_cpp diff --git a/scripts/debian-11/1-install-deps.sh b/scripts/debian-11/1-install-deps.sh new file mode 100755 index 00000000..6d4e7441 --- /dev/null +++ b/scripts/debian-11/1-install-deps.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +apt-get update -y + +apt-get install -y curl zip unzip tar cmake make git g++ ninja-build pkg-config diff --git a/scripts/debian-11/1.5-git-safe.sh b/scripts/debian-11/1.5-git-safe.sh new file mode 100644 index 00000000..0ffc84fd --- /dev/null +++ b/scripts/debian-11/1.5-git-safe.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +git config --global --add safe.directory $(pwd) +git config --global --add safe.directory $(pwd)/vcpkg +git config --global --add safe.directory $(pwd)/deps/commandline \ No newline at end of file diff --git a/scripts/debian-11/2-configure.sh b/scripts/debian-11/2-configure.sh new file mode 100755 index 00000000..400acbed --- /dev/null +++ b/scripts/debian-11/2-configure.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +./vcpkg/bootstrap-vcpkg.sh + +cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,--build-id=none -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections" -DBeamMP-Launcher_ENABLE_LTO=ON diff --git a/scripts/debian-11/3-build-tests.sh b/scripts/debian-11/3-build-tests.sh new file mode 100755 index 00000000..35bc5399 --- /dev/null +++ b/scripts/debian-11/3-build-tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher-tests diff --git a/scripts/debian-11/3-build.sh b/scripts/debian-11/3-build.sh new file mode 100755 index 00000000..aa3ace39 --- /dev/null +++ b/scripts/debian-11/3-build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher + +objcopy --only-keep-debug bin/BeamMP-Launcher bin/BeamMP-Launcher.debug +objcopy --add-gnu-debuglink bin/BeamMP-Launcher bin/BeamMP-Launcher.debug + +strip -s bin/BeamMP-Launcher diff --git a/scripts/debian-11/4-install-runtime-deps.sh b/scripts/debian-11/4-install-runtime-deps.sh new file mode 100755 index 00000000..ee021687 --- /dev/null +++ b/scripts/debian-11/4-install-runtime-deps.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -ex + +apt-get update -y + +apt-get install -y curl + diff --git a/scripts/debian-12/1-install-deps.sh b/scripts/debian-12/1-install-deps.sh new file mode 100755 index 00000000..6d4e7441 --- /dev/null +++ b/scripts/debian-12/1-install-deps.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +apt-get update -y + +apt-get install -y curl zip unzip tar cmake make git g++ ninja-build pkg-config diff --git a/scripts/debian-12/1.5-git-safe.sh b/scripts/debian-12/1.5-git-safe.sh new file mode 100644 index 00000000..0ffc84fd --- /dev/null +++ b/scripts/debian-12/1.5-git-safe.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +git config --global --add safe.directory $(pwd) +git config --global --add safe.directory $(pwd)/vcpkg +git config --global --add safe.directory $(pwd)/deps/commandline \ No newline at end of file diff --git a/scripts/debian-12/2-configure.sh b/scripts/debian-12/2-configure.sh new file mode 100755 index 00000000..400acbed --- /dev/null +++ b/scripts/debian-12/2-configure.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +./vcpkg/bootstrap-vcpkg.sh + +cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,--build-id=none -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections" -DBeamMP-Launcher_ENABLE_LTO=ON diff --git a/scripts/debian-12/3-build-tests.sh b/scripts/debian-12/3-build-tests.sh new file mode 100755 index 00000000..35bc5399 --- /dev/null +++ b/scripts/debian-12/3-build-tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher-tests diff --git a/scripts/debian-12/3-build.sh b/scripts/debian-12/3-build.sh new file mode 100755 index 00000000..aa3ace39 --- /dev/null +++ b/scripts/debian-12/3-build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher + +objcopy --only-keep-debug bin/BeamMP-Launcher bin/BeamMP-Launcher.debug +objcopy --add-gnu-debuglink bin/BeamMP-Launcher bin/BeamMP-Launcher.debug + +strip -s bin/BeamMP-Launcher diff --git a/scripts/debian-12/4-install-runtime-deps.sh b/scripts/debian-12/4-install-runtime-deps.sh new file mode 100755 index 00000000..ee021687 --- /dev/null +++ b/scripts/debian-12/4-install-runtime-deps.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -ex + +apt-get update -y + +apt-get install -y curl + diff --git a/scripts/ubuntu-20.04/1-install-deps.sh b/scripts/ubuntu-20.04/1-install-deps.sh new file mode 100755 index 00000000..6d4e7441 --- /dev/null +++ b/scripts/ubuntu-20.04/1-install-deps.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +apt-get update -y + +apt-get install -y curl zip unzip tar cmake make git g++ ninja-build pkg-config diff --git a/scripts/ubuntu-20.04/1.5-git-safe.sh b/scripts/ubuntu-20.04/1.5-git-safe.sh new file mode 100644 index 00000000..0ffc84fd --- /dev/null +++ b/scripts/ubuntu-20.04/1.5-git-safe.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +git config --global --add safe.directory $(pwd) +git config --global --add safe.directory $(pwd)/vcpkg +git config --global --add safe.directory $(pwd)/deps/commandline \ No newline at end of file diff --git a/scripts/ubuntu-20.04/2-configure.sh b/scripts/ubuntu-20.04/2-configure.sh new file mode 100755 index 00000000..400acbed --- /dev/null +++ b/scripts/ubuntu-20.04/2-configure.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +./vcpkg/bootstrap-vcpkg.sh + +cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,--build-id=none -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections" -DBeamMP-Launcher_ENABLE_LTO=ON diff --git a/scripts/ubuntu-20.04/3-build-tests.sh b/scripts/ubuntu-20.04/3-build-tests.sh new file mode 100755 index 00000000..35bc5399 --- /dev/null +++ b/scripts/ubuntu-20.04/3-build-tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher-tests diff --git a/scripts/ubuntu-20.04/3-build.sh b/scripts/ubuntu-20.04/3-build.sh new file mode 100755 index 00000000..aa3ace39 --- /dev/null +++ b/scripts/ubuntu-20.04/3-build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher + +objcopy --only-keep-debug bin/BeamMP-Launcher bin/BeamMP-Launcher.debug +objcopy --add-gnu-debuglink bin/BeamMP-Launcher bin/BeamMP-Launcher.debug + +strip -s bin/BeamMP-Launcher diff --git a/scripts/ubuntu-20.04/4-install-runtime-deps.sh b/scripts/ubuntu-20.04/4-install-runtime-deps.sh new file mode 100755 index 00000000..ee021687 --- /dev/null +++ b/scripts/ubuntu-20.04/4-install-runtime-deps.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -ex + +apt-get update -y + +apt-get install -y curl + diff --git a/scripts/ubuntu-22.04/1-install-deps.sh b/scripts/ubuntu-22.04/1-install-deps.sh new file mode 100755 index 00000000..6d4e7441 --- /dev/null +++ b/scripts/ubuntu-22.04/1-install-deps.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +apt-get update -y + +apt-get install -y curl zip unzip tar cmake make git g++ ninja-build pkg-config diff --git a/scripts/ubuntu-22.04/1.5-git-safe.sh b/scripts/ubuntu-22.04/1.5-git-safe.sh new file mode 100644 index 00000000..0ffc84fd --- /dev/null +++ b/scripts/ubuntu-22.04/1.5-git-safe.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +git config --global --add safe.directory $(pwd) +git config --global --add safe.directory $(pwd)/vcpkg +git config --global --add safe.directory $(pwd)/deps/commandline \ No newline at end of file diff --git a/scripts/ubuntu-22.04/2-configure.sh b/scripts/ubuntu-22.04/2-configure.sh new file mode 100755 index 00000000..400acbed --- /dev/null +++ b/scripts/ubuntu-22.04/2-configure.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +./vcpkg/bootstrap-vcpkg.sh + +cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,--build-id=none -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections" -DBeamMP-Launcher_ENABLE_LTO=ON diff --git a/scripts/ubuntu-22.04/3-build-tests.sh b/scripts/ubuntu-22.04/3-build-tests.sh new file mode 100755 index 00000000..35bc5399 --- /dev/null +++ b/scripts/ubuntu-22.04/3-build-tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher-tests diff --git a/scripts/ubuntu-22.04/3-build.sh b/scripts/ubuntu-22.04/3-build.sh new file mode 100755 index 00000000..aa3ace39 --- /dev/null +++ b/scripts/ubuntu-22.04/3-build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher + +objcopy --only-keep-debug bin/BeamMP-Launcher bin/BeamMP-Launcher.debug +objcopy --add-gnu-debuglink bin/BeamMP-Launcher bin/BeamMP-Launcher.debug + +strip -s bin/BeamMP-Launcher diff --git a/scripts/ubuntu-22.04/4-install-runtime-deps.sh b/scripts/ubuntu-22.04/4-install-runtime-deps.sh new file mode 100755 index 00000000..ee021687 --- /dev/null +++ b/scripts/ubuntu-22.04/4-install-runtime-deps.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -ex + +apt-get update -y + +apt-get install -y curl + diff --git a/scripts/windows/1-configure.sh b/scripts/windows/1-configure.sh new file mode 100755 index 00000000..6f8dd38b --- /dev/null +++ b/scripts/windows/1-configure.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -ex + +cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DBeamMP-Launcher_ENABLE_LTO=ON -DVCPKG_TARGET_TRIPLET=x64-windows-static diff --git a/scripts/windows/2-build-tests.sh b/scripts/windows/2-build-tests.sh new file mode 100755 index 00000000..35bc5399 --- /dev/null +++ b/scripts/windows/2-build-tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher-tests diff --git a/scripts/windows/2-build.sh b/scripts/windows/2-build.sh new file mode 100755 index 00000000..0e8e281a --- /dev/null +++ b/scripts/windows/2-build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -ex + +cmake --build bin --parallel -t BeamMP-Launcher --config Release diff --git a/src/ClientNetwork.cpp b/src/ClientNetwork.cpp new file mode 100644 index 00000000..67b4762e --- /dev/null +++ b/src/ClientNetwork.cpp @@ -0,0 +1,643 @@ +#include "ClientNetwork.h" +#include "ClientPacket.h" +#include "ClientTransport.h" +#include "Http.h" +#include "Identity.h" +#include "Launcher.h" +#include "Transport.h" +#include "Util.h" + +#include +#include +#include + +using json = nlohmann::json; + +void ClientNetwork::run() { + ip::tcp::endpoint listen_ep(ip::address::from_string("0.0.0.0"), static_cast(m_listen_port)); + ip::tcp::socket listener(m_io); + boost::system::error_code ec; + listener.open(listen_ep.protocol(), ec); + if (ec) { + spdlog::error("Failed to open m_game_socket: {}", ec.message()); + return; + } + ip::tcp::socket::linger linger_opt {}; + linger_opt.enabled(false); + listener.set_option(linger_opt, ec); + if (ec) { + spdlog::error("Failed to set up listening m_game_socket to not linger / reuse address. " + "This may cause the m_game_socket to refuse to bind(). spdlog::error: {}", + ec.message()); + } + + m_acceptor = ip::tcp::acceptor(m_io, listen_ep); + m_acceptor.listen(ip::tcp::socket::max_listen_connections, ec); + if (ec) { + spdlog::error("listen() failed, which is needed for the launcher to operate. " + "Shutting down. spdlog::error: {}", + ec.message()); + std::this_thread::sleep_for(std::chrono::seconds(3)); + std::exit(1); + } + start_accept(); + m_io.run(); +} + +ClientNetwork::ClientNetwork(Launcher& launcher, uint16_t port) + : m_listen_port(port) + , launcher(launcher) { + spdlog::debug("Client network created"); +} + +ClientNetwork::~ClientNetwork() { + spdlog::debug("Client network destroyed"); +} + +void ClientNetwork::start_read() { + client_tcp_read([this](auto&& packet) { + handle_packet(packet); + start_read(); + }); +} + +void ClientNetwork::handle_connection() { + // immediately send launcher info (first step of client identification) + m_client_state = bmp::ClientState::ClientIdentification; + + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::LauncherInfo, + .raw_data = json_to_vec( + { + { "implementation", "Official BeamMP Launcher" }, + { "version", { PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH } }, + { "mod_cache_path", "/idk sorry/" }, // TODO: mod_cache_path in LauncherInfo + }) }); + + start_read(); +} + +void ClientNetwork::handle_packet(bmp::ClientPacket& packet) { + spdlog::debug("Got client packet: purpose: 0x{:x}, flags: 0x{:x}, pid: {}, vid: {}, size: {}", + uint16_t(packet.purpose), + uint8_t(packet.flags), + packet.pid, packet.vid, + packet.get_readable_data().size()); + spdlog::debug("Client State: 0x{:x}", int(m_client_state)); + + switch (m_client_state) { + case bmp::ClientIdentification: + handle_client_identification(packet); + break; + case bmp::Login: + handle_login(packet); + break; + case bmp::QuickJoin: + handle_quick_join(packet); + break; + case bmp::Browsing: + handle_browsing(packet); + break; + case bmp::ServerIdentification: + handle_server_identification(packet); + break; + case bmp::ServerAuthentication: + handle_server_authentication(packet); + break; + case bmp::ServerModDownload: + handle_server_mod_download(packet); + break; + case bmp::ServerSessionSetup: + handle_server_session_setup(packet); + break; + case bmp::ServerPlaying: + handle_server_playing(packet); + break; + case bmp::ServerLeaving: + handle_server_leaving(packet); + break; + } +} + +void ClientNetwork::handle_client_identification(bmp::ClientPacket& packet) { + switch (packet.purpose) { + case bmp::ClientPurpose::GameInfo: { + try { + auto game_info = vec_to_json(packet.get_readable_data()); + std::string impl = game_info.at("implementation"); + std::vector mod_version = game_info.at("mod_version"); + std::vector game_version = game_info.at("game_version"); + std::vector protocol_version = game_info.at("protocol_version"); + if (protocol_version.at(0) != 1) { + disconnect(fmt::format("Incompatible protocol version, expected v{}", "1.x.x")); + return; + } + *launcher.mod_version = Version { uint8_t(mod_version.at(0)), uint8_t(mod_version.at(1)), uint8_t(mod_version.at(2)) }; + *launcher.game_version = Version { uint8_t(game_version.at(0)), uint8_t(game_version.at(1)), uint8_t(game_version.at(2)) }; + spdlog::info("Connected to {} (mod v{}, game v{}, protocol v{}.{}.{})", + impl, + launcher.mod_version->to_string(), + launcher.game_version->to_string(), + protocol_version.at(0), + protocol_version.at(1), protocol_version.at(2)); + } catch (const std::exception& e) { + spdlog::error("Failed to read json for purpose 0x{:x}: {}", uint16_t(packet.purpose), e.what()); + disconnect(fmt::format("Invalid json in purpose 0x{:x}, see launcher logs for more info", uint16_t(packet.purpose))); + } + start_login(); + break; + } + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::start_login() { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::StateChangeLogin, + }); + + m_client_state = bmp::ClientState::Login; + + if (ident::is_login_cached()) { + auto login = ident::login_cached(); + // case in which the login is cached and login was successful: + // we just send the login result and continue to the next state. + if (login.has_value()) { + *m_identity = login.value(); + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::LoginResult, + .raw_data = json_to_vec({ + { "success", true }, + { "message", m_identity->Message }, + { "username", m_identity->Username }, + { "role", m_identity->Role }, + }), + }); + // move on to quick join right away + start_quick_join(); + return; // done + } else { + spdlog::warn("Failed to automatically login with cached/saved login details: {}", login.error()); + spdlog::info("Trying normal login"); + } + // fallthrough to normal login + } + + // first packet in login + // TODO: send LoginResult if already logged in. + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::AskForCredentials, + }); + // wait for response, so return +} + +void ClientNetwork::disconnect(const std::string& reason) { + spdlog::debug("Disconnecting game: {}", reason); + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::Error, + .raw_data = json_to_vec({ "message", reason }) }, + [this](auto ec) { + // ignore any error and just shut down + // we pass `ec` to both of these and ignore their errors as well + // because we dont care if they fail + m_game_socket.shutdown(boost::asio::socket_base::shutdown_both, ec); + m_game_socket.close(ec); + start_accept(); + }); +} + +void ClientNetwork::handle_login(bmp::ClientPacket& packet) { + switch (packet.purpose) { + case bmp::ClientPurpose::Credentials: + try { + auto creds = vec_to_json(packet.get_readable_data()); + std::string username = creds.at("username"); + std::string password = creds.at("password"); + bool remember = creds.at("remember"); + spdlog::debug("Got credentials username: '{}', password: ({} chars) (remember: {})", username, password.size(), remember ? "yes" : "no"); + // login! + auto result = ident::login(username, password, remember); + if (result.has_error()) { + ; + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::LoginResult, + .raw_data = json_to_vec({ + { "success", false }, + { "message", result.error() }, + }), + }); + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::AskForCredentials, + }); + return; + } + *m_identity = result.value(); + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::LoginResult, + .raw_data = json_to_vec({ + { "success", true }, + { "message", m_identity->Message }, + { "username", m_identity->Username }, + { "role", m_identity->Role }, + }), + }); + + start_quick_join(); + } catch (const std::exception& e) { + spdlog::error("Failed to read json for purpose 0x{:x}: {}", uint16_t(packet.purpose), e.what()); + disconnect(fmt::format("Invalid json in purpose 0x{:x}, see launcher logs for more info", uint16_t(packet.purpose))); + } + break; + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::handle_quick_join(bmp::ClientPacket& packet) { + switch (packet.purpose) { + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::handle_browsing(bmp::ClientPacket& packet) { + switch (packet.purpose) { + case bmp::ClientPurpose::ServerListRequest: { + post(m_io, [&, this] { + auto list = load_server_list(); + if (list.has_value()) { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::ServerListResponse, + .raw_data = list.value(), + }); + } else { + spdlog::error("Failed to load server list: {}", list.error()); + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::Error, + .raw_data = json_to_vec({ "message", list.error() }), + }); + } + }); + } break; + case bmp::ClientPurpose::Logout: { + spdlog::error("Logout is not yet implemented"); + } break; + case bmp::ClientPurpose::Connect: { + try { + auto details = json::parse(packet.get_readable_data()); + std::string host = details.at("host"); + uint16_t port = details.at("port"); + spdlog::info("Game requesting to connect to server [{}]:{}", host, port); + auto connect_result = launcher.start_server_network(host, port); + if (connect_result.has_error()) { + spdlog::error("Failed to connect to server: {}", connect_result.error()); + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::ConnectError, + .raw_data = json_to_vec({ "message", connect_result.error() }), + }); + } else { + spdlog::info("Connected to server!"); + start_server_identification(); + } + } catch (const std::exception& e) { + spdlog::error("Failed to read json for purpose 0x{:x}: {}", uint16_t(packet.purpose), e.what()); + disconnect(fmt::format("Invalid json in purpose 0x{:x}, see launcher logs for more info", uint16_t(packet.purpose))); + } + } break; + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::handle_server_identification(bmp::ClientPacket& packet) { + switch (packet.purpose) { + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::handle_server_authentication(bmp::ClientPacket& packet) { + switch (packet.purpose) { + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::handle_server_mod_download(bmp::ClientPacket& packet) { + switch (packet.purpose) { + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::handle_server_session_setup(bmp::ClientPacket& packet) { + switch (packet.purpose) { + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::handle_server_playing(bmp::ClientPacket& packet) { + switch (packet.purpose) { + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::handle_server_leaving(bmp::ClientPacket& packet) { + switch (packet.purpose) { + default: + disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose))); + break; + } +} + +void ClientNetwork::client_tcp_read(std::function handler) { + m_tmp_header_buffer.resize(bmp::ClientHeader::SERIALIZED_SIZE); + boost::asio::async_read(m_game_socket, buffer(m_tmp_header_buffer), + bind_executor(m_read_strand, [this, handler](auto ec, auto) { + if (ec) { + disconnect(fmt::format("Failed to read from game: {}", ec.message())); + } else { + bmp::ClientHeader hdr {}; + hdr.deserialize_from(m_tmp_header_buffer); + // vector eaten up by now, recv again + m_tmp_packet.raw_data.resize(hdr.data_size); + m_tmp_packet.purpose = hdr.purpose; + m_tmp_packet.flags = hdr.flags; + spdlog::trace("Got header: purpose: 0x{:x}, flags: 0x{:x}, pid: {}, vid: {}, size: {}", + uint16_t(m_tmp_packet.purpose), + uint8_t(m_tmp_packet.flags), + m_tmp_packet.pid, m_tmp_packet.vid, + m_tmp_packet.get_readable_data().size()); + boost::asio::async_read(m_game_socket, buffer(m_tmp_packet.raw_data), + bind_executor(m_read_strand, [handler, this](auto ec, auto) { + if (ec) { + disconnect(fmt::format("Failed to read from game: {}", ec.message())); + } else { + // ok! + handler(std::move(m_tmp_packet)); + } + })); + } + })); +} + +void ClientNetwork::client_tcp_write(bmp::ClientPacket&& packet, std::function handler) { + boost::asio::post(m_write_strand, [this, packet = std::move(packet), handler = std::move(handler)] { + auto purpose = packet.purpose; + m_outbox.emplace_back(OutPacket { std::move(packet), handler }); + if (m_outbox.size() > 1) { + // outstanding async_write + spdlog::debug("Outstanding write, not writing 0x{:x} now", int(purpose)); + return; + } + client_tcp_write_impl(); + }); +} + +void ClientNetwork::client_tcp_write_impl() { + OutPacket out_packet = std::move(m_outbox.front()); + + spdlog::debug("Writing 0x{:x} now", int(out_packet.packet.purpose)); + // don't pop just yet + + auto header = out_packet.packet.finalize(); + // copy packet + auto owned_packet = std::make_shared(std::move(out_packet.packet)); + // serialize header + auto header_data = std::make_shared>(bmp::ClientHeader::SERIALIZED_SIZE); + header.serialize_to(*header_data); + std::array buffers = { + buffer(*header_data), + buffer(owned_packet->raw_data) + }; + boost::asio::async_write(m_game_socket, buffers, + bind_executor(m_write_strand, [this, header_data, owned_packet, handler = std::move(out_packet.handler)](auto ec, auto size) { + // only now pop the packet we just sent from the outbox + spdlog::trace("Wrote {} bytes for 0x{:x}", size, int(owned_packet->purpose)); + if (handler) { + handler(ec); + } else { + if (ec) { + disconnect(fmt::format("Failed to send packet to game: {}", ec.message())); + } else { + // ok! + spdlog::debug("Sent packet of type 0x{:x}", int(owned_packet->purpose)); + } + } + m_outbox.pop_front(); + // trigger the next send if there's more to send + if (!m_outbox.empty()) { + // async so this is not recursion! + client_tcp_write_impl(); + } + })); +} + +std::vector ClientNetwork::json_to_vec(const nlohmann::json& value) { + auto str = value.dump(); + return std::vector(str.begin(), str.end()); +} +nlohmann::json ClientNetwork::vec_to_json(const std::vector& vec) { + return json::parse(std::string(vec.begin(), vec.end())); +} +void ClientNetwork::start_quick_join() { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::StateChangeQuickJoin, + }); + m_client_state = bmp::ClientState::QuickJoin; + + // TODO: Implement DoJoin, etc + + start_browsing(); +} + +void ClientNetwork::start_browsing() { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::StateChangeBrowsing, + }); + m_client_state = bmp::ClientState::Browsing; +} + +Result, std::string> ClientNetwork::load_server_list() noexcept { + try { + auto list = HTTP::Get("https://backend.beammp.com/servers-info/"); + if (list == "-1") { + return outcome::failure("Failed to fetch server list, see launcher log for more information."); + } + return outcome::success(std::vector(list.begin(), list.end())); + } catch (const std::exception& e) { + return outcome::failure(fmt::format("Failed to fetch server list from backend: {}", e.what())); + } +} +void ClientNetwork::handle_server_packet(bmp::Packet&& packet) { + post(m_io, [packet, this] { + switch (packet.purpose) { + case bmp::ProtocolVersionBad: + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::ConnectError, + .raw_data = json_to_vec({ "message", "Server is outdated or too new - protocol is incompatible. Please try a different server and make sure your Launcher and Mod are up-to-date." }), + }); + start_browsing(); + break; + case bmp::ProtocolVersionOk: + start_server_authentication(); + break; + case bmp::AuthOk: + uint32_t player_id; + bmp::deserialize(player_id, packet.get_readable_data()); + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::AuthenticationOk, + .raw_data = json_to_vec({ "pid", player_id }), + }); + start_server_mod_download(); + break; + case bmp::AuthFailed: + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::AuthenticationError, + .raw_data = json_to_vec( + { "message", + std::string( + packet.get_readable_data().begin(), + packet.get_readable_data().end()) }), + }); + start_browsing(); + break; + case bmp::PlayerRejected: + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::PlayerRejected, + .raw_data = json_to_vec( + { "message", + std::string( + packet.get_readable_data().begin(), + packet.get_readable_data().end()) }), + }); + start_browsing(); + break; + case bmp::ModsInfo: + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::ModSyncStatus, + .raw_data = json_to_vec(json::array()), // TODO: send more here i guess + }); + spdlog::debug("Mod stuff not implemented, skipping to session setup"); + break; + case bmp::MapInfo: { + auto data = packet.get_readable_data(); + auto map = std::string(data.begin(), data.end()); + spdlog::debug("Map: '{}'", map); + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::MapInfo, + .raw_data = json_to_vec({ "map", map }), + }); + start_server_session_setup(); + } break; + case bmp::PlayersVehiclesInfo: + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::PlayersAndVehiclesInfo, + .raw_data = packet.get_readable_data(), + }); + start_server_playing(); + break; + case bmp::ClientInfo: + case bmp::ServerInfo: + case bmp::PlayerPublicKey: + case bmp::StartUDP: + case bmp::ModRequest: + case bmp::ModResponse: + case bmp::ModRequestInvalid: + case bmp::ModsSyncDone: + case bmp::SessionReady: + case bmp::Ping: + case bmp::VehicleSpawn: + case bmp::VehicleDelete: + case bmp::VehicleReset: + case bmp::VehicleEdited: + case bmp::VehicleCouplerChanged: + case bmp::SpectatorSwitched: + case bmp::ApplyInput: + case bmp::ApplyElectrics: + case bmp::ApplyNodes: + case bmp::ApplyBreakgroups: + case bmp::ApplyPowertrain: + case bmp::ApplyPosition: + case bmp::ChatMessage: + case bmp::Event: + case bmp::PlayerJoined: + case bmp::PlayerLeft: + case bmp::PlayerPingUpdate: + case bmp::Notification: + case bmp::Kicked: + case bmp::StateChangeIdentification: + case bmp::StateChangeAuthentication: + case bmp::StateChangeModDownload: + case bmp::StateChangeSessionSetup: + case bmp::StateChangePlaying: + case bmp::StateChangeLeaving: + break; + case bmp::Invalid: + case bmp::ProtocolVersion: + break; + } + }); +} + +void ClientNetwork::start_accept() { + m_acceptor.async_accept(m_game_socket, [this](const auto& ec) { handle_accept(ec); }); +} + +void ClientNetwork::handle_accept(boost::system::error_code ec) { + if (ec) { + spdlog::error("Failed accepting game connection: {}", ec.message()); + } else { + spdlog::info("Game connected!"); + auto game_ep = m_game_socket.remote_endpoint(); + spdlog::debug("Game: [{}]:{}", game_ep.address().to_string(), game_ep.port()); + handle_connection(); + } + // TODO: We should probably accept() again somewhere once the game disconnected +} +void ClientNetwork::start_server_identification() { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::StateChangeServerIdentification, + }); + m_client_state = bmp::ClientState::ServerIdentification; +} + +void ClientNetwork::start_server_authentication() { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::StateChangeServerAuthentication, + }); + m_client_state = bmp::ClientState::ServerAuthentication; +} + +void ClientNetwork::start_server_mod_download() { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::StateChangeServerModDownload, + }); + m_client_state = bmp::ClientState::ServerModDownload; +} + +void ClientNetwork::start_server_session_setup() { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::StateChangeServerSessionSetup, + }); + m_client_state = bmp::ClientState::ServerSessionSetup; +} + +void ClientNetwork::start_server_playing() { + client_tcp_write(bmp::ClientPacket { + .purpose = bmp::ClientPurpose::StateChangeServerPlaying, + }); + m_client_state = bmp::ClientState::ServerPlaying; +} diff --git a/src/ClientNetwork.h b/src/ClientNetwork.h new file mode 100644 index 00000000..0f3707ae --- /dev/null +++ b/src/ClientNetwork.h @@ -0,0 +1,87 @@ +#pragma once + +#include "ClientPacket.h" +#include "ClientState.h" +#include "Launcher.h" +#include "Packet.h" +#include "Sync.h" +#include "Version.h" + +#include +#include +#include + +using namespace boost::asio; + +class ClientNetwork { +public: + ClientNetwork(Launcher& launcher, uint16_t port); + + ~ClientNetwork(); + + void run(); + + void handle_server_packet(bmp::Packet&& packet); + +private: + void start_accept(); + void handle_accept(boost::system::error_code ec); + + void start_read(); + + void handle_connection(); + void client_tcp_read(std::function handler); + void client_tcp_write(bmp::ClientPacket&& packet, std::function handler = nullptr); + + struct OutPacket { + bmp::ClientPacket packet; + std::function handler; + }; + std::deque m_outbox {}; + void client_tcp_write_impl(); + + void handle_packet(bmp::ClientPacket& packet); + void handle_client_identification(bmp::ClientPacket& packet); + void handle_login(bmp::ClientPacket& packet); + void handle_quick_join(bmp::ClientPacket& packet); + void handle_browsing(bmp::ClientPacket& packet); + void handle_server_identification(bmp::ClientPacket& packet); + void handle_server_authentication(bmp::ClientPacket& packet); + void handle_server_mod_download(bmp::ClientPacket& packet); + void handle_server_session_setup(bmp::ClientPacket& packet); + void handle_server_playing(bmp::ClientPacket& packet); + void handle_server_leaving(bmp::ClientPacket& packet); + + void disconnect(const std::string& reason); + void start_login(); + void start_quick_join(); + void start_browsing(); + void start_server_identification(); + void start_server_authentication(); + void start_server_mod_download(); + void start_server_session_setup(); + void start_server_playing(); + + Result, std::string> load_server_list() noexcept; + + static std::vector json_to_vec(const nlohmann::json& json); + static nlohmann::json vec_to_json(const std::vector& vec); + + Sync m_identity {}; + + uint16_t m_listen_port {}; + io_context m_io {}; + ip::tcp::socket m_game_socket { m_io }; + Sync m_shutdown { false }; + bmp::ClientState m_client_state; + + ip::tcp::acceptor m_acceptor { m_io }; + boost::asio::io_context::strand m_read_strand { m_io }; + boost::asio::io_context::strand m_write_strand { m_io }; + + // temporary packet and header buffer for async reads + bmp::ClientPacket m_tmp_packet {}; + std::vector m_tmp_header_buffer {}; + + Launcher& launcher; +}; diff --git a/src/Compression.h b/src/Compression.h new file mode 100644 index 00000000..99f9f7f3 --- /dev/null +++ b/src/Compression.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +#define Biggest 30000 + +template +inline T Comp(const T& Data) { + std::array C {}; + // obsolete + C.fill(0); + z_stream defstream; + defstream.zalloc = nullptr; + defstream.zfree = nullptr; + defstream.opaque = nullptr; + defstream.avail_in = uInt(Data.size()); + defstream.next_in = const_cast(reinterpret_cast(&Data[0])); + defstream.avail_out = Biggest; + defstream.next_out = reinterpret_cast(C.data()); + deflateInit(&defstream, Z_BEST_COMPRESSION); + deflate(&defstream, Z_SYNC_FLUSH); + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + size_t TotalOut = defstream.total_out; + T Ret; + Ret.resize(TotalOut); + std::fill(Ret.begin(), Ret.end(), 0); + std::copy_n(C.begin(), TotalOut, Ret.begin()); + return Ret; +} + +template +inline T DeComp(const T& Compressed) { + std::array C {}; + // not needed + C.fill(0); + z_stream infstream; + infstream.zalloc = nullptr; + infstream.zfree = nullptr; + infstream.opaque = nullptr; + infstream.avail_in = Biggest; + infstream.next_in = const_cast(reinterpret_cast(&Compressed[0])); + infstream.avail_out = Biggest; + infstream.next_out = const_cast(reinterpret_cast(C.data())); + inflateInit(&infstream); + inflate(&infstream, Z_SYNC_FLUSH); + inflate(&infstream, Z_FINISH); + inflateEnd(&infstream); + size_t TotalOut = infstream.total_out; + T Ret; + Ret.resize(TotalOut); + std::fill(Ret.begin(), Ret.end(), 0); + std::copy_n(C.begin(), TotalOut, Ret.begin()); + return Ret; +} \ No newline at end of file diff --git a/src/Compressor.cpp b/src/Compressor.cpp deleted file mode 100755 index 6ee00ef8..00000000 --- a/src/Compressor.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/15/2020 -/// - -#include -#include - -#define Biggest 30000 -std::string Comp(std::string Data){ - char*C = new char[Biggest]; - memset(C, 0, Biggest); - z_stream defstream; - defstream.zalloc = Z_NULL; - defstream.zfree = Z_NULL; - defstream.opaque = Z_NULL; - defstream.avail_in = (uInt)Data.length(); - defstream.next_in = (Bytef *)&Data[0]; - defstream.avail_out = Biggest; - defstream.next_out = reinterpret_cast(C); - deflateInit(&defstream, Z_BEST_COMPRESSION); - deflate(&defstream, Z_SYNC_FLUSH); - deflate(&defstream, Z_FINISH); - deflateEnd(&defstream); - int TO = defstream.total_out; - std::string Ret(TO,0); - memcpy_s(&Ret[0],TO,C,TO); - delete [] C; - return Ret; -} -std::string DeComp(std::string Compressed){ - char*C = new char[Biggest]; - memset(C, 0, Biggest); - z_stream infstream; - infstream.zalloc = Z_NULL; - infstream.zfree = Z_NULL; - infstream.opaque = Z_NULL; - infstream.avail_in = Biggest; - infstream.next_in = (Bytef *)(&Compressed[0]); - infstream.avail_out = Biggest; - infstream.next_out = (Bytef *)(C); - inflateInit(&infstream); - inflate(&infstream, Z_SYNC_FLUSH); - inflate(&infstream, Z_FINISH); - inflateEnd(&infstream); - int TO = infstream.total_out; - std::string Ret(TO,0); - memcpy_s(&Ret[0],TO,C,TO); - delete [] C; - return Ret; -} \ No newline at end of file diff --git a/src/Config.cpp b/src/Config.cpp old mode 100755 new mode 100644 index 0b673eb1..1c31933b --- a/src/Config.cpp +++ b/src/Config.cpp @@ -1,58 +1,41 @@ -/// -/// Created by Anonymous275 on 2/23/2021 -/// - -#include -#include "Network/network.h" -#include -#include "Logger.h" -#include -#include -namespace fs = std::filesystem; - -std::string Branch; -void ParseConfig(const nlohmann::json& d){ - if(d["Port"].is_number()){ - DEFAULT_PORT = d["Port"].get(); - } - //Default -1 - //Release 1 - //EA 2 - //Dev 3 - //Custom 3 - - if(d["Build"].is_string()){ - Branch = d["Build"].get(); - for(char& c : Branch)c = char(tolower(c)); - } -} - -void ConfigInit(){ - if(fs::exists("Launcher.cfg")){ - std::ifstream cfg("Launcher.cfg"); - if(cfg.is_open()){ - auto Size = fs::file_size("Launcher.cfg"); - std::string Buffer(Size, 0); - cfg.read(&Buffer[0], Size); - cfg.close(); - nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); - if(d.is_discarded()){ - fatal("Config failed to parse make sure it's valid JSON!"); - } - ParseConfig(d); - }else fatal("Failed to open Launcher.cfg!"); - }else{ - std::ofstream cfg("Launcher.cfg"); - if(cfg.is_open()){ - cfg << - R"({ -"Port": 4444, -"Build": "Default" -})"; - cfg.close(); - }else{ - fatal("Failed to write config on disk!"); - } - } -} - +#include "Config.h" + +#include +#include +#include +#include + +Config::Config() { + if (std::filesystem::exists("Launcher.cfg")) { + boost::iostreams::mapped_file cfg("Launcher.cfg", boost::iostreams::mapped_file::mapmode::readonly); + nlohmann::json d = nlohmann::json::parse(cfg.const_data(), nullptr, false); + if (d.is_discarded()) { + is_valid = false; + } + // parse config + if (d["Port"].is_number()) { + port = d["Port"].get(); + } + if (d["Build"].is_string()) { + branch = d["Build"].get(); + for (char& c : branch) { + c = char(tolower(c)); + } + } + if (d["GameDir"].is_string()) { + game_dir = d["GameDir"].get(); + } + } else { + write_to_file(); + } +} + +void Config::write_to_file() const { + nlohmann::json d { + { "Port", port }, + { "Branch", branch }, + { "GameDir", game_dir }, + }; + std::ofstream of("Launcher.cfg", std::ios::trunc); + of << d.dump(4); +} diff --git a/src/Config.h b/src/Config.h new file mode 100644 index 00000000..4a8767ad --- /dev/null +++ b/src/Config.h @@ -0,0 +1,14 @@ +#pragma once + +#include +struct Config { + Config(); + + void write_to_file() const; + + bool is_valid = true; + int port = 4444; + std::string branch = "Default"; + std::string game_dir = ""; +}; + diff --git a/src/Discord.cpp b/src/Discord.cpp deleted file mode 100755 index 62c28b44..00000000 --- a/src/Discord.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/16/2020 -/// -#include "Discord/discord_rpc.h" -#include "Logger.h" -#include -#include -#include - -struct DInfo{ - std::string Name; - std::string Tag; - std::string DID; -}; -DInfo* DiscordInfo = nullptr; -int64_t StartTime; -void updateDiscordPresence(){ - //if (SendPresence) { - //char buffer[256]; - DiscordRichPresence discordPresence; - memset(&discordPresence, 0, sizeof(discordPresence)); - std::string P = "Playing with friends!"; ///to be revisited - discordPresence.state = P.c_str(); - //sprintf(buffer, "Frustration level: %d", FrustrationLevel); - //discordPresence.details = buffer; - discordPresence.startTimestamp = StartTime; - //discordPresence.endTimestamp = time(0) + 5 * 60; - - discordPresence.largeImageKey = "mainlogo"; - //discordPresence.smallImageKey = "logo"; - //discordPresence.partyId = "party1234"; - //discordPresence.partySize = 1; - //discordPresence.partyMax = 6; - //discordPresence.matchSecret = "xyzzy"; - //discordPresence.joinSecret = "join"; - //discordPresence.spectateSecret = "look"; - //discordPresence.instance = 0; - Discord_UpdatePresence(&discordPresence); - //} - //else { - // Discord_ClearPresence(); - //} -} -void handleDiscordReady(const DiscordUser* User){ - DiscordInfo = new DInfo{ - User->username, - User->discriminator, - User->userId - }; -} -void discordInit(){ - DiscordEventHandlers handlers; - memset(&handlers, 0, sizeof(handlers)); - handlers.ready = handleDiscordReady; - /*handlers.disconnected = handleDiscordDisconnected; - handlers.errored = handleDiscordError; - handlers.joinGame = handleDiscordJoin; - handlers.spectateGame = handleDiscordSpectate; - handlers.joinRequest = handleDiscordJoinRequest;*/ - Discord_Initialize("629743237988352010", &handlers, 1,nullptr); -} -[[noreturn]] void Loop(){ - StartTime = time(nullptr); - while (true) { - updateDiscordPresence(); - #ifdef DISCORD_DISABLE_IO_THREAD - Discord_UpdateConnection(); - #endif - Discord_RunCallbacks(); - if(DiscordInfo == nullptr){ - std::this_thread::sleep_for(std::chrono::milliseconds(250)); - }else std::this_thread::sleep_for(std::chrono::seconds(2)); - } -} - -void DMain(){ - discordInit(); - Loop(); -} -std::string GetDName(){ - return DiscordInfo->Name; -} -std::string GetDTag(){ - return DiscordInfo->Tag; -} -std::string GetDID(){ - return DiscordInfo->DID; -} -void DAboard(){ - DiscordInfo = nullptr; -} -void ErrorAboard(){ - error("Discord timeout! please start the discord app and try again after 30 secs"); - std::this_thread::sleep_for(std::chrono::seconds(5)); - exit(6); -} -void Discord_Main(){ - /*std::thread t1(DMain); - t1.detach();*/ - /*info("Connecting to discord client..."); - int C = 0; - while(DiscordInfo == nullptr && C < 80){ - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - C++; - } - if(DiscordInfo == nullptr)ErrorAboard();*/ -} diff --git a/src/GameStart.cpp b/src/GameStart.cpp deleted file mode 100755 index 3a85eda0..00000000 --- a/src/GameStart.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/19/2020 -/// - -#include -#include -#include "Startup.h" -#include "Logger.h" -#include - -unsigned long GamePID = 0; -std::string QueryKey(HKEY hKey,int ID); -std::string GetGamePath(){ - static std::string Path; - if(!Path.empty())return Path; - - HKEY hKey; - LPCTSTR sk = "Software\\BeamNG\\BeamNG.drive"; - LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey); - if (openRes != ERROR_SUCCESS){ - fatal("Please launch the game at least once!"); - } - Path = QueryKey(hKey,4); - - if(Path.empty()){ - sk = R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders)"; - openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey); - if (openRes != ERROR_SUCCESS){ - fatal("Cannot get Local Appdata directory!"); - } - Path = QueryKey(hKey,5); - Path += "\\BeamNG.drive\\"; - } - std::string Ver = CheckVer(GetGameDir()); - Ver = Ver.substr(0,Ver.find('.',Ver.find('.')+1)); - Path += Ver + "\\"; - return Path; -} - -void StartGame(std::string Dir){ - BOOL bSuccess = FALSE; - PROCESS_INFORMATION pi; - STARTUPINFO si = {0}; - si.cb = sizeof(si); - std::string BaseDir = Dir; //+"\\Bin64"; - //Dir += R"(\Bin64\BeamNG.drive.x64.exe)"; - Dir += "\\BeamNG.drive.exe"; - bSuccess = CreateProcessA(Dir.c_str(), nullptr, nullptr, nullptr, TRUE, 0, nullptr, BaseDir.c_str(), &si, &pi); - if (bSuccess){ - info("Game Launched!"); - GamePID = pi.dwProcessId; - WaitForSingleObject(pi.hProcess, INFINITE); - error("Game Closed! launcher closing soon"); - }else{ - error("Failed to Launch the game! launcher closing soon"); - } - std::this_thread::sleep_for(std::chrono::seconds(5)); - exit(2); -} - -void InitGame(const std::string& Dir){ - if(!Dev){ - std::thread Game(StartGame, Dir); - Game.detach(); - } -} diff --git a/src/Hashing.cpp b/src/Hashing.cpp new file mode 100644 index 00000000..0f59b3be --- /dev/null +++ b/src/Hashing.cpp @@ -0,0 +1,35 @@ +#include "Hashing.h" + +#include +#include +#include +#include +#include +#include +#include + +std::string sha256_file(const std::string& path) { + CryptoPP::byte digest[CryptoPP::SHA256::DIGESTSIZE]; + digest[sizeof(digest) - 1] = 0; + FILE* file = std::fopen(path.c_str(), "rb"); + if (!file) { + throw std::runtime_error(fmt::format("Failed to open {}: {}", path, std::strerror(errno))); + } + std::vector buffer{}; + buffer.resize(std::filesystem::file_size(path)); + std::fread(buffer.data(), 1, buffer.size(), file); + std::fclose(file); + CryptoPP::SHA256().CalculateDigest( + digest, + buffer.data(), buffer.size()); + CryptoPP::HexEncoder encoder; + encoder.Put(digest, sizeof(digest)); + encoder.MessageEnd(); + std::string encoded {}; + if (auto size = encoder.MaxRetrievable(); size != 0) + { + encoded.resize(size); + encoder.Get(reinterpret_cast(&encoded[0]), encoded.size()); + } + return encoded; +} diff --git a/src/Hashing.h b/src/Hashing.h new file mode 100644 index 00000000..36fdafc9 --- /dev/null +++ b/src/Hashing.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::string sha256_file(const std::string& path); diff --git a/src/Http.cpp b/src/Http.cpp new file mode 100644 index 00000000..eb77e143 --- /dev/null +++ b/src/Http.cpp @@ -0,0 +1,116 @@ +#include "Http.h" +#include +#include +#include +#include +#include +#include + +static httplib::Client& get_client(const std::string& host) { + static thread_local std::unordered_map s_clients_cache {}; + if (!s_clients_cache.contains(host)) { + spdlog::debug("Caching connection to {}", host); + s_clients_cache.emplace(host, httplib::Client(host)); + } else { + spdlog::debug("Reusing cached connection to {}", host); + } + return s_clients_cache.at(host); +} + +bool HTTP::isDownload = false; +std::string HTTP::Get(const std::string& host_and_target) { + static std::mutex Lock; + std::scoped_lock Guard(Lock); + + auto pos = host_and_target.find('/', 10); + + auto& cli = get_client(host_and_target.substr(0, pos).c_str()); + cli.set_connection_timeout(std::chrono::seconds(10)); + cli.set_follow_location(true); + + httplib::Headers headers { + { "Accept-Encoding", "gzip" } + }; + + auto res = cli.Get(host_and_target.substr(pos).c_str(), headers); + std::string Ret; + + if (res) { + if (res->status == 200) { + Ret = res->body; + } else + spdlog::error(res->reason); + + } else { + if (isDownload) { + std::cout << "\n"; + } + spdlog::error("HTTP Get failed on " + to_string(res.error())); + } + + return Ret; +} + +std::string HTTP::Post(const std::string& host_and_target, const std::string& Fields) { + static std::mutex Lock; + std::scoped_lock Guard(Lock); + + auto pos = host_and_target.find('/', 10); + + auto& cli = get_client(host_and_target.substr(0, pos).c_str()); + cli.set_connection_timeout(std::chrono::seconds(10)); + std::string Ret; + + if (!Fields.empty()) { + httplib::Result res = cli.Post(host_and_target.substr(pos).c_str(), Fields, "application/json"); + + if (res) { + if (res->status != 200) { + spdlog::error(res->reason); + } + Ret = res->body; + } else { + spdlog::error("HTTP Post failed on " + to_string(res.error())); + } + } else { + httplib::Result res = cli.Post(host_and_target.substr(pos).c_str()); + if (res) { + if (res->status != 200) { + spdlog::error(res->reason); + } + Ret = res->body; + } else { + spdlog::error("HTTP Post failed on " + to_string(res.error())); + } + } + + if (Ret.empty()) + return "-1"; + else + return Ret; +} + +bool HTTP::Download(const std::string& IP, const std::string& Path) { + static std::mutex Lock; + std::scoped_lock Guard(Lock); + + isDownload = true; + std::string Ret = Get(IP); + isDownload = false; + + if (Ret.empty()) + return false; + + std::ofstream File(Path, std::ios::binary); + if (File.is_open()) { + File << Ret; + File.close(); + std::cout << "\n"; + spdlog::info("Download Complete!"); + } else { + spdlog::error("Failed to open file directory: " + Path); + return false; + } + + return true; +} diff --git a/include/Http.h b/src/Http.h old mode 100755 new mode 100644 similarity index 97% rename from include/Http.h rename to src/Http.h index 69ab4033..afa5544d --- a/include/Http.h +++ b/src/Http.h @@ -7,7 +7,6 @@ /// #pragma once #include -#include "Logger.h" class HTTP { public: static bool Download(const std::string &IP, const std::string &Path); @@ -16,4 +15,4 @@ class HTTP { static bool ProgressBar(size_t c, size_t t); public: static bool isDownload; -}; \ No newline at end of file +}; diff --git a/src/Identity.cpp b/src/Identity.cpp new file mode 100644 index 00000000..b13328d3 --- /dev/null +++ b/src/Identity.cpp @@ -0,0 +1,103 @@ +#include "Identity.h" +#include "Http.h" +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +Result ident::login_cached() noexcept { + std::string private_key; + try { + std::ifstream key_file(ident::KEYFILE); + if (key_file.is_open()) { + auto size = fs::file_size(ident::KEYFILE); + private_key.resize(size); + key_file.read(&private_key[0], static_cast(size)); + key_file.close(); + } + } catch (const std::exception& e) { + return fmt::format("Failed to read cached key: {}", e.what()); + } + + std::string private_key_json {}; + + try { + private_key_json = nlohmann::json { + { "pk", private_key } + }.dump(); + } catch (const std::exception& e) { + spdlog::error("Private key had invalid format, please log in again."); + return std::string("Invalid login saved, please log in again."); + } + + // login and remember (again) + return detail::login(private_key_json, true); +} + +bool ident::is_login_cached() noexcept { + return std::filesystem::exists(KEYFILE) && std::filesystem::is_regular_file(KEYFILE); +} + +Result ident::login(const std::string& username_or_email, const std::string& password, bool remember) { + std::string login_json {}; + + try { + login_json = nlohmann::json { + { "username", username_or_email }, + { "password", password }, + } + .dump(); + } catch (const std::exception& e) { + spdlog::error("Username or password has invalid format, please try again."); + return std::string("Username or password contain illegal characters, please try again."); + } + + return detail::login(login_json, remember); +} + +void ident::detail::cache_key(const std::string& private_key) noexcept { + try { + std::ofstream key_file(ident::KEYFILE, std::ios::trunc); + key_file << private_key; + } catch (const std::exception& e) { + spdlog::warn("Failed to cache key - login will not be remembered: {}", e.what()); + } +} +Result ident::detail::login(const std::string& json_params, bool remember) noexcept { + auto result = HTTP::Post("https://auth.beammp.com/userlogin", json_params); + + nlohmann::json json = nlohmann::json::parse(result, nullptr, false); + + if (result == "-1" || result.at(0) != '{' || json.is_discarded()) { + spdlog::error("auth.beammp.com failed to respond with valid user details"); + spdlog::trace("auth.beammp.com/userlogin responded with: {}", result); + return std::string("Invalid answer from auth server. Please check your internet connection and see if you can reach https://beammp.com."); + } + + try { + if (json["success"].get()) { + spdlog::info("{}", json["message"].get()); + ident::Identity id { + .PublicKey = json["public_key"].get(), + .PrivateKey = json["private_key"].get(), + .Role = json["role"].get(), + .Username = json["username"].get(), + .Message = json["message"].get(), + }; + if (remember) { + cache_key(id.PrivateKey); + } + return id; + } else { + spdlog::info("Auto-Authentication unsuccessful please re-login!"); + return json["message"].is_string() ? json["message"].get() : "Unknown error, please log in again."; + } + } catch (const std::exception& e) { + spdlog::error("Incomplete or invalid answer from auth servers. Please try logging in again."); + spdlog::trace("auth.beammp.com/userlogin responded with incomplete data: {}", result); + return std::string("Failed to auto-login with saved details, because the auth server responded with invalid details."); + } +} diff --git a/src/Identity.h b/src/Identity.h new file mode 100644 index 00000000..e20e4c49 --- /dev/null +++ b/src/Identity.h @@ -0,0 +1,33 @@ +#pragma once +#include "Result.h" +#include +#include + +namespace ident { + +constexpr const char* KEYFILE = "key"; + +struct Identity { + std::string PublicKey {}; + std::string PrivateKey {}; + std::string Role {}; + std::string Username {}; + std::string Message {}; +}; + +/// Whether a login is cached / remembered +bool is_login_cached() noexcept; + +Result login_cached() noexcept; + +Result login(const std::string& username_or_email, const std::string& password, bool remember); + +namespace detail { + + Result login(const std::string& json_params, bool remember) noexcept; + + void cache_key(const std::string& private_key) noexcept; + +} + +} diff --git a/src/Launcher.cpp b/src/Launcher.cpp new file mode 100644 index 00000000..3219a429 --- /dev/null +++ b/src/Launcher.cpp @@ -0,0 +1,326 @@ +#include "Launcher.h" +#include "ClientNetwork.h" +#include "Compression.h" +#include "Hashing.h" +#include "Http.h" +#include "Identity.h" +#include "Platform.h" +#include "ServerNetwork.h" +#include "Version.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::asio; + +namespace fs = std::filesystem; + +Launcher::Launcher() { + spdlog::debug("Launcher startup"); + m_config = Config {}; + if (!m_config->is_valid) { + spdlog::error("Launcher config invalid!"); + } + // try logging in immediately, for later update requests and such stuff + try_auto_login(); + + client_network = std::make_unique(*this, m_config->port); + + m_client_network_thread = boost::scoped_thread<>([&] { + client_network->run(); + }); +} + +/// Sets shared headers for all backend proxy messages +static void proxy_set_headers(httplib::Response& res) { + res.set_header("Access-Control-Allow-Origin", "*"); + res.set_header("Access-Control-Request-Method", "POST, OPTIONS, GET"); + res.set_header("Access-Control-Request-Headers", "X-API-Version"); +} + +void Launcher::proxy_main() { + httplib::Server HTTPProxy; + httplib::Headers headers = { + { "User-Agent", fmt::format("BeamMP-Launcher/{}.{}.{}", PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH) }, + { "Accept", "*/*" } + }; + std::string pattern = "/:any1"; + for (int i = 2; i <= 4; i++) { + HTTPProxy.Get(pattern, [&](const httplib::Request& req, httplib::Response& res) { + httplib::Client cli("https://backend.beammp.com"); + proxy_set_headers(res); + if (req.has_header("X-BMP-Authentication")) { + headers.emplace("X-BMP-Authentication", identity->PrivateKey); + } + if (req.has_header("X-API-Version")) { + headers.emplace("X-API-Version", req.get_header_value("X-API-Version")); + } + if (auto cli_res = cli.Get(req.path, headers); cli_res) { + res.set_content(cli_res->body, cli_res->get_header_value("Content-Type")); + } else { + res.set_content(to_string(cli_res.error()), "text/plain"); + } + }); + + HTTPProxy.Post(pattern, [&](const httplib::Request& req, httplib::Response& res) { + httplib::Client cli("https://backend.beammp.com"); + proxy_set_headers(res); + if (req.has_header("X-BMP-Authentication")) { + headers.emplace("X-BMP-Authentication", identity->PrivateKey); + } + if (req.has_header("X-API-Version")) { + headers.emplace("X-API-Version", req.get_header_value("X-API-Version")); + } + if (auto cli_res = cli.Post(req.path, headers, req.body, + req.get_header_value("Content-Type")); + cli_res) { + res.set_content(cli_res->body, cli_res->get_header_value("Content-Type")); + } else { + res.set_content(to_string(cli_res.error()), "text/plain"); + } + }); + pattern += "/:any" + std::to_string(i); + } + HTTPProxy.listen_after_bind(); +} + +Launcher::~Launcher() { + m_proxy_thread.interrupt(); + m_game_thread.detach(); +} + +void Launcher::parse_config() { +} + +void Launcher::check_for_updates(int argc, char** argv) { + std::string LatestHash = HTTP::Get(fmt::format("https://backend.beammp.com/sha/launcher?branch={}&pk={}", m_config->branch, identity->PublicKey)); + std::string LatestVersion = HTTP::Get(fmt::format("https://backend.beammp.com/version/launcher?branch={}&pk={}", m_config->branch, identity->PublicKey)); + std::string DownloadURL = fmt::format("https://backend.beammp.com/builds/launcher?download=true" + "&pk={}" + "&branch={}", + identity->PublicKey, m_config->branch); + + spdlog::debug("Latest hash: {}", LatestHash); + spdlog::debug("Latest version: {}", LatestVersion); + + transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); + std::string EP = (m_exe_path.get() / m_exe_name.get()).generic_string(); + std::string Back = (m_exe_path.get() / "BeamMP-Launcher.back").generic_string(); + + std::string FileHash = sha256_file(EP); + + if (FileHash != LatestHash + && Version(PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH).is_outdated(Version(LatestVersion))) { + spdlog::info("Launcher update found!"); + fs::remove(Back); + fs::rename(EP, Back); + spdlog::info("Downloading Launcher update " + LatestHash); + HTTP::Download(DownloadURL, EP); + plat::URelaunch(argc, argv); + } else { + spdlog::info("Launcher version is up to date"); + } +} + +void Launcher::find_game() { + // try to find the game by multiple means + + spdlog::info("Locating game"); + // 0. config! + if (!m_config->game_dir.empty() + && std::filesystem::exists(std::filesystem::path(m_config->game_dir) / "BeamNG.drive.exe")) { + spdlog::debug("Found game directory in config: '{}'", m_config->game_dir); + return; + } + + // 1. magic + auto game_dir = plat::get_game_dir_magically(); + if (!game_dir.empty()) { + m_config->game_dir = game_dir; + m_config->write_to_file(); + return; + } else { + spdlog::debug("Couldn't magically find game directory"); + } + + // 2. ask + m_config->game_dir = plat::ask_for_folder(); + + spdlog::debug("Located game at '{}'", m_config->game_dir); + m_config->write_to_file(); +} + +static std::string check_game_version(const std::filesystem::path& dir) { + std::string temp; + std::string Path = (dir / "integrity.json").generic_string(); + boost::iostreams::mapped_file file(Path); + auto json = nlohmann::json::parse(file.const_begin(), file.const_end()); + return json["version"].is_string() ? json["version"].get() : ""; +} + +void Launcher::pre_game() { + std::string GameVer = check_game_version(m_config->game_dir); + if (GameVer.empty()) { + spdlog::error("Game version is empty!"); + } + spdlog::info("Game Version: " + GameVer); + + check_mp((std::filesystem::path(m_config->game_dir) / "mods/multiplayer").generic_string()); + + std::string LatestHash = HTTP::Get(fmt::format("https://backend.beammp.com/sha/mod?branch={}&pk={}", m_config->branch, identity->PublicKey)); + transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); + LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(), + [](auto const& c) -> bool { return !std::isalnum(c); }), + LatestHash.end()); + + try { + if (!fs::exists(std::filesystem::path(m_config->game_dir) / "mods/multiplayer")) { + fs::create_directories(std::filesystem::path(m_config->game_dir) / "mods/multiplayer"); + } + enable_mp(); + } catch (std::exception& e) { + spdlog::error("Fatal: {}", e.what()); + std::exit(1); + } + + auto ZipPath(std::filesystem::path(m_config->game_dir) / "mods/multiplayer/BeamMP.zip"); + + std::string FileHash = sha256_file(ZipPath.generic_string()); + + if (FileHash != LatestHash) { + spdlog::info("Downloading BeamMP Update " + LatestHash); + HTTP::Download(fmt::format("https://backend.beammp.com/builds/client?download=true&pk={}&branch={}", identity->PublicKey, m_config->branch), ZipPath.generic_string()); + } + + auto Target = std::filesystem::path(m_config->game_dir) / "mods/unpacked/beammp"; + if (fs::is_directory(Target)) { + fs::remove_all(Target); + } +} + +static size_t count_items_in_dir(const std::filesystem::path& path) { + return static_cast(std::distance(std::filesystem::directory_iterator { path }, std::filesystem::directory_iterator {})); +} + +void Launcher::check_mp(const std::string& path) { + if (!fs::exists(path)) + return; + size_t c = count_items_in_dir(fs::path(path)); + try { + for (auto& p : fs::directory_iterator(path)) { + if (p.exists() && !p.is_directory()) { + std::string name = p.path().filename().string(); + for (char& Ch : name) + Ch = char(tolower(Ch)); + if (name != "beammp.zip") + fs::remove(p.path()); + } + } + } catch (...) { + spdlog::error("We were unable to clean the multiplayer mods folder! Is the game still running or do you have something open in that folder?"); + std::this_thread::sleep_for(std::chrono::seconds(5)); + std::exit(1); + } +} + +void Launcher::enable_mp() { + std::string File = (std::filesystem::path(m_config->game_dir) / "mods/db.json").generic_string(); + if (!fs::exists(File)) + return; + auto Size = fs::file_size(File); + if (Size < 2) + return; + std::ifstream db(File); + if (db.is_open()) { + std::string Data(Size, 0); + db.read(&Data[0], Size); + db.close(); + nlohmann::json d = nlohmann::json::parse(Data, nullptr, false); + if (Data.at(0) != '{' || d.is_discarded()) { + // spdlog::error("Failed to parse " + File); //TODO illegal formatting + return; + } + if (d.contains("mods") && d["mods"].contains("multiplayerbeammp")) { + d["mods"]["multiplayerbeammp"]["active"] = true; + std::ofstream ofs(File); + if (ofs.is_open()) { + ofs << d.dump(); + ofs.close(); + } else { + spdlog::error("Failed to write " + File); + } + } + } +} +void Launcher::game_main() { + auto path = std::filesystem::current_path(); + std::filesystem::current_path(m_config->game_dir); +#if defined(PLATFORM_LINUX) + auto game_path = (std::filesystem::path(m_config->game_dir) / "BinLinux/BeamNG.drive.x64").generic_string(); +#elif defined(PLATFORM_WINDOWS) + auto game_path = (std::filesystem::path(m_config->game_dir) / "Bin64/BeamNG.drive.x64.exe").generic_string(); +#endif + boost::process::child game(game_path, boost::process::std_out > boost::process::null); + std::filesystem::current_path(path); + if (game.running()) { + spdlog::info("Game launched!"); + game.wait(); + spdlog::info("Game closed! Launcher closing soon"); + } else { + spdlog::error("Failed to launch the game! Please start the game manually."); + } +} + +void Launcher::start_game() { + m_game_thread = boost::scoped_thread<>(&Launcher::game_main, this); +} +void Launcher::try_auto_login() { + if (identity->PublicKey.empty() && ident::is_login_cached()) { + auto login = ident::login_cached(); + if (login.has_value()) { + *identity = login.value(); + } + } +} + +Result Launcher::start_server_network(const std::string& host, uint16_t port) { + ip::tcp::resolver resolver(m_io); + boost::system::error_code ec; + auto resolved = resolver.resolve(host, std::to_string(port), ec); + if (ec) { + spdlog::error("Failed to resolve '{}': {}", host, ec.message()); + return fmt::format("Failed to resolve '{}': {}", host, ec.message()); + } + bool connected = false; + + for (const auto& addr : resolved) { + try { + server_network = std::make_unique(*this, addr.endpoint()); + spdlog::info("Resolved and connected to '[{}]:{}'", + addr.endpoint().address().to_string(), + addr.endpoint().port()); + connected = true; + break; + } catch (...) { + // ignore + } + } + if (!connected) { + spdlog::error("Failed to connect to [{}]:{}", host, port); + return fmt::format("Failed to connect to [{}]:{}", host, port); + } else { + m_server_network_thread = boost::scoped_thread<>([&] { + server_network->run(); + }); + } + return outcome::success(); +} diff --git a/src/Launcher.h b/src/Launcher.h new file mode 100644 index 00000000..d1a1a7ed --- /dev/null +++ b/src/Launcher.h @@ -0,0 +1,74 @@ +#pragma once + +#include "Config.h" +#include "Identity.h" +#include "Sync.h" +#include "Version.h" +#include +#include +#include +#include +#include +#include +#include + +class ClientNetwork; +class ServerNetwork; + +class Launcher { +public: + Launcher(); + ~Launcher(); + + void check_for_updates(int argc, char** argv); + + void set_exe_name(const std::string& name) { m_exe_name = name; } + void set_exe_path(const std::filesystem::path& path) { m_exe_path = path; } + + void find_game(); + + void pre_game(); + + void start_game(); + + std::string get_public_key(); + + Sync identity {}; + + Result start_server_network(const std::string& host, uint16_t port); + + Sync mod_version {}; + Sync game_version {}; + + std::unique_ptr client_network {}; + std::unique_ptr server_network {}; + +private: + /// Thread main function for the http(s) proxy thread. + void proxy_main(); + + /// Thread main function for the game thread. + void game_main(); + + void parse_config(); + + static void check_mp(const std::string& path); + + void enable_mp(); + + void try_auto_login(); + + Sync m_mod_loaded { false }; + + Sync m_config; + + boost::scoped_thread<> m_proxy_thread { &Launcher::proxy_main, this }; + boost::scoped_thread<> m_game_thread; + boost::scoped_thread<> m_client_network_thread; + boost::scoped_thread<> m_server_network_thread; + + Sync m_exe_name; + Sync m_exe_path; + boost::asio::io_context m_io {}; + Sync m_shutdown { false }; +}; diff --git a/src/Logger.cpp b/src/Logger.cpp deleted file mode 100755 index e23bf9a6..00000000 --- a/src/Logger.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/17/2020 -/// - -#include "Startup.h" -#include "Logger.h" -#include -#include -#include -#include - -std::string getDate() { - time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - tm local_tm = *localtime(&tt); - std::stringstream date; - int S = local_tm.tm_sec; - int M = local_tm.tm_min; - int H = local_tm.tm_hour; - std::string Secs = (S > 9 ? std::to_string(S) : "0" + std::to_string(S)); - std::string Min = (M > 9 ? std::to_string(M) : "0" + std::to_string(M)); - std::string Hour = (H > 9 ? std::to_string(H) : "0" + std::to_string(H)); - date - << "[" - << local_tm.tm_mday << "/" - << local_tm.tm_mon + 1 << "/" - << local_tm.tm_year + 1900 << " " - << Hour << ":" - << Min << ":" - << Secs - << "] "; - return date.str(); -} -void InitLog(){ - std::ofstream LFS; - LFS.open(GetEP() + "Launcher.log"); - if(!LFS.is_open()){ - error("logger file init failed!"); - }else LFS.close(); -} -void addToLog(const std::string& Line){ - std::ofstream LFS; - LFS.open(GetEP() + "Launcher.log", std::ios_base::app); - LFS << Line.c_str(); - LFS.close(); -} -void info(const std::string& toPrint) { - std::string Print = getDate() + "[INFO] " + toPrint + "\n"; - std::cout << Print; - addToLog(Print); -} -void debug(const std::string& toPrint) { - if(!Dev)return; - std::string Print = getDate() + "[DEBUG] " + toPrint + "\n"; - std::cout << Print; - addToLog(Print); -} -void warn(const std::string& toPrint){ - std::string Print = getDate() + "[WARN] " + toPrint + "\n"; - std::cout << Print; - addToLog(Print); -} -void error(const std::string& toPrint) { - std::string Print = getDate() + "[ERROR] " + toPrint + "\n"; - std::cout << Print; - addToLog(Print); -} -void fatal(const std::string& toPrint) { - std::string Print = getDate() + "[FATAL] " + toPrint + "\n"; - std::cout << Print; - addToLog(Print); - std::this_thread::sleep_for(std::chrono::seconds(5)); - _Exit(-1); -} -void except(const std::string& toPrint) { - std::string Print = getDate() + "[EXCEP] " + toPrint + "\n"; - std::cout << Print; - addToLog(Print); -} diff --git a/src/Network/Core.cpp b/src/Network/Core.cpp deleted file mode 100755 index 2b21ea9f..00000000 --- a/src/Network/Core.cpp +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/20/2020 -/// - -#include "Network/network.h" -#include "Security/Init.h" -#include -#include "Http.h" -#include -#include -#include "Startup.h" -#include "Logger.h" -#include -#include -#include - -extern int TraceBack; -std::set* ConfList = nullptr; -bool TCPTerminate = false; -int DEFAULT_PORT = 4444; -bool Terminate = false; -bool LoginAuth = false; -std::string UlStatus; -std::string MStatus; -bool ModLoaded; -int ping = -1; - -void StartSync(const std::string &Data){ - std::string IP = GetAddr(Data.substr(1,Data.find(':')-1)); - if(IP.find('.') == -1){ - if(IP == "DNS")UlStatus ="UlConnection Failed! (DNS Lookup Failed)"; - else UlStatus = "UlConnection Failed! (WSA failed to start)"; - ListOfMods = "-"; - Terminate = true; - return; - } - CheckLocalKey(); - UlStatus = "UlLoading..."; - TCPTerminate = false; - Terminate = false; - ConfList->clear(); - ping = -1; - std::thread GS(TCPGameServer,IP,std::stoi(Data.substr(Data.find(':')+1))); - GS.detach(); - info("Connecting to server"); -} - -bool IsAllowedLink(const std::string& Link) { - std::regex link_pattern(R"(https:\/\/(?:\w+)?(?:\.)?(?:beammp\.com|discord\.gg))"); - std::smatch link_match; - return std::regex_search(Link,link_match, link_pattern) && link_match.position() == 0; -} - -void Parse(std::string Data,SOCKET CSocket){ - char Code = Data.at(0), SubCode = 0; - if(Data.length() > 1)SubCode = Data.at(1); - switch (Code){ - case 'A': - Data = Data.substr(0,1); - break; - case 'B': - NetReset(); - Terminate = true; - TCPTerminate = true; - Data = Code + HTTP::Get("https://backend.beammp.com/servers-info"); - break; - case 'C': - ListOfMods.clear(); - StartSync(Data); - while(ListOfMods.empty() && !Terminate){ - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - if(ListOfMods == "-")Data = "L"; - else Data = "L"+ListOfMods; - break; - case 'O': //open default browser with URL - if(IsAllowedLink(Data.substr(1))) { - ShellExecuteA(nullptr, "open", Data.substr(1).c_str(), nullptr, nullptr,SW_SHOW); ///TODO: Look at when working on linux port - info("Opening Link \"" + Data.substr(1) + "\""); - } - Data.clear(); - break; - case 'P': - Data = Code + std::to_string(ProxyPort); - break; - case 'U': - if(SubCode == 'l')Data = UlStatus; - if(SubCode == 'p'){ - if(ping > 800){ - Data = "Up-2"; - }else Data = "Up" + std::to_string(ping); - } - if(!SubCode){ - std::string Ping; - if(ping > 800)Ping = "-2"; - else Ping = std::to_string(ping); - Data = std::string(UlStatus) + "\n" + "Up" + Ping; - } - break; - case 'M': - Data = MStatus; - break; - case 'Q': - if(SubCode == 'S'){ - NetReset(); - Terminate = true; - TCPTerminate = true; - ping = -1; - } - if(SubCode == 'G')exit(2); - Data.clear(); - break; - case 'R': //will send mod name - if(ConfList->find(Data) == ConfList->end()){ - ConfList->insert(Data); - ModLoaded = true; - } - Data.clear(); - break; - case 'Z': - Data = "Z" + GetVer(); - break; - case 'N': - if (SubCode == 'c'){ - Data = "N{\"Auth\":"+std::to_string(LoginAuth)+"}"; - }else{ - Data = "N" + Login(Data.substr(Data.find(':') + 1)); - } - break; - default: - Data.clear(); - break; - } - if(!Data.empty() && CSocket != -1){ - int res = send(CSocket, (Data+"\n").c_str(), int(Data.size())+1, 0); - if(res < 0){ - debug("(Core) send failed with error: " + std::to_string(WSAGetLastError())); - } - } -} -void GameHandler(SOCKET Client){ - - int32_t Size,Temp,Rcv; - char Header[10] = {0}; - do{ - Rcv = 0; - do{ - Temp = recv(Client,&Header[Rcv],1,0); - if(Temp < 1)break; - if(!isdigit(Header[Rcv]) && Header[Rcv] != '>') { - error("(Core) Invalid lua communication"); - KillSocket(Client); - return; - } - }while(Header[Rcv++] != '>'); - if(Temp < 1)break; - if(std::from_chars(Header,&Header[Rcv],Size).ptr[0] != '>'){ - debug("(Core) Invalid lua Header -> " + std::string(Header,Rcv)); - break; - } - std::string Ret(Size,0); - Rcv = 0; - - do{ - Temp = recv(Client,&Ret[Rcv],Size-Rcv,0); - if(Temp < 1)break; - Rcv += Temp; - }while(Rcv < Size); - if(Temp < 1)break; - - std::thread Respond(Parse, Ret, Client); - Respond.detach(); - }while(Temp > 0); - if (Temp == 0) { - debug("(Core) Connection closing"); - } else { - debug("(Core) recv failed with error: " + std::to_string(WSAGetLastError())); - } - NetReset(); - KillSocket(Client); -} -void localRes(){ - MStatus = " "; - UlStatus = "Ulstart"; - if(ConfList != nullptr){ - ConfList->clear(); - delete ConfList; - ConfList = nullptr; - } - ConfList = new std::set; -} -void CoreMain() { - debug("Core Network on start!"); - WSADATA wsaData; - SOCKET LSocket,CSocket; - struct addrinfo *res = nullptr; - struct addrinfo hints{}; - int iRes = WSAStartup(514, &wsaData); //2.2 - if (iRes)debug("WSAStartup failed with error: " + std::to_string(iRes)); - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT).c_str(), &hints, &res); - if (iRes){ - debug("(Core) addr info failed with error: " + std::to_string(iRes)); - WSACleanup(); - return; - } - LSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (LSocket == -1){ - debug("(Core) socket failed with error: " + std::to_string(WSAGetLastError())); - freeaddrinfo(res); - WSACleanup(); - return; - } - iRes = bind(LSocket, res->ai_addr, int(res->ai_addrlen)); - if (iRes == SOCKET_ERROR) { - error("(Core) bind failed with error: " + std::to_string(WSAGetLastError())); - freeaddrinfo(res); - KillSocket(LSocket); - WSACleanup(); - return; - } - iRes = listen(LSocket, SOMAXCONN); - if (iRes == SOCKET_ERROR) { - debug("(Core) listen failed with error: " + std::to_string(WSAGetLastError())); - freeaddrinfo(res); - KillSocket(LSocket); - WSACleanup(); - return; - } - do{ - CSocket = accept(LSocket, nullptr, nullptr); - if (CSocket == -1) { - error("(Core) accept failed with error: " + std::to_string(WSAGetLastError())); - continue; - } - localRes(); - info("Game Connected!"); - GameHandler(CSocket); - warn("Game Reconnecting..."); - }while(CSocket); - KillSocket(LSocket); - WSACleanup(); -} -int Handle(EXCEPTION_POINTERS *ep){ - char* hex = new char[100]; - sprintf_s(hex,100, "%lX", ep->ExceptionRecord->ExceptionCode); - except("(Core) Code : " + std::string(hex)); - delete [] hex; - return 1; -} - - -[[noreturn]] void CoreNetwork(){ - while(true) { -#ifndef __MINGW32__ - __try{ -#endif - CoreMain(); -#ifndef __MINGW32__ - }__except(Handle(GetExceptionInformation())){} -#endif - std::this_thread::sleep_for(std::chrono::seconds(1)); - } -} diff --git a/src/Network/DNS.cpp b/src/Network/DNS.cpp deleted file mode 100755 index eff5dcf6..00000000 --- a/src/Network/DNS.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 9/25/2020 -/// - -#include -#include -#include "Logger.h" - -std::string GetAddr(const std::string&IP){ - if(IP.find_first_not_of("0123456789.") == -1)return IP; - WSADATA wsaData; - hostent *host; - if(WSAStartup(514, &wsaData) != 0){ - error("WSA Startup Failed!"); - WSACleanup(); - return ""; - } - host = gethostbyname(IP.c_str()); - if(!host){ - error("DNS lookup failed! on " + IP); - WSACleanup(); - return "DNS"; - } - std::string Ret = inet_ntoa(*((struct in_addr *)host->h_addr)); - WSACleanup(); - return Ret; -} \ No newline at end of file diff --git a/src/Network/GlobalHandler.cpp b/src/Network/GlobalHandler.cpp deleted file mode 100755 index d6cb7771..00000000 --- a/src/Network/GlobalHandler.cpp +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/25/2020 -/// -#include "Network/network.h" -#include -#include -#include "Logger.h" -#include -#include -#include -#include - -std::chrono::time_point PingStart,PingEnd; -bool GConnected = false; -bool CServer = true; -SOCKET CSocket = -1; -SOCKET GSocket = -1; - -int KillSocket(uint64_t Dead){ - if(Dead == (SOCKET)-1){ - debug("Kill socket got -1 returning..."); - return 0; - } - shutdown(Dead,SD_BOTH); - int a = closesocket(Dead); - if(a != 0){ - warn("Failed to close socket!"); - } - return a; -} - -bool CheckBytes(uint32_t Bytes){ - if(Bytes == 0){ - debug("(Proxy) Connection closing"); - return false; - }else if(Bytes < 0){ - debug("(Proxy) send failed with error: " + std::to_string(WSAGetLastError())); - return false; - } - return true; -} - -void GameSend(std::string Data){ - static std::mutex Lock; - std::scoped_lock Guard(Lock); - if(TCPTerminate || !GConnected || CSocket == -1)return; - int32_t Size,Temp,Sent; - Data += '\n'; - Size = int32_t(Data.size()); - Sent = 0; - #ifdef DEBUG - if(Size > 1000){ - debug("Launcher -> game (" +std::to_string(Size)+")"); - } - #endif - do{ - if(Sent > -1){ - Temp = send(CSocket, &Data[Sent], Size - Sent, 0); - } - if(!CheckBytes(Temp))return; - Sent += Temp; - }while(Sent < Size); -} -void ServerSend(std::string Data, bool Rel){ - if(Terminate || Data.empty())return; - if(Data.find("Zp") != std::string::npos && Data.size() > 500){ - abort(); - } - char C = 0; - bool Ack = false; - int DLen = int(Data.length()); - if(DLen > 3)C = Data.at(0); - if (C == 'O' || C == 'T')Ack = true; - if(C == 'N' || C == 'W' || C == 'Y' || C == 'V' || C == 'E' || C == 'C')Rel = true; - if(Ack || Rel){ - if(Ack || DLen > 1000)SendLarge(Data); - else TCPSend(Data,TCPSock); - }else UDPSend(Data); - - if (DLen > 1000) { - debug("(Launcher->Server) Bytes sent: " + std::to_string(Data.length()) + " : " - + Data.substr(0, 10) - + Data.substr(Data.length() - 10)); - }else if(C == 'Z'){ - //debug("(Game->Launcher) : " + Data); - } -} - -void NetReset(){ - TCPTerminate = false; - GConnected = false; - Terminate = false; - UlStatus = "Ulstart"; - MStatus = " "; - if(UDPSock != (SOCKET)(-1)){ - debug("Terminating UDP Socket : " + std::to_string(TCPSock)); - KillSocket(UDPSock); - } - UDPSock = -1; - if(TCPSock != (SOCKET)(-1)){ - debug("Terminating TCP Socket : " + std::to_string(TCPSock)); - KillSocket(TCPSock); - } - TCPSock = -1; - if(GSocket != (SOCKET)(-1)){ - debug("Terminating GTCP Socket : " + std::to_string(GSocket)); - KillSocket(GSocket); - } - GSocket = -1; -} - -SOCKET SetupListener(){ - if(GSocket != -1)return GSocket; - struct addrinfo *result = nullptr; - struct addrinfo hints{}; - WSADATA wsaData; - int iRes = WSAStartup(514, &wsaData); //2.2 - if (iRes != 0) { - error("(Proxy) WSAStartup failed with error: " + std::to_string(iRes)); - return -1; - } - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT+1).c_str(), &hints, &result); - if (iRes != 0) { - error("(Proxy) info failed with error: " + std::to_string(iRes)); - WSACleanup(); - } - GSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); - if (GSocket == -1) { - error("(Proxy) socket failed with error: " + std::to_string(WSAGetLastError())); - freeaddrinfo(result); - WSACleanup(); - return -1; - } - iRes = bind(GSocket, result->ai_addr, (int) result->ai_addrlen); - if (iRes == SOCKET_ERROR) { - error("(Proxy) bind failed with error: " + std::to_string(WSAGetLastError())); - freeaddrinfo(result); - KillSocket(GSocket); - WSACleanup(); - return -1; - } - freeaddrinfo(result); - iRes = listen(GSocket, SOMAXCONN); - if (iRes == SOCKET_ERROR) { - error("(Proxy) listen failed with error: " + std::to_string(WSAGetLastError())); - KillSocket(GSocket); - WSACleanup(); - return -1; - } - return GSocket; -} -void AutoPing(){ - while(!Terminate){ - ServerSend("p",false); - PingStart = std::chrono::high_resolution_clock::now(); - std::this_thread::sleep_for(std::chrono::seconds (1)); - } -} -int ClientID = -1; -void ParserAsync(const std::string& Data){ - if(Data.empty())return; - char Code = Data.at(0),SubCode = 0; - if(Data.length() > 1)SubCode = Data.at(1); - switch (Code) { - case 'p': - PingEnd = std::chrono::high_resolution_clock::now(); - if(PingStart > PingEnd)ping = 0; - else ping = int(std::chrono::duration_cast(PingEnd-PingStart).count()); - return; - case 'M': - MStatus = Data; - UlStatus = "Uldone"; - return; - default: - break; - } - GameSend(Data); -} -void ServerParser(const std::string& Data){ - ParserAsync(Data); -} -void NetMain(const std::string& IP, int Port){ - std::thread Ping(AutoPing); - Ping.detach(); - UDPClientMain(IP,Port); - CServer = true; - Terminate = true; - info("Connection Terminated!"); -} -void TCPGameServer(const std::string& IP, int Port){ - GSocket = SetupListener(); - while (!TCPTerminate && GSocket != -1){ - debug("MAIN LOOP OF GAME SERVER"); - GConnected = false; - if(!CServer){ - warn("Connection still alive terminating"); - NetReset(); - TCPTerminate = true; - Terminate = true; - break; - } - if(CServer) { - std::thread Client(TCPClientMain, IP, Port); - Client.detach(); - } - CSocket = accept(GSocket, nullptr, nullptr); - if (CSocket == -1) { - debug("(Proxy) accept failed with error: " + std::to_string(WSAGetLastError())); - break; - } - debug("(Proxy) Game Connected!"); - GConnected = true; - if(CServer){ - std::thread t1(NetMain, IP, Port); - t1.detach(); - CServer = false; - } - int32_t Size,Temp,Rcv; - char Header[10] = {0}; - - //Read byte by byte until '>' is rcved then get the size and read based on it - do{ - Rcv = 0; - - do{ - Temp = recv(CSocket,&Header[Rcv],1,0); - if(Temp < 1 || TCPTerminate)break; - }while(Header[Rcv++] != '>'); - if(Temp < 1 || TCPTerminate)break; - if(std::from_chars(Header,&Header[Rcv],Size).ptr[0] != '>'){ - debug("(Game) Invalid lua Header -> " + std::string(Header,Rcv)); - break; - } - std::string Ret(Size,0); - Rcv = 0; - do{ - Temp = recv(CSocket,&Ret[Rcv],Size-Rcv,0); - if(Temp < 1)break; - Rcv += Temp; - }while(Rcv < Size && !TCPTerminate); - if(Temp < 1 || TCPTerminate)break; - - ServerSend(Ret,false); - - }while(Temp > 0 && !TCPTerminate); - if(Temp == 0)debug("(Proxy) Connection closing"); - else debug("(Proxy) recv failed error : " + std::to_string(WSAGetLastError())); - } - TCPTerminate = true; - GConnected = false; - Terminate = true; - if(CSocket != SOCKET_ERROR)KillSocket(CSocket); - debug("END OF GAME SERVER"); -} \ No newline at end of file diff --git a/src/Network/Http.cpp b/src/Network/Http.cpp deleted file mode 100755 index a0d5dd08..00000000 --- a/src/Network/Http.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/18/2020 -/// -#define CPPHTTPLIB_OPENSSL_SUPPORT - -#include -#include -#include -#include "Http.h" -#include -#include -#include - -bool HTTP::isDownload = false; -std::string HTTP::Get(const std::string &IP) { - static std::mutex Lock; - std::scoped_lock Guard(Lock); - - auto pos = IP.find('/',10); - - httplib::Client cli(IP.substr(0, pos).c_str()); - cli.set_connection_timeout(std::chrono::seconds(10)); - cli.set_follow_location(true); - auto res = cli.Get(IP.substr(pos).c_str(), ProgressBar); - std::string Ret; - - if(res){ - if(res->status == 200){ - Ret = res->body; - }else error(res->reason); - - }else{ - if(isDownload) { - std::cout << "\n"; - } - error("HTTP Get failed on " + to_string(res.error())); - } - - return Ret; -} - -std::string HTTP::Post(const std::string& IP, const std::string& Fields) { - static std::mutex Lock; - std::scoped_lock Guard(Lock); - - auto pos = IP.find('/',10); - - httplib::Client cli(IP.substr(0, pos).c_str()); - cli.set_connection_timeout(std::chrono::seconds(10)); - std::string Ret; - - if(!Fields.empty()) { - httplib::Result res = cli.Post(IP.substr(pos).c_str(), Fields, "application/json"); - - if(res) { - if (res->status != 200) { - error(res->reason); - } - Ret = res->body; - }else{ - error("HTTP Post failed on " + to_string(res.error())); - } - }else{ - httplib::Result res = cli.Post(IP.substr(pos).c_str()); - if(res) { - if (res->status != 200) { - error(res->reason); - } - Ret = res->body; - }else{ - error("HTTP Post failed on " + to_string(res.error())); - } - } - - if(Ret.empty())return "-1"; - else return Ret; -} - -bool HTTP::ProgressBar(size_t c, size_t t){ - if(isDownload) { - static double last_progress, progress_bar_adv; - progress_bar_adv = round(c / double(t) * 25); - std::cout << "\r"; - std::cout << "Progress : [ "; - std::cout << round(c / double(t) * 100); - std::cout << "% ] ["; - int i; - for (i = 0; i <= progress_bar_adv; i++)std::cout << "#"; - for (i = 0; i < 25 - progress_bar_adv; i++)std::cout << "."; - std::cout << "]"; - last_progress = round(c / double(t) * 100); - } - return true; -} - -bool HTTP::Download(const std::string &IP, const std::string &Path) { - static std::mutex Lock; - std::scoped_lock Guard(Lock); - - isDownload = true; - std::string Ret = Get(IP); - isDownload = false; - - if(Ret.empty())return false; - - std::ofstream File(Path, std::ios::binary); - if(File.is_open()) { - File << Ret; - File.close(); - std::cout << "\n"; - info("Download Complete!"); - }else{ - error("Failed to open file directory: " + Path); - return false; - } - - return true; -} diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp deleted file mode 100755 index 29de1cb5..00000000 --- a/src/Network/Resources.cpp +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 4/11/2020 -/// - -#include "Network/network.h" -#include -#include -#include "Startup.h" -#include "Logger.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fs = std::filesystem; -std::string ListOfMods; -std::vector Split(const std::string& String,const std::string& delimiter){ - std::vector Val; - size_t pos; - std::string token,s = String; - while ((pos = s.find(delimiter)) != std::string::npos) { - token = s.substr(0, pos); - if(!token.empty())Val.push_back(token); - s.erase(0, pos + delimiter.length()); - } - if(!s.empty())Val.push_back(s); - return Val; -} - -void CheckForDir(){ - if(!fs::exists("Resources")){ - _wmkdir(L"Resources"); - } -} -void WaitForConfirm(){ - while(!Terminate && !ModLoaded){ - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - ModLoaded = false; -} - - -void Abord(){ - Terminate = true; - TCPTerminate = true; - info("Terminated!"); -} - -std::string Auth(SOCKET Sock){ - TCPSend("VC" + GetVer(),Sock); - - auto Res = TCPRcv(Sock); - - if(Res.empty() || Res[0] == 'E' || Res[0] == 'K'){ - Abord(); - return ""; - } - - TCPSend(PublicKey,Sock); - if(Terminate)return ""; - - Res = TCPRcv(Sock); - if(Res.empty() || Res[0] != 'P'){ - Abord(); - return ""; - } - - Res = Res.substr(1); - if(Res.find_first_not_of("0123456789") == std::string::npos){ - ClientID = std::stoi(Res); - }else{ - Abord(); - UUl("Authentication failed!"); - return ""; - } - TCPSend("SR",Sock); - if(Terminate)return ""; - - Res = TCPRcv(Sock); - - if(Res[0] == 'E' || Res[0] == 'K'){ - Abord(); - return ""; - } - - if(Res.empty() || Res == "-"){ - info("Didn't Receive any mods..."); - ListOfMods = "-"; - TCPSend("Done",Sock); - info("Done!"); - return ""; - } - return Res; -} - -void UpdateUl(bool D,const std::string& msg){ - if(D)UlStatus = "UlDownloading Resource " + msg; - else UlStatus = "UlLoading Resource " + msg; -} - -void AsyncUpdate(uint64_t& Rcv,uint64_t Size,const std::string& Name){ - do { - double pr = double(Rcv) / double(Size) * 100; - std::string Per = std::to_string(trunc(pr * 10) / 10); - UpdateUl(true, Name + " (" + Per.substr(0, Per.find('.') + 2) + "%)"); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - }while(!Terminate && Rcv < Size); -} - -char* TCPRcvRaw(SOCKET Sock,uint64_t& GRcv, uint64_t Size){ - if(Sock == -1){ - Terminate = true; - UUl("Invalid Socket"); - return nullptr; - } - char* File = new char[Size]; - uint64_t Rcv = 0; - do{ - int Len = int(Size-Rcv); - if(Len > 1000000)Len = 1000000; - int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL); - if(Temp < 1){ - info(std::to_string(Temp)); - UUl("Socket Closed Code 1"); - KillSocket(Sock); - Terminate = true; - delete[] File; - return nullptr; - } - Rcv += Temp; - GRcv += Temp; - }while(Rcv < Size && !Terminate); - return File; -} -void MultiKill(SOCKET Sock,SOCKET Sock1){ - KillSocket(Sock1); - KillSocket(Sock); - Terminate = true; -} -SOCKET InitDSock(){ - SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - SOCKADDR_IN ServerAddr; - if(DSock < 1){ - KillSocket(DSock); - Terminate = true; - return 0; - } - ServerAddr.sin_family = AF_INET; - ServerAddr.sin_port = htons(LastPort); - inet_pton(AF_INET, LastIP.c_str(), &ServerAddr.sin_addr); - if(connect(DSock, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)) != 0){ - KillSocket(DSock); - Terminate = true; - return 0; - } - char Code[2] = {'D',char(ClientID)}; - if(send(DSock,Code,2,0) != 2){ - KillSocket(DSock); - Terminate = true; - return 0; - } - return DSock; -} - -std::string MultiDownload(SOCKET MSock,SOCKET DSock, uint64_t Size, const std::string& Name){ - - uint64_t GRcv = 0, MSize = Size/2, DSize = Size - MSize; - - std::thread Au(AsyncUpdate,std::ref(GRcv), Size, Name); - - std::packaged_task task([&] { return TCPRcvRaw(MSock,GRcv,MSize); }); - std::future f1 = task.get_future(); - std::thread Dt(std::move(task)); - Dt.detach(); - - char* DData = TCPRcvRaw(DSock,GRcv,DSize); - - if(!DData){ - MultiKill(MSock,DSock); - return ""; - } - - f1.wait(); - char* MData = f1.get(); - - if(!MData){ - MultiKill(MSock,DSock); - return ""; - } - - if(Au.joinable())Au.join(); - - - ///omg yes very ugly my god but i was in a rush will revisit - std::string Ret(Size,0); - memcpy_s(&Ret[0],MSize,MData,MSize); - delete[]MData; - - memcpy_s(&Ret[MSize],DSize,DData,DSize); - delete[]DData; - - return Ret; -} - -void InvalidResource(const std::string& File){ - UUl("Invalid mod \"" + File + "\""); - warn("The server tried to sync \"" + File + "\" that is not a .zip file!"); - Terminate = true; -} - -void SyncResources(SOCKET Sock){ - std::string Ret = Auth(Sock); - if(Ret.empty())return; - - info("Checking Resources..."); - CheckForDir(); - - std::vector list = Split(Ret, ";"); - std::vector FNames(list.begin(), list.begin() + (list.size() / 2)); - std::vector FSizes(list.begin() + (list.size() / 2), list.end()); - list.clear(); - Ret.clear(); - - int Amount = 0,Pos = 0; - std::string a,t; - for(const std::string&name : FNames){ - if(!name.empty()){ - t += name.substr(name.find_last_of('/') + 1) + ";"; - } - } - if(t.empty())ListOfMods = "-"; - else ListOfMods = t; - t.clear(); - for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) { - auto pos = FN->find_last_of('/'); - auto ZIP = FN->find(".zip"); - if (ZIP == std::string::npos || FN->length() - ZIP != 4) { - InvalidResource(*FN); - return; - } - if (pos == std::string::npos)continue; - Amount++; - } - if(!FNames.empty())info("Syncing..."); - SOCKET DSock = InitDSock(); - for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) { - auto pos = FN->find_last_of('/'); - if (pos != std::string::npos) { - a = "Resources" + FN->substr(pos); - } else continue; - Pos++; - if (fs::exists(a)) { - if (FS->find_first_not_of("0123456789") != std::string::npos)continue; - if (fs::file_size(a) == std::stoull(*FS)){ - UpdateUl(false,std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + a.substr(a.find_last_of('/'))); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - try { - if(!fs::exists(GetGamePath() + "mods/multiplayer")){ - fs::create_directories(GetGamePath() + "mods/multiplayer"); - } - auto name = GetGamePath() + "mods/multiplayer" + a.substr(a.find_last_of('/')); - auto tmp_name = name + ".tmp"; - fs::copy_file(a,tmp_name,fs::copy_options::overwrite_existing); - fs::rename(tmp_name, name); - } catch (std::exception& e) { - error("Failed copy to the mods folder! " + std::string(e.what())); - Terminate = true; - continue; - } - WaitForConfirm(); - continue; - }else remove(a.c_str()); - } - CheckForDir(); - std::string FName = a.substr(a.find_last_of('/')); - do { - TCPSend("f" + *FN,Sock); - - std::string Data = TCPRcv(Sock); - if (Data == "CO" || Terminate){ - Terminate = true; - UUl("Server cannot find " + FName); - break; - } - - std::string Name = std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName; - - Data = MultiDownload(Sock,DSock,std::stoull(*FS), Name); - - if(Terminate)break; - UpdateUl(false,std::to_string(Pos)+"/"+std::to_string(Amount)+": "+FName); - std::ofstream LFS; - LFS.open(a.c_str(), std::ios_base::app | std::ios::binary); - if (LFS.is_open()) { - LFS.write(&Data[0], Data.size()); - LFS.close(); - } - - }while(fs::file_size(a) != std::stoull(*FS) && !Terminate); - if(!Terminate){ - if(!fs::exists(GetGamePath() + "mods/multiplayer")){ - fs::create_directories(GetGamePath() + "mods/multiplayer"); - } - fs::copy_file(a,GetGamePath() + "mods/multiplayer" + FName, fs::copy_options::overwrite_existing); - } - WaitForConfirm(); - } - KillSocket(DSock); - if(!Terminate){ - TCPSend("Done",Sock); - info("Done!"); - }else{ - UlStatus = "Ulstart"; - info("Connection Terminated!"); - } -} \ No newline at end of file diff --git a/src/Network/VehicleData.cpp b/src/Network/VehicleData.cpp deleted file mode 100755 index d1c399db..00000000 --- a/src/Network/VehicleData.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 5/8/2020 -/// -#include "Zlib/Compressor.h" -#include "Network/network.h" - -#include -#include "Logger.h" -#include -#include - -SOCKET UDPSock = -1; -sockaddr_in* ToServer = nullptr; - -void UDPSend(std::string Data){ - if(ClientID == -1 || UDPSock == -1)return; - if(Data.length() > 400){ - std::string CMP(Comp(Data)); - Data = "ABG:" + CMP; - } - std::string Packet = char(ClientID+1) + std::string(":") + Data; - int sendOk = sendto(UDPSock, Packet.c_str(), int(Packet.size()), 0, (sockaddr*)ToServer, sizeof(*ToServer)); - if (sendOk == SOCKET_ERROR)error("Error Code : " + std::to_string(WSAGetLastError())); -} - - -void SendLarge(std::string Data){ - if(Data.length() > 400){ - std::string CMP(Comp(Data)); - Data = "ABG:" + CMP; - } - TCPSend(Data,TCPSock); -} - -void UDPParser(std::string Packet){ - if(Packet.substr(0,4) == "ABG:"){ - Packet = DeComp(Packet.substr(4)); - } - ServerParser(Packet); -} -void UDPRcv(){ - sockaddr_in FromServer{}; - int clientLength = sizeof(FromServer); - ZeroMemory(&FromServer, clientLength); - std::string Ret(10240,0); - if(UDPSock == -1)return; - int32_t Rcv = recvfrom(UDPSock, &Ret[0], 10240, 0, (sockaddr*)&FromServer, &clientLength); - if (Rcv == SOCKET_ERROR)return; - UDPParser(Ret.substr(0,Rcv)); -} -void UDPClientMain(const std::string& IP,int Port){ - WSADATA data; - if (WSAStartup(514, &data)){ - error("Can't start Winsock!"); - return; - } - delete ToServer; - ToServer = new sockaddr_in; - ToServer->sin_family = AF_INET; - ToServer->sin_port = htons(Port); - inet_pton(AF_INET, IP.c_str(), &ToServer->sin_addr); - UDPSock = socket(AF_INET, SOCK_DGRAM, 0); - GameSend("P"+std::to_string(ClientID)); - TCPSend("H",TCPSock); - UDPSend("p"); - while(!Terminate)UDPRcv(); - KillSocket(UDPSock); - WSACleanup(); -} \ No newline at end of file diff --git a/src/Network/VehicleEvent.cpp b/src/Network/VehicleEvent.cpp deleted file mode 100755 index 989e923c..00000000 --- a/src/Network/VehicleEvent.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 5/8/2020 -/// - -#include -#include -#include "Logger.h" -#include -#include -#include - -#include "Network/network.h" - - -int LastPort; -std::string LastIP; -SOCKET TCPSock = -1; - -bool CheckBytes(int32_t Bytes){ - if (Bytes == 0){ - debug("(TCP) Connection closing... CheckBytes(16)"); - Terminate = true; - return false; - }else if (Bytes < 0) { - debug("(TCP CB) recv failed with error: " + std::to_string(WSAGetLastError())); - KillSocket(TCPSock); - Terminate = true; - return false; - } - return true; -} -void UUl(const std::string& R){ - UlStatus = "UlDisconnected: " + R; -} - -void TCPSend(const std::string&Data,uint64_t Sock){ - if(Sock == -1){ - Terminate = true; - UUl("Invalid Socket"); - return; - } - - int32_t Size,Sent,Temp; - std::string Send(4,0); - Size = int32_t(Data.size()); - memcpy(&Send[0],&Size,sizeof(Size)); - Send += Data; - // Do not use Size before this point for anything but the header - Sent = 0; - Size += 4; - do{ - if (size_t(Sent) >= Send.size()) { - error("string OOB in " + std::string(__func__)); - UUl("TCP Send OOB"); - return; - } - Temp = send(Sock, &Send[Sent], Size - Sent, 0); - if(!CheckBytes(Temp)){ - UUl("Socket Closed Code 2"); - return; - } - Sent += Temp; - }while(Sent < Size); -} - -std::string TCPRcv(SOCKET Sock){ - if(Sock == -1){ - Terminate = true; - UUl("Invalid Socket"); - return ""; - } - int32_t Header,BytesRcv = 0,Temp; - std::vector Data(sizeof(Header)); - do{ - Temp = recv(Sock,&Data[BytesRcv],4-BytesRcv,0); - if(!CheckBytes(Temp)){ - UUl("Socket Closed Code 3"); - return ""; - } - BytesRcv += Temp; - }while(BytesRcv < 4); - memcpy(&Header,&Data[0],sizeof(Header)); - - if(!CheckBytes(BytesRcv)){ - UUl("Socket Closed Code 4"); - return ""; - } - Data.resize(Header); - BytesRcv = 0; - do{ - Temp = recv(Sock,&Data[BytesRcv],Header-BytesRcv,0); - if(!CheckBytes(Temp)){ - UUl("Socket Closed Code 5"); - return ""; - } - BytesRcv += Temp; - }while(BytesRcv < Header); - - std::string Ret(Data.data(),Header); - - if (Ret.substr(0, 4) == "ABG:") { - Ret = DeComp(Ret.substr(4)); - } - -#ifdef DEBUG - //debug("Parsing from server -> " + std::to_string(Ret.size())); -#endif - if(Ret[0] == 'E' || Ret[0] == 'K')UUl(Ret.substr(1)); - return Ret; -} - -void TCPClientMain(const std::string& IP,int Port){ - LastIP = IP; - LastPort = Port; - WSADATA wsaData; - SOCKADDR_IN ServerAddr; - int RetCode; - WSAStartup(514, &wsaData); //2.2 - TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - if(TCPSock == -1){ - printf("Client: socket failed! Error code: %d\n", WSAGetLastError()); - WSACleanup(); - return; - } - ServerAddr.sin_family = AF_INET; - ServerAddr.sin_port = htons(Port); - inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr); - RetCode = connect(TCPSock, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)); - if(RetCode != 0){ - UlStatus = "UlConnection Failed!"; - error("Client: connect failed! Error code: " + std::to_string(WSAGetLastError())); - KillSocket(TCPSock); - WSACleanup(); - Terminate = true; - return; - } - info("Connected!"); - - char Code = 'C'; - send(TCPSock, &Code, 1, 0); - SyncResources(TCPSock); - while(!Terminate){ - ServerParser(TCPRcv(TCPSock)); - } - GameSend("T"); - ////Game Send Terminate - if(KillSocket(TCPSock) != 0) - debug("(TCP) Cannot close socket. Error code: " + std::to_string(WSAGetLastError())); - - if(WSACleanup() != 0) - debug("(TCP) Client: WSACleanup() failed!..."); -} diff --git a/src/Platform.h b/src/Platform.h new file mode 100644 index 00000000..14941361 --- /dev/null +++ b/src/Platform.h @@ -0,0 +1,14 @@ +#pragma once + +/// Platform-specific code +#include +namespace plat { + +void clear_screen(); +void set_console_title(const std::string& title); +void URelaunch(int argc,char* args[]); +void ReLaunch(int argc,char*args[]); +std::string get_game_dir_magically(); +std::string ask_for_folder(); + +} \ No newline at end of file diff --git a/src/PlatformLinux.cpp b/src/PlatformLinux.cpp new file mode 100644 index 00000000..77214e83 --- /dev/null +++ b/src/PlatformLinux.cpp @@ -0,0 +1,49 @@ +#include "Platform.h" +#if defined(PLATFORM_LINUX) + +#include +#include +#include + +void plat::ReLaunch(int argc, char* args[]) { + spdlog::warn("Not implemented: {}", __func__); +} +void plat::URelaunch(int argc, char** argv) { + spdlog::warn("Not implemented: {}", __func__); +} +void plat::set_console_title(const std::string& title) { + //fmt::print("\x1b]2;{}\0", title); +} +void plat::clear_screen() { + // unwanted on linux, due to the ability to run this as a command in an existing console. +} +std::string plat::get_game_dir_magically() { + spdlog::warn("Not implemented: {}", __func__); + for (const auto& path : { "" }) { + if (std::filesystem::exists(std::filesystem::path(path) / "BeamNG.drive.exe")) { + return path; + } + } + return ""; +} + +#include +#include + +std::string plat::ask_for_folder() { + spdlog::info("Asking user for path"); + fmt::print("====\n" + "Couldn't find game directory, please enter the path to the game (the folder that contains 'BeamNG.drive.exe')\n" + "Path: "); + std::string folder; + std::getline(std::cin, folder); + while (!std::filesystem::exists(std::filesystem::path(folder) / "BeamNG.drive.exe")) { + fmt::print("This folder ('{}') doesn't contain 'BeamNG.drive.exe', please try again.\n", folder); + fmt::print("Path: "); + std::getline(std::cin, folder); + } + fmt::print("Thank you!\n====\n"); + spdlog::info("Game path found"); + return folder; +} +#endif diff --git a/src/PlatformWindows.cpp b/src/PlatformWindows.cpp new file mode 100644 index 00000000..b530ee54 --- /dev/null +++ b/src/PlatformWindows.cpp @@ -0,0 +1,193 @@ +#include "Platform.h" + +#if defined(PLATFORM_WINDOWS) +#include +#include +#include + +#include +// include after shlobj +#include + +void plat::ReLaunch(int argc, char** argv) { + std::string Arg; + for (int c = 2; c <= argc; c++) { + Arg += " "; + Arg += argv[c - 1]; + } + system("cls"); + ShellExecute(nullptr, "runas", argv[0], Arg.c_str(), nullptr, SW_SHOWNORMAL); + ShowWindow(GetConsoleWindow(), 0); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); +} +void plat::URelaunch(int argc, char** argv) { + std::string Arg; + for (int c = 2; c <= argc; c++) { + Arg += " "; + Arg += argv[c - 1]; + } + ShellExecute(nullptr, "open", argv[0], Arg.c_str(), nullptr, SW_SHOWNORMAL); + ShowWindow(GetConsoleWindow(), 0); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); +} +void plat::set_console_title(const std::string& title) { + SetConsoleTitleA(title.c_str()); +} +void plat::clear_screen() { + system("cls"); +} +LONG OpenKey(HKEY root,const char* path,PHKEY hKey){ + return RegOpenKeyEx(root, reinterpret_cast(path), 0, KEY_READ, hKey); +} +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +std::string QueryKey(HKEY hKey,int ID){ + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + DWORD i, retCode; + + TCHAR achValue[MAX_VALUE_NAME]; + DWORD cchValue = MAX_VALUE_NAME; + + retCode = RegQueryInfoKey( + hKey, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + nullptr, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + BYTE* buffer = new BYTE[cbMaxValueData]; + ZeroMemory(buffer, cbMaxValueData); + if (cSubKeys){ + for (i=0; iFree(pidl); + imalloc->Release(); + } + + return path; + } + + return ""; +} + +#include + +std::string plat::ask_for_folder() { + auto folder = impl_ask_for_folder(); + while (!std::filesystem::exists(std::filesystem::path(folder) / "BeamNG.drive.exe")) { + spdlog::error("This folder ('{}') doesn't contain 'BeamNG.drive.exe', please try again.\n", folder); + folder = impl_ask_for_folder(); + } + return folder; +} + +#endif \ No newline at end of file diff --git a/src/Result.h b/src/Result.h new file mode 100644 index 00000000..1d5f776a --- /dev/null +++ b/src/Result.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#ifdef WIN32 +#define BEAMMP_WARN_UNUSED +#else +#define BEAMMP_WARN_UNUSED __attribute__((warn_unused_result)) +#endif + +// attribute because [[nodiscard]] isn't possible on using decls - if you wanna write a standards proposal, +// this is one to write. +template +using Result BEAMMP_WARN_UNUSED = boost::outcome_v2::result; + +namespace outcome = boost::outcome_v2; diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp deleted file mode 100755 index 25959342..00000000 --- a/src/Security/BeamNG.cpp +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/18/2020 -/// - -#include -#include -#include "Logger.h" -#include -#include -#include -#include - -#define MAX_KEY_LENGTH 255 -#define MAX_VALUE_NAME 16383 - -int TraceBack = 0; -std::string GameDir; - -void lowExit(int code){ - TraceBack = 0; - std::string msg = - "Failed to find the game please launch it. Report this if the issue persists code "; - error(msg+std::to_string(code)); - std::this_thread::sleep_for(std::chrono::seconds(10)); - exit(2); -} -/*void Exit(int code){ - TraceBack = 0; - std::string msg = - "Sorry. We do not support cracked copies report this if you believe this is a mistake code "; - error(msg+std::to_string(code)); - std::this_thread::sleep_for(std::chrono::seconds(10)); - exit(3); -} -void SteamExit(int code){ - TraceBack = 0; - std::string msg = - "Illegal steam modifications detected report this if you believe this is a mistake code "; - error(msg+std::to_string(code)); - std::this_thread::sleep_for(std::chrono::seconds(10)); - exit(4); -}*/ -std::string GetGameDir(){ - //if(TraceBack != 4)Exit(0); - return GameDir.substr(0,GameDir.find_last_of('\\')); -} -LONG OpenKey(HKEY root,const char* path,PHKEY hKey){ - return RegOpenKeyEx(root, reinterpret_cast(path), 0, KEY_READ, hKey); -} -std::string QueryKey(HKEY hKey,int ID){ - TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name - DWORD cbName; // size of name string - TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name - DWORD cchClassName = MAX_PATH; // size of class string - DWORD cSubKeys=0; // number of subkeys - DWORD cbMaxSubKey; // longest subkey size - DWORD cchMaxClass; // longest class string - DWORD cValues; // number of values for key - DWORD cchMaxValue; // longest value name - DWORD cbMaxValueData; // longest value data - DWORD cbSecurityDescriptor; // size of security descriptor - FILETIME ftLastWriteTime; // last write time - - DWORD i, retCode; - - TCHAR achValue[MAX_VALUE_NAME]; - DWORD cchValue = MAX_VALUE_NAME; - - retCode = RegQueryInfoKey( - hKey, // key handle - achClass, // buffer for class name - &cchClassName, // size of class string - nullptr, // reserved - &cSubKeys, // number of subkeys - &cbMaxSubKey, // longest subkey size - &cchMaxClass, // longest class string - &cValues, // number of values for this key - &cchMaxValue, // longest value name - &cbMaxValueData, // longest value data - &cbSecurityDescriptor, // security descriptor - &ftLastWriteTime); // last write time - - BYTE* buffer = new BYTE[cbMaxValueData]; - ZeroMemory(buffer, cbMaxValueData); - if (cSubKeys){ - for (i=0; i&a,const std::string& Path){ - for (const auto &entry : fs::directory_iterator(Path)) { - const auto& DPath = entry.path(); - if (!entry.is_directory()) { - a.emplace_back(DPath.string()); - }else if(NameValid(DPath.filename().string())){ - FileList(a, DPath.string()); - } - } -} -bool Find(const std::string& FName,const std::string& Path){ - std::vector FS; - FileList(FS,Path+"\\userdata"); - for(std::string&a : FS){ - if(a.find(FName) != std::string::npos){ - FS.clear(); - return true; - } - } - FS.clear(); - return false; -} -bool FindHack(const std::string& Path){ - bool s = true; - for (const auto &entry : fs::directory_iterator(Path)) { - std::string Name = entry.path().filename().string(); - for(char&c : Name)c = char(tolower(c)); - if(Name == "steam.exe")s = false; - if(Name.find("greenluma") != -1){ - error("Found malicious file/folder \"" + Name+"\""); - return true; - } - Name.clear(); - } - return s; -} -std::vector GetID(const std::string& log){ - std::string vec,t,r; - std::vector Ret; - std::ifstream f(log.c_str(), std::ios::binary); - f.seekg(0, std::ios_base::end); - std::streampos fileSize = f.tellg(); - vec.resize(size_t(fileSize) + 1); - f.seekg(0, std::ios_base::beg); - f.read(&vec[0], fileSize); - f.close(); - std::stringstream ss(vec); - bool S = false; - while (std::getline(ss, t, '{')) { - if(!S)S = true; - else{ - for(char& c : t){ - if(isdigit(c))r += c; - } - break; - } - } - Ret.emplace_back(r); - r.clear(); - S = false; - bool L = true; - while (std::getline(ss, t, '}')) { - if(L){ - L = false; - continue; - } - for(char& c : t){ - if(c == '"'){ - if(!S)S = true; - else{ - if(r.length() > 10) { - Ret.emplace_back(r); - } - r.clear(); - S = false; - continue; - } - } - if(isdigit(c))r += c; - } - } - vec.clear(); - return Ret; -} -std::string GetManifest(const std::string& Man){ - std::string vec; - std::ifstream f(Man.c_str(), std::ios::binary); - f.seekg(0, std::ios_base::end); - std::streampos fileSize = f.tellg(); - vec.resize(size_t(fileSize) + 1); - f.seekg(0, std::ios_base::beg); - f.read(&vec[0], fileSize); - f.close(); - std::string ToFind = "\"LastOwner\"\t\t\""; - int pos = int(vec.find(ToFind)); - if(pos != -1){ - pos += int(ToFind.length()); - vec = vec.substr(pos); - return vec.substr(0,vec.find('\"')); - }else return ""; -} -bool IDCheck(std::string Man, std::string steam){ - bool a = false,b = true; - int pos = int(Man.rfind("steamapps")); - // if(pos == -1)Exit(5); - Man = Man.substr(0,pos+9) + "\\appmanifest_284160.acf"; - steam += "\\config\\loginusers.vdf"; - if(fs::exists(Man) && fs::exists(steam)){ - for(const std::string&ID : GetID(steam)){ - if(ID == GetManifest(Man))b = false; - } - //if(b)Exit(6); - }else a = true; - return a; -} -void LegitimacyCheck(){ - - //std::string K1 = R"(Software\Valve\Steam)"; - //std::string K2 = R"(Software\Valve\Steam\Apps\284160)"; - - /*LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K1.c_str(), &hKey); - - if(dwRegOPenKey == ERROR_SUCCESS) { - Result = QueryKey(hKey, 1); - if(Result.empty())Exit(1); - - if(fs::exists(Result)){ - if(!Find("284160.json",Result))Exit(2); - if(FindHack(Result))SteamExit(1); - }else Exit(3); - - T = Result; - Result.clear(); - TraceBack++; - }else Exit(4); - - K1.clear(); - RegCloseKey(hKey); - dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K2.c_str(), &hKey); - if(dwRegOPenKey == ERROR_SUCCESS) { - Result = QueryKey(hKey, 2); - if(Result.empty())lowExit(1); - TraceBack++; - }else lowExit(2); - K2.clear(); - RegCloseKey(hKey);*/ - std::string Result; - std::string K3 = R"(Software\BeamNG\BeamNG.drive)"; - HKEY hKey; - LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey); - if(dwRegOPenKey == ERROR_SUCCESS) { - Result = QueryKey(hKey, 3); - if(Result.empty())lowExit(3); - //if(IDCheck(Result,T))lowExit(5); - GameDir = Result; - //TraceBack++; - }else lowExit(4); - K3.clear(); - Result.clear(); - RegCloseKey(hKey); - //if(TraceBack < 3)exit(-1); -} -std::string CheckVer(const std::string &dir){ - std::string temp,Path = dir + "\\integrity.json"; - std::ifstream f(Path.c_str(), std::ios::binary); - int Size = int(std::filesystem::file_size(Path)); - std::string vec(Size,0); - f.read(&vec[0], Size); - f.close(); - - vec = vec.substr(vec.find_last_of("version"),vec.find_last_of('"')); - for(const char &a : vec){ - if(isdigit(a) || a == '.')temp+=a; - } - return temp; -} diff --git a/src/Security/Login.cpp b/src/Security/Login.cpp deleted file mode 100755 index bbb9051b..00000000 --- a/src/Security/Login.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 11/26/2020 -/// - -#include -#include "Http.h" -#include -#include "Logger.h" -#include - - -namespace fs = std::filesystem; -std::string PublicKey; -std::string PrivateKey; -extern bool LoginAuth; -std::string Role; - -void UpdateKey(const char* newKey){ - if(newKey && std::isalnum(newKey[0])){ - PrivateKey = newKey; - std::ofstream Key("key"); - if(Key.is_open()){ - Key << newKey; - Key.close(); - }else fatal("Cannot write to disk!"); - }else if(fs::exists("key")){ - remove("key"); - } -} - -/// "username":"value","password":"value" -/// "Guest":"Name" -/// "pk":"private_key" - -std::string GetFail(const std::string& R){ - std::string DRet = R"({"success":false,"message":)"; - DRet += "\""+R+"\"}"; - error(R); - return DRet; -} - -std::string Login(const std::string& fields){ - if(fields == "LO"){ - LoginAuth = false; - UpdateKey(nullptr); - return ""; - } - info("Attempting to authenticate..."); - std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields); - - if(Buffer == "-1"){ - return GetFail("Failed to communicate with the auth system!"); - } - - nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); - - if (Buffer.at(0) != '{' || d.is_discarded()) { - error(Buffer); - return GetFail("Invalid answer from authentication servers, please try again later!"); - } - if(d.contains("success") && d["success"].get()){ - LoginAuth = true; - if(!d.contains("private_key")) { - UpdateKey(d["private_key"].get().c_str()); - } - if(!d.contains("public_key")){ - PublicKey = d["public_key"].get(); - } - info("Authentication successful!"); - }else info("Authentication failed!"); - if(!d.contains("message")){ - d.erase("private_key"); - d.erase("public_key"); - return d.dump(); - } - return GetFail("Invalid message parsing!"); -} - -void CheckLocalKey(){ - if(fs::exists("key") && fs::file_size("key") < 100){ - std::ifstream Key("key"); - if(Key.is_open()) { - auto Size = fs::file_size("key"); - std::string Buffer(Size, 0); - Key.read(&Buffer[0], Size); - Key.close(); - - for (char& c : Buffer) { - if (!std::isalnum(c) && c != '-') { - UpdateKey(nullptr); - return; - } - } - - Buffer = HTTP::Post("https://auth.beammp.com/userlogin", R"({"pk":")" + Buffer + "\"}"); - - nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); - - if (Buffer == "-1" || Buffer.at(0) != '{' || d.is_discarded()) { - error(Buffer); - info("Invalid answer from authentication servers."); - UpdateKey(nullptr); - } - if(d["success"].get()){ - LoginAuth = true; - UpdateKey(d["private_key"].get().c_str()); - PublicKey = d["public_key"].get(); - Role = d["role"].get(); - //info(Role); - }else{ - info("Auto-Authentication unsuccessful please re-login!"); - UpdateKey(nullptr); - } - }else{ - warn("Could not open saved key!"); - UpdateKey(nullptr); - } - }else UpdateKey(nullptr); -} diff --git a/src/ServerNetwork.cpp b/src/ServerNetwork.cpp new file mode 100644 index 00000000..a31b4bf6 --- /dev/null +++ b/src/ServerNetwork.cpp @@ -0,0 +1,356 @@ +#include "ServerNetwork.h" +#include "ClientInfo.h" +#include "ClientNetwork.h" +#include "ClientPacket.h" +#include "ClientTransport.h" +#include "Identity.h" +#include "ImplementationInfo.h" +#include "Launcher.h" +#include "Packet.h" +#include "ProtocolVersion.h" +#include "ServerInfo.h" +#include "Transport.h" +#include "Util.h" +#include +#include + +ServerNetwork::ServerNetwork(Launcher& launcher, const ip::tcp::endpoint& ep) + : m_tcp_ep(ep) + , launcher(launcher) { + spdlog::debug("Server network created"); + boost::system::error_code ec; + m_tcp_socket.connect(m_tcp_ep, ec); + if (ec) { + throw std::runtime_error(ec.message()); + } +} + +ServerNetwork::~ServerNetwork() { + spdlog::debug("Server network destroyed"); +} + +void ServerNetwork::run() { + // start the connection by going to the identification state and sending + // the first packet in the identification protocol (protocol version) + m_state = bmp::State::Identification; + bmp::Packet version_packet { + .purpose = bmp::Purpose::ProtocolVersion, + .raw_data = std::vector(6), + }; + spdlog::trace("Protocol version: v{}.{}.{}", 1, 0, 0); + struct bmp::ProtocolVersion version { + .version = { + .major = 1, + .minor = 0, + .patch = 0, + }, + }; + version.serialize_to(version_packet.raw_data); + tcp_write(std::move(version_packet)); + + start_tcp_read(); + + m_io.run(); +} + +void ServerNetwork::handle_packet(bmp::Packet&& packet) { + // handle ping immediately + if (m_state > bmp::State::Identification && packet.purpose == bmp::Purpose::Ping) { + tcp_write(bmp::Packet { + .purpose = bmp::Purpose::Ping, + }); + return; + } + switch (m_state) { + case bmp::State::None: + m_state = bmp::State::Identification; + [[fallthrough]]; + case bmp::State::Identification: + handle_identification(packet); + break; + case bmp::State::Authentication: + handle_authentication(packet); + break; + case bmp::State::ModDownload: + handle_mod_download(packet); + break; + case bmp::State::SessionSetup: + handle_session_setup(packet); + break; + case bmp::State::Playing: + handle_playing(packet); + break; + case bmp::State::Leaving: + break; + } + + launcher.client_network->handle_server_packet(std::move(packet)); +} + +void ServerNetwork::handle_mod_download(const bmp::Packet& packet) { + switch (packet.purpose) { + case bmp::Purpose::ModsInfo: { + auto data = packet.get_readable_data(); + auto mods_info = nlohmann::json::parse(data.begin(), data.end()); + spdlog::info("Got info about {} mod(s)", mods_info.size()); + for (const auto& mod : mods_info) { + spdlog::warn("Mod download not implemented, but data is: {}", mod.dump(4)); + } + // TODO: implement mod download + // for now we just pretend we're all good! + tcp_write(bmp::Packet { + .purpose = bmp::Purpose::ModsSyncDone, + }); + spdlog::info("Done syncing mods"); + break; + } + case bmp::Purpose::MapInfo: { + auto data = packet.get_readable_data(); + std::string map(data.begin(), data.end()); + // TODO: Send to game + spdlog::debug("Map: '{}'", map); + break; + } + case bmp::Purpose::StateChangeSessionSetup: { + spdlog::info("Starting session setup"); + m_state = bmp::State::SessionSetup; + break; + } + default: + spdlog::error("Got 0x{:x} in state {}. This is not allowed. Disconnecting", uint16_t(packet.purpose), int(m_state)); + // todo: disconnect gracefully + break; + } +} + +void ServerNetwork::handle_authentication(const bmp::Packet& packet) { + switch (packet.purpose) { + case bmp::Purpose::AuthOk: { + spdlog::info("Authentication succeeded"); + uint32_t player_id; + bmp::deserialize(player_id, packet.get_readable_data()); + spdlog::debug("Player id: {}", player_id); + break; + } + case bmp::Purpose::AuthFailed: { + auto data = packet.get_readable_data(); + spdlog::error("Authentication failed: {}", std::string(data.begin(), data.end())); + break; + } + case bmp::Purpose::PlayerRejected: { + auto data = packet.get_readable_data(); + spdlog::error("Server rejected me: {}", std::string(data.begin(), data.end())); + break; + } + case bmp::Purpose::StartUDP: { + bmp::deserialize(m_udp_magic, packet.get_readable_data()); + spdlog::debug("UDP start, got magic 0x{:x}", m_udp_magic); + m_udp_ep = ip::udp::endpoint(m_tcp_ep.address(), m_tcp_ep.port()); + m_udp_socket.open(m_tcp_ep.address().is_v4() ? ip::udp::v4() : ip::udp::v6()); + auto copy = bmp::Packet { + .purpose = bmp::Purpose::StartUDP, + .raw_data = packet.get_readable_data(), + }; + udp_write(copy); + break; + } + case bmp::Purpose::StateChangeModDownload: { + spdlog::info("Starting mod sync"); + m_state = bmp::State::ModDownload; + break; + } + default: + spdlog::error("Got 0x{:x} in state {}. This is not allowed. Disconnecting", uint16_t(packet.purpose), int(m_state)); + // todo: disconnect gracefully + break; + } +} + +void ServerNetwork::handle_identification(const bmp::Packet& packet) { + switch (packet.purpose) { + case bmp::Purpose::ProtocolVersionOk: { + spdlog::debug("Protocol version ok"); + bmp::Packet ci_packet { + .purpose = bmp::Purpose::ClientInfo, + .raw_data = std::vector(1024), + }; + struct bmp::ClientInfo ci { + .program_version = { .major = PRJ_VERSION_MAJOR, .minor = PRJ_VERSION_MINOR, .patch = PRJ_VERSION_PATCH }, + .game_version = { + .major = launcher.game_version->major, + .minor = launcher.game_version->minor, + .patch = launcher.game_version->patch, + }, + .mod_version = { + .major = launcher.mod_version->major, + .minor = launcher.mod_version->minor, + .patch = launcher.mod_version->patch, + }, + .implementation = bmp::ImplementationInfo { + .value = "Official BeamMP Launcher", + }, + }; + auto sz = ci.serialize_to(ci_packet.raw_data); + ci_packet.raw_data.resize(sz); + tcp_write(std::move(ci_packet)); + break; + } + case bmp::Purpose::ProtocolVersionBad: + spdlog::error("The server rejected our protocol version!"); + throw std::runtime_error("Protocol version rejected"); + break; + case bmp::Purpose::ServerInfo: { + struct bmp::ServerInfo si { }; + si.deserialize_from(packet.get_readable_data()); + spdlog::debug("Connected to server implementation '{}' v{}.{}.{}", + si.implementation.value, + si.program_version.major, + si.program_version.minor, + si.program_version.patch); + break; + } + case bmp::Purpose::StateChangeAuthentication: { + spdlog::debug("Starting authentication"); + m_state = bmp::State::Authentication; + auto ident = launcher.identity.synchronize(); + tcp_write(bmp::Packet { + .purpose = bmp::Purpose::PlayerPublicKey, + .raw_data = std::vector(ident->PublicKey.begin(), ident->PublicKey.end()) }); + break; + } + default: + spdlog::error("Got 0x{:x} in state {}. This is not allowed. Disconnecting", uint16_t(packet.purpose), int(m_state)); + // todo: disconnect gracefully + break; + } +} + +void ServerNetwork::tcp_read(std::function handler) { + m_tmp_header_buffer.resize(bmp::Header::SERIALIZED_SIZE); + boost::asio::async_read(m_tcp_socket, buffer(m_tmp_header_buffer), + [this, handler](auto ec, auto) { + if (ec) { + spdlog::error("Failed to read from server: {}", ec.message()); + } else { + bmp::Header hdr {}; + hdr.deserialize_from(m_tmp_header_buffer); + // vector eaten up by now, recv again + m_tmp_packet.raw_data.resize(hdr.size); + m_tmp_packet.purpose = hdr.purpose; + m_tmp_packet.flags = hdr.flags; + boost::asio::async_read(m_tcp_socket, buffer(m_tmp_packet.raw_data), + [handler, this](auto ec, auto) { + if (ec) { + spdlog::error("Failed to read from server: {}", ec.message()); + } else { + // ok! + handler(std::move(m_tmp_packet)); + } + }); + } + }); +} + +void ServerNetwork::tcp_write(bmp::Packet&& packet, std::function handler) { + // finalize the packet (compress etc) and produce header + auto header = packet.finalize(); + + auto owned_packet = std::make_shared(std::move(packet)); + // serialize header + auto header_data = std::make_shared>(bmp::Header::SERIALIZED_SIZE); + header.serialize_to(*header_data); + std::array buffers = { + buffer(*header_data), + buffer(owned_packet->raw_data) + }; + boost::asio::async_write(m_tcp_socket, buffers, + [this, header_data, owned_packet, handler](auto ec, auto size) { + spdlog::trace("Wrote {} bytes for 0x{:x} to server", size, int(owned_packet->purpose)); + if (handler) { + handler(ec); + } else { + if (ec) { + spdlog::error("Failed to send packet of type 0x{:x} to server", int(owned_packet->purpose)); + } else { + // ok! + spdlog::trace("Sent packet of type 0x{:x} to server", int(owned_packet->purpose)); + } + } + }); +} + +void ServerNetwork::udp_read(std::function handler) { + // maximum we can ever expect from udp + auto ep = std::make_shared(); + m_udp_socket.async_receive_from(buffer(m_tmp_udp_buffer), *ep, [this, ep, handler](auto ec, auto) { + if (ec) { + spdlog::error("Failed to receive UDP from server: {}", ec.message()); + } else { + bmp::Packet packet; + bmp::Header header {}; + auto offset = header.deserialize_from(m_tmp_udp_buffer); + packet.raw_data.resize(header.size); + std::copy(m_tmp_udp_buffer.begin() + long(offset), m_tmp_udp_buffer.begin() + long(offset) + header.size, packet.raw_data.begin()); + handler(std::move(*ep), std::move(packet)); + } + }); +} + +void ServerNetwork::udp_write(bmp::Packet& packet) { + auto header = packet.finalize(); + auto data = std::make_shared>(header.size + bmp::Header::SERIALIZED_SIZE); + auto offset = header.serialize_to(*data); + std::copy(packet.raw_data.begin(), packet.raw_data.end(), data->begin() + static_cast(offset)); + m_udp_socket.async_send_to(buffer(*data), m_udp_ep, [data](auto ec, auto size) { + if (ec) { + spdlog::error("Failed to UDP write to server: {}", ec.message()); + } else { + spdlog::trace("Wrote {} bytes to server via UDP", size); + } + }); +} +void ServerNetwork::handle_session_setup(const bmp::Packet& packet) { + switch (packet.purpose) { + case bmp::Purpose::PlayersVehiclesInfo: { + spdlog::debug("Players and vehicles info: {} bytes ({} bytes on arrival)", packet.get_readable_data().size(), packet.raw_data.size()); + // TODO: Send to game + tcp_write(bmp::Packet { + .purpose = bmp::Purpose::SessionReady, + }); + break; + } + case bmp::Purpose::StateChangePlaying: { + spdlog::info("Playing!"); + break; + } + default: + spdlog::error("Got 0x{:x} in state {}. This is not allowed. Disconnecting", uint16_t(packet.purpose), int(m_state)); + // todo: disconnect gracefully + break; + } +} + +void ServerNetwork::handle_playing(const bmp::Packet& packet) { + switch (packet.purpose) { + default: + spdlog::error("Got 0x{:x} in state {}. This is not allowed. Disconnecting", uint16_t(packet.purpose), int(m_state)); + // todo: disconnect gracefully + break; + } +} + +void ServerNetwork::start_tcp_read() { + tcp_read([this](auto&& packet) { + spdlog::debug("Got packet 0x{:x} from server", int(packet.purpose)); + handle_packet(std::move(packet)); + start_tcp_read(); + }); +} + +void ServerNetwork::start_udp_read() { + udp_read([this](auto&& ep, auto&& packet) { + spdlog::debug("Got packet 0x{:x} from server via UDP", int(packet.purpose)); + handle_packet(std::move(packet)); + start_udp_read(); + }); +} diff --git a/src/ServerNetwork.h b/src/ServerNetwork.h new file mode 100644 index 00000000..3cadd829 --- /dev/null +++ b/src/ServerNetwork.h @@ -0,0 +1,60 @@ +#pragma once + +#include "Packet.h" +#include "State.h" +#include + +using namespace boost::asio; + +class Launcher; + +class ServerNetwork { +public: + ServerNetwork(Launcher& launcher, const ip::tcp::endpoint& ep); + ~ServerNetwork(); + + /// Starts and runs the connection to the server. + /// Calls back to the Launcher. + /// Blocking! + void run(); + +private: + void start_tcp_read(); + void start_udp_read(); + + /// Reads a single packet from the TCP stream. Blocks all other reads (not writes). + void tcp_read(std::function handler); + /// Writes the packet to the TCP stream. Blocks all other writes. + void tcp_write(bmp::Packet&& packet, std::function handler = nullptr); + + /// Reads a packet from the given UDP socket, returning the client's endpoint as an out-argument. + void udp_read(std::function handler); + /// Sends a packet to the specified UDP endpoint via the UDP socket. + void udp_write(bmp::Packet& packet); + + void handle_packet(bmp::Packet&& packet); + void handle_identification(const bmp::Packet& packet); + void handle_authentication(const bmp::Packet& packet); + void handle_mod_download(const bmp::Packet& packet); + void handle_session_setup(const bmp::Packet& packet); + void handle_playing(const bmp::Packet& packet); + + io_context m_io {}; + ip::tcp::socket m_tcp_socket { m_io }; + ip::udp::socket m_udp_socket { m_io }; + + // these two tmp fields are used for temporary reading into by read, don't use anywhere else please + bmp::Packet m_tmp_packet {}; + std::vector m_tmp_header_buffer {}; + // this is used by udp read, don't touch + std::vector m_tmp_udp_buffer { static_cast(std::numeric_limits::max()) }; + + bmp::State m_state {}; + + uint64_t m_udp_magic {}; + + ip::tcp::endpoint m_tcp_ep; + ip::udp::endpoint m_udp_ep; + + Launcher& launcher; +}; diff --git a/src/Startup.cpp b/src/Startup.cpp deleted file mode 100755 index d6cb5b4c..00000000 --- a/src/Startup.cpp +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/16/2020 -/// - -#include -#include -#include "zip_file.h" -#include -#include "Discord/discord_info.h" -#include "Network/network.h" -#include "Security/Init.h" -#include -#include "Startup.h" -#include "hashpp.h" -#include "Logger.h" -#include -#include -#include "Http.h" - - -extern int TraceBack; -bool Dev = false; -int ProxyPort = 0; - -namespace fs = std::filesystem; - -VersionParser::VersionParser(const std::string& from_string) { - std::string token; - std::istringstream tokenStream(from_string); - while (std::getline(tokenStream, token, '.')) { - data.emplace_back(std::stol(token)); - split.emplace_back(token); - } -} - -std::strong_ordering VersionParser::operator<=>( - const VersionParser& rhs) const noexcept { - size_t const fields = std::min(data.size(), rhs.data.size()); - for (size_t i = 0; i != fields; ++i) { - if (data[i] == rhs.data[i]) continue; - else if (data[i] < rhs.data[i]) return std::strong_ordering::less; - else return std::strong_ordering::greater; - } - if (data.size() == rhs.data.size()) return std::strong_ordering::equal; - else if (data.size() > rhs.data.size()) return std::strong_ordering::greater; - else return std::strong_ordering::less; -} - -bool VersionParser::operator==(const VersionParser& rhs) const noexcept { - return std::is_eq(*this <=> rhs); -} - - -std::string GetEN(){ - return "BeamMP-Launcher.exe"; -} -std::string GetVer(){ - return "2.0"; -} -std::string GetPatch(){ - return ".84"; -} - -std::string GetEP(char*P){ - static std::string Ret = [&](){ - std::string path(P); - return path.substr(0, path.find_last_of("\\/") + 1); - } (); - return Ret; -} -void ReLaunch(int argc,char*args[]){ - std::string Arg; - for(int c = 2; c <= argc; c++){ - Arg += " "; - Arg += args[c-1]; - } - system("cls"); - ShellExecute(nullptr,"runas",(GetEP() + GetEN()).c_str(),Arg.c_str(),nullptr,SW_SHOWNORMAL); - ShowWindow(GetConsoleWindow(),0); - std::this_thread::sleep_for(std::chrono::seconds(1)); - exit(1); -} -void URelaunch(int argc,char* args[]){ - std::string Arg; - for(int c = 2; c <= argc; c++){ - Arg += " "; - Arg += args[c-1]; - } - ShellExecute(nullptr,"open",(GetEP() + GetEN()).c_str(),Arg.c_str(),nullptr,SW_SHOWNORMAL); - ShowWindow(GetConsoleWindow(),0); - std::this_thread::sleep_for(std::chrono::seconds(1)); - exit(1); -} -void CheckName(int argc,char* args[]){ - std::string DN = GetEN(),CDir = args[0],FN = CDir.substr(CDir.find_last_of('\\')+1); - if(FN != DN){ - if(fs::exists(DN))remove(DN.c_str()); - if(fs::exists(DN))ReLaunch(argc,args); - std::rename(FN.c_str(), DN.c_str()); - URelaunch(argc,args); - } -} - -void CheckForUpdates(int argc, char* args[], const std::string& CV) { - std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/launcher?branch=" + Branch + "&pk=" + PublicKey); - std::string LatestVersion = HTTP::Get( - "https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey); - - transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); - std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back"); - - std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, EP); - - if (FileHash != LatestHash && VersionParser(LatestVersion) > VersionParser(GetVer()+GetPatch())) { - info("Launcher update found!"); - fs::remove(Back); - fs::rename(EP, Back); - info("Downloading Launcher update " + LatestHash); - HTTP::Download( - "https://backend.beammp.com/builds/launcher?download=true" - "&pk=" + - PublicKey + "&branch=" + Branch, - EP); - URelaunch(argc, args); - } else info("Launcher version is up to date"); - TraceBack++; -} - -void CustomPort(int argc, char* argv[]){ - if(argc > 1){ - std::string Port = argv[1]; - if(Port.find_first_not_of("0123456789") == std::string::npos){ - if(std::stoi(Port) > 1000){ - DEFAULT_PORT = std::stoi(Port); - warn("Running on custom port : " + std::to_string(DEFAULT_PORT)); - } - } - if(argc > 2)Dev = true; - } -} - -void LinuxPatch(){ - HKEY hKey = nullptr; - LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Wine)", 0, KEY_READ, &hKey); - if (result != ERROR_SUCCESS || getenv("USER") == nullptr)return; - RegCloseKey(hKey); - info("Wine/Proton Detected! If you are on windows delete HKEY_CURRENT_USER\\Software\\Wine in regedit"); - info("Applying patches..."); - - result = RegCreateKey(HKEY_CURRENT_USER, R"(Software\Valve\Steam\Apps\284160)", &hKey); - - if (result != ERROR_SUCCESS){ - fatal(R"(failed to create HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)"); - return; - } - - result = RegSetValueEx(hKey, "Name", 0, REG_SZ, (BYTE*)"BeamNG.drive", 12); - - if (result != ERROR_SUCCESS){ - fatal(R"(failed to create the value "Name" under HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)"); - return; - } - RegCloseKey(hKey); - - info("Patched!"); -} - -void InitLauncher(int argc, char* argv[]) { - system("cls"); - SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str()); - InitLog(); - CheckName(argc, argv); - LinuxPatch(); - CheckLocalKey(); - ConfigInit(); - CustomPort(argc, argv); - Discord_Main(); - CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch()); -} - -size_t DirCount(const std::filesystem::path& path){ - return (size_t)std::distance(std::filesystem::directory_iterator{path}, std::filesystem::directory_iterator{}); -} - -void CheckMP(const std::string& Path) { - if (!fs::exists(Path))return; - size_t c = DirCount(fs::path(Path)); - try { - for (auto& p : fs::directory_iterator(Path)){ - if(p.exists() && !p.is_directory()){ - std::string Name = p.path().filename().string(); - for(char&Ch : Name)Ch = char(tolower(Ch)); - if(Name != "beammp.zip")fs::remove(p.path()); - } - } - } catch (...) { - fatal("We were unable to clean the multiplayer mods folder! Is the game still running or do you have something open in that folder?"); - } - -} - -void EnableMP(){ - std::string File(GetGamePath() + "mods/db.json"); - if(!fs::exists(File))return; - auto Size = fs::file_size(File); - if(Size < 2)return; - std::ifstream db(File); - if(db.is_open()) { - std::string Data(Size, 0); - db.read(&Data[0], Size); - db.close(); - nlohmann::json d = nlohmann::json::parse(Data, nullptr, false); - if(Data.at(0) != '{' || d.is_discarded()) { - //error("Failed to parse " + File); //TODO illegal formatting - return; - } - if(d.contains("mods") && d["mods"].contains("multiplayerbeammp")){ - d["mods"]["multiplayerbeammp"]["active"] = true; - std::ofstream ofs(File); - if(ofs.is_open()){ - ofs << d.dump(); - ofs.close(); - }else{ - error("Failed to write " + File); - } - } - } -} - -void PreGame(const std::string& GamePath){ - std::string GameVer = CheckVer(GamePath); - info("Game Version : " + GameVer); - - CheckMP(GetGamePath() + "mods/multiplayer"); - - if(!Dev) { - std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey); - transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); - LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(), - [](auto const& c ) -> bool { return !std::isalnum(c); } ), LatestHash.end()); - - try { - if (!fs::exists(GetGamePath() + "mods/multiplayer")) { - fs::create_directories(GetGamePath() + "mods/multiplayer"); - } - EnableMP(); - }catch(std::exception&e){ - fatal(e.what()); - } - - std::string ZipPath(GetGamePath() + R"(mods\multiplayer\BeamMP.zip)"); - - std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, ZipPath); - - if (FileHash != LatestHash) { - info("Downloading BeamMP Update " + LatestHash); - HTTP::Download("https://backend.beammp.com/builds/client?download=true" - "&pk=" + PublicKey + "&branch=" + Branch, ZipPath); - } - - std::string Target(GetGamePath() + "mods/unpacked/beammp"); - - if(fs::is_directory(Target)) { - fs::remove_all(Target); - } - } -} - -void set_headers(httplib::Response& res) { - res.set_header("Access-Control-Allow-Origin", "*"); - res.set_header("Access-Control-Request-Method", "POST, OPTIONS, GET"); - res.set_header("Access-Control-Request-Headers", "X-API-Version"); -} - -void StartProxy() { - std::thread proxy([&](){ - httplib::Server HTTPProxy; - httplib::Headers headers = { - {"User-Agent", "BeamMP-Launcher/" + GetVer() + GetPatch()}, - {"Accept", "*/*"} - }; - std::string pattern = "/:any1"; - for (int i = 2; i <= 4; i++) { - HTTPProxy.Get(pattern, [&](const httplib::Request &req, httplib::Response &res) { - httplib::Client cli("https://backend.beammp.com"); - set_headers(res); - if (req.has_header("X-BMP-Authentication")) { - headers.emplace("X-BMP-Authentication", PrivateKey); - } - if (req.has_header("X-API-Version")) { - headers.emplace("X-API-Version", req.get_header_value("X-API-Version")); - } - if (auto cli_res = cli.Get(req.path, headers); cli_res) { - res.set_content(cli_res->body, cli_res->get_header_value("Content-Type")); - } else { - res.set_content(to_string(cli_res.error()), "text/plain"); - } - }); - - HTTPProxy.Post(pattern, [&](const httplib::Request &req, httplib::Response &res) { - httplib::Client cli("https://backend.beammp.com"); - set_headers(res); - if (req.has_header("X-BMP-Authentication")) { - headers.emplace("X-BMP-Authentication", PrivateKey); - } - if (req.has_header("X-API-Version")) { - headers.emplace("X-API-Version", req.get_header_value("X-API-Version")); - } - if (auto cli_res = cli.Post(req.path, headers, req.body, - req.get_header_value("Content-Type")); cli_res) { - res.set_content(cli_res->body, cli_res->get_header_value("Content-Type")); - } else { - res.set_content(to_string(cli_res.error()), "text/plain"); - } - }); - pattern += "/:any" + std::to_string(i); - } - ProxyPort = HTTPProxy.bind_to_any_port("0.0.0.0"); - HTTPProxy.listen_after_bind(); - }); - proxy.detach(); -} diff --git a/src/Sync.h b/src/Sync.h new file mode 100644 index 00000000..90a14ea6 --- /dev/null +++ b/src/Sync.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +/// This header provides convenience aliases for synchronization primitives. + +template +using Sync = boost::synchronized_value; + + diff --git a/src/Version.cpp b/src/Version.cpp new file mode 100644 index 00000000..921b2c88 --- /dev/null +++ b/src/Version.cpp @@ -0,0 +1,37 @@ +#include "Version.h" +#include +#include +#include + +Version::Version(uint8_t major, uint8_t minor, uint8_t patch) + : major(major) + , minor(minor) + , patch(patch) { } + +Version::Version(const std::array& v) + : Version(v[0], v[1], v[2]) { +} + +std::string Version::to_string() { + return fmt::format("{:d}.{:d}.{:d}", major, minor, patch); +} + +bool Version::is_outdated(const Version& new_version) { + if (new_version.major > major) { + return true; + } else if (new_version.major == major && new_version.minor > minor) { + return true; + } else if (new_version.major == major && new_version.minor == minor && new_version.patch > patch) { + return true; + } else { + return false; + } +} +Version::Version(const std::string& str) { + std::stringstream ss(str); + std::string Part; + std::getline(ss, Part, '.'); + std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), major); + std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), minor); + std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), patch); +} diff --git a/src/Version.h b/src/Version.h new file mode 100644 index 00000000..781e2cc7 --- /dev/null +++ b/src/Version.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +struct Version { + uint8_t major {}; + uint8_t minor {}; + uint8_t patch {}; + Version() {} + Version(uint8_t major, uint8_t minor, uint8_t patch); + Version(const std::array& v); + explicit Version(const std::string& str); + std::string to_string(); + bool is_outdated(const Version& new_version); +}; diff --git a/src/main.cpp b/src/main.cpp old mode 100755 new mode 100644 index 15523a6e..ebd930d8 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,26 +1,150 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/16/2020 -/// -#include "Network/network.h" -#include "Security/Init.h" -#include "Startup.h" +#include +#include +#include +#include +#include + +#include "ClientNetwork.h" +#include "Launcher.h" +#include "Platform.h" +#include "ServerNetwork.h" #include -#include "Logger.h" #include -#include "Http.h" -[[noreturn]] void flush(){ - while(true){ - std::cout.flush(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); +/// Sets up a file- and console logger and makes it the default spdlog logger. +static void setup_logger(bool debug = false); +static std::shared_ptr default_logger {}; + +int main(int argc, char** argv) { + bool enable_debug = false; + bool enable_dev = false; + int custom_port = 0; + std::string_view invalid_arg; + std::string all_args = std::string { argv[0] } + " "; + for (int i = 1; i < argc; ++i) { + std::string_view arg(argv[i]); + all_args += "'" + std::string(arg) + "' "; + std::string_view next(i + 1 < argc ? argv[i + 1] : ""); + // --debug flag enables debug printing in console + if (arg == "--debug") { + enable_debug = true; + // --dev enables developer mode (game is not started) + } else if (arg == "--dev") { + enable_dev = true; + } else if (arg == "0" && next == "0") { + enable_dev = true; + ++i; + // an argument that is all digits and not 0 is a custom port, backwards-compat + } else if (std::all_of(arg.begin(), arg.end(), [](char c) { return std::isdigit(c); })) { + custom_port = std::stoi(arg.data()); + } else { + invalid_arg = arg; + } + } + setup_logger(enable_debug || enable_dev); + + spdlog::trace("BeamMP Launcher invoked as: {}", all_args); + + if (enable_debug) { + spdlog::debug("Debug mode enabled"); + } + if (enable_dev) { + spdlog::debug("Development mode enabled"); + } + if (custom_port != 0) { + spdlog::debug("Custom port set: {}", custom_port); + } + + if (!invalid_arg.empty()) { + spdlog::warn("One or more invalid argument(s) passed via commandline switches, last one: '{}'. This argument was ignored.", invalid_arg); + } + + plat::clear_screen(); + plat::set_console_title(fmt::format("BeamMP Launcher v{}.{}.{}", PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH)); + spdlog::trace("BeamMP Launcher v{}.{}.{}", PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH); + + spdlog::info("BeamMP Launcher v{}.{}.{} is a PRE-RELEASE build. Please report any errors immediately at https://github.com/BeamMP/BeamMP-Launcher.", + PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH); + + Launcher launcher {}; + + std::filesystem::path arg0(argv[0]); + launcher.set_exe_name(arg0.filename().generic_string()); + launcher.set_exe_path(arg0.parent_path()); + + if (custom_port > 0) { + // launcher.set_port(custom_port); + } + + if (!enable_dev) { + //&launcher.check_for_updates(argc, argv); + } else { + spdlog::debug("Skipping update check due to dev mode"); + } + + if (!enable_dev) { + launcher.find_game(); + } else { + spdlog::debug("Skipping game finding due to dev mode"); } + + if (!enable_dev) { + launcher.pre_game(); + launcher.start_game(); + } else { + spdlog::debug("Not launching the game due to dev mode"); + } + + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + // auto _ = launcher.start_server_network("beamcruise.com", 10814); + + // old: launcher.start_network(); + + /* + Launcher launcher {}; + + std::filesystem::path arg0(argv[0]); + launcher.set_exe_name(arg0.filename().generic_string()); + launcher.set_exe_path(arg0.parent_path()); + + try { + ServerNetwork sn(launcher, ip::tcp::endpoint(ip::address::from_string("127.0.0.1"), 30814)); + sn.run(); + } catch (const std::exception& e) { + spdlog::error("Connection to server closed: {}", e.what()); + } + spdlog::info("Shutting down."); + */ +} + +void setup_logger(bool debug) { + auto console_sink = std::make_shared(); + console_sink->set_pattern("[%H:%M:%S] [%^%l%$] %v"); + if (debug) { + console_sink->set_level(spdlog::level::debug); + } else { + console_sink->set_level(spdlog::level::info); + } + + auto file_sink = std::make_shared("Launcher.log", true); + file_sink->set_level(spdlog::level::trace); + file_sink->set_pattern("[%H:%M:%S.%e] [%t] [%L] %v"); + + default_logger = std::make_shared(spdlog::logger("default", { console_sink, file_sink })); + + default_logger->set_level(spdlog::level::trace); + default_logger->flush_on(spdlog::level::trace); + + spdlog::set_default_logger(default_logger); + + spdlog::debug("Logger initialized"); } -int main(int argc, char* argv[]) { +/* +int oldmain(int argc, char* argv[]) { #ifdef DEBUG std::thread th(flush); th.detach(); @@ -31,7 +155,7 @@ int main(int argc, char* argv[]) { try { LegitimacyCheck(); - } catch (std::exception &e) { + } catch (std::exception& e) { fatal("Main 1 : " + std::string(e.what())); } @@ -40,5 +164,6 @@ int main(int argc, char* argv[]) { InitGame(GetGameDir()); CoreNetwork(); - ///TODO: make sure to use argv[0] for everything that should be in the same dir (mod down ect...) + /// TODO: make sure to use argv[0] for everything that should be in the same dir (mod down ect...) } +*/ diff --git a/tests/test_main.cpp b/tests/test_main.cpp new file mode 100644 index 00000000..f78f4e2c --- /dev/null +++ b/tests/test_main.cpp @@ -0,0 +1,3 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include + diff --git a/vcpkg b/vcpkg new file mode 160000 index 00000000..11e79b10 --- /dev/null +++ b/vcpkg @@ -0,0 +1 @@ +Subproject commit 11e79b1072c7f12d84258b77948cda3b166aac59 diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000..f26b9f7b --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,19 @@ +{ + "name": "my-project", + "version-string": "0.1.0", + "dependencies": [ + "fmt", + "doctest", + "spdlog", + "openssl", + "cpp-httplib", + "boost-system", + "boost-iostreams", + "boost-process", + "boost-thread", + "boost-outcome", + "nlohmann-json", + "cryptopp", + "zstd" + ] +}