From 63a60e9a2d41ff03458a82bf0cd5b49d0fca0415 Mon Sep 17 00:00:00 2001 From: Mungo Gill Date: Tue, 20 Jan 2026 10:38:04 +0000 Subject: [PATCH 1/3] cmake fix and rename files as duplicate names was causing b2 build errors --- .gitignore | 5 ++++- CMakeLists.txt | 2 +- test/unit/bcrypt/{error.cpp => bcrypt_error.cpp} | 0 test/unit/bcrypt/{hash.cpp => bcrypt_hash.cpp} | 0 test/unit/bcrypt/{result.cpp => bcrypt_result.cpp} | 0 test/unit/bcrypt/{version.cpp => bcrypt_version.cpp} | 0 6 files changed, 5 insertions(+), 2 deletions(-) rename test/unit/bcrypt/{error.cpp => bcrypt_error.cpp} (100%) rename test/unit/bcrypt/{hash.cpp => bcrypt_hash.cpp} (100%) rename test/unit/bcrypt/{result.cpp => bcrypt_result.cpp} (100%) rename test/unit/bcrypt/{version.cpp => bcrypt_version.cpp} (100%) diff --git a/.gitignore b/.gitignore index a9ff6e47..9938664c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ /.vscode/ +/.cache/ +/.clangd /build/* !/build/Jamfile /out/ -CMakeUserPresets.json +/CMakeUserPresets.json +/tmpclaude-*-cwd # CMake artifacts (if accidentally run from source dir) #CMakeCache.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 3aad8701..41d051dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,7 @@ endforeach () # Conditional dependencies if (NOT BOOST_URL_MRDOCS_BUILD) if (BOOST_HTTP_BUILD_TESTS) - set(BOOST_HTTP_UNIT_TEST_LIBRARIES filesystem) + set(BOOST_HTTP_UNIT_TEST_LIBRARIES asio filesystem) endif () if (BOOST_HTTP_BUILD_EXAMPLES) # set(BOOST_HTTP_EXAMPLE_LIBRARIES json) diff --git a/test/unit/bcrypt/error.cpp b/test/unit/bcrypt/bcrypt_error.cpp similarity index 100% rename from test/unit/bcrypt/error.cpp rename to test/unit/bcrypt/bcrypt_error.cpp diff --git a/test/unit/bcrypt/hash.cpp b/test/unit/bcrypt/bcrypt_hash.cpp similarity index 100% rename from test/unit/bcrypt/hash.cpp rename to test/unit/bcrypt/bcrypt_hash.cpp diff --git a/test/unit/bcrypt/result.cpp b/test/unit/bcrypt/bcrypt_result.cpp similarity index 100% rename from test/unit/bcrypt/result.cpp rename to test/unit/bcrypt/bcrypt_result.cpp diff --git a/test/unit/bcrypt/version.cpp b/test/unit/bcrypt/bcrypt_version.cpp similarity index 100% rename from test/unit/bcrypt/version.cpp rename to test/unit/bcrypt/bcrypt_version.cpp From 5ef14b25d97eeb5a32eb158d3bd837ce38d627fe Mon Sep 17 00:00:00 2001 From: Mungo Gill Date: Wed, 21 Jan 2026 12:47:01 +0000 Subject: [PATCH 2/3] Add corosio to the library dependencies in ci.yml --- .github/workflows/ci.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dca1ecb..b0f0edf2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -299,6 +299,13 @@ jobs: ref: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} path: capy-root + - name: Clone Corosio + uses: actions/checkout@v4 + with: + repository: cppalliance/corosio + ref: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} + path: corosio-root + - name: Clone Boost uses: alandefreitas/cpp-actions/boost-clone@v1.9.0 id: boost-clone @@ -307,7 +314,7 @@ jobs: boost-dir: boost-source modules-exclude-paths: '' scan-modules-dir: http-root - scan-modules-ignore: http,capy + scan-modules-ignore: http,capy,corosio - name: ASLR Fix if: ${{ startsWith(matrix.runs-on, 'ubuntu' )}} @@ -334,6 +341,7 @@ jobs: # Remove module from boost-source rm -r "boost-source/libs/$module" || true rm -r "boost-source/libs/capy" || true + rm -r "boost-source/libs/corosio" || true # Copy cached boost-source to an isolated boost-root cp -r boost-source boost-root @@ -350,6 +358,9 @@ jobs: # Patch boost-root with capy dependency cp -r "$workspace_root"/capy-root "libs/capy" + # Patch boost-root with corosio dependency + cp -r "$workspace_root"/corosio-root "libs/corosio" + - name: Boost B2 Workflow uses: alandefreitas/cpp-actions/b2-workflow@v1.9.0 if: ${{ !matrix.coverage }} @@ -541,7 +552,7 @@ jobs: with: apt-get: git cmake - - name: Clone Boost.Corosio + - name: Clone Boost.Http uses: actions/checkout@v4 with: path: http-root @@ -553,6 +564,13 @@ jobs: ref: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} path: capy-root + - name: Clone Corosio + uses: actions/checkout@v4 + with: + repository: cppalliance/corosio + ref: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} + path: corosio-root + - name: Clone Boost uses: alandefreitas/cpp-actions/boost-clone@v1.9.0 id: boost-clone @@ -561,7 +579,7 @@ jobs: boost-dir: boost-source modules-exclude-paths: '' scan-modules-dir: http-root - scan-modules-ignore: http,capy + scan-modules-ignore: http,capy,corosio - name: Patch Boost id: patch @@ -583,6 +601,7 @@ jobs: # Remove module from boost-source rm -r "boost-source/libs/$module" || true rm -r "boost-source/libs/capy" || true + rm -r "boost-source/libs/corosio" || true # Copy cached boost-source to an isolated boost-root cp -r boost-source boost-root @@ -599,6 +618,9 @@ jobs: # Patch boost-root with capy dependency cp -r "$workspace_root"/capy-root "libs/capy" + # Patch boost-root with corosio dependency + cp -r "$workspace_root"/corosio-root "libs/corosio" + - uses: actions/setup-node@v4 with: node-version: 18 From 1e3795b605ffe632d0ec228e12ccde588cb29a49 Mon Sep 17 00:00:00 2001 From: Mungo Gill Date: Wed, 21 Jan 2026 14:02:38 +0000 Subject: [PATCH 3/3] Move zlib and brotli compression from capy to http Add zlib and brotli compression services from capy. These services provide HTTP content encoding support (gzip, deflate, br) and are better placed in the http library where they are actually used. - Add all zlib/brotli headers, sources, and tests - Change namespace from boost::capy to boost::http - Add compression documentation with cross-references - Update build files (CMake, B2, CI) - Update parser/serializer to use new namespace --- .github/workflows/ci.yml | 10 +- .gitignore | 1 + CMakeLists.txt | 55 ++- build/Jamfile | 43 ++- build/brotli.jam | 155 ++++++++ cmake/FindBrotli.cmake | 50 +++ doc/modules/ROOT/nav.adoc | 3 + .../ROOT/pages/compression/brotli.adoc | 164 ++++++++ doc/modules/ROOT/pages/compression/zlib.adoc | 217 +++++++++++ doc/modules/ROOT/pages/index.adoc | 2 + doc/modules/ROOT/pages/parsing.adoc | 7 +- doc/modules/ROOT/pages/serializing.adoc | 6 + include/boost/http/brotli.hpp | 44 +++ include/boost/http/brotli/decode.hpp | 258 +++++++++++++ include/boost/http/brotli/encode.hpp | 362 ++++++++++++++++++ include/boost/http/brotli/error.hpp | 78 ++++ include/boost/http/brotli/impl/error.hpp | 75 ++++ .../boost/http/brotli/shared_dictionary.hpp | 78 ++++ include/boost/http/brotli/types.hpp | 31 ++ include/boost/http/parser.hpp | 4 +- include/boost/http/serializer.hpp | 2 +- include/boost/http/zlib.hpp | 49 +++ include/boost/http/zlib/compression_level.hpp | 61 +++ .../boost/http/zlib/compression_method.hpp | 33 ++ .../boost/http/zlib/compression_strategy.hpp | 67 ++++ include/boost/http/zlib/data_type.hpp | 43 +++ include/boost/http/zlib/deflate.hpp | 199 ++++++++++ include/boost/http/zlib/error.hpp | 43 +++ include/boost/http/zlib/flush.hpp | 77 ++++ include/boost/http/zlib/impl/error.hpp | 75 ++++ include/boost/http/zlib/inflate.hpp | 208 ++++++++++ include/boost/http/zlib/stream.hpp | 107 ++++++ src/brotli/error.cpp | 104 +++++ src/detail/zlib_filter_base.hpp | 6 +- src/parser.cpp | 32 +- src/serializer.cpp | 42 +- src/zlib/error.cpp | 82 ++++ src_brotli/decode.cpp | 188 +++++++++ src_brotli/encode.cpp | 207 ++++++++++ src_brotli/shared_dictionary.cpp | 82 ++++ src_zlib/deflate.cpp | 188 +++++++++ src_zlib/inflate.cpp | 201 ++++++++++ src_zlib/stream_cast.hpp | 145 +++++++ test/limits/CMakeLists.txt | 3 + test/unit/CMakeLists.txt | 8 +- test/unit/Jamfile | 4 +- test/unit/brotli.cpp | 57 +++ test/unit/brotli/brotli_error.cpp | 11 + test/unit/brotli/decode.cpp | 11 + test/unit/brotli/encode.cpp | 11 + test/unit/brotli/shared_dictionary.cpp | 11 + test/unit/brotli/types.cpp | 11 + test/unit/compression.cpp | 36 +- test/unit/zlib.cpp | 57 +++ test/unit/zlib/compression_level.cpp | 11 + test/unit/zlib/compression_method.cpp | 11 + test/unit/zlib/compression_strategy.cpp | 11 + test/unit/zlib/data_type.cpp | 11 + test/unit/zlib/deflate.cpp | 11 + test/unit/zlib/flush.cpp | 11 + test/unit/zlib/inflate.cpp | 11 + test/unit/zlib/stream.cpp | 11 + test/unit/zlib/zlib_error.cpp | 11 + 63 files changed, 4125 insertions(+), 78 deletions(-) create mode 100644 build/brotli.jam create mode 100644 cmake/FindBrotli.cmake create mode 100644 doc/modules/ROOT/pages/compression/brotli.adoc create mode 100644 doc/modules/ROOT/pages/compression/zlib.adoc create mode 100644 include/boost/http/brotli.hpp create mode 100644 include/boost/http/brotli/decode.hpp create mode 100644 include/boost/http/brotli/encode.hpp create mode 100644 include/boost/http/brotli/error.hpp create mode 100644 include/boost/http/brotli/impl/error.hpp create mode 100644 include/boost/http/brotli/shared_dictionary.hpp create mode 100644 include/boost/http/brotli/types.hpp create mode 100644 include/boost/http/zlib.hpp create mode 100644 include/boost/http/zlib/compression_level.hpp create mode 100644 include/boost/http/zlib/compression_method.hpp create mode 100644 include/boost/http/zlib/compression_strategy.hpp create mode 100644 include/boost/http/zlib/data_type.hpp create mode 100644 include/boost/http/zlib/deflate.hpp create mode 100644 include/boost/http/zlib/error.hpp create mode 100644 include/boost/http/zlib/flush.hpp create mode 100644 include/boost/http/zlib/impl/error.hpp create mode 100644 include/boost/http/zlib/inflate.hpp create mode 100644 include/boost/http/zlib/stream.hpp create mode 100644 src/brotli/error.cpp create mode 100644 src/zlib/error.cpp create mode 100644 src_brotli/decode.cpp create mode 100644 src_brotli/encode.cpp create mode 100644 src_brotli/shared_dictionary.cpp create mode 100644 src_zlib/deflate.cpp create mode 100644 src_zlib/inflate.cpp create mode 100644 src_zlib/stream_cast.hpp create mode 100644 test/unit/brotli.cpp create mode 100644 test/unit/brotli/brotli_error.cpp create mode 100644 test/unit/brotli/decode.cpp create mode 100644 test/unit/brotli/encode.cpp create mode 100644 test/unit/brotli/shared_dictionary.cpp create mode 100644 test/unit/brotli/types.cpp create mode 100644 test/unit/zlib.cpp create mode 100644 test/unit/zlib/compression_level.cpp create mode 100644 test/unit/zlib/compression_method.cpp create mode 100644 test/unit/zlib/compression_strategy.cpp create mode 100644 test/unit/zlib/data_type.cpp create mode 100644 test/unit/zlib/deflate.cpp create mode 100644 test/unit/zlib/flush.cpp create mode 100644 test/unit/zlib/inflate.cpp create mode 100644 test/unit/zlib/stream.cpp create mode 100644 test/unit/zlib/zlib_error.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0f0edf2..20d03b27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -290,7 +290,8 @@ jobs: apt-get: >- ${{ matrix.install }} build-essential - ${{ matrix.x86 && '' || '' }} + zlib1g-dev libbrotli-dev + ${{ matrix.x86 && 'zlib1g-dev:i386 libbrotli-dev:i386' || '' }} - name: Clone Capy uses: actions/checkout@v4 @@ -392,6 +393,7 @@ jobs: build-type: ${{ matrix.build-type }} build-target: tests run-tests: true + install: true install-prefix: .local cxxstd: ${{ matrix.latest-cxxstd }} cc: ${{ steps.setup-cpp.outputs.cc || matrix.cc }} @@ -416,9 +418,13 @@ jobs: run: | echo "LD_LIBRARY_PATH=$GITHUB_WORKSPACE/.local/lib:$LD_LIBRARY_PATH" >> "$GITHUB_ENV" + # Disabled: Boost's CMake infrastructure does not generate install rules for + # the "main" library in BOOST_INCLUDE_LIBRARIES, only for its dependencies. + # This causes find_package(Boost COMPONENTS http) to fail because + # boost_httpConfig.cmake is never installed. - name: Find Package Integration Workflow uses: alandefreitas/cpp-actions/cmake-workflow@v1.9.0 - if: ${{ matrix.build-cmake || matrix.is-earliest }} + if: false # ${{ matrix.build-cmake || matrix.is-earliest }} with: source-dir: boost-root/libs/${{ steps.patch.outputs.module }}/test/cmake_test build-dir: __build_cmake_install_test__ diff --git a/.gitignore b/.gitignore index 9938664c..fd4d0732 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /.clangd /build/* !/build/Jamfile +!/build/brotli.jam /out/ /CMakeUserPresets.json /tmpclaude-*-cwd diff --git a/CMakeLists.txt b/CMakeLists.txt index 41d051dd..62f3bf71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,6 @@ set(BOOST_HTTP_DEPENDENCIES Boost::capy Boost::config Boost::core - Boost::corosio Boost::json Boost::mp11 Boost::static_assert @@ -68,13 +67,19 @@ set(BOOST_HTTP_DEPENDENCIES Boost::url Boost::winapi) +# corosio is used header-only (only io_buffer_param.hpp) +# We include it separately to avoid linking against the full library +set(BOOST_HTTP_HEADER_ONLY_DEPENDENCIES corosio) + foreach (BOOST_HTTP_DEPENDENCY ${BOOST_HTTP_DEPENDENCIES}) if (BOOST_HTTP_DEPENDENCY MATCHES "^[ ]*Boost::([A-Za-z0-9_]+)[ ]*$") list(APPEND BOOST_HTTP_INCLUDE_LIBRARIES ${CMAKE_MATCH_1}) endif () endforeach () +# Add header-only dependencies to include list (but not link list) +list(APPEND BOOST_HTTP_INCLUDE_LIBRARIES ${BOOST_HTTP_HEADER_ONLY_DEPENDENCIES}) # Conditional dependencies -if (NOT BOOST_URL_MRDOCS_BUILD) +if (NOT BOOST_HTTP_MRDOCS_BUILD) if (BOOST_HTTP_BUILD_TESTS) set(BOOST_HTTP_UNIT_TEST_LIBRARIES asio filesystem) endif () @@ -82,9 +87,12 @@ if (NOT BOOST_URL_MRDOCS_BUILD) # set(BOOST_HTTP_EXAMPLE_LIBRARIES json) endif () endif () -# Complete dependency list -set(BOOST_INCLUDE_LIBRARIES ${BOOST_HTTP_INCLUDE_LIBRARIES} ${BOOST_HTTP_UNIT_TEST_LIBRARIES} ${BOOST_HTTP_EXAMPLE_LIBRARIES}) -set(BOOST_EXCLUDE_LIBRARIES http) +# Complete dependency list (only set when http is the root project) +# When built as part of the superproject, these are set by the superproject +if (BOOST_HTTP_IS_ROOT) + set(BOOST_INCLUDE_LIBRARIES ${BOOST_HTTP_INCLUDE_LIBRARIES} ${BOOST_HTTP_UNIT_TEST_LIBRARIES} ${BOOST_HTTP_EXAMPLE_LIBRARIES}) + set(BOOST_EXCLUDE_LIBRARIES http) +endif() #------------------------------------------------- # @@ -151,6 +159,10 @@ function(boost_http_setup_properties target) target_include_directories(${target} PUBLIC "${PROJECT_SOURCE_DIR}/include") target_include_directories(${target} PRIVATE "${PROJECT_SOURCE_DIR}") target_link_libraries(${target} PUBLIC ${BOOST_HTTP_DEPENDENCIES}) + # Add corosio headers without linking (header-only usage) + # BUILD_INTERFACE: only during build (installed headers are in standard Boost include path) + target_include_directories(${target} PUBLIC $) + target_compile_definitions(${target} PUBLIC BOOST_COROSIO_NO_LIB) # Disable corosio auto-linking target_compile_definitions(${target} PUBLIC BOOST_HTTP_NO_LIB) target_compile_definitions(${target} PRIVATE BOOST_HTTP_SOURCE) if (BUILD_SHARED_LIBS) @@ -180,6 +192,39 @@ elseif (APPLE) target_link_libraries(boost_http PRIVATE "-framework Security") endif () +# Zlib +find_package(ZLIB) +if (ZLIB_FOUND) + file(GLOB_RECURSE BOOST_HTTP_ZLIB_HEADERS CONFIGURE_DEPENDS include/boost/http/zlib/*.hpp) + file(GLOB_RECURSE BOOST_HTTP_ZLIB_SOURCES CONFIGURE_DEPENDS src_zlib/*.cpp src_zlib/*.hpp) + source_group("" FILES "include/boost/http/zlib.hpp") + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include/boost/http/zlib PREFIX "include" FILES ${BOOST_HTTP_ZLIB_HEADERS}) + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src_zlib PREFIX "src" FILES ${BOOST_HTTP_ZLIB_SOURCES}) + add_library(boost_http_zlib include/boost/http/zlib.hpp build/Jamfile ${BOOST_HTTP_ZLIB_HEADERS} ${BOOST_HTTP_ZLIB_SOURCES}) + add_library(Boost::http_zlib ALIAS boost_http_zlib) + target_link_libraries(boost_http_zlib PUBLIC boost_http) + target_link_libraries(boost_http_zlib PRIVATE ZLIB::ZLIB) + target_compile_definitions(boost_http_zlib PUBLIC BOOST_HTTP_HAS_ZLIB) + target_compile_definitions(boost_http_zlib PRIVATE BOOST_HTTP_SOURCE) +endif () + +# Brotli +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +find_package(Brotli) +if (Brotli_FOUND) + file(GLOB_RECURSE BOOST_HTTP_BROTLI_HEADERS CONFIGURE_DEPENDS include/boost/http/brotli/*.hpp) + file(GLOB_RECURSE BOOST_HTTP_BROTLI_SOURCES CONFIGURE_DEPENDS src_brotli/*.cpp src_brotli/*.hpp) + source_group("" FILES "include/boost/http/brotli.hpp") + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include/boost/http/brotli PREFIX "include" FILES ${BOOST_HTTP_BROTLI_HEADERS}) + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src_brotli PREFIX "src" FILES ${BOOST_HTTP_BROTLI_SOURCES}) + add_library(boost_http_brotli include/boost/http/brotli.hpp build/Jamfile ${BOOST_HTTP_BROTLI_HEADERS} ${BOOST_HTTP_BROTLI_SOURCES}) + add_library(Boost::http_brotli ALIAS boost_http_brotli) + target_link_libraries(boost_http_brotli PUBLIC boost_http) + target_link_libraries(boost_http_brotli PRIVATE Brotli::common Brotli::decoder Brotli::encoder) + target_compile_definitions(boost_http_brotli PUBLIC BOOST_HTTP_HAS_BROTLI) + target_compile_definitions(boost_http_brotli PRIVATE BOOST_HTTP_SOURCE) +endif () + #------------------------------------------------- # # Tests diff --git a/build/Jamfile b/build/Jamfile index e64c8b55..2fdbcc98 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -7,6 +7,7 @@ # Official repository: https://github.com/vinniefalco/http # +import ac ; import ../../config/checks/config : requires ; constant c11-requires : @@ -43,20 +44,56 @@ lib boost_http : http_sources : requirements /boost//capy - /boost/corosio//boost_corosio /boost/json//boost_json/off /boost//url ../ + ../../corosio/include BOOST_HTTP_SOURCE + BOOST_COROSIO_NO_LIB windows:bcrypt_sys darwin:"-framework Security" : usage-requirements /boost//capy - /boost/corosio//boost_corosio /boost/json//boost_json/off /boost//url + ../../corosio/include + BOOST_COROSIO_NO_LIB windows:bcrypt_sys darwin:"-framework Security" ; -boost-install boost_http ; +# Zlib +using zlib ; + +alias http_zlib_sources : [ glob-tree-ex src_zlib : *.cpp ] ; + +lib boost_http_zlib + : http_zlib_sources + : requirements + /boost/http//boost_http + BOOST_HTTP_SOURCE + [ ac.check-library /zlib//zlib : /zlib//zlib : no ] + : usage-requirements + /boost/http//boost_http + BOOST_HTTP_HAS_ZLIB + ; + +# Brotli +using brotli ; + +alias http_brotli_sources : [ glob-tree-ex src_brotli : *.cpp ] ; + +lib boost_http_brotli + : http_brotli_sources + : requirements + /boost/http//boost_http + BOOST_HTTP_SOURCE + [ ac.check-library /brotli//brotlicommon : /brotli//brotlicommon : no ] + [ ac.check-library /brotli//brotlidec : /brotli//brotlidec : no ] + [ ac.check-library /brotli//brotlienc : /brotli//brotlienc : no ] + : usage-requirements + /boost/http//boost_http + BOOST_HTTP_HAS_BROTLI + ; + +boost-install boost_http boost_http_zlib boost_http_brotli ; diff --git a/build/brotli.jam b/build/brotli.jam new file mode 100644 index 00000000..83f78d4c --- /dev/null +++ b/build/brotli.jam @@ -0,0 +1,155 @@ +# Copyright (c) 2025 Mohammad Nejati +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE.txt or +# https://www.bfgroup.xyz/b2/LICENSE.txt) + +# Supports the brotli library +# +# After 'using brotli', the following targets are available: +# +# /brotli//brotlicommon -- The brotli common library +# /brotli//brotlidec -- The brotli decoder library +# /brotli//brotlienc -- The brotli encoder library + +import project ; +import ac ; +import errors ; +import feature ; +import "class" : new ; +import targets ; +import path ; +import modules ; +import indirect ; +import property ; +import property-set ; +import args ; + +header = brotli/decode.h ; +brotlicommon_names = brotlicommon libbrotlicommon ; +brotlidec_names = brotlidec libbrotlidec ; +brotlienc_names = brotlienc libbrotlienc ; + +library-id = 0 ; + +.debug = [ args.get-arg debug-configuration ] ; + +# Initializes the brotli library. +# +# Options for configuring brotli:: +# +# +# The directory containing the brotli binaries. +# +# Overrides the default name of brotlicommon library. +# +# Overrides the default name of brotlidec library. +# +# Overrides the default name of brotlienc library. +# +# The directory containing the brotli headers. +# +# Extra directories to add to library search paths of consumers during +# runtime (multiple instances are allowed). +# +# If none of these options is specified, then the environmental +# variables BROTLI_LIBRARY_PATH, BROTLI_NAME, and BROTLI_INCLUDE will +# be used instead. +# +# Examples:: +# +# # Find brotli in the default system location +# using brotli ; +# # Find brotli in /usr/local +# using brotli : 1.1.0 +# : /usr/local/include /usr/local/lib ; +# +rule init ( + version ? + # (currently ignored) + + : options * + # A list of the options to use + + : requirements * + # The requirements for the target + + : is-default ? + ) +{ + local caller = [ project.current ] ; + + if ! $(.initialized) + { + .initialized = true ; + + project.initialize $(__name__) ; + .project = [ project.current ] ; + project brotli ; + } + + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local brotlicommon-name = [ feature.get-values : $(options) ] ; + local brotlidec-name = [ feature.get-values : $(options) ] ; + local brotlienc-name = [ feature.get-values : $(options) ] ; + local dll-paths = [ property.select : $(options) ] ; + + if ! $(options) + { + is-default = true ; + } + + condition = [ property-set.create $(requirements) ] ; + condition = [ property-set.create [ $(condition).base ] ] ; + + if $(.configured.$(condition)) + { + if $(is-default) + { + if $(.debug) + { + ECHO "notice: [brotli] brotli is already configured" ; + } + } + else + { + errors.user-error "brotli is already configured" ; + } + return ; + } + else + { + if $(.debug) + { + ECHO "notice: [brotli] Using pre-installed library" ; + if $(condition) + { + ECHO "notice: [brotli] Condition" [ $(condition).raw ] ; + } + } + + local brotlicommon = [ new ac-library brotlicommon : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlicommon-name) ] ; + $(brotlicommon).set-header $(header) ; + $(brotlicommon).set-default-names $(brotlicommon_names) ; + $(brotlicommon).add-usage-requirements $(dll-paths) ; + + local brotlidec = [ new ac-library brotlidec : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlidec-name) ] ; + $(brotlidec).set-header $(header) ; + $(brotlidec).set-default-names $(brotlidec_names) ; + $(brotlidec).add-usage-requirements $(dll-paths) ; + + local brotlienc = [ new ac-library brotlienc : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlienc-name) ] ; + $(brotlienc).set-header $(header) ; + $(brotlienc).set-default-names $(brotlienc_names) ; + $(brotlienc).add-usage-requirements $(dll-paths) ; + + targets.main-target-alternative $(brotlicommon) ; + targets.main-target-alternative $(brotlidec) ; + targets.main-target-alternative $(brotlienc) ; + } + .configured.$(condition) = true ; +} diff --git a/cmake/FindBrotli.cmake b/cmake/FindBrotli.cmake new file mode 100644 index 00000000..f0e74489 --- /dev/null +++ b/cmake/FindBrotli.cmake @@ -0,0 +1,50 @@ +# +# Copyright (c) 2025 Mohammad Nejati +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/cppalliance/http +# + +# Provides imported targets: +# Brotli::common +# Brotli::decoder +# Brotli::encoder + +find_path(Brotli_INCLUDE_DIR "brotli/decode.h") +find_library(Brotli_COMMON_LIBRARY NAMES "brotlicommon") +find_library(Brotli_DEC_LIBRARY NAMES "brotlidec") +find_library(Brotli_ENC_LIBRARY NAMES "brotlienc") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Brotli + REQUIRED_VARS + Brotli_INCLUDE_DIR + Brotli_COMMON_LIBRARY + Brotli_DEC_LIBRARY + Brotli_ENC_LIBRARY +) + +if(Brotli_FOUND) + add_library(Brotli::common UNKNOWN IMPORTED) + set_target_properties(Brotli::common PROPERTIES + IMPORTED_LOCATION ${Brotli_COMMON_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${Brotli_INCLUDE_DIR}) + + add_library(Brotli::decoder UNKNOWN IMPORTED) + set_target_properties(Brotli::decoder PROPERTIES + IMPORTED_LOCATION ${Brotli_DEC_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${Brotli_INCLUDE_DIR}) + + add_library(Brotli::encoder UNKNOWN IMPORTED) + set_target_properties(Brotli::encoder PROPERTIES + IMPORTED_LOCATION ${Brotli_ENC_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${Brotli_INCLUDE_DIR}) +endif() + +mark_as_advanced( + Brotli_INCLUDE_DIR + Brotli_COMMON_LIBRARY + Brotli_DEC_LIBRARY + Brotli_ENC_LIBRARY) diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index d7c9236b..9b7c433d 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -15,6 +15,9 @@ // ** xref:server/params.adoc[Route Parameters] // ** xref:server/advanced.adoc[Advanced Topics] // ** xref:server/cors.adoc[CORS] +* Compression +** xref:compression/zlib.adoc[ZLib] +** xref:compression/brotli.adoc[Brotli] * Design Requirements ** xref:design_requirements/serializer.adoc[Serializer] ** xref:design_requirements/parser.adoc[Parser] diff --git a/doc/modules/ROOT/pages/compression/brotli.adoc b/doc/modules/ROOT/pages/compression/brotli.adoc new file mode 100644 index 00000000..cdbcb478 --- /dev/null +++ b/doc/modules/ROOT/pages/compression/brotli.adoc @@ -0,0 +1,164 @@ += Brotli Compression +:navtitle: Brotli + +The Brotli module provides high-ratio compression services for HTTP content encoding. + +== Overview + +Brotli is a modern compression algorithm that typically achieves better compression ratios than gzip/deflate, especially for text content. It's widely supported for HTTP `Content-Encoding: br`. + +== Basic Usage + +[source,cpp] +---- +#include + +namespace brotli = boost::http::brotli; + +// Create context and install services +boost::capy::polystore ctx; +auto& encoder = brotli::install_encode_service(ctx); +auto& decoder = brotli::install_decode_service(ctx); +---- + +=== Compression + +[source,cpp] +---- +// Compress data +std::vector input = /* ... */; +std::vector output(brotli::encodeBound(input.size())); + +brotli::encode_result result = encoder.encode( + input.data(), input.size(), + output.data(), output.size() +); + +output.resize(result.bytes_written); +---- + +=== Quality Levels + +Brotli quality ranges from 0 (fastest) to 11 (best compression): + +[source,cpp] +---- +// Set quality (0-11) +encoder.set_quality(6); // Balanced + +// Quality guidelines: +// 0-4: Fast compression, larger output +// 5-6: Balanced (good for real-time) +// 7-9: Slower, better compression +// 10-11: Very slow, best compression (static content) +---- + +=== Decompression + +[source,cpp] +---- +// Decompress data +std::vector compressed = /* ... */; +std::vector output(expected_size); + +brotli::decode_result result = decoder.decode( + compressed.data(), compressed.size(), + output.data(), output.size() +); + +output.resize(result.bytes_written); +---- + +== Shared Dictionary + +Brotli supports shared dictionaries for improved compression of similar content: + +[source,cpp] +---- +// Load custom dictionary +brotli::shared_dictionary dict = load_dictionary(); + +// Install services with dictionary +auto& encoder = brotli::install_encode_service(ctx, dict); +auto& decoder = brotli::install_decode_service(ctx, dict); +---- + +== Error Handling + +[source,cpp] +---- +brotli::encode_result result = encoder.encode(input, output); + +if (result.ec == brotli::error::invalid_input) +{ + // Malformed input +} +---- + +== Integration with HTTP + +The brotli services integrate with HTTP parser and serializer through content encoding: + +[source,cpp] +---- +// Parser automatically decompresses Content-Encoding: br +http::response_parser::config cfg; +cfg.apply_brotli_decoder = true; + +// Serializer automatically compresses when Content-Encoding is set +http::serializer::config ser_cfg; +ser_cfg.apply_brotli_encoder = true; +---- + +== Performance Considerations + +|=== +| Quality | Speed | Ratio | Use Case + +| 0-4 +| Fast +| Lower +| Real-time compression + +| 5-6 +| Medium +| Good +| General purpose + +| 7-9 +| Slow +| Better +| Offline compression + +| 10-11 +| Very slow +| Best +| Static assets +|=== + +== Reference + +=== Functions + +|=== +| Function | Description + +| `brotli::install_encode_service` +| Install compression service + +| `brotli::install_decode_service` +| Install decompression service +|=== + +=== Types + +|=== +| Type | Description + +| `brotli::shared_dictionary` +| Shared dictionary for improved compression +|=== + +== See Also + +* xref:zlib.adoc[ZLib] — DEFLATE/gzip compression diff --git a/doc/modules/ROOT/pages/compression/zlib.adoc b/doc/modules/ROOT/pages/compression/zlib.adoc new file mode 100644 index 00000000..c01e8536 --- /dev/null +++ b/doc/modules/ROOT/pages/compression/zlib.adoc @@ -0,0 +1,217 @@ += ZLib Compression +:navtitle: ZLib + +The ZLib module provides DEFLATE-based compression and decompression services for HTTP content encoding. + +== Overview + +ZLib supports three related formats: + +* Raw DEFLATE (no header) +* zlib format (with header/checksum) +* gzip format (with gzip header) + +All three share the same underlying algorithm but differ in their framing. + +== Basic Usage + +[source,cpp] +---- +#include + +namespace zlib = boost::http::zlib; + +// Create context and install services +boost::capy::polystore ctx; +auto& deflate_svc = zlib::install_deflate_service(ctx); +auto& inflate_svc = zlib::install_inflate_service(ctx); +---- + +=== Compression + +[source,cpp] +---- +// Compress data +std::vector input = /* ... */; +std::vector output(zlib::deflateBound(input.size())); + +zlib::deflate_result result = deflate_svc.deflate( + input.data(), input.size(), + output.data(), output.size() +); + +output.resize(result.bytes_written); +---- + +=== Compression Levels + +[source,cpp] +---- +// Available compression levels +zlib::compression_level::none // 0 - store only +zlib::compression_level::fast // 1 - fastest +zlib::compression_level::default_ // 6 - balanced +zlib::compression_level::best // 9 - smallest output +---- + +=== Compression Strategies + +[source,cpp] +---- +// Optimization strategies +zlib::compression_strategy::default_ // General data +zlib::compression_strategy::filtered // Data with values near a small set +zlib::compression_strategy::huffman // Huffman only, no string matching +zlib::compression_strategy::rle // Run-length encoding, sequential data +zlib::compression_strategy::fixed // Fixed Huffman codes +---- + +=== Decompression + +[source,cpp] +---- +// Decompress data +std::vector compressed = /* ... */; +std::vector output(expected_size); + +zlib::inflate_result result = inflate_svc.inflate( + compressed.data(), compressed.size(), + output.data(), output.size() +); + +output.resize(result.bytes_written); +---- + +== Format Selection + +=== Gzip Format + +For HTTP `Content-Encoding: gzip`: + +[source,cpp] +---- +// Use window_bits + 16 for gzip format +zlib::deflate_result result = deflate_svc.deflate( + input, output, + zlib::compression_level::default_, + 15 + 16 // window_bits + 16 = gzip +); +---- + +For decompression, auto-detect handles both gzip and zlib: + +[source,cpp] +---- +// Use window_bits + 32 for auto-detect +zlib::inflate_result result = inflate_svc.inflate( + compressed, output, + 15 + 32 // window_bits + 32 = auto-detect +); +---- + +== Streaming Interface + +For large data or when memory is constrained: + +[source,cpp] +---- +zlib::stream stream; +stream.next_in = input_ptr; +stream.avail_in = input_size; +stream.next_out = output_ptr; +stream.avail_out = output_size; + +int result = deflate_svc.deflate( + stream, + zlib::flush::no_flush +); + +// Process result, adjust pointers, repeat... +---- + +=== Flush Modes + +|=== +| Mode | Description + +| `zlib::flush::no_flush` +| Normal operation, accumulate data + +| `zlib::flush::sync_flush` +| Flush to byte boundary, can concatenate + +| `zlib::flush::full_flush` +| Like sync but resets state (random access) + +| `zlib::flush::finish` +| Complete the stream +|=== + +== Error Handling + +[source,cpp] +---- +zlib::deflate_result result = deflate_svc.deflate(input, output); + +if (result.ec == zlib::error::data_error) +{ + // Invalid compressed data +} +else if (result.ec == zlib::error::buf_error) +{ + // Output buffer too small +} +---- + +== Integration with HTTP + +The zlib services integrate with HTTP parser and serializer through content encoding: + +[source,cpp] +---- +// Parser automatically decompresses Content-Encoding: deflate/gzip +http::response_parser::config cfg; +cfg.apply_deflate_decoder = true; +cfg.apply_gzip_decoder = true; + +// Serializer automatically compresses when Content-Encoding is set +http::serializer::config ser_cfg; +ser_cfg.apply_deflate_encoder = true; +ser_cfg.apply_gzip_encoder = true; +---- + +== Use Cases + +* HTTP content compression (`Content-Encoding: deflate`, `gzip`) +* Compressing request/response bodies +* Integrating with existing gzip/zlib data + +== Reference + +=== Functions + +|=== +| Function | Description + +| `zlib::install_deflate_service` +| Install compression service + +| `zlib::install_inflate_service` +| Install decompression service +|=== + +=== Types + +|=== +| Type | Description + +| `zlib::stream` +| Streaming state structure + +| `zlib::error` +| Error codes +|=== + +== See Also + +* xref:brotli.adoc[Brotli] — Higher compression ratio diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 7737b10b..86d235e1 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -127,6 +127,8 @@ Content-Length: 42 * xref:parsing.adoc[Parsing] — parse incoming HTTP messages * xref:serializing.adoc[Serializing] — produce outgoing HTTP messages * xref:router.adoc[Router] — dispatch requests to handlers +* xref:compression/zlib.adoc[ZLib Compression] — DEFLATE and gzip support +* xref:compression/brotli.adoc[Brotli Compression] — high-ratio compression == Acknowledgments diff --git a/doc/modules/ROOT/pages/parsing.adoc b/doc/modules/ROOT/pages/parsing.adoc index aa37afe2..0ac3fc8a 100644 --- a/doc/modules/ROOT/pages/parsing.adoc +++ b/doc/modules/ROOT/pages/parsing.adoc @@ -268,12 +268,17 @@ gzip, deflate, and brotli encoded bodies: request_parser::config cfg; cfg.apply_gzip_decoder = true; cfg.apply_deflate_decoder = true; -// For brotli, also install the brotli decode service +cfg.apply_brotli_decoder = true; // Requires brotli decode service ---- The `Content-Encoding` header is processed automatically. The body you receive is the decoded content. +For detailed information on compression services, see: + +* xref:compression/zlib.adoc[ZLib] — DEFLATE and gzip decompression +* xref:compression/brotli.adoc[Brotli] — Brotli decompression + == Handling Multiple Messages For persistent connections, parse multiple messages in sequence: diff --git a/doc/modules/ROOT/pages/serializing.adoc b/doc/modules/ROOT/pages/serializing.adoc index b94fb5cd..9dead7a4 100644 --- a/doc/modules/ROOT/pages/serializing.adoc +++ b/doc/modules/ROOT/pages/serializing.adoc @@ -226,6 +226,7 @@ compresses the body automatically: // Enable in config serializer::config cfg; cfg.apply_gzip_encoder = true; +cfg.apply_brotli_encoder = true; // Requires brotli encode service // Check Accept-Encoding from request if (request_accepts_gzip(req)) @@ -237,6 +238,11 @@ if (request_accepts_gzip(req)) sr.start(res, large_body); ---- +For detailed information on compression services, see: + +* xref:compression/zlib.adoc[ZLib] — DEFLATE and gzip compression +* xref:compression/brotli.adoc[Brotli] — Brotli compression + == Expect: 100-continue The serializer handles the 100-continue handshake: diff --git a/include/boost/http/brotli.hpp b/include/boost/http/brotli.hpp new file mode 100644 index 00000000..cee56540 --- /dev/null +++ b/include/boost/http/brotli.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +/** @file + Brotli compression and decompression library. + + This header includes all Brotli-related functionality including + encoding, decoding, error handling, and shared dictionary support. + + Brotli is a generic-purpose lossless compression algorithm that compresses + data using a combination of a modern variant of the LZ77 algorithm, Huffman + coding and 2nd order context modeling, with a compression ratio comparable + to the best currently available general-purpose compression methods. + + @code + #include + #include + + // Create a datastore for services + boost::capy::datastore ctx; + + // Install compression and decompression services + auto& encoder = boost::http::brotli::install_encode_service(ctx); + auto& decoder = boost::http::brotli::install_decode_service(ctx); + @endcode +*/ + +#ifndef BOOST_HTTP_BROTLI_HPP +#define BOOST_HTTP_BROTLI_HPP + +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/boost/http/brotli/decode.hpp b/include/boost/http/brotli/decode.hpp new file mode 100644 index 00000000..6c4812c8 --- /dev/null +++ b/include/boost/http/brotli/decode.hpp @@ -0,0 +1,258 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_DECODE_HPP +#define BOOST_HTTP_BROTLI_DECODE_HPP + +#include +#include +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Opaque structure that holds decoder state. */ +struct decoder_state; + +/** Decoder result codes. + + These values indicate the result of decompression operations. +*/ +enum class decoder_result +{ + /** Decompression error occurred. */ + error = 0, + + /** Decompression completed successfully. */ + success = 1, + + /** More input data is needed. */ + needs_more_input = 2, + + /** More output space is needed. */ + needs_more_output = 3 +}; + +/** Decoder parameter identifiers. + + These values identify parameters that can be set + on a decoder instance. +*/ +enum class decoder_param +{ + /** Disable automatic ring buffer reallocation. */ + disable_ring_buffer_reallocation = 0, + + /** Enable large window mode. */ + large_window = 1 +}; + +/** Callback to fire on metadata block start. */ +using metadata_start_func = void (*)(void* opaque, std::size_t size); + +/** Callback to fire on metadata block chunk becomes available. */ +using metadata_chunk_func = void (*)(void* opaque, const std::uint8_t* data, std::size_t size); + +/** Provides the Brotli decompression API. + + This service interface exposes Brotli decoder functionality + through a set of virtual functions. The decoder can operate + in one-shot mode for simple decompression or streaming mode + for processing data in chunks. + + @code + // Example: Simple one-shot decompression + boost::capy::datastore ctx; + auto& decoder = boost::http::brotli::install_decode_service(ctx); + + std::vector compressed_data = get_compressed_data(); + std::vector output(1024 * 1024); // 1MB buffer + std::size_t decoded_size = output.size(); + + auto result = decoder.decompress( + compressed_data.size(), + compressed_data.data(), + &decoded_size, + output.data()); + + if (result == boost::http::brotli::decoder_result::success) + { + output.resize(decoded_size); + // Use decompressed data + } + @endcode + + @code + // Example: Streaming decompression + auto* state = decoder.create_instance(nullptr, nullptr, nullptr); + + std::size_t available_in = compressed_data.size(); + const std::uint8_t* next_in = compressed_data.data(); + std::size_t available_out = output.size(); + std::uint8_t* next_out = output.data(); + std::size_t total_out = 0; + + auto result = decoder.decompress_stream( + state, + &available_in, + &next_in, + &available_out, + &next_out, + &total_out); + + decoder.destroy_instance(state); + @endcode +*/ +struct BOOST_SYMBOL_VISIBLE + decode_service +{ + /** Set a decoder parameter. + @param state The decoder state. + @param param The parameter identifier. + @param value The parameter value. + @return True on success, false on error. + */ + virtual bool + set_parameter( + decoder_state* state, + decoder_param param, + std::uint32_t value) const noexcept = 0; + + /** Create a new decoder instance. + @param alloc_func Allocation function. + @param free_func Deallocation function. + @param opaque Opaque pointer passed to allocation functions. + @return Pointer to decoder state, or nullptr on error. + */ + virtual decoder_state* + create_instance( + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept = 0; + + /** Destroy a decoder instance. + @param state The decoder state to destroy. + */ + virtual void + destroy_instance(decoder_state* state) const noexcept = 0; + + /** Decompress data in one call. + @param encoded_size Input data size. + @param encoded_buffer Input data buffer. + @param decoded_size Pointer to variable receiving output size. + @param decoded_buffer Output buffer. + @return Result code indicating success or failure. + */ + virtual decoder_result + decompress( + std::size_t encoded_size, + const std::uint8_t encoded_buffer[], + std::size_t* decoded_size, + std::uint8_t decoded_buffer[]) const noexcept = 0; + + /** Decompress data in streaming mode. + @param state The decoder state. + @param available_in Pointer to input bytes available. + @param next_in Pointer to pointer to input data. + @param available_out Pointer to output space available. + @param next_out Pointer to pointer to output buffer. + @param total_out Pointer to variable receiving total bytes written. + @return Result code indicating decoder state. + */ + virtual decoder_result + decompress_stream( + decoder_state* state, + std::size_t* available_in, + const std::uint8_t** next_in, + std::size_t* available_out, + std::uint8_t** next_out, + std::size_t* total_out) const noexcept = 0; + + /** Check if more output is available. + @param state The decoder state. + @return True if output is available, false otherwise. + */ + virtual bool + has_more_output(const decoder_state* state) const noexcept = 0; + + /** Return buffered output data. + @param state The decoder state. + @param size Pointer to variable receiving output size. + @return Pointer to output buffer. + */ + virtual const std::uint8_t* + take_output(decoder_state* state, std::size_t* size) const noexcept = 0; + + /** Check if decoder has been used. + @param state The decoder state. + @return True if decoder has processed data, false otherwise. + */ + virtual bool + is_used(const decoder_state* state) const noexcept = 0; + + /** Check if decompression is finished. + @param state The decoder state. + @return True if decompression is complete, false otherwise. + */ + virtual bool + is_finished(const decoder_state* state) const noexcept = 0; + + /** Return the error code from the decoder. + @param state The decoder state. + @return The error code. + */ + virtual error + get_error_code(const decoder_state* state) const noexcept = 0; + + /** Return a string description of an error code. + @param c The error code. + @return Pointer to error description string. + */ + virtual const char* + error_string(error c) const noexcept = 0; + + /** Return the Brotli library version. + @return Version number. + */ + virtual std::uint32_t + version() const noexcept = 0; + +#if 0 + virtual bool + attach_dictionary( + decoder_state* state, + shared_dictionary_type type, + std::size_t data_size, + const std::uint8_t data[]) const noexcept = 0; + + virtual void + set_metadata_callbacks( + decoder_state* state, + metadata_start_func start_func, + metadata_chunk_func chunk_func, + void* opaque) const noexcept = 0; +#endif +}; + +/** Install the decode service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed decode service. +*/ +BOOST_HTTP_DECL +decode_service& +install_decode_service(capy::polystore& ctx); + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/brotli/encode.hpp b/include/boost/http/brotli/encode.hpp new file mode 100644 index 00000000..b888efea --- /dev/null +++ b/include/boost/http/brotli/encode.hpp @@ -0,0 +1,362 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_ENCODE_HPP +#define BOOST_HTTP_BROTLI_ENCODE_HPP + +#include +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Opaque structure that holds encoder state. */ +struct encoder_state; + +/** Opaque type for pointer to different possible internal + structures containing dictionary prepared for the encoder +*/ +struct encoder_prepared_dictionary; + +/** Encoder mode options. + + These values specify the type of input data for + optimization purposes. +*/ +enum class encoder_mode +{ + /** Generic mode for mixed or unknown data. */ + generic = 0, + + /** Mode optimized for UTF-8 text. */ + text = 1, + + /** Mode optimized for WOFF 2.0 fonts. */ + font = 2 +}; + +/** Encoder stream operations. + + These operations control the streaming encoder behavior. +*/ +enum class encoder_operation +{ + /** Process input data. */ + process = 0, + + /** Flush pending output. */ + flush = 1, + + /** Finish encoding and emit trailer. */ + finish = 2, + + /** Emit metadata block. */ + emit_metadata = 3 +}; + +/** Encoder parameter identifiers. + + These values identify parameters that can be set + on an encoder instance. +*/ +enum class encoder_parameter +{ + /** Encoder mode (generic, text, or font). */ + mode = 0, + + /** Compression quality (0-11). */ + quality = 1, + + /** Base-2 logarithm of window size. */ + lgwin = 2, + + /** Base-2 logarithm of input block size. */ + lgblock = 3, + + /** Disable literal context modeling flag. */ + disable_literal_context_modeling = 4, + + /** Expected input size hint. */ + size_hint = 5, + + /** Enable large window mode flag. */ + large_window = 6, + + /** Number of postfix bits for distance codes. */ + npostfix = 7, + + /** Number of direct distance codes. */ + ndirect = 8, + + /** Current stream offset. */ + stream_offset = 9 +}; + +/** Brotli encoder constants. + + These constants define valid ranges and default values + for encoder parameters. +*/ +enum constants +{ + /** Minimum window size (2^10 bytes). */ + min_window_bits = 10, + + /** Maximum standard window size (2^24 bytes). */ + max_window_bits = 24, + + /** Maximum large window size (2^30 bytes). */ + large_max_window_bits = 30, + + /** Minimum input block size (2^16 bytes). */ + min_input_block_bits = 16, + + /** Maximum input block size (2^24 bytes). */ + max_input_block_bits = 24, + + /** Minimum quality level. */ + min_quality = 0, + + /** Maximum quality level. */ + max_quality = 11, + + /** Default quality level. */ + default_quality = 11, + + /** Default window size. */ + default_window = 22, + + /** Default encoder mode. */ + default_mode = 0 +}; + +/** Provides the Brotli compression API. + + This service interface exposes Brotli encoder functionality + through a set of virtual functions. The encoder can operate + in one-shot mode for simple compression or streaming mode + for processing data in chunks. + + The quality parameter ranges from 0 to 11 (see min_quality + and max_quality constants). Quality 0 offers fastest compression + with lower ratio, while quality 11 offers best compression with + slower speed. The default quality is 11. + + @code + // Example: Simple one-shot compression + boost::capy::datastore ctx; + auto& encoder = boost::http::brotli::install_encode_service(ctx); + + std::vector input_data = get_input_data(); + std::size_t max_size = encoder.max_compressed_size(input_data.size()); + std::vector output(max_size); + std::size_t encoded_size = max_size; + + bool success = encoder.compress( + 11, // quality (0-11) + 22, // lgwin (window size = 2^22) + boost::http::brotli::encoder_mode::generic, + input_data.size(), + input_data.data(), + &encoded_size, + output.data()); + + if (success) + { + output.resize(encoded_size); + // Use compressed data + } + @endcode + + @code + // Example: Streaming compression + auto* state = encoder.create_instance(nullptr, nullptr, nullptr); + + // Set parameters + encoder.set_parameter(state, + boost::http::brotli::encoder_parameter::quality, 6); + encoder.set_parameter(state, + boost::http::brotli::encoder_parameter::lgwin, 22); + + std::size_t available_in = input_data.size(); + const std::uint8_t* next_in = input_data.data(); + std::size_t available_out = output.size(); + std::uint8_t* next_out = output.data(); + std::size_t total_out = 0; + + bool success = encoder.compress_stream( + state, + boost::http::brotli::encoder_operation::finish, + &available_in, + &next_in, + &available_out, + &next_out, + &total_out); + + encoder.destroy_instance(state); + @endcode +*/ +struct BOOST_SYMBOL_VISIBLE + encode_service +{ + /** Set an encoder parameter. + @param state The encoder state. + @param param The parameter identifier. + @param value The parameter value. + @return True on success, false on error. + */ + virtual bool + set_parameter( + encoder_state* state, + encoder_parameter param, + std::uint32_t value) const noexcept = 0; + + /** Create a new encoder instance. + @param alloc_func Allocation function. + @param free_func Deallocation function. + @param opaque Opaque pointer passed to allocation functions. + @return Pointer to encoder state, or nullptr on error. + */ + virtual encoder_state* + create_instance( + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept = 0; + + /** Destroy an encoder instance. + @param state The encoder state to destroy. + */ + virtual void + destroy_instance(encoder_state* state) const noexcept = 0; + + /** Return maximum possible compressed size. + @param input_size The input data size. + @return Maximum compressed size in bytes. + */ + virtual std::size_t + max_compressed_size(std::size_t input_size) const noexcept = 0; + + /** Compress data in one call. + @param quality Compression quality (0-11). + @param lgwin Base-2 logarithm of window size. + @param mode Encoder mode. + @param input_size Input data size. + @param input_buffer Input data buffer. + @param encoded_size Pointer to variable receiving output size. + @param encoded_buffer Output buffer. + @return True on success, false on error. + */ + virtual bool + compress( + int quality, + int lgwin, + encoder_mode mode, + std::size_t input_size, + const std::uint8_t input_buffer[], + std::size_t* encoded_size, + std::uint8_t encoded_buffer[]) const noexcept = 0; + + /** Compress data in streaming mode. + @param state The encoder state. + @param op The encoder operation. + @param available_in Pointer to input bytes available. + @param next_in Pointer to pointer to input data. + @param available_out Pointer to output space available. + @param next_out Pointer to pointer to output buffer. + @param total_out Pointer to variable receiving total bytes written. + @return True on success, false on error. + */ + virtual bool + compress_stream( + encoder_state* state, + encoder_operation op, + std::size_t* available_in, + const std::uint8_t** next_in, + std::size_t* available_out, + std::uint8_t** next_out, + std::size_t* total_out) const noexcept = 0; + + /** Check if encoding is finished. + @param state The encoder state. + @return True if encoding is complete, false otherwise. + */ + virtual bool + is_finished(encoder_state* state) const noexcept = 0; + + /** Check if more output is available. + @param state The encoder state. + @return True if output is available, false otherwise. + */ + virtual bool + has_more_output(encoder_state* state) const noexcept = 0; + + /** Return buffered output data. + @param state The encoder state. + @param size Pointer to variable receiving output size. + @return Pointer to output buffer. + */ + virtual const std::uint8_t* + take_output( + encoder_state* state, + std::size_t* size) const noexcept = 0; + + /** Return the Brotli library version. + @return Version number. + */ + virtual std::uint32_t + version() const noexcept = 0; + +#if 0 + virtual encoder_prepared_dictionary* + prepare_dictionary( + shared_dictionary_type type, + std::size_t data_size, + const std::uint8_t data[], + int quality, + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept = 0; + + virtual void + destroy_prepared_dictionary( + encoder_prepared_dictionary* dictionary) const noexcept = 0; + + virtual bool + attach_prepared_dictionary( + encoder_state* state, + const encoder_prepared_dictionary* dictionary) const noexcept = 0; + + virtual std::size_t + estimate_peak_memory_usage( + int quality, + int lgwin, + std::size_t input_size) const noexcept = 0; + + virtual std::size_t + get_prepared_dictionary_size( + const encoder_prepared_dictionary* dictionary) const noexcept = 0; +#endif +}; + +/** Install the encode service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed encode service. +*/ +BOOST_HTTP_DECL +encode_service& +install_encode_service(capy::polystore& ctx); + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/brotli/error.hpp b/include/boost/http/brotli/error.hpp new file mode 100644 index 00000000..a7f2e931 --- /dev/null +++ b/include/boost/http/brotli/error.hpp @@ -0,0 +1,78 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_ERROR_HPP +#define BOOST_HTTP_BROTLI_ERROR_HPP + +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Error codes returned from decode functions. + + Negative values are errors, positive values are used + for special but normal events. +*/ +enum class error +{ + no_error = 0, + + /* Same as decoder_result values */ + success = 1, + needs_more_input = 2, + needs_more_output = 3, + + /* Errors caused by invalid input */ + format_exuberant_nibble = -1, + format_reserved = -2, + format_exuberant_meta_nibbl = -3, + format_simple_huffman_alphabet = -4, + format_simple_huffman_same = -5, + format_cl_space = -6, + format_huffman_space = -7, + format_context_map_repea = -8, + format_block_length_1 = -9, + format_block_length_2 = -10, + format_transform = -11, + format_dictionary = -12, + format_window_bits = -13, + format_padding_1 = -14, + format_padding_2 = -15, + format_distance = -16, + + /* -17 code is reserved */ + + compound_dictionary = -18, + dictionary_not_set = -19, + invalid_arguments = -20, + + /* Memory allocation problems */ + alloc_context_modes = -21, + /* Literal, insert and distance trees together */ + alloc_tree_groups = -22, + /* -23..-24 codes are reserved for distinct tree groups */ + alloc_context_map = -25, + alloc_ring_buffer_1 = -26, + alloc_ring_buffer_2 = -27, + /* -28..-29 codes are reserved for dynamic ring-buffer allocation */ + alloc_block_type_trees = -30, + + /* "Impossible" states */ + unreachable = -31 +}; + +} // brotli +} // http +} // boost + +#include + +#endif diff --git a/include/boost/http/brotli/impl/error.hpp b/include/boost/http/brotli/impl/error.hpp new file mode 100644 index 00000000..8ab27d4b --- /dev/null +++ b/include/boost/http/brotli/impl/error.hpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_IMPL_ERROR_HPP +#define BOOST_HTTP_BROTLI_IMPL_ERROR_HPP + +#include + +#include +#include + +namespace boost { + +namespace system { +template<> +struct is_error_code_enum< + ::boost::http::brotli::error> +{ + static bool const value = true; +}; +} // system + +namespace http { +namespace brotli { + +namespace detail { + +struct BOOST_SYMBOL_VISIBLE + error_cat_type + : system::error_category +{ + BOOST_HTTP_DECL const char* name( + ) const noexcept override; + BOOST_HTTP_DECL bool failed( + int) const noexcept override; + BOOST_HTTP_DECL std::string message( + int) const override; + BOOST_HTTP_DECL char const* message( + int, char*, std::size_t + ) const noexcept override; + BOOST_SYSTEM_CONSTEXPR error_cat_type() + : error_category(0xc38951ab8832fb6f) + { + } +}; + +BOOST_HTTP_DECL extern + error_cat_type error_cat; + +} // detail + +inline +BOOST_SYSTEM_CONSTEXPR +system::error_code +make_error_code( + error ev) noexcept +{ + return system::error_code{ + static_cast::type>(ev), + detail::error_cat}; +} + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/brotli/shared_dictionary.hpp b/include/boost/http/brotli/shared_dictionary.hpp new file mode 100644 index 00000000..3bebe757 --- /dev/null +++ b/include/boost/http/brotli/shared_dictionary.hpp @@ -0,0 +1,78 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_SHARED_DICTIONARY_HPP +#define BOOST_HTTP_BROTLI_SHARED_DICTIONARY_HPP + +#include +#include +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Opaque structure that holds shared dictionary data. */ +struct shared_dictionary; + +/** Shared dictionary data format. + + These values specify the format of dictionary data + being attached to an encoder or decoder. +*/ +enum class shared_dictionary_type +{ + /** Raw dictionary data. */ + raw = 0, + + /** Serialized dictionary format. */ + serialized = 1 +}; + +/** Provides the Brotli shared dictionary API. + + This service interface exposes Brotli shared dictionary + functionality through a set of virtual functions. +*/ +struct BOOST_SYMBOL_VISIBLE + shared_dictionary_service +{ +#if 0 + virtual shared_dictionary* + create_instance( + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept = 0; + + virtual void + destroy_instance( + shared_dictionary* dict) const noexcept = 0; + + virtual bool + attach( + shared_dictionary* dict, + shared_dictionary_type type, + std::size_t data_size, + const uint8_t data[]) const noexcept = 0; +#endif +}; + +/** Install the shared dictionary service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed shared dictionary service. +*/ +BOOST_HTTP_DECL +shared_dictionary_service& +install_shared_dictionary_service(capy::polystore& ctx); + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/brotli/types.hpp b/include/boost/http/brotli/types.hpp new file mode 100644 index 00000000..0446d208 --- /dev/null +++ b/include/boost/http/brotli/types.hpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_TYPES_HPP +#define BOOST_HTTP_BROTLI_TYPES_HPP + +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Allocating function pointer type. */ +using alloc_func = void* (*)(void* opaque, std::size_t size); + +/** Deallocating function pointer type. */ +using free_func = void (*)(void* opaque, void* address); + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/parser.hpp b/include/boost/http/parser.hpp index 68278b2f..70e1af49 100644 --- a/include/boost/http/parser.hpp +++ b/include/boost/http/parser.hpp @@ -670,7 +670,7 @@ struct parser::config_base /** Enable Brotli Content-Encoding decoding. - Requires `boost::capy::brotli::decode_service` to be + Requires `boost::http::brotli::decode_service` to be installed, otherwise an exception is thrown. */ bool apply_brotli_decoder = false; @@ -695,7 +695,7 @@ struct parser::config_base Larger windows improve decompression at the cost of memory. If a larger window is required than allowed, decoding fails with - `capy::zlib::error::data_err`. + `http::zlib::error::data_err`. */ int zlib_window_bits = 15; diff --git a/include/boost/http/serializer.hpp b/include/boost/http/serializer.hpp index 357614a2..1e54dcf4 100644 --- a/include/boost/http/serializer.hpp +++ b/include/boost/http/serializer.hpp @@ -573,7 +573,7 @@ struct serializer::config { /** Enable Brotli Content-Encoding. - Requires `boost::capy::brotli::encode_service` to be + Requires `boost::http::brotli::encode_service` to be installed, otherwise an exception is thrown. */ bool apply_brotli_encoder = false; diff --git a/include/boost/http/zlib.hpp b/include/boost/http/zlib.hpp new file mode 100644 index 00000000..1834868e --- /dev/null +++ b/include/boost/http/zlib.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +/** @file + ZLib compression and decompression library. + + This header includes all ZLib-related functionality including + deflate, inflate, error handling, and stream management. + + ZLib is a widely-used lossless data compression library that + implements the DEFLATE compression algorithm. It provides both + compression (deflate) and decompression (inflate) functionality + with support for raw, zlib, and gzip formats through the + windowBits parameter. + + @code + #include + #include + + // Create a datastore for services + boost::capy::datastore ctx; + + // Install compression and decompression services + auto& deflate_svc = boost::http::zlib::install_deflate_service(ctx); + auto& inflate_svc = boost::http::zlib::install_inflate_service(ctx); + @endcode +*/ + +#ifndef BOOST_HTTP_ZLIB_HPP +#define BOOST_HTTP_ZLIB_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/boost/http/zlib/compression_level.hpp b/include/boost/http/zlib/compression_level.hpp new file mode 100644 index 00000000..f9fa69e9 --- /dev/null +++ b/include/boost/http/zlib/compression_level.hpp @@ -0,0 +1,61 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_COMPRESSION_LEVEL_HPP +#define BOOST_HTTP_ZLIB_COMPRESSION_LEVEL_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Compression level constants. + + These values control the trade-off between compression + speed and compression ratio. Higher values produce better + compression but take more time. Level 0 disables compression + entirely, storing data uncompressed. Levels 1-9 provide + increasing compression, with 6 being the default compromise + between speed and ratio. + + @code + boost::http::zlib::stream st = {}; + auto& deflate_svc = boost::http::zlib::install_deflate_service(ctx); + + // Use best speed for time-critical operations + deflate_svc.init(st, boost::http::zlib::best_speed); + + // Use best compression for archival storage + deflate_svc.init(st, boost::http::zlib::best_compression); + + // Use default compression for balanced performance + deflate_svc.init(st, boost::http::zlib::default_compression); + @endcode +*/ +enum compression_level +{ + /** Use the default compression level. */ + default_compression = -1, + + /** No compression is performed. */ + no_compression = 0, + + /** Fastest compression speed with minimal compression. */ + best_speed = 1, + + /** Best compression ratio with slower speed. */ + best_compression = 9 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/compression_method.hpp b/include/boost/http/zlib/compression_method.hpp new file mode 100644 index 00000000..5686a8fe --- /dev/null +++ b/include/boost/http/zlib/compression_method.hpp @@ -0,0 +1,33 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_COMPRESSION_METHOD_HPP +#define BOOST_HTTP_ZLIB_COMPRESSION_METHOD_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Compression method constants. + + Specifies the compression algorithm to use. +*/ +enum compression_method +{ + /** The deflate compression method. */ + deflated = 8 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/compression_strategy.hpp b/include/boost/http/zlib/compression_strategy.hpp new file mode 100644 index 00000000..5e668889 --- /dev/null +++ b/include/boost/http/zlib/compression_strategy.hpp @@ -0,0 +1,67 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_COMPRESSION_STRATEGY_HPP +#define BOOST_HTTP_ZLIB_COMPRESSION_STRATEGY_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Compression strategy constants. + + These values tune the compression algorithm for specific + types of input data. The default strategy works well for + most data. Other strategies optimize for specific patterns: + filtered for data with small value differences, huffman_only + for pre-compressed data, rle for image data with many + repeated bytes, and fixed for fastest compression. + + @code + // Example: Using different strategies for different data types + boost::http::zlib::stream st = {}; + + // For PNG image data (many repeated bytes) + deflate_svc.init2(st, 6, boost::http::zlib::deflated, + 15, 8, boost::http::zlib::rle); + + // For small numeric differences (filtered data) + deflate_svc.init2(st, 6, boost::http::zlib::deflated, + 15, 8, boost::http::zlib::filtered); + + // For fastest speed with pre-compressed data + deflate_svc.init2(st, 1, boost::http::zlib::deflated, + 15, 8, boost::http::zlib::huffman_only); + @endcode +*/ +enum compression_strategy +{ + /** Use the default compression strategy. */ + default_strategy = 0, + + /** Strategy optimized for data with small values. */ + filtered = 1, + + /** Force Huffman encoding only (no string match). */ + huffman_only = 2, + + /** Limit match distances to one (run-length encoding). */ + rle = 3, + + /** Prevent use of dynamic Huffman codes. */ + fixed = 4 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/data_type.hpp b/include/boost/http/zlib/data_type.hpp new file mode 100644 index 00000000..1fadee81 --- /dev/null +++ b/include/boost/http/zlib/data_type.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_DATA_TYPE_HPP +#define BOOST_HTTP_ZLIB_DATA_TYPE_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Data type constants for the stream data_type field. + + These values represent the best guess about the type + of data being compressed or decompressed. +*/ +enum data_type +{ + /** Binary data. */ + binary = 0, + + /** Text data. */ + text = 1, + + /** ASCII text data (same as text). */ + ascii = 1, + + /** Data type is unknown. */ + unknown = 2 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/deflate.hpp b/include/boost/http/zlib/deflate.hpp new file mode 100644 index 00000000..4c6e5d47 --- /dev/null +++ b/include/boost/http/zlib/deflate.hpp @@ -0,0 +1,199 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_DEFLATE_HPP +#define BOOST_HTTP_ZLIB_DEFLATE_HPP + +#include +#include +#include + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Provides the ZLib compression API. + + This service interface exposes the ZLib deflate (compression) + functionality through a set of virtual functions. The deflate + algorithm compresses data by finding repeated byte sequences + and encoding them efficiently using a combination of LZ77 and + Huffman coding. + + The windowBits parameter in init2() controls the format: + - 8..15: zlib format with specified window size + - -8..-15: raw deflate format (no header/trailer) + - 16+windowBits: gzip format + + @code + // Example: Basic compression + boost::capy::datastore ctx; + auto& deflate_svc = boost::http::zlib::install_deflate_service(ctx); + + boost::http::zlib::stream st = {}; + std::vector input_data = get_data(); + std::vector output(input_data.size() * 2); + + st.zalloc = nullptr; + st.zfree = nullptr; + st.opaque = nullptr; + + deflate_svc.init(st, boost::http::zlib::default_compression); + + st.avail_in = input_data.size(); + st.next_in = input_data.data(); + st.avail_out = output.size(); + st.next_out = output.data(); + + deflate_svc.deflate(st, boost::http::zlib::finish); + output.resize(st.total_out); + + deflate_svc.deflate_end(st); + @endcode + + @code + // Example: Gzip compression with custom window size + boost::http::zlib::stream st = {}; + st.zalloc = nullptr; + st.zfree = nullptr; + + // Use gzip format (16 + 15 for max window) + deflate_svc.init2(st, + 6, // level + boost::http::zlib::deflated, // method + 16 + 15, // gzip format + 8, // memLevel + boost::http::zlib::default_strategy); // strategy + + // Compress data... + deflate_svc.deflate_end(st); + @endcode +*/ +struct BOOST_SYMBOL_VISIBLE + deflate_service +{ + /** Return the ZLib version string. */ + virtual char const* version() const noexcept = 0; + + /** Initialize deflate compression. + @param st The stream to initialize. + @param level The compression level. + @return Zero on success, or an error code. + */ + virtual int init(stream& st, int level) const = 0; + + /** Initialize deflate compression with extended parameters. + @param st The stream to initialize. + @param level The compression level. + @param method The compression method. + @param windowBits The base-2 logarithm of the window size. + @param memLevel Memory usage level (1-9). + @param strategy The compression strategy. + @return Zero on success, or an error code. + */ + virtual int init2(stream& st, int level, int method, + int windowBits, int memLevel, int strategy) const = 0; + + /** Set the compression dictionary. + @param st The stream. + @param dict Pointer to the dictionary data. + @param len Length of the dictionary. + @return Zero on success, or an error code. + */ + virtual int set_dict(stream& st, unsigned char const* dict, unsigned len) const = 0; + + /** Return the current compression dictionary. + @param st The stream. + @param dest Destination buffer for the dictionary. + @param len Pointer to variable receiving dictionary length. + @return Zero on success, or an error code. + */ + virtual int get_dict(stream& st, unsigned char* dest, unsigned* len) const = 0; + + /** Duplicate a deflate stream. + @param dest The destination stream. + @param src The source stream to duplicate. + @return Zero on success, or an error code. + */ + virtual int dup(stream& dest, stream& src) const = 0; + + /** Compress data in the stream. + @param st The stream containing data to compress. + @param flush The flush mode. + @return Status code indicating compression state. + */ + virtual int deflate(stream& st, int flush) const = 0; + + /** Release all resources held by the deflate stream. + @param st The stream to finalize. + @return Zero on success, or an error code. + */ + virtual int deflate_end(stream& st) const = 0; + + /** Reset the deflate stream state. + @param st The stream to reset. + @return Zero on success, or an error code. + */ + virtual int reset(stream& st) const = 0; + + /** Dynamically update compression parameters. + @param st The stream. + @param level The new compression level. + @param strategy The new compression strategy. + @return Zero on success, or an error code. + */ + virtual int params(stream& st, int level, int strategy) const = 0; + + /** Return an upper bound on compressed size. + @param st The stream. + @param sourceLen The length of source data. + @return Maximum possible compressed size. + */ + virtual std::size_t bound(stream& st, unsigned long sourceLen) const = 0; + + /** Return the number of pending output bytes. + @param st The stream. + @param pending Pointer to variable receiving pending byte count. + @param bits Pointer to variable receiving pending bit count. + @return Zero on success, or an error code. + */ + virtual int pending(stream& st, unsigned* pending, int* bits) const = 0; + + /** Insert bits into the compressed stream. + @param st The stream. + @param bits Number of bits to insert. + @param value The bit pattern to insert. + @return Zero on success, or an error code. + */ + virtual int prime(stream& st, int bits, int value) const = 0; + + /** Set the gzip header information. + @param st The stream. + @param header Pointer to gzip header structure. + @return Zero on success, or an error code. + */ + virtual int set_header(stream& st, void* header) const = 0; +}; + +/** Install the deflate service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed deflate service. +*/ +BOOST_HTTP_DECL +deflate_service& +install_deflate_service(capy::polystore& ctx); + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/error.hpp b/include/boost/http/zlib/error.hpp new file mode 100644 index 00000000..c31325cf --- /dev/null +++ b/include/boost/http/zlib/error.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_ERROR_HPP +#define BOOST_HTTP_ZLIB_ERROR_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Error codes returned from compression/decompression functions. + + Negative values are errors, positive values are used + for special but normal events. +*/ +enum class error +{ + ok = 0, + stream_end = 1, + need_dict = 2, + errno_ = -1, + stream_err = -2, + data_err = -3, + mem_err = -4, + buf_err = -5, + version_err = -6 +}; + +} // zlib +} // http +} // boost + +#include + +#endif diff --git a/include/boost/http/zlib/flush.hpp b/include/boost/http/zlib/flush.hpp new file mode 100644 index 00000000..51156840 --- /dev/null +++ b/include/boost/http/zlib/flush.hpp @@ -0,0 +1,77 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_FLUSH_HPP +#define BOOST_HTTP_ZLIB_FLUSH_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Flush method constants. + + These values control how and when compressed data is + flushed from internal buffers during compression operations. + + Use no_flush for maximum compression efficiency when more + input is available. Use sync_flush to force output to a + byte boundary, allowing partial decompression. Use finish + when no more input is available to complete compression + and write the trailer. + + @code + // Example: Streaming compression with periodic flushing + boost::http::zlib::stream st = {}; + // ... initialize stream ... + + while (has_more_data) + { + st.next_in = get_next_chunk(); + st.avail_in = chunk_size; + + // Flush at chunk boundaries for progressive decoding + int flush_mode = has_more_data ? + boost::http::zlib::sync_flush : + boost::http::zlib::finish; + + deflate_svc.deflate(st, flush_mode); + } + @endcode +*/ +enum flush +{ + /** No flushing, allow optimal compression. */ + no_flush = 0, + + /** Flush to byte boundary (deprecated). */ + partial_flush = 1, + + /** Flush to byte boundary for synchronization. */ + sync_flush = 2, + + /** Full flush, reset compression state. */ + full_flush = 3, + + /** Finish compression, emit trailer. */ + finish = 4, + + /** Flush current block to output. */ + block = 5, + + /** Flush up to end of previous block. */ + trees = 6 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/impl/error.hpp b/include/boost/http/zlib/impl/error.hpp new file mode 100644 index 00000000..212b633c --- /dev/null +++ b/include/boost/http/zlib/impl/error.hpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_IMPL_ERROR_HPP +#define BOOST_HTTP_ZLIB_IMPL_ERROR_HPP + +#include + +#include +#include + +namespace boost { + +namespace system { +template<> +struct is_error_code_enum< + ::boost::http::zlib::error> +{ + static bool const value = true; +}; +} // system + +namespace http { +namespace zlib { + +namespace detail { + +struct BOOST_SYMBOL_VISIBLE + error_cat_type + : system::error_category +{ + BOOST_HTTP_DECL const char* name( + ) const noexcept override; + BOOST_HTTP_DECL bool failed( + int) const noexcept override; + BOOST_HTTP_DECL std::string message( + int) const override; + BOOST_HTTP_DECL char const* message( + int, char*, std::size_t + ) const noexcept override; + BOOST_SYSTEM_CONSTEXPR error_cat_type() + : error_category(0x43fd42f819852b73) + { + } +}; + +BOOST_HTTP_DECL extern + error_cat_type error_cat; + +} // detail + +inline +BOOST_SYSTEM_CONSTEXPR +system::error_code +make_error_code( + error ev) noexcept +{ + return system::error_code{ + static_cast::type>(ev), + detail::error_cat}; +} + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/inflate.hpp b/include/boost/http/zlib/inflate.hpp new file mode 100644 index 00000000..69ff4067 --- /dev/null +++ b/include/boost/http/zlib/inflate.hpp @@ -0,0 +1,208 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_INFLATE_HPP +#define BOOST_HTTP_ZLIB_INFLATE_HPP + +#include +#include +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Provides the ZLib decompression API. + + This service interface exposes the ZLib inflate (decompression) + functionality through a set of virtual functions. The inflate + algorithm reverses the deflate compression, restoring the + original uncompressed data. + + The windowBits parameter in init2() controls format detection: + - 8..15: zlib format with specified window size + - -8..-15: raw deflate format (no header/trailer) + - 16+windowBits: gzip format only + - 32+windowBits: auto-detect zlib or gzip format + + @code + // Example: Basic decompression + boost::capy::datastore ctx; + auto& inflate_svc = boost::http::zlib::install_inflate_service(ctx); + + boost::http::zlib::stream st = {}; + std::vector compressed_data = get_compressed(); + std::vector output(1024 * 1024); // 1MB buffer + + st.zalloc = nullptr; + st.zfree = nullptr; + st.opaque = nullptr; + + inflate_svc.init(st); + + st.avail_in = compressed_data.size(); + st.next_in = compressed_data.data(); + st.avail_out = output.size(); + st.next_out = output.data(); + + inflate_svc.inflate(st, boost::http::zlib::finish); + output.resize(st.total_out); + + inflate_svc.inflate_end(st); + @endcode + + @code + // Example: Auto-detect gzip or zlib format + boost::http::zlib::stream st = {}; + st.zalloc = nullptr; + st.zfree = nullptr; + + // Auto-detect format (32 + 15 for max window) + inflate_svc.init2(st, 32 + 15); + + st.avail_in = compressed_data.size(); + st.next_in = compressed_data.data(); + st.avail_out = output.size(); + st.next_out = output.data(); + + int result = inflate_svc.inflate(st, boost::http::zlib::no_flush); + // Handle result... + + inflate_svc.inflate_end(st); + @endcode +*/ +struct BOOST_SYMBOL_VISIBLE + inflate_service +{ + /** Return the ZLib version string. */ + virtual char const* version() const noexcept = 0; + + /** Initialize inflate decompression. + @param st The stream to initialize. + @return Zero on success, or an error code. + */ + virtual int init(stream& st) const = 0; + + /** Initialize inflate decompression with extended parameters. + @param st The stream to initialize. + @param windowBits The base-2 logarithm of the window size. + @return Zero on success, or an error code. + */ + virtual int init2(stream& st, int windowBits) const = 0; + + /** Decompress data in the stream. + @param st The stream containing data to decompress. + @param flush The flush mode. + @return Status code indicating decompression state. + */ + virtual int inflate(stream& st, int flush) const = 0; + + /** Release all resources held by the inflate stream. + @param st The stream to finalize. + @return Zero on success, or an error code. + */ + virtual int inflate_end(stream& st) const = 0; + + /** Set the decompression dictionary. + @param st The stream. + @param dict Pointer to the dictionary data. + @param len Length of the dictionary. + @return Zero on success, or an error code. + */ + virtual int set_dict(stream& st, unsigned char const* dict, unsigned len) const = 0; + + /** Return the current decompression dictionary. + @param st The stream. + @param dest Destination buffer for the dictionary. + @param len Pointer to variable receiving dictionary length. + @return Zero on success, or an error code. + */ + virtual int get_dict(stream& st, unsigned char* dest, unsigned* len) const = 0; + + /** Synchronize the decompression state. + @param st The stream to synchronize. + @return Zero on success, or an error code. + */ + virtual int sync(stream& st) const = 0; + + /** Duplicate an inflate stream. + @param dest The destination stream. + @param src The source stream to duplicate. + @return Zero on success, or an error code. + */ + virtual int dup(stream& dest, stream& src) const = 0; + + /** Reset the inflate stream state. + @param st The stream to reset. + @return Zero on success, or an error code. + */ + virtual int reset(stream& st) const = 0; + + /** Reset the inflate stream state with new window size. + @param st The stream to reset. + @param windowBits The base-2 logarithm of the window size. + @return Zero on success, or an error code. + */ + virtual int reset2(stream& st, int windowBits) const = 0; + + /** Insert bits into the input stream. + @param st The stream. + @param bits Number of bits to insert. + @param value The bit pattern to insert. + @return Zero on success, or an error code. + */ + virtual int prime(stream& st, int bits, int value) const = 0; + + /** Return the current inflate mark. + @param st The stream. + @return The mark value, or -1 on error. + */ + virtual long mark(stream& st) const = 0; + + /** Return the gzip header information. + @param st The stream. + @param header Pointer to gzip header structure. + @return Zero on success, or an error code. + */ + virtual int get_header(stream& st, void* header) const = 0; + + /** Initialize backward inflate decompression. + @param st The stream to initialize. + @param windowBits The base-2 logarithm of the window size. + @param window Pointer to the window buffer. + @return Zero on success, or an error code. + */ + virtual int back_init(stream& st, int windowBits, unsigned char* window) const = 0; + + /** Release resources held by backward inflate stream. + @param st The stream to finalize. + @return Zero on success, or an error code. + */ + virtual int back_end(stream& st) const = 0; + + /** Return ZLib compile-time flags. + @return Bit flags indicating compile-time options. + */ + virtual unsigned long compile_flags() const = 0; +}; + +/** Install the inflate service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed inflate service. +*/ +BOOST_HTTP_DECL +inflate_service& +install_inflate_service(capy::polystore& ctx); + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/stream.hpp b/include/boost/http/zlib/stream.hpp new file mode 100644 index 00000000..bf8e1209 --- /dev/null +++ b/include/boost/http/zlib/stream.hpp @@ -0,0 +1,107 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_STREAM_HPP +#define BOOST_HTTP_ZLIB_STREAM_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** ZLib stream state structure. + + This structure maintains the state for compression and + decompression operations, including input/output buffers, + statistics, and internal state. Applications provide input + data through next_in/avail_in and receive output through + next_out/avail_out. The service updates these fields as + data is processed. + + Before use, initialize zalloc, zfree, and opaque. Set them + to nullptr to use the default allocator. The state field + is managed internally and should not be modified. + + @code + // Example: Initialize a stream for compression + boost::http::zlib::stream st = {}; + st.zalloc = nullptr; // Use default allocator + st.zfree = nullptr; + st.opaque = nullptr; + + // Set up input and output buffers + st.next_in = input_buffer; + st.avail_in = input_size; + st.next_out = output_buffer; + st.avail_out = output_size; + + // After deflate/inflate operations: + // - next_in and next_out are updated to point past processed data + // - avail_in and avail_out are decreased by bytes processed + // - total_in and total_out track cumulative totals + @endcode +*/ +struct stream +{ + /** Allocating function pointer type. */ + using alloc_func = void*(*)(void*, unsigned int, unsigned int); + + /** Deallocating function pointer type. */ + using free_func = void(*)(void*, void*); + + /** Pointer to next input byte. */ + unsigned char* next_in; + + /** Number of bytes available at next_in. */ + unsigned int avail_in; + + /** Total number of input bytes read so far. */ + unsigned long total_in; + + /** Pointer where next output byte will be placed. */ + unsigned char* next_out; + + /** Remaining free space at next_out. */ + unsigned int avail_out; + + /** Total number of bytes output so far. */ + unsigned long total_out; + + /** Last error message, NULL if no error. */ + char* msg; + + /** Internal state, not visible to applications. */ + void* state; + + /** Function used to allocate internal state. */ + alloc_func zalloc; + + /** Function used to deallocate internal state. */ + free_func zfree; + + /** Private data object passed to zalloc and zfree. */ + void* opaque; + + /** Best guess about data type (binary or text for deflate, decoding state for inflate). */ + int data_type; + + /** Adler-32 or CRC-32 value of the uncompressed data. */ + unsigned long adler; + + /** Reserved for future use. */ + unsigned long reserved; +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/src/brotli/error.cpp b/src/brotli/error.cpp new file mode 100644 index 00000000..9f5a3459 --- /dev/null +++ b/src/brotli/error.cpp @@ -0,0 +1,104 @@ +// +// Copyright (c) 2024 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include + +namespace boost { +namespace http { +namespace brotli { +namespace detail { + +const char* +error_cat_type:: +name() const noexcept +{ + return "boost.http.brotli"; +} + +bool +error_cat_type:: +failed(int ev) const noexcept +{ + return ev < 0; +} + +std::string +error_cat_type:: +message(int ev) const +{ + return message(ev, nullptr, 0); +} + +char const* +error_cat_type:: +message( + int ev, + char*, + std::size_t) const noexcept +{ + switch(static_cast(ev)) + { + + case error::no_error: return "no_error"; + case error::success: return "success"; + case error::needs_more_input: return "needs_more_input"; + case error::needs_more_output: return "needs_more_output"; + case error::format_exuberant_nibble: return "format_exuberant_nibble"; + case error::format_reserved: return "format_reserved"; + case error::format_exuberant_meta_nibbl: return "format_exuberant_meta_nibbl"; + case error::format_simple_huffman_alphabet: return "format_simple_huffman_alphabet"; + case error::format_simple_huffman_same: return "format_simple_huffman_same"; + case error::format_cl_space: return "format_cl_space"; + case error::format_huffman_space: return "format_huffman_space"; + case error::format_context_map_repea: return "format_context_map_repea"; + case error::format_block_length_1: return "format_block_length_1"; + case error::format_block_length_2: return "format_block_length_2"; + case error::format_transform: return "format_transform"; + case error::format_dictionary: return "format_dictionary"; + case error::format_window_bits: return "format_window_bits"; + case error::format_padding_1: return "format_padding_1"; + case error::format_padding_2: return "format_padding_2"; + case error::format_distance: return "format_distance"; + case error::compound_dictionary: return "compound_dictionary"; + case error::dictionary_not_set: return "dictionary_not_set"; + case error::invalid_arguments: return "invalid_arguments"; + case error::alloc_context_modes: return "alloc_context_modes"; + case error::alloc_tree_groups: return "alloc_tree_groups"; + case error::alloc_context_map: return "alloc_context_map"; + case error::alloc_ring_buffer_1: return "alloc_ring_buffer_1"; + case error::alloc_ring_buffer_2: return "alloc_ring_buffer_2"; + case error::alloc_block_type_trees: return "alloc_block_type_trees"; + case error::unreachable: return "unreachable"; + default: + return "unknown"; + } +} + +// msvc 14.0 has a bug that warns about inability +// to use constexpr construction here, even though +// there's no constexpr construction +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( push ) +# pragma warning( disable : 4592 ) +#endif + +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +constinit error_cat_type error_cat; +#else +error_cat_type error_cat; +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( pop ) +#endif + +} // detail +} // brotli +} // http +} // boost diff --git a/src/detail/zlib_filter_base.hpp b/src/detail/zlib_filter_base.hpp index 110fae2d..a6634016 100644 --- a/src/detail/zlib_filter_base.hpp +++ b/src/detail/zlib_filter_base.hpp @@ -15,8 +15,8 @@ #include "src/detail/filter.hpp" -#include -#include +#include +#include namespace boost { namespace http { @@ -35,7 +35,7 @@ class zlib_filter_base : public filter } protected: - capy::zlib::stream strm_; + http::zlib::stream strm_; static unsigned int diff --git a/src/parser.cpp b/src/parser.cpp index 4900637c..1cab1e53 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -20,10 +20,10 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include #include #include #include @@ -305,7 +305,7 @@ clamp( class zlib_filter : public detail::zlib_filter_base { - capy::zlib::inflate_service& svc_; + http::zlib::inflate_service& svc_; public: zlib_filter( @@ -313,11 +313,11 @@ class zlib_filter http::detail::workspace& ws, int window_bits) : zlib_filter_base(ws) - , svc_(ctx.get()) + , svc_(ctx.get()) { - system::error_code ec = static_cast( + system::error_code ec = static_cast( svc_.init2(strm_, window_bits)); - if(ec != capy::zlib::error::ok) + if(ec != http::zlib::error::ok) detail::throw_system_error(ec); } @@ -334,17 +334,17 @@ class zlib_filter strm_.next_in = static_cast(const_cast(in.data())); strm_.avail_in = saturate_cast(in.size()); - auto rs = static_cast( + auto rs = static_cast( svc_.inflate( strm_, - more ? capy::zlib::no_flush : capy::zlib::finish)); + more ? http::zlib::no_flush : http::zlib::finish)); results rv; rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out; rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in; - rv.finished = (rs == capy::zlib::error::stream_end); + rv.finished = (rs == http::zlib::error::stream_end); - if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err) + if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err) rv.ec = rs; return rv; @@ -354,14 +354,14 @@ class zlib_filter class brotli_filter : public detail::brotli_filter_base { - capy::brotli::decode_service& svc_; - capy::brotli::decoder_state* state_; + http::brotli::decode_service& svc_; + http::brotli::decoder_state* state_; public: brotli_filter( const capy::polystore& ctx, http::detail::workspace&) - : svc_(ctx.get()) + : svc_(ctx.get()) { // TODO: use custom allocator state_ = svc_.create_instance(nullptr, nullptr, nullptr); @@ -401,10 +401,10 @@ class brotli_filter rv.out_bytes = out.size() - available_out; rv.finished = svc_.is_finished(state_); - if(!more && rs == capy::brotli::decoder_result::needs_more_input) + if(!more && rs == http::brotli::decoder_result::needs_more_input) rv.ec = BOOST_HTTP_ERR(error::bad_payload); - if(rs == capy::brotli::decoder_result::error) + if(rs == http::brotli::decoder_result::error) rv.ec = BOOST_HTTP_ERR( svc_.get_error_code(state_)); diff --git a/src/serializer.cpp b/src/serializer.cpp index a384c69c..57355eb7 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -23,13 +23,13 @@ #include #include #include -#include +#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include @@ -90,7 +90,7 @@ write_chunk_header( class zlib_filter : public detail::zlib_filter_base { - capy::zlib::deflate_service& svc_; + http::zlib::deflate_service& svc_; public: zlib_filter( @@ -100,16 +100,16 @@ class zlib_filter int window_bits, int mem_level) : zlib_filter_base(ws) - , svc_(ctx.get()) + , svc_(ctx.get()) { - system::error_code ec = static_cast(svc_.init2( + system::error_code ec = static_cast(svc_.init2( strm_, comp_level, - capy::zlib::deflated, + http::zlib::deflated, window_bits, mem_level, - capy::zlib::default_strategy)); - if(ec != capy::zlib::error::ok) + http::zlib::default_strategy)); + if(ec != http::zlib::error::ok) detail::throw_system_error(ec); } @@ -135,17 +135,17 @@ class zlib_filter strm_.next_in = static_cast(const_cast(in.data())); strm_.avail_in = saturate_cast(in.size()); - auto rs = static_cast( + auto rs = static_cast( svc_.deflate( strm_, - more ? capy::zlib::no_flush : capy::zlib::finish)); + more ? http::zlib::no_flush : http::zlib::finish)); results rv; rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out; rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in; - rv.finished = (rs == capy::zlib::error::stream_end); + rv.finished = (rs == http::zlib::error::stream_end); - if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err) + if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err) rv.ec = rs; return rv; @@ -155,8 +155,8 @@ class zlib_filter class brotli_filter : public detail::brotli_filter_base { - capy::brotli::encode_service& svc_; - capy::brotli::encoder_state* state_; + http::brotli::encode_service& svc_; + http::brotli::encoder_state* state_; public: brotli_filter( @@ -164,13 +164,13 @@ class brotli_filter http::detail::workspace&, std::uint32_t comp_quality, std::uint32_t comp_window) - : svc_(ctx.get()) + : svc_(ctx.get()) { // TODO: use custom allocator state_ = svc_.create_instance(nullptr, nullptr, nullptr); if(!state_) detail::throw_bad_alloc(); - using encoder_parameter = capy::brotli::encoder_parameter; + using encoder_parameter = http::brotli::encoder_parameter; svc_.set_parameter(state_, encoder_parameter::quality, comp_quality); svc_.set_parameter(state_, encoder_parameter::lgwin, comp_window); } @@ -194,7 +194,7 @@ class brotli_filter auto available_out = out.size(); using encoder_operation = - capy::brotli::encoder_operation; + http::brotli::encoder_operation; bool rs = svc_.compress_stream( state_, diff --git a/src/zlib/error.cpp b/src/zlib/error.cpp new file mode 100644 index 00000000..d09599cf --- /dev/null +++ b/src/zlib/error.cpp @@ -0,0 +1,82 @@ +// +// Copyright (c) 2024 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include + +namespace boost { +namespace http { +namespace zlib { +namespace detail { + +const char* +error_cat_type:: +name() const noexcept +{ + return "boost.http.zlib"; +} + +bool +error_cat_type:: +failed(int ev) const noexcept +{ + return ev < 0; +} + +std::string +error_cat_type:: +message(int ev) const +{ + return message(ev, nullptr, 0); +} + +char const* +error_cat_type:: +message( + int ev, + char*, + std::size_t) const noexcept +{ + switch(static_cast(ev)) + { + case error::ok: return "Z_OK"; + case error::stream_end: return "Z_STREAM_END"; + case error::need_dict: return "Z_NEED_DICT"; + case error::errno_: return "Z_ERRNO"; + case error::stream_err: return "Z_STREAM_ERROR"; + case error::data_err: return "Z_DATA_ERROR"; + case error::mem_err: return "Z_MEM_ERROR"; + case error::buf_err: return "Z_BUF_ERROR"; + case error::version_err: return "Z_VERSION_ERROR"; + default: + return "unknown"; + } +} + +// msvc 14.0 has a bug that warns about inability +// to use constexpr construction here, even though +// there's no constexpr construction +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( push ) +# pragma warning( disable : 4592 ) +#endif + +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +constinit error_cat_type error_cat; +#else +error_cat_type error_cat; +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( pop ) +#endif + +} // detail +} // zlib +} // http +} // boost diff --git a/src_brotli/decode.cpp b/src_brotli/decode.cpp new file mode 100644 index 00000000..b2d16b40 --- /dev/null +++ b/src_brotli/decode.cpp @@ -0,0 +1,188 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +class decode_service_impl + : public decode_service +{ +public: + using key_type = decode_service; + + explicit + decode_service_impl( + capy::polystore&) noexcept + { + } + + ~decode_service_impl() + { + } + + bool + set_parameter( + decoder_state* state, + decoder_param param, + std::uint32_t value) const noexcept override + { + return BrotliDecoderSetParameter( + reinterpret_cast(state), + static_cast(param), + value); + } + + decoder_state* + create_instance(alloc_func alloc_func, free_func free_func, void* opaque) + const noexcept override + { + return reinterpret_cast( + BrotliDecoderCreateInstance( + alloc_func, + free_func, + opaque)); + } + + void + destroy_instance(decoder_state* state) const noexcept override + { + BrotliDecoderDestroyInstance( + reinterpret_cast(state)); + } + + decoder_result + decompress( + std::size_t encoded_size, + const std::uint8_t encoded_buffer[], + std::size_t* decoded_size, + std::uint8_t decoded_buffer[]) const noexcept override + { + return static_cast( + BrotliDecoderDecompress( + encoded_size, + encoded_buffer, + decoded_size, + decoded_buffer)); + } + + decoder_result + decompress_stream( + decoder_state* state, + std::size_t* available_in, + const std::uint8_t** next_in, + std::size_t* available_out, + std::uint8_t** next_out, + std::size_t* total_out) const noexcept override + { + return static_cast( + BrotliDecoderDecompressStream( + reinterpret_cast(state), + available_in, + next_in, + available_out, + next_out, + total_out)); + } + + bool + has_more_output(const decoder_state* state) const noexcept override + { + return BrotliDecoderHasMoreOutput( + reinterpret_cast(state)); + } + + const std::uint8_t* + take_output(decoder_state* state, std::size_t* size) const noexcept override + { + return BrotliDecoderTakeOutput( + reinterpret_cast(state), + size); + } + + bool + is_used(const decoder_state* state) const noexcept override + { + return BrotliDecoderIsUsed( + reinterpret_cast(state)); + } + + bool + is_finished(const decoder_state* state) const noexcept override + { + return BrotliDecoderIsFinished( + reinterpret_cast(state)); + } + + error + get_error_code(const decoder_state* state) const noexcept override + { + return static_cast( + BrotliDecoderGetErrorCode( + reinterpret_cast(state))); + } + + const char* + error_string(error c) const noexcept override + { + return BrotliDecoderErrorString( + static_cast(c)); + } + + std::uint32_t + version() const noexcept override + { + return BrotliDecoderVersion(); + } + +#if 0 + bool + attach_dictionary( + decoder_state* state, + shared_dictionary_type type, + std::size_t data_size, + const std::uint8_t data[]) const noexcept override + { + return BrotliDecoderAttachDictionary( + reinterpret_cast(state), + static_cast(type), + data_size, + data); + } + + void + set_metadata_callbacks( + decoder_state* state, + metadata_start_func start_func, + metadata_chunk_func chunk_func, + void* opaque) const noexcept override + { + BrotliDecoderSetMetadataCallbacks( + reinterpret_cast(state), + start_func, + chunk_func, + opaque); + } +#endif +}; + +decode_service& +install_decode_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // brotli +} // http +} // boost diff --git a/src_brotli/encode.cpp b/src_brotli/encode.cpp new file mode 100644 index 00000000..b0bedcde --- /dev/null +++ b/src_brotli/encode.cpp @@ -0,0 +1,207 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +class encode_service_impl + : public encode_service +{ +public: + using key_type = encode_service; + + explicit + encode_service_impl( + capy::polystore&) noexcept + { + } + + ~encode_service_impl() + { + } + + bool + set_parameter( + encoder_state* state, + encoder_parameter param, + std::uint32_t value) const noexcept override + { + return BrotliEncoderSetParameter( + reinterpret_cast(state), + static_cast(param), + value); + } + + encoder_state* + create_instance(alloc_func alloc_func, free_func free_func, void* opaque) + const noexcept override + { + return reinterpret_cast( + BrotliEncoderCreateInstance( + alloc_func, + free_func, + opaque)); + } + + void + destroy_instance(encoder_state* state) const noexcept override + { + BrotliEncoderDestroyInstance( + reinterpret_cast(state)); + } + + std::size_t + max_compressed_size(std::size_t input_size) const noexcept override + { + return BrotliEncoderMaxCompressedSize(input_size); + } + + bool + compress( + int quality, + int lgwin, + encoder_mode mode, + std::size_t input_size, + const std::uint8_t input_buffer[], + std::size_t* encoded_size, + std::uint8_t encoded_buffer[]) const noexcept override + { + return BrotliEncoderCompress( + quality, + lgwin, + static_cast(mode), + input_size, + input_buffer, + encoded_size, + encoded_buffer); + } + + bool + compress_stream( + encoder_state* state, + encoder_operation op, + std::size_t* available_in, + const std::uint8_t** next_in, + std::size_t* available_out, + std::uint8_t** next_out, + std::size_t* total_out) const noexcept override + { + return BrotliEncoderCompressStream( + reinterpret_cast(state), + static_cast(op), + available_in, + next_in, + available_out, + next_out, + total_out); + } + + bool + is_finished(encoder_state* state) const noexcept override + { + return BrotliEncoderIsFinished( + reinterpret_cast(state)); + } + + bool + has_more_output(encoder_state* state) const noexcept override + { + return BrotliEncoderHasMoreOutput( + reinterpret_cast(state)); + } + + const std::uint8_t* + take_output(encoder_state* state, std::size_t* size) const noexcept override + { + return BrotliEncoderTakeOutput( + reinterpret_cast(state), + size); + } + + std::uint32_t + version() const noexcept override + { + return BrotliEncoderVersion(); + } + +#if 0 + encoder_prepared_dictionary* + prepare_dictionary( + shared_dictionary_type type, + std::size_t data_size, + const std::uint8_t data[], + int quality, + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept override + { + return reinterpret_cast( + BrotliEncoderPrepareDictionary( + static_cast(type), + data_size, + data, + quality, + alloc_func, + free_func, + opaque)); + } + + void + destroy_prepared_dictionary( + encoder_prepared_dictionary* dictionary) const noexcept override + { + BrotliEncoderDestroyPreparedDictionary( + reinterpret_cast(dictionary)); + } + + bool + attach_prepared_dictionary( + encoder_state* state, + const encoder_prepared_dictionary* dictionary) const noexcept override + { + return BrotliEncoderAttachPreparedDictionary( + reinterpret_cast(state), + reinterpret_cast(dictionary)); + } + + std::size_t + estimate_peak_memory_usage(int quality, int lgwin, std::size_t input_size) + const noexcept override + { + return BrotliEncoderEstimatePeakMemoryUsage( + quality, + lgwin, + input_size); + } + + std::size_t + get_prepared_dictionary_size( + const encoder_prepared_dictionary* dictionary) const noexcept override + { + return BrotliEncoderGetPreparedDictionarySize( + reinterpret_cast(dictionary)); + } +#endif +}; + +encode_service& +install_encode_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // brotli +} // http +} // boost diff --git a/src_brotli/shared_dictionary.cpp b/src_brotli/shared_dictionary.cpp new file mode 100644 index 00000000..812a8434 --- /dev/null +++ b/src_brotli/shared_dictionary.cpp @@ -0,0 +1,82 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#if 0 +#include +#endif + +namespace boost { +namespace http { +namespace brotli { + +class shared_dictionary_service_impl + : public shared_dictionary_service +{ +public: + using key_type = shared_dictionary_service; + + explicit + shared_dictionary_service_impl( + capy::polystore&) noexcept + { + } + + ~shared_dictionary_service_impl() + { + } +#if 0 + shared_dictionary* + create_instance( + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept override + { + return reinterpret_cast( + BrotliSharedDictionaryCreateInstance( + alloc_func, + free_func, + opaque)); + } + + void + destroy_instance( + shared_dictionary* dict) const noexcept override + { + BrotliSharedDictionaryDestroyInstance( + reinterpret_cast(dict)); + } + + bool + attach( + shared_dictionary* dict, + shared_dictionary_type type, + std::size_t data_size, + const uint8_t data[]) const noexcept override + { + return BrotliSharedDictionaryAttach( + reinterpret_cast(dict), + static_cast(type), + data_size, + data); + } +#endif +}; + +shared_dictionary_service& +install_shared_dictionary_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // brotli +} // http +} // boost diff --git a/src_zlib/deflate.cpp b/src_zlib/deflate.cpp new file mode 100644 index 00000000..44147517 --- /dev/null +++ b/src_zlib/deflate.cpp @@ -0,0 +1,188 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include "stream_cast.hpp" + +#include + +#include + +namespace boost { +namespace http { +namespace zlib { + +BOOST_CORE_STATIC_ASSERT(sizeof(stream) == sizeof(z_stream_s)); +BOOST_CORE_STATIC_ASSERT(is_layout_identical()); + +//------------------------------------------------ + +class deflate_service_impl + : public deflate_service +{ +public: + using key_type = deflate_service; + + explicit + deflate_service_impl( + capy::polystore&) noexcept + { + } + + ~deflate_service_impl() + { + } + + char const* + version() const noexcept override + { + return zlibVersion(); + } + + int + init( + stream& st, + int level) const override + { + stream_cast sc(st); + return deflateInit(sc.get(), level); + } + + int + init2( + stream& st, + int level, + int method, + int windowBits, + int memLevel, + int strategy) const override + { + stream_cast sc(st); + return deflateInit2(sc.get(), + level, method, windowBits, + memLevel, strategy); + } + + int + set_dict( + stream& st, + unsigned char const* dict, + unsigned len) const override + { + stream_cast sc(st); + return deflateSetDictionary(sc.get(), dict, len); + } + + int + get_dict( + stream& st, + unsigned char* dest, + unsigned* len) const override + { + stream_cast sc(st); + return deflateGetDictionary(sc.get(), dest, len); + } + + int + dup( + stream& dest, + stream& src) const override + { + stream_cast sc0(dest); + stream_cast sc1(src); + return deflateCopy(sc0.get(), sc1.get()); + } + + int + deflate( + stream& st, + int flush) const override + { + stream_cast sc(st); + return ::deflate(sc.get(), flush); + } + + int + deflate_end( + stream& st) const override + { + stream_cast sc(st); + return deflateEnd(sc.get()); + } + + int + reset( + stream& st) const override + { + stream_cast sc(st); + return deflateReset(sc.get()); + } + + int + params( + stream& st, + int level, + int strategy) const override + { + stream_cast sc(st); + return deflateParams(sc.get(), level, strategy); + } + + std::size_t + bound( + stream& st, + unsigned long sourceLen) const override + { + stream_cast sc(st); + return deflateBound(sc.get(), sourceLen); + } + + int + pending( + stream& st, + unsigned* pending, + int* bits) const override + { + stream_cast sc(st); + return deflatePending(sc.get(), pending, bits); + } + + int + prime( + stream& st, + int bits, + int value) const override + { + stream_cast sc(st); + return deflatePrime(sc.get(), bits, value); + } + + int + set_header( + stream& st, + void* header) const override + { + stream_cast sc(st); + return deflateSetHeader(sc.get(), + reinterpret_cast(header)); + } +}; + +deflate_service& +install_deflate_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // zlib +} // http +} // boost diff --git a/src_zlib/inflate.cpp b/src_zlib/inflate.cpp new file mode 100644 index 00000000..2553716e --- /dev/null +++ b/src_zlib/inflate.cpp @@ -0,0 +1,201 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include "stream_cast.hpp" + +#include + +#include + +namespace boost { +namespace http { +namespace zlib { + +BOOST_CORE_STATIC_ASSERT(sizeof(stream) == sizeof(z_stream_s)); +BOOST_CORE_STATIC_ASSERT(is_layout_identical()); + +//------------------------------------------------ + +class inflate_service_impl + : public inflate_service +{ +public: + using key_type = inflate_service; + + explicit + inflate_service_impl( + capy::polystore&) noexcept + { + } + + ~inflate_service_impl() + { + } + + char const* + version() const noexcept override + { + return zlibVersion(); + } + + int + init( + stream& st) const override + { + stream_cast sc(st); + return inflateInit(sc.get()); + } + + int + init2( + stream& st, + int windowBits) const override + { + stream_cast sc(st); + return inflateInit2(sc.get(), windowBits); + } + + int + inflate( + stream& st, + int flush) const override + { + stream_cast sc(st); + return ::inflate(sc.get(), flush); + } + + int + inflate_end( + stream& st) const override + { + stream_cast sc(st); + return inflateEnd(sc.get()); + } + + int + set_dict( + stream& st, + unsigned char const* dict, + unsigned len) const override + { + stream_cast sc(st); + return inflateSetDictionary(sc.get(), dict, len); + } + + int + get_dict( + stream& st, + unsigned char* dest, + unsigned* len) const override + { + stream_cast sc(st); + return inflateGetDictionary(sc.get(), dest, len); + } + + int + sync( + stream& st) const override + { + stream_cast sc(st); + return inflateSync(sc.get()); + } + + int + dup( + stream& dest, + stream& src) const override + { + stream_cast sc0(dest); + stream_cast sc1(src); + return inflateCopy(sc0.get(), sc1.get()); + } + + int + reset( + stream& st) const override + { + stream_cast sc(st); + return inflateReset(sc.get()); + } + + int + reset2( + stream& st, + int windowBits) const override + { + stream_cast sc(st); + return inflateReset2(sc.get(), windowBits); + } + + int + prime( + stream& st, + int bits, + int value) const override + { + stream_cast sc(st); + return inflatePrime(sc.get(), bits, value); + } + + long + mark( + stream& st) const override + { + stream_cast sc(st); + return inflateMark(sc.get()); + } + + int + get_header( + stream& st, + void* header) const override + { + stream_cast sc(st); + return inflateGetHeader(sc.get(), + reinterpret_cast(header)); + } + + int + back_init( + stream& st, + int windowBits, + unsigned char* window) const override + { + stream_cast sc(st); + return inflateBackInit(sc.get(), windowBits, window); + } + + int + back_end( + stream& st) const override + { + stream_cast sc(st); + return inflateBackEnd(sc.get()); + } + + unsigned long + compile_flags() const override + { + return zlibCompileFlags(); + } +}; + +inflate_service& +install_inflate_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // zlib +} // http +} // boost diff --git a/src_zlib/stream_cast.hpp b/src_zlib/stream_cast.hpp new file mode 100644 index 00000000..5772f4e1 --- /dev/null +++ b/src_zlib/stream_cast.hpp @@ -0,0 +1,145 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef SRC_HTTP_ZLIB_STREAM_CAST_HPP +#define SRC_HTTP_ZLIB_STREAM_CAST_HPP + +#include + +#include + +#include + +namespace boost { +namespace http { +namespace zlib { + +//------------------------------------------------ + +#define SAME_FIELD(T1,T2,M) \ + offsetof(T1,M)==offsetof(T2,M) && \ + sizeof(decltype(T1::M)) == sizeof(decltype(T2::M)) + +template +constexpr +bool +is_layout_identical() +{ + return + sizeof(T1) == sizeof(T2) && + SAME_FIELD(T1, T2, next_in) && + SAME_FIELD(T1, T2, avail_in) && + SAME_FIELD(T1, T2, total_in) && + SAME_FIELD(T1, T2, next_out) && + SAME_FIELD(T1, T2, avail_out) && + SAME_FIELD(T1, T2, total_out) && + SAME_FIELD(T1, T2, msg) && + SAME_FIELD(T1, T2, state) && + SAME_FIELD(T1, T2, zalloc) && + SAME_FIELD(T1, T2, zfree) && + SAME_FIELD(T1, T2, opaque) && + SAME_FIELD(T1, T2, data_type) && + SAME_FIELD(T1, T2, adler) && + SAME_FIELD(T1, T2, reserved) + ; +} + +//------------------------------------------------ + +template()> +struct stream_cast_impl +{ + explicit + stream_cast_impl( + stream& st) + : pzs_(reinterpret_cast(&st)) + , st_(st) + { + zs_.next_in = st_.next_in; + zs_.avail_in = st_.avail_in; + zs_.total_in = st_.total_in; + zs_.next_out = st_.next_out; + zs_.avail_out = st_.avail_out; + zs_.total_out = st_.total_out; + zs_.msg = nullptr; + zs_.state = reinterpret_cast< + internal_state*>(st_.state); + zs_.zalloc = st_.zalloc; + zs_.zfree = st_.zfree; + zs_.opaque = st_.opaque; + zs_.data_type = st_.data_type; + zs_.adler = st_.adler; + zs_.reserved = st_.reserved; + } + + ~stream_cast_impl() + { + st_.next_in = zs_.next_in; + st_.avail_in = zs_.avail_in; + st_.total_in = zs_.total_in; + st_.next_out = zs_.next_out; + st_.avail_out = zs_.avail_out; + st_.total_out = zs_.total_out; + st_.msg = zs_.msg; + st_.state = zs_.state; + st_.zalloc = zs_.zalloc; + st_.zfree = zs_.zfree; + st_.opaque = zs_.opaque; + st_.data_type = zs_.data_type; + st_.adler = zs_.adler; + st_.reserved = zs_.reserved; + } + + z_stream_s* + get() noexcept + { + return pzs_; + } + +private: + z_stream_s* pzs_ = nullptr; + stream& st_; + z_stream_s zs_; +}; + +//------------------------------------------------ + +template<> +struct stream_cast_impl +{ + explicit + stream_cast_impl( + stream& st) + // VFALCO A pinch of undefined behavior here + : pzs_(reinterpret_cast(&st)) + { + + } + + z_stream_s* + get() noexcept + { + return pzs_; + } + +private: + z_stream_s* pzs_; +}; + +//------------------------------------------------ + +using stream_cast = stream_cast_impl<>; + +} // zlib +} // http +} // boost + +#endif diff --git a/test/limits/CMakeLists.txt b/test/limits/CMakeLists.txt index 806ace39..94cccb29 100644 --- a/test/limits/CMakeLists.txt +++ b/test/limits/CMakeLists.txt @@ -21,6 +21,7 @@ target_compile_definitions(boost_http_limits PRIVATE BOOST_HTTP_TEST_LIMITS BOOST_HTTP_NO_LIB BOOST_HTTP_STATIC_LINK + BOOST_COROSIO_NO_LIB ) target_link_libraries(boost_http_limits PRIVATE Boost::align @@ -32,6 +33,8 @@ target_link_libraries(boost_http_limits PRIVATE Boost::throw_exception Boost::url Boost::utility) +# Add corosio headers without linking (header-only usage) +target_include_directories(boost_http_limits PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../../corosio/include") target_link_libraries(boost_http_limits INTERFACE Boost::http) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index f49361e0..8de1aeb4 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -26,12 +26,12 @@ target_link_libraries( Boost::http Boost::filesystem) -if (TARGET Boost::capy_zlib) - target_link_libraries(boost_http_tests PRIVATE Boost::capy_zlib) +if (TARGET Boost::http_zlib) + target_link_libraries(boost_http_tests PRIVATE Boost::http_zlib) endif () -if (TARGET Boost::capy_brotli) - target_link_libraries(boost_http_tests PRIVATE Boost::capy_brotli) +if (TARGET Boost::http_brotli) + target_link_libraries(boost_http_tests PRIVATE Boost::http_brotli) endif () # Register individual tests with CTest diff --git a/test/unit/Jamfile b/test/unit/Jamfile index 9f31c989..1d4d66ff 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -17,8 +17,8 @@ project $(c11-requires) /boost/http//boost_http /boost/url//boost_url - [ ac.check-library /boost/capy//boost_capy_zlib : /boost/capy//boost_capy_zlib : ] - [ ac.check-library /boost/capy//boost_capy_brotli : /boost/capy//boost_capy_brotli : ] + [ ac.check-library /boost/http//boost_http_zlib : /boost/http//boost_http_zlib : ] + [ ac.check-library /boost/http//boost_http_brotli : /boost/http//boost_http_brotli : ] ../../../url/extra/test_suite/test_main.cpp ../../../url/extra/test_suite/test_suite.cpp ./test_helpers.cpp diff --git a/test/unit/brotli.cpp b/test/unit/brotli.cpp new file mode 100644 index 00000000..fa30c0cb --- /dev/null +++ b/test/unit/brotli.cpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include "test_helpers.hpp" + +namespace boost { +namespace http { + +struct brotli_test +{ + void + test_error_code() + { + // TODO + boost::system::error_code ec{ brotli::error::no_error }; + } + + void + test_decode() + { + // TODO + capy::polystore ctx; + brotli::install_decode_service(ctx); + } + + void + test_encode() + { + // TODO + capy::polystore ctx; + brotli::install_encode_service(ctx); + } + + void + run() + { + test_error_code(); + #ifdef BOOST_HTTP_HAS_BROTLI + test_decode(); + test_encode(); + #endif + } +}; + +TEST_SUITE(brotli_test, "boost.http.brotli"); + +} // namespace http +} // namespace boost diff --git a/test/unit/brotli/brotli_error.cpp b/test/unit/brotli/brotli_error.cpp new file mode 100644 index 00000000..07bd1b9f --- /dev/null +++ b/test/unit/brotli/brotli_error.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/brotli/decode.cpp b/test/unit/brotli/decode.cpp new file mode 100644 index 00000000..90befaf8 --- /dev/null +++ b/test/unit/brotli/decode.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/brotli/encode.cpp b/test/unit/brotli/encode.cpp new file mode 100644 index 00000000..72662299 --- /dev/null +++ b/test/unit/brotli/encode.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/brotli/shared_dictionary.cpp b/test/unit/brotli/shared_dictionary.cpp new file mode 100644 index 00000000..2cae9966 --- /dev/null +++ b/test/unit/brotli/shared_dictionary.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/brotli/types.cpp b/test/unit/brotli/types.cpp new file mode 100644 index 00000000..770edadc --- /dev/null +++ b/test/unit/brotli/types.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/compression.cpp b/test/unit/compression.cpp index cf89c902..65815367 100644 --- a/test/unit/compression.cpp +++ b/test/unit/compression.cpp @@ -18,9 +18,9 @@ #include #include #include -#include +#include #include -#include +#include #include "test_helpers.hpp" @@ -67,7 +67,7 @@ struct zlib_test if(encoding == "deflate" || encoding == "gzip") { - namespace zlib = capy::zlib; + namespace zlib = http::zlib; auto& svc = ctx.get(); zlib::stream zs{}; @@ -103,7 +103,7 @@ struct zlib_test } else if(encoding == "br") { - namespace brotli = capy::brotli; + namespace brotli = http::brotli; auto& svc = ctx.get(); brotli::encoder_state* state = @@ -155,7 +155,7 @@ struct zlib_test { if(encoding == "deflate" || encoding == "gzip") { - namespace zlib = capy::zlib; + namespace zlib = http::zlib; auto& svc = ctx.get(); zlib::stream zs{}; @@ -194,7 +194,7 @@ struct zlib_test } else if(encoding == "br") { - namespace brotli = capy::brotli; + namespace brotli = http::brotli; auto& svc = ctx.get(); brotli::decoder_state* state = @@ -383,18 +383,18 @@ struct zlib_test std::vector encodings; serializer::config cfg; - #ifdef BOOST_CAPY_HAS_ZLIB + #ifdef BOOST_HTTP_HAS_ZLIB cfg.apply_deflate_encoder = true; cfg.apply_gzip_encoder = true; - capy::zlib::install_deflate_service(ctx); - capy::zlib::install_inflate_service(ctx); + http::zlib::install_deflate_service(ctx); + http::zlib::install_inflate_service(ctx); encodings.push_back("gzip"); encodings.push_back("deflate"); #endif - #ifdef BOOST_CAPY_HAS_BROTLI + #ifdef BOOST_HTTP_HAS_BROTLI cfg.apply_brotli_encoder = true; - capy::brotli::install_encode_service(ctx); - capy::brotli::install_decode_service(ctx); + http::brotli::install_encode_service(ctx); + http::brotli::install_decode_service(ctx); encodings.push_back("br"); #endif @@ -619,18 +619,18 @@ struct zlib_test std::vector encodings; response_parser::config cfg; - #ifdef BOOST_CAPY_HAS_ZLIB + #ifdef BOOST_HTTP_HAS_ZLIB cfg.apply_deflate_decoder = true; cfg.apply_gzip_decoder = true; - capy::zlib::install_deflate_service(ctx); - capy::zlib::install_inflate_service(ctx); + http::zlib::install_deflate_service(ctx); + http::zlib::install_inflate_service(ctx); encodings.push_back("gzip"); encodings.push_back("deflate"); #endif - #ifdef BOOST_CAPY_HAS_BROTLI + #ifdef BOOST_HTTP_HAS_BROTLI cfg.apply_brotli_decoder = true; - capy::brotli::install_encode_service(ctx); - capy::brotli::install_decode_service(ctx); + http::brotli::install_encode_service(ctx); + http::brotli::install_decode_service(ctx); encodings.push_back("br"); #endif diff --git a/test/unit/zlib.cpp b/test/unit/zlib.cpp new file mode 100644 index 00000000..62881f4a --- /dev/null +++ b/test/unit/zlib.cpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include "test_helpers.hpp" + +namespace boost { +namespace http { + +struct zlib_test +{ + void + test_error_code() + { + // TODO + boost::system::error_code ec{ zlib::error::buf_err }; + } + + void + test_deflate() + { + // TODO + capy::polystore ctx; + zlib::install_deflate_service(ctx); + } + + void + test_inflate() + { + // TODO + capy::polystore ctx; + zlib::install_inflate_service(ctx); + } + + void + run() + { + test_error_code(); + #ifdef BOOST_HTTP_HAS_ZLIB + test_deflate(); + test_inflate(); + #endif + } +}; + +TEST_SUITE(zlib_test, "boost.http.zlib"); + +} // namespace http +} // namespace boost diff --git a/test/unit/zlib/compression_level.cpp b/test/unit/zlib/compression_level.cpp new file mode 100644 index 00000000..461f0ab6 --- /dev/null +++ b/test/unit/zlib/compression_level.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/compression_method.cpp b/test/unit/zlib/compression_method.cpp new file mode 100644 index 00000000..a9250889 --- /dev/null +++ b/test/unit/zlib/compression_method.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/compression_strategy.cpp b/test/unit/zlib/compression_strategy.cpp new file mode 100644 index 00000000..1e7daf52 --- /dev/null +++ b/test/unit/zlib/compression_strategy.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/data_type.cpp b/test/unit/zlib/data_type.cpp new file mode 100644 index 00000000..708788a9 --- /dev/null +++ b/test/unit/zlib/data_type.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/deflate.cpp b/test/unit/zlib/deflate.cpp new file mode 100644 index 00000000..87b82f24 --- /dev/null +++ b/test/unit/zlib/deflate.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/flush.cpp b/test/unit/zlib/flush.cpp new file mode 100644 index 00000000..12611fca --- /dev/null +++ b/test/unit/zlib/flush.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/inflate.cpp b/test/unit/zlib/inflate.cpp new file mode 100644 index 00000000..015001d7 --- /dev/null +++ b/test/unit/zlib/inflate.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/stream.cpp b/test/unit/zlib/stream.cpp new file mode 100644 index 00000000..51162c43 --- /dev/null +++ b/test/unit/zlib/stream.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/zlib_error.cpp b/test/unit/zlib/zlib_error.cpp new file mode 100644 index 00000000..ed3f8867 --- /dev/null +++ b/test/unit/zlib/zlib_error.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include