├── .appveyor.yml ├── .cirrus.yml ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bin ├── CMakeLists.txt ├── encode-int.c ├── fuzz-decode.c ├── interop-decode.c └── interop-encode.c ├── deps └── xxhash │ ├── xxhash.c │ └── xxhash.h ├── fuzz ├── decode │ ├── a │ │ ├── README │ │ ├── preamble │ │ └── test-cases │ │ │ ├── id_000000,sig_06,src_000390,op_havoc,rep_4 │ │ │ ├── id_000000,sig_06,src_000579,op_flip1,pos_14 │ │ │ ├── id_000000,src_000000,op_flip2,pos_12 │ │ │ ├── id_000001,sig_11,src_000579,op_havoc,rep_4 │ │ │ ├── id_000002,sig_11,src_000481,op_int16,pos_15,val_-1 │ │ │ ├── id_000002,src_000000,op_havoc,rep_8 │ │ │ ├── id_000006,src_000285,op_flip2,pos_14 │ │ │ ├── id_000008,src_000285,op_flip2,pos_20 │ │ │ ├── id_000010,src_000306,op_flip2,pos_75 │ │ │ ├── id_000011,src_000344,op_havoc,rep_2 │ │ │ └── id_000014,src_000366,op_flip2,pos_28 │ ├── b │ │ ├── README │ │ ├── preamble │ │ └── test-cases │ │ │ └── seed │ ├── c │ │ ├── setup.sh │ │ └── test-cases │ │ │ └── fb-req.qif.proxygen.out.256.100.0-chopped │ └── d │ │ ├── preamble │ │ ├── setup.sh │ │ └── test-cases │ │ └── fb-resp.minhq.256.128.0.ack └── input │ └── 256.100.1 │ ├── fb-req.out.256.100.1 │ ├── fb-resp.out.256.100.1 │ └── netbsd.out.256.100.1 ├── huff-tables.h ├── ls-qpack-config.cmake.in ├── lsqpack.c ├── lsqpack.h ├── lsqpack.pc.in ├── lsxpack_header.h ├── test ├── CMakeLists.txt ├── lsqpack-test.h ├── qifs │ ├── fb-req.qif │ ├── fb-resp.qif │ ├── long-codes.qif │ └── netbsd.qif ├── run-qif.pl ├── run-scenario.sh ├── scenarios │ ├── 0.95-reset.sce │ ├── cancel-stream.sce │ ├── drain-2.sce │ ├── drain.sce │ ├── end-dst-2.sce │ ├── end-dst.sce │ ├── incl-name.sce │ ├── multi-byte-int-dyn-ref-1.sce │ ├── multi-byte-int-dyn-ref-2.sce │ ├── post-base-1.sce │ ├── post-base-2.sce │ ├── post-base-nr.sce │ └── set-max-cap.sce ├── test_circ_list.c ├── test_dec_crash_case.c ├── test_dyn_table_cap_mismatch.c ├── test_enc_str.c ├── test_huff_dec.c ├── test_int.c ├── test_qpack.c ├── test_read_enc_stream.c └── testdata │ ├── encoder_stream │ └── response ├── tools ├── gen-enums.pl ├── har2qif.pl ├── randomize-cookies.pl └── sort-qif.pl └── wincompat └── sys └── queue.h /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | 3 | image: Visual Studio 2017 4 | 5 | cache: 6 | - c:\tools\vcpkg\installed 7 | 8 | environment: 9 | matrix: 10 | - job_name: "Internal xxhash" 11 | LSQPACK_XXH: ON 12 | XXH_CMAKE: UNUSED 13 | - job_name: "External xxhash (CMake)" 14 | LSQPACK_XXH: OFF 15 | XXH_CMAKE: REQUIRE 16 | - job_name: "External xxhash (pkgconfig)" 17 | LSQPACK_XXH: OFF 18 | XXH_CMAKE: DISABLE 19 | 20 | before_build: 21 | - git -C c:\tools\vcpkg\ fetch 22 | - git -C c:\tools\vcpkg\ checkout 2024.08.23 23 | - c:\tools\vcpkg\bootstrap-vcpkg.bat 24 | - vcpkg x-set-installed getopt:x64-windows xxhash:x64-windows pkgconf:x64-windows 25 | - cmake . 26 | -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake 27 | -DVCPKG_INSTALLED_DIR=c:/tools/vcpkg/installed 28 | -DVCPKG_TARGET_TRIPLET=x64-windows 29 | -DPKG_CONFIG_EXECUTABLE=c:/tools/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe 30 | -DCMAKE_BUILD_TYPE=Debug 31 | -DCMAKE_GENERATOR_PLATFORM=x64 32 | -DLSQPACK_TESTS=ON 33 | -DLSQPACK_XXH=%LSQPACK_XXH% 34 | -DCMAKE_%XXH_CMAKE%_FIND_PACKAGE_xxHash=ON 35 | -DGETOPT_INCLUDE_DIR=c:/tools/vcpkg/installed/x64-windows/include 36 | -DGETOPT_LIB=c:/tools/vcpkg/installed/x64-windows/lib/getopt.lib 37 | 38 | build_script: 39 | - cmake --build . 40 | 41 | before_test: 42 | - set PATH=%PATH%;c:\projects\ls-qpack\bin\Debug 43 | 44 | test_script: 45 | - dir 46 | - msbuild RUN_TESTS.vcxproj 47 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | freebsd_instance: 2 | image: freebsd-13-3-release-amd64 3 | 4 | task: 5 | install_script: pkg install -y cmake bash perl5 p5-PkgConfig-LibPkgConf devel/xxhash 6 | script: cmake -DLSQPACK_TESTS=ON -DLSQPACK_XXH=OFF . && make && make test 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: false 3 | addons: 4 | apt: 5 | matrix: 6 | include: 7 | - name: Linux (gcc-8) 8 | os: linux 9 | dist: xenial 10 | compiler: gcc-8 11 | addons: 12 | apt: 13 | sources: 14 | - ubuntu-toolchain-r-test 15 | packages: g++-8 16 | - name: Linux (clang) 17 | os: linux 18 | dist: xenial 19 | compiler: clang 20 | - name: macOS (xcode) 21 | os: osx 22 | - name: macOS (xcode 10.1) 23 | os: osx 24 | image: xcode10.1 25 | before_install: 26 | - $CC --version 27 | - cmake --version 28 | before_script: 29 | - cmake -DLSQPACK_TESTS=ON . 30 | script: 31 | - make 32 | - make test 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following variable can be defined on the command line: 2 | # 3 | # BUILD_SHARED_LIBS 4 | # 5 | # The following environment variables will be taken into account when running 6 | # cmake for the first time: 7 | # 8 | # CFLAGS 9 | # LDFLAGS 10 | 11 | cmake_minimum_required(VERSION 3.1) 12 | project(ls-qpack 13 | LANGUAGES C 14 | HOMEPAGE_URL "https://github.com/litespeedtech/ls-qpack" 15 | DESCRIPTION "QPACK compression library for use with HTTP/3" 16 | VERSION 2.6.1) 17 | 18 | option(LSQPACK_TESTS "Build tests") 19 | option(LSQPACK_BIN "Build binaries" ON) 20 | option(LSQPACK_XXH "Include XXH" ON) 21 | option(BUILD_SHARED_LIBS OFF) 22 | 23 | # Use `cmake -DBUILD_SHARED_LIBS=OFF` to build a static library. 24 | add_library(ls-qpack "") 25 | add_library(ls-qpack::ls-qpack ALIAS ls-qpack) 26 | set_target_properties(ls-qpack PROPERTIES 27 | VERSION ${PROJECT_VERSION} 28 | SOVERSION ${PROJECT_VERSION_MAJOR}) 29 | target_include_directories(ls-qpack PUBLIC 30 | $ 31 | $ 32 | ) 33 | target_sources(ls-qpack PRIVATE lsqpack.c) 34 | 35 | if(LSQPACK_XXH) 36 | target_include_directories(ls-qpack PRIVATE deps/xxhash/) 37 | target_sources(ls-qpack PRIVATE deps/xxhash/xxhash.c) 38 | set(LSQPACK_DEPENDS "") 39 | else() 40 | find_package(xxHash CONFIG) 41 | if(NOT xxHash_FOUND) 42 | find_package(PkgConfig REQUIRED) 43 | pkg_check_modules(LS_QPACK_XXH REQUIRED IMPORTED_TARGET libxxhash GLOBAL) 44 | add_library(xxHash::xxhash ALIAS PkgConfig::LS_QPACK_XXH) 45 | endif() 46 | target_link_libraries(ls-qpack PRIVATE xxHash::xxhash) 47 | set(LSQPACK_DEPENDS "libxxhash") 48 | endif() 49 | 50 | if(WIN32 OR EMSCRIPTEN) 51 | target_include_directories(ls-qpack PUBLIC 52 | $ 53 | $ 54 | ) 55 | endif() 56 | 57 | if(MSVC) 58 | target_compile_options(ls-qpack PRIVATE 59 | /Wall 60 | /wd4100 # unreffed parameter 61 | /wd4200 # zero-sized array 62 | # Apparently this C99 construct is not supported properly by VS: 63 | # https://stackoverflow.com/questions/1064930/struct-initializer-typedef-with-visual-studio 64 | /wd4204 # non-constant aggregate initializer 65 | /wd4255 # no function prototype (getopt) 66 | /wd4334 # result of 32-bit shift implicitly converted to 64 bits 67 | /wd4820 # padding 68 | /wd4668 # undefined macro 69 | /wd4710 # not inlined by default 70 | /wd4996 # unsafe function 71 | /wd4061 # enum is not explicitly handled by a case 72 | /wd5045 # Compiler will insert Spectre mitigation for memory load 73 | ) 74 | else() 75 | target_compile_options(ls-qpack PRIVATE 76 | -Wall 77 | -Wextra 78 | -Wno-unused-parameter 79 | -fno-omit-frame-pointer 80 | ) 81 | endif() 82 | 83 | IF (CMAKE_SYSTEM_NAME STREQUAL Windows) 84 | IF (LSQPACK_TESTS OR LSQPACK_BIN) 85 | FIND_PATH(GETOPT_INCLUDE_DIR NAMES getopt.h) 86 | IF (GETOPT_INCLUDE_DIR) 87 | INCLUDE_DIRECTORIES(${GETOPT_INCLUDE_DIR}) 88 | ELSE() 89 | MESSAGE(FATAL_ERROR "getopt.h was not found") 90 | ENDIF() 91 | FIND_LIBRARY(GETOPT_LIB getopt) 92 | IF(GETOPT_LIB) 93 | MESSAGE(STATUS "Found getopt: ${GETOPT_LIB}") 94 | ELSE() 95 | MESSAGE(STATUS "getopt not found") 96 | ENDIF() 97 | ENDIF() 98 | ENDIF() 99 | 100 | IF(DEFINED LSXPACK_MAX_STRLEN) 101 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSXPACK_MAX_STRLEN=${LSXPACK_MAX_STRLEN}") 102 | ENDIF() 103 | 104 | IF (CMAKE_BUILD_TYPE STREQUAL MinSizeRel) 105 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLS_QPACK_USE_LARGE_TABLES=0") 106 | ENDIF() 107 | 108 | IF(LSQPACK_TESTS) 109 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLSQPACK_DEVEL_MODE=1") 110 | ENDIF() 111 | 112 | INCLUDE(CheckCCompilerFlag) 113 | CHECK_C_COMPILER_FLAG(-Wno-implicit-fallthrough HAS_NO_IMPLICIT_FALLTHROUGH) 114 | IF (HAS_NO_IMPLICIT_FALLTHROUGH) 115 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-implicit-fallthrough") 116 | ENDIF() 117 | 118 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} $ENV{EXTRA_CFLAGS}") 119 | MESSAGE(STATUS "Compiler flags: ${CMAKE_C_FLAGS}") 120 | 121 | if(LSQPACK_TESTS) 122 | enable_testing() 123 | add_subdirectory(test) 124 | endif() 125 | 126 | if(LSQPACK_BIN) 127 | add_subdirectory(bin) 128 | endif() 129 | 130 | include(GNUInstallDirs) 131 | configure_file(lsqpack.pc.in lsqpack.pc @ONLY) 132 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lsqpack.pc" 133 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) 134 | 135 | configure_file(ls-qpack-config.cmake.in ls-qpack-config.cmake @ONLY) 136 | install(TARGETS ls-qpack EXPORT ls-qpack-config 137 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) 138 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ls-qpack-config.cmake" 139 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ls-qpack) 140 | install( 141 | EXPORT ls-qpack-config 142 | FILE ls-qpack-targets.cmake 143 | NAMESPACE ls-qpack:: 144 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ls-qpack 145 | ) 146 | install(FILES lsqpack.h lsxpack_header.h 147 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 148 | 149 | if(LSQPACK_BIN) 150 | install(TARGETS 151 | encode-int 152 | fuzz-decode 153 | interop-decode 154 | interop-encode 155 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 156 | endif() 157 | 158 | if(WIN32 OR EMSCRIPTEN) 159 | install(DIRECTORY wincompat/sys DESTINATION include) 160 | endif() 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 - 2022 LiteSpeed Technologies Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/litespeedtech/ls-qpack.svg?branch=master)](https://travis-ci.org/litespeedtech/ls-qpack) 2 | [![Build status](https://ci.appveyor.com/api/projects/status/kat31lt42ds0rmom?svg=true)](https://ci.appveyor.com/project/litespeedtech/ls-qpack) 3 | [![Build Status](https://api.cirrus-ci.com/github/litespeedtech/ls-qpack.svg)](https://cirrus-ci.com/github/litespeedtech/ls-qpack) 4 | 5 | # ls-qpack 6 | QPACK compression library for use with HTTP/3 7 | 8 | ## Introduction 9 | 10 | QPACK is the compression mechanism used by 11 | [HTTP/3](https://en.wikipedia.org/wiki/HTTP/3) to compress HTTP headers. 12 | It is in the process of being standardazed by the QUIC Working Group. The 13 | [QPACK Internet-Draft](https://tools.ietf.org/html/draft-ietf-quic-qpack-11) 14 | is has been stable for some time and we don't expect functional changes to 15 | it before the final RFC is released. 16 | 17 | ## Functionality 18 | 19 | ls-qpack is a full-featured, tested, and fast QPACK library. The QPACK encoder 20 | produces excellent compression results based on an [innovative mnemonic technique](https://blog.litespeedtech.com/2021/04/05/qpack-mnemonic-technique/). It boasts the fastest Huffman 21 | [encoder](https://blog.litespeedtech.com/2019/10/03/fast-huffman-encoder/) and 22 | [decoder](https://blog.litespeedtech.com/2019/09/16/fast-huffman-decoder/). 23 | 24 | The library is production quality. It is used in 25 | [OpenLiteSpeed](https://openlitespeed.org/), 26 | LiteSpeed [Web Server](https://www.litespeedtech.com/products#lsws), 27 | and LiteSpeed [Web ADC](https://www.litespeedtech.com/products#wadc). 28 | 29 | The library is robust: 30 | 1. The encoder does not assume anything about usual HTTP headers such as `Server` 31 | or `User-Agent`. Instead, it uses its mnemonic compression technique to 32 | achieve good compression results for any input. 33 | 1. The decoder uses modulo arithmetic to track dynamic table insertions. This is 34 | in contrast to all other QPACK implementations, which use an integer counter, 35 | meaning that at some point, the decoder will break. 36 | 1. The decoder processes input in streaming fashion. The caller does not have to 37 | buffer the contents of HTTP/3 `HEADERS` frame. Instead, the decoder can be 38 | supplied input byte-by-byte. 39 | 40 | ## Other Languages 41 | 42 | The ls-qpack library is implemented in vanilla C99. It makes it a good candidate 43 | for wrapping into a library for a higher-level language. As of this writing, we 44 | know of the following wrappers: 45 | - Go: [ls-qpack-go](https://github.com/mpiraux/ls-qpack-go) 46 | - Python: [pylsqpack](https://github.com/aiortc/pylsqpack) 47 | - Rust: [ls-qpack-rs](https://github.com/BiagioFesta/ls-qpack-rs) 48 | - TypeScript: [quicker](https://github.com/rmarx/quicker/tree/draft-20/lib/ls-qpack) 49 | - Ruby: [lsqpack-ruby](https://github.com/unasuke/lsqpack-ruby) 50 | 51 | ## Versioning 52 | 53 | Before the QPACK RFC is released, the three parts of the version are: 54 | - MAJOR: set to zero; 55 | - MINOR: set to the number of QPACK Internet-Draft the lirbary supports; and 56 | - PATCH: set to the patch number 57 | 58 | Once the RFC is released, MAJOR will be set to 1 and the version will follow 59 | the usual MAJOR.MINOR.PATCH pattern. 60 | 61 | ## API 62 | 63 | The API is documented in the header file, [lsqpack.h](lsqpack.h). 64 | One example how it is used in real code can be seen in 65 | [lsquic](https://github.com/litespeedtech/lsquic), a QUIC and HTTP/3 library 66 | developed by LiteSpeed Technologies. 67 | 68 | A different API, without the use of `struct lsxpack_header`, is available 69 | on branch-v1. 70 | -------------------------------------------------------------------------------- /bin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function(lsqpack_add_executable TARGET) 2 | add_executable(${TARGET} "") 3 | target_link_libraries(${TARGET} PRIVATE ls-qpack) 4 | 5 | if(LSQPACK_XXH) 6 | target_sources(${TARGET} PRIVATE ${TARGET}.c ../deps/xxhash/xxhash.c) 7 | target_include_directories(${TARGET} PRIVATE ../deps/xxhash) 8 | else() 9 | target_sources(${TARGET} PRIVATE ${TARGET}.c) 10 | target_link_libraries(${TARGET} PUBLIC xxHash::xxhash) 11 | endif() 12 | 13 | if(WIN32) 14 | target_include_directories(${TARGET} PRIVATE ../wincompat) 15 | target_link_libraries(${TARGET} PRIVATE ${GETOPT_LIB}) 16 | else() 17 | target_link_libraries(${TARGET} PRIVATE m) 18 | endif() 19 | endfunction() 20 | 21 | lsqpack_add_executable(interop-encode) 22 | lsqpack_add_executable(interop-decode) 23 | lsqpack_add_executable(encode-int) 24 | lsqpack_add_executable(fuzz-decode) 25 | 26 | target_include_directories(interop-decode PRIVATE ../test) 27 | -------------------------------------------------------------------------------- /bin/encode-int.c: -------------------------------------------------------------------------------- 1 | /* encode-int: encode an integer into HPACK varint. */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | static unsigned char * 10 | enc_int (unsigned char *dst, unsigned char *const end, uint64_t value, 11 | unsigned prefix_bits) 12 | { 13 | unsigned char *const dst_orig = dst; 14 | 15 | /* This function assumes that at least one byte is available */ 16 | assert(dst < end); 17 | if (value < (1u << prefix_bits) - 1) 18 | *dst++ |= value; 19 | else 20 | { 21 | *dst++ |= (1 << prefix_bits) - 1; 22 | value -= (1 << prefix_bits) - 1; 23 | while (value >= 128) 24 | { 25 | if (dst < end) 26 | { 27 | *dst++ = 0x80 | (unsigned char) value; 28 | value >>= 7; 29 | } 30 | else 31 | return dst_orig; 32 | } 33 | if (dst < end) 34 | *dst++ = (unsigned char) value; 35 | else 36 | return dst_orig; 37 | } 38 | return dst; 39 | } 40 | 41 | static unsigned 42 | lsqpack_val2len (uint64_t value, unsigned prefix_bits) 43 | { 44 | uint64_t mask = (1ULL << prefix_bits) - 1; 45 | return 1 46 | + (value >= mask ) 47 | + (value >= ((1ULL << 7) + mask)) 48 | + (value >= ((1ULL << 14) + mask)) 49 | + (value >= ((1ULL << 21) + mask)) 50 | + (value >= ((1ULL << 28) + mask)) 51 | + (value >= ((1ULL << 35) + mask)) 52 | + (value >= ((1ULL << 42) + mask)) 53 | + (value >= ((1ULL << 49) + mask)) 54 | + (value >= ((1ULL << 56) + mask)) 55 | + (value >= ((1ULL << 63) + mask)) 56 | ; 57 | } 58 | 59 | int 60 | main (int argc, char **argv) 61 | { 62 | unsigned long long val; 63 | unsigned char *p; 64 | unsigned prefix_bits; 65 | unsigned char buf[20]; 66 | 67 | if (argc != 3) 68 | { 69 | fprintf(stderr, "Usage: %s prefix_bits value\n", argv[0]); 70 | exit(EXIT_FAILURE); 71 | } 72 | 73 | prefix_bits = atoi(argv[1]); 74 | val = strtoull(argv[2], NULL, 10); 75 | 76 | fprintf(stderr, "expected size: %u\n", lsqpack_val2len(val, prefix_bits)); 77 | buf[0] = 0; 78 | p = enc_int(buf, buf + sizeof(buf), val, prefix_bits); 79 | 80 | if (p > buf) 81 | { 82 | fwrite(buf, 1, p - buf, stdout); 83 | exit(EXIT_SUCCESS); 84 | } 85 | else 86 | exit(EXIT_FAILURE); 87 | } 88 | -------------------------------------------------------------------------------- /bin/fuzz-decode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * fuzz-decode: special program for fuzzing. It still reads encoded 3 | * files just like interop-decode, but tries to do it faster and 4 | * forgoes several advanced options. 5 | */ 6 | 7 | #ifdef WIN32 8 | 9 | #include 10 | 11 | int 12 | main (int argc, char **argv) 13 | { 14 | fprintf(stderr, "%s is not supported on Windows: need mmap(2)\n", argv[0]); 15 | return 1; 16 | } 17 | 18 | #else 19 | 20 | #include 21 | 22 | #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) 23 | #include 24 | #define bswap_16 bswap16 25 | #define bswap_32 bswap32 26 | #define bswap_64 bswap64 27 | #elif defined(__OpenBSD__) 28 | #define bswap_16 swap16 29 | #define bswap_32 swap32 30 | #define bswap_64 swap64 31 | #elif defined(__APPLE__) 32 | #include 33 | #define bswap_16 OSSwapInt16 34 | #define bswap_32 OSSwapInt32 35 | #define bswap_64 OSSwapInt64 36 | #elif defined(WIN32) 37 | #define bswap_16 _byteswap_ushort 38 | #define bswap_32 _byteswap_ulong 39 | #define bswap_64 _byteswap_uint64 40 | #else 41 | #include 42 | #endif 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #include "lsqpack.h" 58 | #include "lsxpack_header.h" 59 | 60 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 61 | 62 | static void 63 | usage (const char *name) 64 | { 65 | fprintf(stderr, 66 | "Usage: %s [options] [-i input] [-o output]\n" 67 | "\n" 68 | "Options:\n" 69 | " -i FILE Input file.\n" 70 | " -f FILE Fuzz file: this is the stuff the fuzzer will change.\n" 71 | " -s NUMBER Maximum number of risked streams. Defaults to %u.\n" 72 | " -t NUMBER Dynamic table size. Defaults to %u.\n" 73 | "\n" 74 | " -h Print this help screen and exit\n" 75 | , name, LSQPACK_DEF_MAX_RISKED_STREAMS, LSQPACK_DEF_DYN_TABLE_SIZE); 76 | } 77 | 78 | 79 | static void 80 | hblock_unblocked (void *buf_p) 81 | { 82 | exit(1); 83 | } 84 | 85 | 86 | struct header 87 | { 88 | LIST_ENTRY(header) next_header; 89 | struct lsxpack_header xhdr; 90 | char buf[0x10000]; 91 | }; 92 | 93 | 94 | LIST_HEAD(, header) s_headers; 95 | 96 | 97 | static struct lsxpack_header * 98 | prepare_decode (void *hblock_ctx, struct lsxpack_header *xhdr, size_t space) 99 | { 100 | struct header *header; 101 | 102 | if (xhdr) 103 | { 104 | /* If the original buffer is not enough, we don't reallocate */ 105 | header = (struct header *) ((char *) xhdr 106 | - offsetof(struct header, xhdr)); 107 | LIST_REMOVE(header, next_header); 108 | free(header); 109 | return NULL; 110 | } 111 | else if (space <= LSXPACK_MAX_STRLEN) 112 | { 113 | header = malloc(sizeof(*header)); 114 | if (!header) 115 | return NULL; 116 | LIST_INSERT_HEAD(&s_headers, header, next_header); 117 | lsxpack_header_prepare_decode(&header->xhdr, header->buf, 118 | 0, sizeof(header->buf)); 119 | return &header->xhdr; 120 | } 121 | else 122 | return NULL; 123 | } 124 | 125 | 126 | static int 127 | process_header (void *hblock_ctx, struct lsxpack_header *xhdr) 128 | { 129 | struct header *header; 130 | 131 | header = (struct header *) ((char *) xhdr - offsetof(struct header, xhdr)); 132 | LIST_REMOVE(header, next_header); 133 | free(header); 134 | return 0; 135 | } 136 | 137 | 138 | static const struct lsqpack_dec_hset_if hset_if = { 139 | .dhi_unblocked = hblock_unblocked, 140 | .dhi_prepare_decode = prepare_decode, 141 | .dhi_process_header = process_header, 142 | }; 143 | 144 | 145 | int 146 | main (int argc, char **argv) 147 | { 148 | int in_fd = -1, fuzz_fd = STDIN_FILENO; 149 | int opt; 150 | unsigned dyn_table_size = LSQPACK_DEF_DYN_TABLE_SIZE, 151 | max_risked_streams = LSQPACK_DEF_MAX_RISKED_STREAMS; 152 | struct lsqpack_dec decoder; 153 | const unsigned char *p, *end; 154 | unsigned char *begin; 155 | struct stat st; 156 | uint64_t stream_id; 157 | uint32_t size; 158 | int r; 159 | struct header *header; 160 | 161 | while (-1 != (opt = getopt(argc, argv, "i:f:s:t:h"))) 162 | { 163 | switch (opt) 164 | { 165 | case 'i': 166 | in_fd = open(optarg, O_RDONLY); 167 | if (in_fd < 0) 168 | { 169 | fprintf(stderr, "cannot open `%s' for reading: %s\n", 170 | optarg, strerror(errno)); 171 | exit(EXIT_FAILURE); 172 | } 173 | break; 174 | case 'f': 175 | fuzz_fd = open(optarg, O_RDONLY); 176 | if (fuzz_fd < 0) 177 | { 178 | fprintf(stderr, "cannot open `%s' for reading: %s\n", 179 | optarg, strerror(errno)); 180 | exit(EXIT_FAILURE); 181 | } 182 | break; 183 | case 's': 184 | max_risked_streams = atoi(optarg); 185 | break; 186 | case 't': 187 | dyn_table_size = atoi(optarg); 188 | break; 189 | case 'h': 190 | usage(argv[0]); 191 | exit(EXIT_SUCCESS); 192 | default: 193 | exit(EXIT_FAILURE); 194 | } 195 | } 196 | 197 | LIST_INIT(&s_headers); 198 | lsqpack_dec_init(&decoder, NULL, dyn_table_size, max_risked_streams, 199 | &hset_if, 0 /* TODO: add command-line option */); 200 | 201 | if (in_fd < 0) 202 | { 203 | fprintf(stderr, "Specify input using `-i' option\n"); 204 | exit(1); 205 | } 206 | 207 | if (0 != fstat(in_fd, &st)) 208 | { 209 | perror("fstat"); 210 | exit(1); 211 | } 212 | 213 | begin = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in_fd, 0); 214 | if (!begin) 215 | { 216 | perror("mmap"); 217 | exit(1); 218 | } 219 | 220 | p = begin; 221 | end = begin + st.st_size; 222 | while (p + sizeof(stream_id) + sizeof(size) < end) 223 | { 224 | stream_id = * (uint64_t *) p; 225 | p += sizeof(uint64_t); 226 | size = * (uint32_t *) p; 227 | p += sizeof(uint32_t); 228 | #if __BYTE_ORDER == __LITTLE_ENDIAN 229 | stream_id = bswap_64(stream_id); 230 | size = bswap_32(size); 231 | #endif 232 | if (p + size > end) 233 | { 234 | fprintf(stderr, "truncated input at offset %u", 235 | (unsigned) (p - begin)); 236 | abort(); 237 | } 238 | if (stream_id == 0) 239 | { 240 | r = lsqpack_dec_enc_in(&decoder, p, size); 241 | if (r != 0) 242 | abort(); 243 | } 244 | else 245 | { 246 | const unsigned char *cur = p; 247 | enum lsqpack_read_header_status rhs; 248 | rhs = lsqpack_dec_header_in(&decoder, NULL, stream_id, 249 | size, &cur, size, NULL, NULL); 250 | if (rhs != LQRHS_DONE || (uint32_t) (cur - p) != size) 251 | abort(); 252 | } 253 | p += size; 254 | } 255 | 256 | munmap(begin, st.st_size); 257 | (void) close(in_fd); 258 | 259 | /* Now let's read the fuzzed part */ 260 | 261 | if (0 != fstat(fuzz_fd, &st)) 262 | { 263 | perror("fstat"); 264 | exit(1); 265 | } 266 | 267 | begin = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fuzz_fd, 0); 268 | if (!begin) 269 | { 270 | perror("mmap"); 271 | exit(1); 272 | } 273 | 274 | p = begin; 275 | end = begin + st.st_size; 276 | while (p + sizeof(stream_id) + sizeof(size) < end) 277 | { 278 | stream_id = * (uint64_t *) p; 279 | p += sizeof(uint64_t); 280 | size = * (uint32_t *) p; 281 | p += sizeof(uint32_t); 282 | #if __BYTE_ORDER == __LITTLE_ENDIAN 283 | stream_id = bswap_64(stream_id); 284 | size = bswap_32(size); 285 | #endif 286 | if (stream_id == 0) 287 | { 288 | r = lsqpack_dec_enc_in(&decoder, p, MIN(size, (uint32_t) (end - p))); 289 | (void) r; 290 | } 291 | else 292 | { 293 | const unsigned char *cur = p; 294 | enum lsqpack_read_header_status rhs; 295 | size = MIN(size, (uint32_t) (end - p)); 296 | rhs = lsqpack_dec_header_in(&decoder, NULL, stream_id, 297 | size, &cur, size, NULL, NULL); 298 | (void) rhs; 299 | } 300 | break; 301 | } 302 | 303 | munmap(begin, st.st_size); 304 | (void) close(fuzz_fd); 305 | 306 | lsqpack_dec_cleanup(&decoder); 307 | while (!LIST_EMPTY(&s_headers)) 308 | { 309 | header = LIST_FIRST(&s_headers); 310 | LIST_REMOVE(header, next_header); 311 | free(header); 312 | } 313 | 314 | exit(0); 315 | } 316 | 317 | #endif 318 | -------------------------------------------------------------------------------- /bin/interop-decode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * QPACK Interop -- decode from intermediate format and output QIF 3 | * 4 | * https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop 5 | */ 6 | 7 | #include 8 | 9 | #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) 10 | #include 11 | #define bswap_16 bswap16 12 | #define bswap_32 bswap32 13 | #define bswap_64 bswap64 14 | #elif defined(__OpenBSD__) 15 | #define bswap_16 swap16 16 | #define bswap_32 swap32 17 | #define bswap_64 swap64 18 | #elif defined(__APPLE__) 19 | #include 20 | #define bswap_16 OSSwapInt16 21 | #define bswap_32 OSSwapInt32 22 | #define bswap_64 OSSwapInt64 23 | #elif defined(WIN32) 24 | #define bswap_16 _byteswap_ushort 25 | #define bswap_32 _byteswap_ulong 26 | #define bswap_64 _byteswap_uint64 27 | #else 28 | #include 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #ifdef WIN32 37 | #include 38 | #else 39 | #include 40 | #endif 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "lsqpack.h" 47 | #include "lsxpack_header.h" 48 | #include "xxhash.h" 49 | #ifndef DEBUG 50 | #include "lsqpack-test.h" 51 | #endif 52 | 53 | #ifndef NDEBUG 54 | struct static_table_entry 55 | { 56 | const char *name; 57 | const char *val; 58 | unsigned name_len; 59 | unsigned val_len; 60 | }; 61 | 62 | /* [draft-ietf-quic-qpack-03] Appendix A */ 63 | static const struct static_table_entry static_table[] = 64 | { 65 | {":authority", "", 10, 0,}, 66 | {":path", "/", 5, 1,}, 67 | {"age", "0", 3, 1,}, 68 | {"content-disposition", "", 19, 0,}, 69 | {"content-length", "0", 14, 1,}, 70 | {"cookie", "", 6, 0,}, 71 | {"date", "", 4, 0,}, 72 | {"etag", "", 4, 0,}, 73 | {"if-modified-since", "", 17, 0,}, 74 | {"if-none-match", "", 13, 0,}, 75 | {"last-modified", "", 13, 0,}, 76 | {"link", "", 4, 0,}, 77 | {"location", "", 8, 0,}, 78 | {"referer", "", 7, 0,}, 79 | {"set-cookie", "", 10, 0,}, 80 | {":method", "CONNECT", 7, 7,}, 81 | {":method", "DELETE", 7, 6,}, 82 | {":method", "GET", 7, 3,}, 83 | {":method", "HEAD", 7, 4,}, 84 | {":method", "OPTIONS", 7, 7,}, 85 | {":method", "POST", 7, 4,}, 86 | {":method", "PUT", 7, 3,}, 87 | {":scheme", "http", 7, 4,}, 88 | {":scheme", "https", 7, 5,}, 89 | {":status", "103", 7, 3,}, 90 | {":status", "200", 7, 3,}, 91 | {":status", "304", 7, 3,}, 92 | {":status", "404", 7, 3,}, 93 | {":status", "503", 7, 3,}, 94 | {"accept", "*/*", 6, 3,}, 95 | {"accept", "application/dns-message", 6, 23,}, 96 | {"accept-encoding", "gzip, deflate, br", 15, 17,}, 97 | {"accept-ranges", "bytes", 13, 5,}, 98 | {"access-control-allow-headers", "cache-control", 28, 13,}, 99 | {"access-control-allow-headers", "content-type", 28, 12,}, 100 | {"access-control-allow-origin", "*", 27, 1,}, 101 | {"cache-control", "max-age=0", 13, 9,}, 102 | {"cache-control", "max-age=2592000", 13, 15,}, 103 | {"cache-control", "max-age=604800", 13, 14,}, 104 | {"cache-control", "no-cache", 13, 8,}, 105 | {"cache-control", "no-store", 13, 8,}, 106 | {"cache-control", "public, max-age=31536000", 13, 24,}, 107 | {"content-encoding", "br", 16, 2,}, 108 | {"content-encoding", "gzip", 16, 4,}, 109 | {"content-type", "application/dns-message", 12, 23,}, 110 | {"content-type", "application/javascript", 12, 22,}, 111 | {"content-type", "application/json", 12, 16,}, 112 | {"content-type", "application/x-www-form-urlencoded", 12, 33,}, 113 | {"content-type", "image/gif", 12, 9,}, 114 | {"content-type", "image/jpeg", 12, 10,}, 115 | {"content-type", "image/png", 12, 9,}, 116 | {"content-type", "text/css", 12, 8,}, 117 | {"content-type", "text/html; charset=utf-8", 12, 24,}, 118 | {"content-type", "text/plain", 12, 10,}, 119 | {"content-type", "text/plain;charset=utf-8", 12, 24,}, 120 | {"range", "bytes=0-", 5, 8,}, 121 | {"strict-transport-security", "max-age=31536000", 25, 16,}, 122 | {"strict-transport-security", "max-age=31536000; includesubdomains", 123 | 25, 35,}, 124 | {"strict-transport-security", 125 | "max-age=31536000; includesubdomains; preload", 25, 44,}, 126 | {"vary", "accept-encoding", 4, 15,}, 127 | {"vary", "origin", 4, 6,}, 128 | {"x-content-type-options", "nosniff", 22, 7,}, 129 | {"x-xss-protection", "1; mode=block", 16, 13,}, 130 | {":status", "100", 7, 3,}, 131 | {":status", "204", 7, 3,}, 132 | {":status", "206", 7, 3,}, 133 | {":status", "302", 7, 3,}, 134 | {":status", "400", 7, 3,}, 135 | {":status", "403", 7, 3,}, 136 | {":status", "421", 7, 3,}, 137 | {":status", "425", 7, 3,}, 138 | {":status", "500", 7, 3,}, 139 | {"accept-language", "", 15, 0,}, 140 | {"access-control-allow-credentials", "FALSE", 32, 5,}, 141 | {"access-control-allow-credentials", "TRUE", 32, 4,}, 142 | {"access-control-allow-headers", "*", 28, 1,}, 143 | {"access-control-allow-methods", "get", 28, 3,}, 144 | {"access-control-allow-methods", "get, post, options", 28, 18,}, 145 | {"access-control-allow-methods", "options", 28, 7,}, 146 | {"access-control-expose-headers", "content-length", 29, 14,}, 147 | {"access-control-request-headers", "content-type", 30, 12,}, 148 | {"access-control-request-method", "get", 29, 3,}, 149 | {"access-control-request-method", "post", 29, 4,}, 150 | {"alt-svc", "clear", 7, 5,}, 151 | {"authorization", "", 13, 0,}, 152 | {"content-security-policy", 153 | "script-src 'none'; object-src 'none'; base-uri 'none'", 23, 53,}, 154 | {"early-data", "1", 10, 1,}, 155 | {"expect-ct", "", 9, 0,}, 156 | {"forwarded", "", 9, 0,}, 157 | {"if-range", "", 8, 0,}, 158 | {"origin", "", 6, 0,}, 159 | {"purpose", "prefetch", 7, 8,}, 160 | {"server", "", 6, 0,}, 161 | {"timing-allow-origin", "*", 19, 1,}, 162 | {"upgrade-insecure-requests", "1", 25, 1,}, 163 | {"user-agent", "", 10, 0,}, 164 | {"x-forwarded-for", "", 15, 0,}, 165 | {"x-frame-options", "deny", 15, 4,}, 166 | {"x-frame-options", "sameorigin", 15, 10,}, 167 | }; 168 | #endif 169 | 170 | static size_t s_max_read_size = SIZE_MAX; 171 | 172 | static int s_verbose; 173 | static enum lsqpack_dec_opts s_dec_opts = LSQPACK_DEC_OPT_HASH_NAME 174 | | LSQPACK_DEC_OPT_HASH_NAMEVAL; 175 | 176 | static int s_check_unset_qpack_idx = 1; 177 | 178 | static FILE *s_out; 179 | 180 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 181 | 182 | static void 183 | usage (const char *name) 184 | { 185 | fprintf(stderr, 186 | "Usage: %s [options] [-i input] [-o output]\n" 187 | "\n" 188 | "Options:\n" 189 | " -i FILE Input file. If not specified or set to `-', the input is\n" 190 | " read from stdin.\n" 191 | " -o FILE Output file. If not spepcified or set to `-', the output\n" 192 | " is written to stdout.\n" 193 | " -r FILE Recipe file. Without a recipe, buffers are processed in\n" 194 | " order.\n" 195 | " -s NUMBER Maximum number of risked streams. Defaults to %u.\n" 196 | " -t NUMBER Dynamic table size. Defaults to %u.\n" 197 | " -m NUMBER Maximum read size. Defaults to %zu.\n" 198 | " -H [0|1] Use HTTP/1.x mode and test each header (defaults to `off').\n" 199 | " -v Verbose: print headers and table state to stderr.\n" 200 | " -S Don't swap encoder stream and header blocks.\n" 201 | " -Q Don't check static table when LSXPACK_QPACK_IDX is not set.\n" 202 | "\n" 203 | " -h Print this help screen and exit\n" 204 | , name, LSQPACK_DEF_MAX_RISKED_STREAMS, LSQPACK_DEF_DYN_TABLE_SIZE, SIZE_MAX); 205 | } 206 | 207 | 208 | struct buf 209 | { 210 | TAILQ_ENTRY(buf) next_buf; 211 | struct lsqpack_dec *dec; 212 | uint64_t stream_id; /* Zero means encoder stream */ 213 | size_t size; 214 | size_t off; 215 | size_t file_off; 216 | 217 | /* A single header name/value pair is stored in xhdr_buf. When the 218 | * header is done, the whole buffer can be used again for the next 219 | * header. 220 | */ 221 | struct lsxpack_header xhdr; 222 | unsigned xhdr_off; /* Used in xhdr_buf */ 223 | unsigned xhdr_nalloc; /* Number of bytes allocated */ /* TODO: use it */ 224 | 225 | /* Output is written to out_buf out header at a time and the printed all 226 | * at once. This logic is not very important and we use a reasonably 227 | * large fixed-size buffer. 228 | */ 229 | unsigned out_off; 230 | char out_buf[0x1000]; 231 | 232 | unsigned char buf[0]; 233 | }; 234 | 235 | 236 | TAILQ_HEAD(, buf) bufs = TAILQ_HEAD_INITIALIZER(bufs); 237 | 238 | static void 239 | hblock_unblocked (void *buf_p) 240 | { 241 | struct buf *buf = buf_p; 242 | TAILQ_INSERT_HEAD(&bufs, buf, next_buf); 243 | } 244 | 245 | 246 | static struct lsxpack_header * 247 | prepare_decode (void *hblock_ctx, struct lsxpack_header *xhdr, size_t space) 248 | { 249 | struct buf *const buf = hblock_ctx; 250 | char *new; 251 | 252 | if (space > LSXPACK_MAX_STRLEN) 253 | return NULL; 254 | 255 | if (xhdr) 256 | { 257 | assert(xhdr == &buf->xhdr); 258 | assert(space > xhdr->val_len); 259 | new = realloc(xhdr->buf, space); 260 | if (!new) 261 | return NULL; 262 | xhdr->buf = new; 263 | xhdr->val_len = space; 264 | return xhdr; 265 | } 266 | else 267 | { 268 | xhdr = &buf->xhdr; 269 | new = malloc(space); 270 | if (!new) 271 | return NULL; 272 | lsxpack_header_prepare_decode(xhdr, new, 0, space); 273 | return xhdr; 274 | } 275 | } 276 | 277 | 278 | static int 279 | process_header (void *hblock_ctx, struct lsxpack_header *xhdr) 280 | { 281 | struct buf *const buf = hblock_ctx; 282 | const char *p; 283 | const uint32_t seed = 39378473; 284 | uint32_t hash, name_hash; 285 | int nw; 286 | 287 | if (s_dec_opts & LSQPACK_DEC_OPT_HTTP1X) 288 | { 289 | p = lsxpack_header_get_name(xhdr) + xhdr->name_len; 290 | assert(0 == memcmp(p, ": ", 2)); 291 | p += 2 + xhdr->val_len; 292 | assert(0 == memcmp(p, "\r\n", 2)); 293 | assert(xhdr->dec_overhead == 4); 294 | } 295 | else 296 | assert(xhdr->dec_overhead == 0); 297 | 298 | if (s_dec_opts & LSQPACK_DEC_OPT_HASH_NAME) 299 | { 300 | assert(xhdr->flags & LSXPACK_NAME_HASH); 301 | hash = XXH32(lsxpack_header_get_name(xhdr), xhdr->name_len, seed); 302 | assert(hash == xhdr->name_hash); 303 | } 304 | 305 | if (s_dec_opts & LSQPACK_DEC_OPT_HASH_NAME) 306 | assert(xhdr->flags & LSXPACK_NAME_HASH); 307 | 308 | if (xhdr->flags & LSXPACK_NAME_HASH) 309 | { 310 | name_hash = XXH32(lsxpack_header_get_name(xhdr), xhdr->name_len, seed); 311 | assert(hash == xhdr->name_hash); 312 | } 313 | #ifndef NDEBUG 314 | else if (!(xhdr->flags & LSXPACK_QPACK_IDX)) 315 | /* Calculate for upcoming "not in static table check" */ 316 | name_hash = XXH32(lsxpack_header_get_name(xhdr), xhdr->name_len, seed); 317 | #endif 318 | 319 | if (s_dec_opts & LSQPACK_DEC_OPT_HASH_NAMEVAL) 320 | { 321 | /* This is not required by the API, but internally, if the library 322 | * calculates nameval hash, it should also set the name hash. 323 | */ 324 | assert(xhdr->flags & LSXPACK_NAME_HASH); 325 | assert(xhdr->flags & LSXPACK_NAMEVAL_HASH); 326 | } 327 | 328 | if (xhdr->flags & LSXPACK_NAMEVAL_HASH) 329 | { 330 | hash = XXH32(lsxpack_header_get_name(xhdr), xhdr->name_len, seed); 331 | hash = XXH32(lsxpack_header_get_value(xhdr), xhdr->val_len, hash); 332 | assert(hash == xhdr->nameval_hash); 333 | } 334 | 335 | #ifndef NDEBUG 336 | if (xhdr->flags & LSXPACK_QPACK_IDX) 337 | { 338 | assert(xhdr->qpack_index < 339 | sizeof(static_table) / sizeof(static_table[0])); 340 | assert(static_table[xhdr->qpack_index].name_len == xhdr->name_len); 341 | assert(0 == memcmp(lsxpack_header_get_name(xhdr), 342 | static_table[xhdr->qpack_index].name, xhdr->name_len)); 343 | } 344 | else if (s_check_unset_qpack_idx) 345 | { 346 | /* The decoder does best effort: if the encoder did not use the 347 | * static table, QPACK index is not set. However, since we are 348 | * testing our decoder, we assume that the encoder always uses 349 | * the static table when it can. 350 | */ 351 | int idx = lsqpack_find_in_static_headers(name_hash, 352 | lsxpack_header_get_name(xhdr), xhdr->name_len); 353 | assert(idx < 0); 354 | } 355 | #endif 356 | 357 | nw = snprintf(buf->out_buf + buf->out_off, 358 | sizeof(buf->out_buf) - buf->out_off, 359 | "%.*s\t%.*s\n", 360 | (int) xhdr->name_len, lsxpack_header_get_name(xhdr), 361 | (int) xhdr->val_len, lsxpack_header_get_value(xhdr)); 362 | free(buf->xhdr.buf); 363 | memset(&buf->xhdr, 0, sizeof(buf->xhdr)); 364 | if (nw > 0 && (size_t) nw <= sizeof(buf->out_buf) - buf->out_off) 365 | { 366 | buf->out_off += (unsigned) nw; 367 | return 0; 368 | } 369 | else 370 | { 371 | fprintf(stderr, "header list too long\n"); 372 | return -1; 373 | } 374 | } 375 | 376 | 377 | static const struct lsqpack_dec_hset_if hset_if = { 378 | .dhi_unblocked = hblock_unblocked, 379 | .dhi_prepare_decode = prepare_decode, 380 | .dhi_process_header = process_header, 381 | }; 382 | 383 | 384 | static void 385 | header_block_done (const struct buf *buf) 386 | { 387 | fprintf(s_out, "# stream %"PRIu64"\n", buf->stream_id); 388 | fprintf(s_out, "# (stream ID above is used for sorting)\n"); 389 | fprintf(s_out, "%.*s\n", (int) buf->out_off, buf->out_buf); 390 | } 391 | 392 | 393 | int 394 | main (int argc, char **argv) 395 | { 396 | FILE *in = stdin; 397 | FILE *recipe = NULL; 398 | int opt; 399 | unsigned dyn_table_size = LSQPACK_DEF_DYN_TABLE_SIZE, 400 | max_risked_streams = LSQPACK_DEF_MAX_RISKED_STREAMS; 401 | struct lsqpack_dec decoder; 402 | const struct lsqpack_dec_err *err; 403 | const unsigned char *p; 404 | ssize_t nr; 405 | int r; 406 | uint64_t stream_id; 407 | uint32_t size; 408 | size_t off; /* For debugging */ 409 | size_t file_off; 410 | struct buf *buf; 411 | unsigned lineno; 412 | char *line, *end; 413 | enum lsqpack_read_header_status rhs; 414 | int do_swap = 1; 415 | char command[0x100]; 416 | char line_buf[0x100]; 417 | 418 | while (-1 != (opt = getopt(argc, argv, "i:o:r:s:t:m:hvH:SQ"))) 419 | { 420 | switch (opt) 421 | { 422 | case 'i': 423 | if (0 != strcmp(optarg, "-")) 424 | { 425 | in = fopen(optarg, "rb"); 426 | if (!in) 427 | { 428 | fprintf(stderr, "cannot open `%s' for reading: %s\n", 429 | optarg, strerror(errno)); 430 | exit(EXIT_FAILURE); 431 | } 432 | } 433 | break; 434 | case 'o': 435 | if (0 != strcmp(optarg, "-")) 436 | { 437 | s_out = fopen(optarg, "w"); 438 | if (!s_out) 439 | { 440 | fprintf(stderr, "cannot open `%s' for writing: %s\n", 441 | optarg, strerror(errno)); 442 | exit(EXIT_FAILURE); 443 | } 444 | } 445 | break; 446 | case 'r': 447 | if (0 == strcmp(optarg, "-")) 448 | recipe = stdin; 449 | else 450 | { 451 | recipe = fopen(optarg, "r"); 452 | if (!recipe) 453 | { 454 | fprintf(stderr, "cannot open `%s' for reading: %s\n", 455 | optarg, strerror(errno)); 456 | exit(EXIT_FAILURE); 457 | } 458 | } 459 | break; 460 | case 's': 461 | max_risked_streams = atoi(optarg); 462 | break; 463 | case 't': 464 | dyn_table_size = atoi(optarg); 465 | break; 466 | case 'm': 467 | s_max_read_size = atoi(optarg); 468 | break; 469 | case 'h': 470 | usage(argv[0]); 471 | exit(EXIT_SUCCESS); 472 | case 'v': 473 | ++s_verbose; 474 | break; 475 | case 'S': 476 | do_swap = 0; 477 | break; 478 | case 'H': 479 | if (atoi(optarg)) 480 | s_dec_opts |= LSQPACK_DEC_OPT_HTTP1X; 481 | else 482 | s_dec_opts &= ~LSQPACK_DEC_OPT_HTTP1X; 483 | break; 484 | case 'Q': 485 | s_check_unset_qpack_idx = 0; 486 | break; 487 | default: 488 | exit(EXIT_FAILURE); 489 | } 490 | } 491 | 492 | if (!s_out) 493 | s_out = stdout; 494 | 495 | lsqpack_dec_init(&decoder, s_verbose ? stderr : NULL, dyn_table_size, 496 | max_risked_streams, &hset_if, s_dec_opts); 497 | 498 | off = 0; 499 | while (1) 500 | { 501 | file_off = off; 502 | nr = fread(&stream_id, 1, sizeof(stream_id), in); 503 | if (nr == 0) 504 | break; 505 | if (nr != sizeof(stream_id)) 506 | { 507 | fprintf(stderr, "could not read %zu bytes (stream id) at " 508 | "offset %zu: %s\n", sizeof(stream_id), off, strerror(errno)); 509 | goto read_err; 510 | } 511 | off += nr; 512 | nr = fread(&size, 1, sizeof(size), in); 513 | if (nr != sizeof(size)) 514 | { 515 | fprintf(stderr, "could not read %zu bytes (size) at " 516 | "offset %zu: %s\n", sizeof(size), off, strerror(errno)); 517 | goto read_err; 518 | } 519 | off += nr; 520 | #if __BYTE_ORDER == __LITTLE_ENDIAN 521 | stream_id = bswap_64(stream_id); 522 | size = bswap_32(size); 523 | #endif 524 | if (stream_id == 0 && size == 0) 525 | continue; 526 | buf = malloc(sizeof(*buf) + size); 527 | if (!buf) 528 | { 529 | perror("malloc"); 530 | exit(EXIT_FAILURE); 531 | } 532 | memset(buf, 0, sizeof(*buf)); 533 | nr = fread(buf->buf, 1, size, in); 534 | if (nr != (ssize_t) size) 535 | { 536 | fprintf(stderr, "could not read %"PRIu32" bytes (buffer) at " 537 | "offset %zu: %s\n", size, off, strerror(errno)); 538 | goto read_err; 539 | } 540 | off += nr; 541 | buf->dec = &decoder; 542 | buf->stream_id = stream_id; 543 | buf->size = size; 544 | buf->file_off = file_off; 545 | if (buf->size == 0) 546 | exit(EXIT_FAILURE); 547 | TAILQ_INSERT_TAIL(&bufs, buf, next_buf); 548 | } 549 | (void) fclose(in); 550 | in = NULL; 551 | 552 | if (recipe) 553 | { 554 | lineno = 0; 555 | while (line = fgets(line_buf, sizeof(line_buf), recipe), line != NULL) 556 | { 557 | ++lineno; 558 | end = strchr(line, '\n'); 559 | if (!end) 560 | { 561 | fprintf(stderr, "no newline on line %u\n", lineno); 562 | exit(EXIT_FAILURE); 563 | } 564 | *end = '\0'; 565 | 566 | if (*line == '#') 567 | continue; 568 | 569 | if (3 == sscanf(line, " %[s] %"PRIu64" %"PRIu32" ", command, &stream_id, &size)) 570 | { 571 | TAILQ_FOREACH(buf, &bufs, next_buf) 572 | if (stream_id == buf->stream_id) 573 | break; 574 | if (!buf) 575 | { 576 | fprintf(stderr, "stream %"PRIu64" not found (recipe line %u)\n", 577 | stream_id, lineno); 578 | exit(EXIT_FAILURE); 579 | } 580 | p = buf->buf; 581 | rhs = lsqpack_dec_header_in(&decoder, buf, stream_id, 582 | buf->size, &p, 583 | buf->size /* FIXME: this should be `size' */, 584 | NULL, NULL); 585 | switch (rhs) 586 | { 587 | case LQRHS_DONE: 588 | assert(p == buf->buf + buf->size); 589 | header_block_done(buf); 590 | if (s_verbose) 591 | fprintf(stderr, "compression ratio: %.3f\n", 592 | lsqpack_dec_ratio(&decoder)); 593 | TAILQ_REMOVE(&bufs, buf, next_buf); 594 | free(buf); 595 | break; 596 | case LQRHS_BLOCKED: 597 | buf->off += (unsigned) (p - buf->buf); 598 | TAILQ_REMOVE(&bufs, buf, next_buf); 599 | break; 600 | case LQRHS_NEED: 601 | buf->off += (unsigned) (p - buf->buf); 602 | break; 603 | default: 604 | assert(rhs == LQRHS_ERROR); 605 | fprintf(stderr, "recipe line %u: stream %"PRIu64": " 606 | "header_in error\n", lineno, stream_id); 607 | exit(EXIT_FAILURE); 608 | } 609 | } 610 | else if (2 == sscanf(line, " %[z] %u ", command, &size)) 611 | { 612 | s_max_read_size = size; 613 | } 614 | else 615 | { 616 | perror("sscanf"); 617 | exit(EXIT_FAILURE); 618 | } 619 | } 620 | fclose(recipe); 621 | } 622 | else if (do_swap && max_risked_streams && dyn_table_size 623 | && TAILQ_FIRST(&bufs) && TAILQ_FIRST(&bufs)->stream_id) 624 | { 625 | /* Swap header blocks and encoder stream bufs to exercise blocked 626 | * header blocks logic. 627 | */ 628 | struct buf *saved_hblock = NULL, *next; 629 | for (buf = TAILQ_FIRST(&bufs); buf; buf = next) 630 | { 631 | next = TAILQ_NEXT(buf, next_buf); 632 | if (buf->stream_id) 633 | continue; 634 | if (saved_hblock) 635 | TAILQ_INSERT_BEFORE(buf, saved_hblock, next_buf); 636 | saved_hblock = buf; 637 | TAILQ_REMOVE(&bufs, buf, next_buf); 638 | } 639 | TAILQ_INSERT_TAIL(&bufs, saved_hblock, next_buf); 640 | } 641 | 642 | while (buf = TAILQ_FIRST(&bufs), buf != NULL) 643 | { 644 | TAILQ_REMOVE(&bufs, buf, next_buf); 645 | if (buf->stream_id == 0) 646 | { 647 | r = lsqpack_dec_enc_in(&decoder, buf->buf, buf->size - buf->off); 648 | if (r != 0) 649 | { 650 | err = lsqpack_dec_get_err_info(buf->dec); 651 | fprintf(stderr, "encoder_in error; off %"PRIu64", line %d\n", 652 | err->off, err->line); 653 | exit(EXIT_FAILURE); 654 | } 655 | if (s_verbose) 656 | lsqpack_dec_print_table(&decoder, stderr); 657 | free(buf); 658 | } 659 | else 660 | { 661 | dec_header: 662 | p = buf->buf + buf->off; 663 | if (buf->off == 0) 664 | rhs = lsqpack_dec_header_in(&decoder, buf, buf->stream_id, 665 | buf->size, &p, MIN(s_max_read_size, buf->size), 666 | NULL, NULL); 667 | else 668 | rhs = lsqpack_dec_header_read(buf->dec, buf, &p, 669 | MIN(s_max_read_size, (buf->size - buf->off)), 670 | NULL, NULL); 671 | switch (rhs) 672 | { 673 | case LQRHS_DONE: 674 | assert(p == buf->buf + buf->size); 675 | header_block_done(buf); 676 | if (s_verbose) 677 | fprintf(stderr, "compression ratio: %.3f\n", 678 | lsqpack_dec_ratio(&decoder)); 679 | free(buf); 680 | break; 681 | case LQRHS_BLOCKED: 682 | buf->off = (unsigned) (p - buf->buf); 683 | break; 684 | case LQRHS_NEED: 685 | buf->off = (unsigned) (p - buf->buf); 686 | goto dec_header; 687 | default: 688 | assert(rhs == LQRHS_ERROR); 689 | fprintf(stderr, "stream %"PRIu64": header block error " 690 | "starting at off %zu\n", buf->stream_id, buf->off); 691 | err = lsqpack_dec_get_err_info(&decoder); 692 | fprintf(stderr, "encoder_in error; off %"PRIu64", line %d\n", 693 | err->off, err->line); 694 | exit(EXIT_FAILURE); 695 | } 696 | } 697 | } 698 | 699 | if (!TAILQ_EMPTY(&bufs)) 700 | { 701 | fprintf(stderr, "some streams reamain\n"); 702 | exit(EXIT_FAILURE); 703 | } 704 | /* TODO: check if decoder has any stream references. That would be 705 | * an error. 706 | */ 707 | 708 | if (s_verbose) 709 | lsqpack_dec_print_table(&decoder, stderr); 710 | 711 | lsqpack_dec_cleanup(&decoder); 712 | 713 | assert(TAILQ_EMPTY(&bufs)); 714 | 715 | if (s_out) 716 | (void) fclose(s_out); 717 | 718 | exit(EXIT_SUCCESS); 719 | 720 | read_err: 721 | if (nr < 0) 722 | perror("read"); 723 | else if (nr == 0) 724 | fprintf(stderr, "unexpected EOF\n"); 725 | else 726 | fprintf(stderr, "not enough bytes read (%zu)\n", (size_t) nr); 727 | exit(EXIT_FAILURE); 728 | } 729 | -------------------------------------------------------------------------------- /bin/interop-encode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * QPACK Interop -- encode to intermediate format 3 | * 4 | * https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop 5 | */ 6 | 7 | #include 8 | 9 | #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) 10 | #include 11 | #define bswap_16 bswap16 12 | #define bswap_32 bswap32 13 | #define bswap_64 bswap64 14 | #elif defined(__OpenBSD__) 15 | #define bswap_16 swap16 16 | #define bswap_32 swap32 17 | #define bswap_64 swap64 18 | #elif defined(__APPLE__) 19 | #include 20 | #define bswap_16 OSSwapInt16 21 | #define bswap_32 OSSwapInt32 22 | #define bswap_64 OSSwapInt64 23 | #elif defined(WIN32) 24 | #define bswap_16 _byteswap_ushort 25 | #define bswap_32 _byteswap_ulong 26 | #define bswap_64 _byteswap_uint64 27 | #else 28 | #include 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #ifdef WIN32 36 | #include 37 | #else 38 | #include 39 | #endif 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "lsqpack.h" 46 | #include "lsxpack_header.h" 47 | 48 | static int s_verbose; 49 | 50 | unsigned char * 51 | lsqpack_enc_int (unsigned char *dst, unsigned char *const end, uint64_t value, 52 | unsigned prefix_bits); 53 | 54 | static void 55 | usage (const char *name) 56 | { 57 | fprintf(stderr, 58 | "Usage: %s [options] [-i input] [-o output]\n" 59 | "\n" 60 | "Options:\n" 61 | " -i FILE Input file. If not specified or set to `-', the input is\n" 62 | " read from stdin.\n" 63 | " -o FILE Output file. If not spepcified or set to `-', the output\n" 64 | " is written to stdout.\n" 65 | " -s NUMBER Maximum number of risked streams. Defaults to %u.\n" 66 | " -t NUMBER Dynamic table size. Defaults to %u.\n" 67 | " -a MODE Header acknowledgement mode. 0 means headers are never\n" 68 | " acknowledged, non-zero means header blocks are acknowledged\n" 69 | " immediately. Default value is 0.\n" 70 | " -n Process annotations.\n" 71 | " -S Server mode.\n" 72 | " -D Do not emit \"Duplicate\" instructions.\n" 73 | " -A Aggressive indexing.\n" 74 | " -M Turn off memory guard.\n" 75 | " -f Fast: use maximum output buffers.\n" 76 | " -v Verbose: print various messages to stderr.\n" 77 | "\n" 78 | " -h Print this help screen and exit\n" 79 | , name, LSQPACK_DEF_MAX_RISKED_STREAMS, LSQPACK_DEF_DYN_TABLE_SIZE); 80 | } 81 | 82 | 83 | static void 84 | write_enc_stream (FILE *out, const unsigned char *enc_buf, size_t enc_sz) 85 | { 86 | uint64_t stream_id_enc; 87 | uint32_t length_enc; 88 | size_t written; 89 | 90 | if (enc_sz <= 0) 91 | return; 92 | stream_id_enc = 0; 93 | length_enc = enc_sz; 94 | #if __BYTE_ORDER == __LITTLE_ENDIAN 95 | stream_id_enc = bswap_64(stream_id_enc); 96 | length_enc = bswap_32(length_enc); 97 | #endif 98 | written = fwrite(&stream_id_enc, 1, sizeof(stream_id_enc), out); 99 | if (written != sizeof(stream_id_enc)) 100 | { 101 | perror("fwrite"); 102 | exit(EXIT_FAILURE); 103 | } 104 | written = fwrite(&length_enc, 1, sizeof(length_enc), out); 105 | if (written != sizeof(length_enc)) 106 | { 107 | perror("fwrite"); 108 | exit(EXIT_FAILURE); 109 | } 110 | written = fwrite(enc_buf, 1, enc_sz, out); 111 | if (written != enc_sz) 112 | { 113 | perror("fwrite"); 114 | exit(EXIT_FAILURE); 115 | } 116 | } 117 | 118 | 119 | static void 120 | write_enc_and_header_streams (FILE *out, unsigned stream_id, 121 | const unsigned char *enc_buf, size_t enc_sz, 122 | const unsigned char *pref_buf, size_t pref_sz, 123 | const unsigned char *hea_buf, size_t hea_sz) 124 | { 125 | uint64_t stream_id_enc; 126 | uint32_t length_enc; 127 | size_t written; 128 | 129 | if (s_verbose) 130 | fprintf(stderr, "%s: stream %"PRIu32"\n", __func__, stream_id); 131 | 132 | if (enc_sz) 133 | { 134 | stream_id_enc = 0; 135 | length_enc = enc_sz; 136 | #if __BYTE_ORDER == __LITTLE_ENDIAN 137 | stream_id_enc = bswap_64(stream_id_enc); 138 | length_enc = bswap_32(length_enc); 139 | #endif 140 | written = fwrite(&stream_id_enc, 1, sizeof(stream_id_enc), out); 141 | if (written != sizeof(stream_id_enc)) 142 | goto write_err; 143 | written = fwrite(&length_enc, 1, sizeof(length_enc), out); 144 | if (written != sizeof(length_enc)) 145 | goto write_err; 146 | written = fwrite(enc_buf, 1, enc_sz, out); 147 | if (written != enc_sz) 148 | goto write_err; 149 | } 150 | 151 | stream_id_enc = stream_id; 152 | length_enc = pref_sz + hea_sz; 153 | #if __BYTE_ORDER == __LITTLE_ENDIAN 154 | stream_id_enc = bswap_64(stream_id_enc); 155 | length_enc = bswap_32(length_enc); 156 | #endif 157 | written = fwrite(&stream_id_enc, 1, sizeof(stream_id_enc), out); 158 | if (written != sizeof(stream_id_enc)) 159 | goto write_err; 160 | written = fwrite(&length_enc, 1, sizeof(length_enc), out); 161 | if (written != sizeof(length_enc)) 162 | goto write_err; 163 | written = fwrite(pref_buf, 1, pref_sz, out); 164 | if (written != pref_sz) 165 | goto write_err; 166 | written = fwrite(hea_buf, 1, hea_sz, out); 167 | if (written != hea_sz) 168 | goto write_err; 169 | return; 170 | 171 | write_err: 172 | perror("fwrite"); 173 | exit(EXIT_FAILURE); 174 | } 175 | 176 | 177 | static unsigned s_saved_ins_count; 178 | static int 179 | ack_last_entry_id (struct lsqpack_enc *encoder) 180 | { 181 | unsigned char *end_cmd; 182 | unsigned char cmd[80]; 183 | unsigned val; 184 | 185 | if (s_verbose) 186 | fprintf(stderr, "ACK entry ID %u\n", encoder->qpe_ins_count); 187 | 188 | cmd[0] = 0x00; 189 | val = encoder->qpe_ins_count - s_saved_ins_count; 190 | s_saved_ins_count = encoder->qpe_ins_count; 191 | end_cmd = lsqpack_enc_int(cmd, cmd + sizeof(cmd), val, 6); 192 | assert(end_cmd > cmd); 193 | return lsqpack_enc_decoder_in(encoder, cmd, end_cmd - cmd); 194 | } 195 | 196 | 197 | static int 198 | ack_stream (struct lsqpack_enc *encoder, uint64_t stream_id) 199 | { 200 | unsigned char *end_cmd; 201 | unsigned char cmd[80]; 202 | 203 | if (s_verbose) 204 | fprintf(stderr, "ACK stream ID %"PRIu64"\n", stream_id); 205 | 206 | cmd[0] = 0x80; 207 | end_cmd = lsqpack_enc_int(cmd, cmd + sizeof(cmd), stream_id, 7); 208 | assert(end_cmd > cmd); 209 | return lsqpack_enc_decoder_in(encoder, cmd, end_cmd - cmd); 210 | } 211 | 212 | 213 | static int 214 | sync_table (struct lsqpack_enc *encoder, uint64_t num_inserts) 215 | { 216 | unsigned char *end_cmd; 217 | unsigned char cmd[80]; 218 | 219 | if (s_verbose) 220 | fprintf(stderr, "Sync table num inserts %"PRIu64"\n", num_inserts); 221 | 222 | cmd[0] = 0x00; 223 | end_cmd = lsqpack_enc_int(cmd, cmd + sizeof(cmd), num_inserts, 6); 224 | assert(end_cmd > cmd); 225 | return lsqpack_enc_decoder_in(encoder, cmd, end_cmd - cmd); 226 | } 227 | 228 | 229 | static int 230 | cancel_stream (struct lsqpack_enc *encoder, uint64_t stream_id) 231 | { 232 | unsigned char *end_cmd; 233 | unsigned char cmd[80]; 234 | 235 | if (s_verbose) 236 | fprintf(stderr, "Cancel stream ID %"PRIu64"\n", stream_id); 237 | 238 | cmd[0] = 0x40; 239 | end_cmd = lsqpack_enc_int(cmd, cmd + sizeof(cmd), stream_id, 6); 240 | assert(end_cmd > cmd); 241 | return lsqpack_enc_decoder_in(encoder, cmd, end_cmd - cmd); 242 | } 243 | 244 | 245 | int 246 | main (int argc, char **argv) 247 | { 248 | FILE *in = stdin; 249 | FILE *out = stdout; 250 | int opt; 251 | unsigned dyn_table_size = LSQPACK_DEF_DYN_TABLE_SIZE, 252 | max_risked_streams = LSQPACK_DEF_MAX_RISKED_STREAMS; 253 | unsigned lineno, stream_id; 254 | struct lsqpack_enc encoder; 255 | char *line, *end, *tab; 256 | ssize_t pref_sz; 257 | enum lsqpack_enc_status st; 258 | enum lsqpack_enc_opts enc_opts = 0; 259 | size_t enc_sz, hea_sz, enc_off, hea_off; 260 | int header_opened, r; 261 | unsigned arg; 262 | enum { ACK_NEVER, ACK_IMMEDIATE, } ack_mode = ACK_NEVER; 263 | int process_annotations = 0; 264 | char line_buf[0x1000]; 265 | unsigned char tsu_buf[LSQPACK_LONGEST_SDTC]; 266 | size_t tsu_buf_sz; 267 | enum lsqpack_enc_header_flags hflags; 268 | int fast = 0; 269 | struct lsxpack_header xhdr; 270 | unsigned char enc_buf[0x1000], hea_buf[0x1000], pref_buf[0x20]; 271 | 272 | while (-1 != (opt = getopt(argc, argv, "ADMSa:i:no:s:t:hvf"))) 273 | { 274 | switch (opt) 275 | { 276 | case 'S': 277 | enc_opts |= LSQPACK_ENC_OPT_SERVER; 278 | break; 279 | case 'D': 280 | enc_opts |= LSQPACK_ENC_OPT_NO_DUP; 281 | break; 282 | case 'A': 283 | enc_opts |= LSQPACK_ENC_OPT_IX_AGGR; 284 | break; 285 | case 'M': 286 | enc_opts |= LSQPACK_ENC_OPT_NO_MEM_GUARD; 287 | break; 288 | case 'n': 289 | ++process_annotations; 290 | break; 291 | case 'a': 292 | ack_mode = atoi(optarg) ? ACK_IMMEDIATE : ACK_NEVER; 293 | break; 294 | case 'i': 295 | if (0 != strcmp(optarg, "-")) 296 | { 297 | in = fopen(optarg, "r"); 298 | if (!in) 299 | { 300 | fprintf(stderr, "cannot open `%s' for reading: %s\n", 301 | optarg, strerror(errno)); 302 | exit(EXIT_FAILURE); 303 | } 304 | } 305 | break; 306 | case 'o': 307 | if (0 != strcmp(optarg, "-")) 308 | { 309 | out = fopen(optarg, "wb"); 310 | if (!out) 311 | { 312 | fprintf(stderr, "cannot open `%s' for writing: %s\n", 313 | optarg, strerror(errno)); 314 | exit(EXIT_FAILURE); 315 | } 316 | } 317 | break; 318 | case 's': 319 | max_risked_streams = atoi(optarg); 320 | break; 321 | case 't': 322 | dyn_table_size = atoi(optarg); 323 | break; 324 | case 'h': 325 | usage(argv[0]); 326 | exit(EXIT_SUCCESS); 327 | case 'f': 328 | fast = 1; 329 | break; 330 | case 'v': 331 | ++s_verbose; 332 | break; 333 | default: 334 | exit(EXIT_FAILURE); 335 | } 336 | } 337 | 338 | tsu_buf_sz = sizeof(tsu_buf); 339 | if (0 != lsqpack_enc_init(&encoder, s_verbose ? stderr : NULL, dyn_table_size, 340 | dyn_table_size, max_risked_streams, enc_opts, tsu_buf, 341 | &tsu_buf_sz)) 342 | { 343 | perror("lsqpack_enc_init"); 344 | exit(EXIT_FAILURE); 345 | } 346 | 347 | lineno = 0; 348 | stream_id = 0; 349 | enc_off = 0; 350 | hea_off = 0; 351 | header_opened = 0; 352 | 353 | while (line = fgets(line_buf, sizeof(line_buf), in), line != NULL) 354 | { 355 | ++lineno; 356 | end = strchr(line, '\n'); 357 | if (!end) 358 | { 359 | fprintf(stderr, "no newline on line %u\n", lineno); 360 | exit(EXIT_FAILURE); 361 | } 362 | *end = '\0'; 363 | 364 | if (end == line) 365 | { 366 | if (header_opened) 367 | { 368 | size_t sz, pref_max = sizeof(pref_buf); 369 | for (sz = (fast ? pref_max : 0); sz <= pref_max; sz++) 370 | { 371 | pref_sz = lsqpack_enc_end_header(&encoder, pref_buf, sz, &hflags); 372 | if (pref_sz > 0) 373 | { 374 | if (max_risked_streams == 0) 375 | assert(!(hflags & LSQECH_REF_AT_RISK)); 376 | break; 377 | } 378 | } 379 | assert(pref_sz <= lsqpack_enc_header_block_prefix_size(&encoder)); 380 | if (pref_sz < 0) 381 | { 382 | fprintf(stderr, "end_header failed: %s", strerror(errno)); 383 | exit(EXIT_FAILURE); 384 | } 385 | if (ack_mode == ACK_IMMEDIATE) 386 | { 387 | if (!(2 == pref_sz && pref_buf[0] == 0 && pref_buf[1] == 0)) 388 | r = ack_stream(&encoder, stream_id); 389 | else 390 | r = 0; 391 | if (r == 0 && encoder.qpe_ins_count > s_saved_ins_count) 392 | r = ack_last_entry_id(&encoder); 393 | else 394 | r = 0; 395 | if (r != 0) 396 | { 397 | fprintf(stderr, "acking stream %u failed: %s", stream_id, 398 | strerror(errno)); 399 | exit(EXIT_FAILURE); 400 | } 401 | } 402 | if (s_verbose) 403 | fprintf(stderr, "compression ratio: %.3f\n", 404 | lsqpack_enc_ratio(&encoder)); 405 | write_enc_and_header_streams(out, stream_id, enc_buf, enc_off, 406 | pref_buf, pref_sz, hea_buf, hea_off); 407 | enc_off = 0; 408 | hea_off = 0; 409 | header_opened = 0; 410 | } 411 | continue; 412 | } 413 | 414 | if (*line == '#') 415 | { 416 | if (!process_annotations) 417 | continue; 418 | 419 | /* Lines starting with ## are potential annotations */ 420 | if (ack_mode != ACK_IMMEDIATE 421 | /* Ignore ACK annotations in immediate ACK mode, as we do 422 | * not tolerate duplicate ACKs. 423 | */ 424 | && 1 == sscanf(line, "## %*[a] %u ", &arg)) 425 | { 426 | if (0 != ack_stream(&encoder, arg)) 427 | { 428 | fprintf(stderr, "ACKing stream ID %u failed\n", arg); 429 | exit(EXIT_FAILURE); 430 | } 431 | } 432 | else if (1 == sscanf(line, "## %*[s] %u", &arg)) 433 | sync_table(&encoder, arg); 434 | else if (1 == sscanf(line, "## %*[c] %u", &arg)) 435 | cancel_stream(&encoder, arg); 436 | else if (1 == sscanf(line, "## %*[t] %u", &arg)) 437 | { 438 | tsu_buf_sz = sizeof(tsu_buf); 439 | if (0 != lsqpack_enc_set_max_capacity(&encoder, arg, tsu_buf, 440 | &tsu_buf_sz)) 441 | { 442 | fprintf(stderr, "cannot set capacity to %u: %s\n", arg, 443 | strerror(errno)); 444 | exit(EXIT_FAILURE); 445 | } 446 | write_enc_stream(out, tsu_buf, tsu_buf_sz); 447 | } 448 | continue; 449 | } 450 | 451 | tab = strchr(line, '\t'); 452 | if (!tab) 453 | { 454 | fprintf(stderr, "no TAB on line %u\n", lineno); 455 | exit(EXIT_FAILURE); 456 | } 457 | 458 | if (!header_opened) 459 | { 460 | ++stream_id; 461 | if (0 != lsqpack_enc_start_header(&encoder, stream_id, 0)) 462 | { 463 | fprintf(stderr, "start_header failed: %s\n", strerror(errno)); 464 | exit(EXIT_FAILURE); 465 | } 466 | header_opened = 1; 467 | } 468 | if (fast) 469 | { 470 | enc_sz = sizeof(enc_buf) - enc_off; 471 | hea_sz = sizeof(hea_buf) - hea_off; 472 | } 473 | else 474 | { 475 | /* Increase buffers one by one to exercise error conditions */ 476 | enc_sz = 0; 477 | hea_sz = 0; 478 | } 479 | while (1) 480 | { 481 | lsxpack_header_set_offset2(&xhdr, line, 0, tab - line, 482 | tab + 1 - line, end - tab - 1); 483 | st = lsqpack_enc_encode(&encoder, enc_buf + enc_off, &enc_sz, 484 | hea_buf + hea_off, &hea_sz, &xhdr, 0); 485 | switch (st) 486 | { 487 | case LQES_NOBUF_ENC: 488 | if (enc_sz < sizeof(enc_buf) - enc_off) 489 | ++enc_sz; 490 | else 491 | assert(0); 492 | break; 493 | case LQES_NOBUF_HEAD: 494 | if (hea_sz < sizeof(hea_buf) - hea_off) 495 | ++hea_sz; 496 | else 497 | assert(0); 498 | break; 499 | default: 500 | assert(st == LQES_OK); 501 | goto end_encode_one_header; 502 | } 503 | } 504 | if (st != LQES_OK) 505 | { 506 | /* It could only run of of output space, so it's not really an 507 | * error, but we make no provision in the interop encoder to 508 | * grow the buffers. 509 | */ 510 | fprintf(stderr, "Could not encode header on line %u: %u\n", 511 | lineno, st); 512 | exit(EXIT_FAILURE); 513 | } 514 | end_encode_one_header: 515 | enc_off += enc_sz; 516 | hea_off += hea_sz; 517 | } 518 | 519 | if (s_verbose) 520 | fprintf(stderr, "exited while loop\n"); 521 | 522 | (void) fclose(in); 523 | 524 | if (header_opened) 525 | { 526 | if (s_verbose) 527 | fprintf(stderr, "close opened header\n"); 528 | pref_sz = lsqpack_enc_end_header(&encoder, pref_buf, sizeof(pref_buf), 529 | &hflags); 530 | if (pref_sz < 0) 531 | { 532 | fprintf(stderr, "end_header failed: %s", strerror(errno)); 533 | exit(EXIT_FAILURE); 534 | } 535 | if (max_risked_streams == 0) 536 | assert(!(hflags & LSQECH_REF_AT_RISK)); 537 | if (ack_mode == ACK_IMMEDIATE 538 | && !(2 == pref_sz && pref_buf[0] == 0 && pref_buf[1] == 0) 539 | && 0 != ack_stream(&encoder, stream_id)) 540 | { 541 | fprintf(stderr, "acking stream %u failed: %s", stream_id, 542 | strerror(errno)); 543 | exit(EXIT_FAILURE); 544 | } 545 | if (s_verbose) 546 | fprintf(stderr, "compression ratio: %.3f\n", 547 | lsqpack_enc_ratio(&encoder)); 548 | write_enc_and_header_streams(out, stream_id, enc_buf, enc_off, pref_buf, 549 | pref_sz, hea_buf, hea_off); 550 | } 551 | 552 | lsqpack_enc_cleanup(&encoder); 553 | 554 | if (0 != fclose(out)) 555 | { 556 | perror("fclose(out)"); 557 | exit(EXIT_FAILURE); 558 | } 559 | 560 | exit(EXIT_SUCCESS); 561 | } 562 | -------------------------------------------------------------------------------- /deps/xxhash/xxhash.c: -------------------------------------------------------------------------------- 1 | /* 2 | xxHash - Fast Hash algorithm 3 | Copyright (C) 2012-2014, Yann Collet. 4 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following disclaimer 14 | in the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | You can contact the author at : 30 | - xxHash source repository : http://code.google.com/p/xxhash/ 31 | - public discussion board : https://groups.google.com/forum/#!forum/lz4c 32 | */ 33 | 34 | 35 | //************************************** 36 | // Tuning parameters 37 | //************************************** 38 | // Unaligned memory access is automatically enabled for "common" CPU, such as x86. 39 | // For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected. 40 | // If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance. 41 | // You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32). 42 | #if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) 43 | # define XXH_USE_UNALIGNED_ACCESS 1 44 | #endif 45 | 46 | // XXH_ACCEPT_NULL_INPUT_POINTER : 47 | // If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. 48 | // When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. 49 | // This option has a very small performance cost (only measurable on small inputs). 50 | // By default, this option is disabled. To enable it, uncomment below define : 51 | // #define XXH_ACCEPT_NULL_INPUT_POINTER 1 52 | 53 | // XXH_FORCE_NATIVE_FORMAT : 54 | // By default, xxHash library provides endian-independant Hash values, based on little-endian convention. 55 | // Results are therefore identical for little-endian and big-endian CPU. 56 | // This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. 57 | // Should endian-independance be of no importance for your application, you may set the #define below to 1. 58 | // It will improve speed for Big-endian CPU. 59 | // This option has no impact on Little_Endian CPU. 60 | #define XXH_FORCE_NATIVE_FORMAT 0 61 | 62 | //************************************** 63 | // Compiler Specific Options 64 | //************************************** 65 | // Disable some Visual warning messages 66 | #ifdef _MSC_VER // Visual Studio 67 | # pragma warning(disable : 4127) // disable: C4127: conditional expression is constant 68 | #endif 69 | 70 | #ifdef _MSC_VER // Visual Studio 71 | # define FORCE_INLINE static __forceinline 72 | #else 73 | # ifdef __GNUC__ 74 | # define FORCE_INLINE static inline __attribute__((always_inline)) 75 | # else 76 | # define FORCE_INLINE static inline 77 | # endif 78 | #endif 79 | 80 | //************************************** 81 | // Includes & Memory related functions 82 | //************************************** 83 | #include "xxhash.h" 84 | // Modify the local functions below should you wish to use some other memory routines 85 | // for malloc(), free() 86 | #include 87 | static void *XXH_malloc(size_t s) { return malloc(s); } 88 | static void XXH_free(void *p) { free(p); } 89 | // for memcpy() 90 | #include 91 | static void *XXH_memcpy(void *dest, const void *src, size_t size) 92 | { 93 | return memcpy(dest, src, size); 94 | } 95 | 96 | 97 | //************************************** 98 | // Basic Types 99 | //************************************** 100 | #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99 101 | # include 102 | typedef uint8_t BYTE; 103 | typedef uint16_t U16; 104 | typedef uint32_t U32; 105 | typedef int32_t S32; 106 | typedef uint64_t U64; 107 | #else 108 | typedef unsigned char BYTE; 109 | typedef unsigned short U16; 110 | typedef unsigned int U32; 111 | typedef signed int S32; 112 | typedef unsigned long long U64; 113 | #endif 114 | 115 | #if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS) 116 | # define _PACKED __attribute__ ((packed)) 117 | #else 118 | # define _PACKED 119 | #endif 120 | 121 | #if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) 122 | # ifdef __IBMC__ 123 | # pragma pack(1) 124 | # else 125 | # pragma pack(push, 1) 126 | # endif 127 | #endif 128 | 129 | typedef struct _U32_S 130 | { 131 | U32 v; 132 | } _PACKED U32_S; 133 | typedef struct _U64_S 134 | { 135 | U64 v; 136 | } _PACKED U64_S; 137 | 138 | #if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) 139 | # pragma pack(pop) 140 | #endif 141 | 142 | #define A32(x) (((U32_S *)(x))->v) 143 | #define A64(x) (((U64_S *)(x))->v) 144 | 145 | 146 | //*************************************** 147 | // Compiler-specific Functions and Macros 148 | //*************************************** 149 | #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) 150 | 151 | // Note : although _rotl exists for minGW (GCC under windows), performance seems poor 152 | #if defined(_MSC_VER) 153 | # define XXH_rotl32(x,r) _rotl(x,r) 154 | # define XXH_rotl64(x,r) _rotl64(x,r) 155 | #else 156 | # define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) 157 | # define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) 158 | #endif 159 | 160 | #if defined(_MSC_VER) // Visual Studio 161 | # define XXH_swap32 _byteswap_ulong 162 | # define XXH_swap64 _byteswap_uint64 163 | #elif GCC_VERSION >= 403 164 | # define XXH_swap32 __builtin_bswap32 165 | # define XXH_swap64 __builtin_bswap64 166 | #else 167 | static inline U32 XXH_swap32(U32 x) 168 | { 169 | return ((x << 24) & 0xff000000) | 170 | ((x << 8) & 0x00ff0000) | 171 | ((x >> 8) & 0x0000ff00) | 172 | ((x >> 24) & 0x000000ff); 173 | } 174 | static inline U64 XXH_swap64(U64 x) 175 | { 176 | return ((x << 56) & 0xff00000000000000ULL) | 177 | ((x << 40) & 0x00ff000000000000ULL) | 178 | ((x << 24) & 0x0000ff0000000000ULL) | 179 | ((x << 8) & 0x000000ff00000000ULL) | 180 | ((x >> 8) & 0x00000000ff000000ULL) | 181 | ((x >> 24) & 0x0000000000ff0000ULL) | 182 | ((x >> 40) & 0x000000000000ff00ULL) | 183 | ((x >> 56) & 0x00000000000000ffULL); 184 | } 185 | #endif 186 | 187 | 188 | //************************************** 189 | // Constants 190 | //************************************** 191 | #define PRIME32_1 2654435761U 192 | #define PRIME32_2 2246822519U 193 | #define PRIME32_3 3266489917U 194 | #define PRIME32_4 668265263U 195 | #define PRIME32_5 374761393U 196 | 197 | #define PRIME64_1 11400714785074694791ULL 198 | #define PRIME64_2 14029467366897019727ULL 199 | #define PRIME64_3 1609587929392839161ULL 200 | #define PRIME64_4 9650029242287828579ULL 201 | #define PRIME64_5 2870177450012600261ULL 202 | 203 | //************************************** 204 | // Architecture Macros 205 | //************************************** 206 | typedef enum { XXH_bigEndian = 0, XXH_littleEndian = 1 } XXH_endianess; 207 | #ifndef XXH_CPU_LITTLE_ENDIAN // It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch 208 | static const int one = 1; 209 | # define XXH_CPU_LITTLE_ENDIAN (*(char*)(&one)) 210 | #endif 211 | 212 | 213 | //************************************** 214 | // Macros 215 | //************************************** 216 | #define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; } // use only *after* variable declarations 217 | 218 | 219 | //**************************** 220 | // Memory reads 221 | //**************************** 222 | typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; 223 | 224 | FORCE_INLINE U32 XXH_readLE32_align(const void *ptr, XXH_endianess endian, 225 | XXH_alignment align) 226 | { 227 | if (align == XXH_unaligned) 228 | return endian == XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr)); 229 | else 230 | return endian == XXH_littleEndian ? *(U32 *)ptr : XXH_swap32(*(U32 *)ptr); 231 | } 232 | 233 | FORCE_INLINE U32 XXH_readLE32(const void *ptr, XXH_endianess endian) 234 | { 235 | return XXH_readLE32_align(ptr, endian, XXH_unaligned); 236 | } 237 | 238 | FORCE_INLINE U64 XXH_readLE64_align(const void *ptr, XXH_endianess endian, 239 | XXH_alignment align) 240 | { 241 | if (align == XXH_unaligned) 242 | return endian == XXH_littleEndian ? A64(ptr) : XXH_swap64(A64(ptr)); 243 | else 244 | return endian == XXH_littleEndian ? *(U64 *)ptr : XXH_swap64(*(U64 *)ptr); 245 | } 246 | 247 | FORCE_INLINE U64 XXH_readLE64(const void *ptr, XXH_endianess endian) 248 | { 249 | return XXH_readLE64_align(ptr, endian, XXH_unaligned); 250 | } 251 | 252 | 253 | //**************************** 254 | // Simple Hash Functions 255 | //**************************** 256 | FORCE_INLINE U32 XXH32_endian_align(const void *input, size_t len, 257 | U32 seed, XXH_endianess endian, XXH_alignment align) 258 | { 259 | const BYTE *p = (const BYTE *)input; 260 | const BYTE *bEnd = p + len; 261 | U32 h32; 262 | #define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) 263 | 264 | #ifdef XXH_ACCEPT_NULL_INPUT_POINTER 265 | if (p == NULL) 266 | { 267 | len = 0; 268 | bEnd = p = (const BYTE *)(size_t)16; 269 | } 270 | #endif 271 | 272 | if (len >= 16) 273 | { 274 | const BYTE *const limit = bEnd - 16; 275 | U32 v1 = seed + PRIME32_1 + PRIME32_2; 276 | U32 v2 = seed + PRIME32_2; 277 | U32 v3 = seed + 0; 278 | U32 v4 = seed - PRIME32_1; 279 | 280 | do 281 | { 282 | v1 += XXH_get32bits(p) * PRIME32_2; 283 | v1 = XXH_rotl32(v1, 13); 284 | v1 *= PRIME32_1; 285 | p += 4; 286 | v2 += XXH_get32bits(p) * PRIME32_2; 287 | v2 = XXH_rotl32(v2, 13); 288 | v2 *= PRIME32_1; 289 | p += 4; 290 | v3 += XXH_get32bits(p) * PRIME32_2; 291 | v3 = XXH_rotl32(v3, 13); 292 | v3 *= PRIME32_1; 293 | p += 4; 294 | v4 += XXH_get32bits(p) * PRIME32_2; 295 | v4 = XXH_rotl32(v4, 13); 296 | v4 *= PRIME32_1; 297 | p += 4; 298 | } 299 | while (p <= limit); 300 | 301 | h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 302 | 12) + XXH_rotl32(v4, 18); 303 | } 304 | else 305 | h32 = seed + PRIME32_5; 306 | 307 | h32 += (U32) len; 308 | 309 | while (p + 4 <= bEnd) 310 | { 311 | h32 += XXH_get32bits(p) * PRIME32_3; 312 | h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; 313 | p += 4; 314 | } 315 | 316 | while (p < bEnd) 317 | { 318 | h32 += (*p) * PRIME32_5; 319 | h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; 320 | p++; 321 | } 322 | 323 | h32 ^= h32 >> 15; 324 | h32 *= PRIME32_2; 325 | h32 ^= h32 >> 13; 326 | h32 *= PRIME32_3; 327 | h32 ^= h32 >> 16; 328 | 329 | return h32; 330 | } 331 | 332 | 333 | unsigned int XXH32(const void *input, size_t len, unsigned seed) 334 | { 335 | #if 0 336 | // Simple version, good for code maintenance, but unfortunately slow for small inputs 337 | XXH32_state_t state; 338 | XXH32_reset(&state, seed); 339 | XXH32_update(&state, input, len); 340 | return XXH32_digest(&state); 341 | #else 342 | XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; 343 | 344 | # if !defined(XXH_USE_UNALIGNED_ACCESS) 345 | if ((((size_t)input) & 3) == 346 | 0) // Input is aligned, let's leverage the speed advantage 347 | { 348 | if ((endian_detected == XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) 349 | return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); 350 | else 351 | return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); 352 | } 353 | # endif 354 | 355 | if ((endian_detected == XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) 356 | return XXH32_endian_align(input, len, seed, XXH_littleEndian, 357 | XXH_unaligned); 358 | else 359 | return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); 360 | #endif 361 | } 362 | 363 | FORCE_INLINE U64 XXH64_endian_align(const void *input, size_t len, 364 | U64 seed, XXH_endianess endian, XXH_alignment align) 365 | { 366 | const BYTE *p = (const BYTE *)input; 367 | const BYTE *bEnd = p + len; 368 | U64 h64; 369 | #define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) 370 | 371 | #ifdef XXH_ACCEPT_NULL_INPUT_POINTER 372 | if (p == NULL) 373 | { 374 | len = 0; 375 | bEnd = p = (const BYTE *)(size_t)32; 376 | } 377 | #endif 378 | 379 | if (len >= 32) 380 | { 381 | const BYTE *const limit = bEnd - 32; 382 | U64 v1 = seed + PRIME64_1 + PRIME64_2; 383 | U64 v2 = seed + PRIME64_2; 384 | U64 v3 = seed + 0; 385 | U64 v4 = seed - PRIME64_1; 386 | 387 | do 388 | { 389 | v1 += XXH_get64bits(p) * PRIME64_2; 390 | p += 8; 391 | v1 = XXH_rotl64(v1, 31); 392 | v1 *= PRIME64_1; 393 | v2 += XXH_get64bits(p) * PRIME64_2; 394 | p += 8; 395 | v2 = XXH_rotl64(v2, 31); 396 | v2 *= PRIME64_1; 397 | v3 += XXH_get64bits(p) * PRIME64_2; 398 | p += 8; 399 | v3 = XXH_rotl64(v3, 31); 400 | v3 *= PRIME64_1; 401 | v4 += XXH_get64bits(p) * PRIME64_2; 402 | p += 8; 403 | v4 = XXH_rotl64(v4, 31); 404 | v4 *= PRIME64_1; 405 | } 406 | while (p <= limit); 407 | 408 | h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 409 | 12) + XXH_rotl64(v4, 18); 410 | 411 | v1 *= PRIME64_2; 412 | v1 = XXH_rotl64(v1, 31); 413 | v1 *= PRIME64_1; 414 | h64 ^= v1; 415 | h64 = h64 * PRIME64_1 + PRIME64_4; 416 | 417 | v2 *= PRIME64_2; 418 | v2 = XXH_rotl64(v2, 31); 419 | v2 *= PRIME64_1; 420 | h64 ^= v2; 421 | h64 = h64 * PRIME64_1 + PRIME64_4; 422 | 423 | v3 *= PRIME64_2; 424 | v3 = XXH_rotl64(v3, 31); 425 | v3 *= PRIME64_1; 426 | h64 ^= v3; 427 | h64 = h64 * PRIME64_1 + PRIME64_4; 428 | 429 | v4 *= PRIME64_2; 430 | v4 = XXH_rotl64(v4, 31); 431 | v4 *= PRIME64_1; 432 | h64 ^= v4; 433 | h64 = h64 * PRIME64_1 + PRIME64_4; 434 | } 435 | else 436 | h64 = seed + PRIME64_5; 437 | 438 | h64 += (U64) len; 439 | 440 | while (p + 8 <= bEnd) 441 | { 442 | U64 k1 = XXH_get64bits(p); 443 | k1 *= PRIME64_2; 444 | k1 = XXH_rotl64(k1, 31); 445 | k1 *= PRIME64_1; 446 | h64 ^= k1; 447 | h64 = XXH_rotl64(h64, 27) * PRIME64_1 + PRIME64_4; 448 | p += 8; 449 | } 450 | 451 | if (p + 4 <= bEnd) 452 | { 453 | h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; 454 | h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; 455 | p += 4; 456 | } 457 | 458 | while (p < bEnd) 459 | { 460 | h64 ^= (*p) * PRIME64_5; 461 | h64 = XXH_rotl64(h64, 11) * PRIME64_1; 462 | p++; 463 | } 464 | 465 | h64 ^= h64 >> 33; 466 | h64 *= PRIME64_2; 467 | h64 ^= h64 >> 29; 468 | h64 *= PRIME64_3; 469 | h64 ^= h64 >> 32; 470 | 471 | return h64; 472 | } 473 | 474 | 475 | unsigned long long XXH64(const void *input, size_t len, 476 | unsigned long long seed) 477 | { 478 | #if 0 479 | // Simple version, good for code maintenance, but unfortunately slow for small inputs 480 | XXH64_state_t state; 481 | XXH64_reset(&state, seed); 482 | XXH64_update(&state, input, len); 483 | return XXH64_digest(&state); 484 | #else 485 | XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; 486 | 487 | # if !defined(XXH_USE_UNALIGNED_ACCESS) 488 | if ((((size_t)input) & 7) == 489 | 0) // Input is aligned, let's leverage the speed advantage 490 | { 491 | if ((endian_detected == XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) 492 | return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); 493 | else 494 | return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); 495 | } 496 | # endif 497 | 498 | if ((endian_detected == XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) 499 | return XXH64_endian_align(input, len, seed, XXH_littleEndian, 500 | XXH_unaligned); 501 | else 502 | return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); 503 | #endif 504 | } 505 | 506 | /**************************************************** 507 | * Advanced Hash Functions 508 | ****************************************************/ 509 | 510 | /*** Allocation ***/ 511 | typedef struct 512 | { 513 | U64 total_len; 514 | U32 seed; 515 | U32 v1; 516 | U32 v2; 517 | U32 v3; 518 | U32 v4; 519 | U32 mem32[4]; /* defined as U32 for alignment */ 520 | U32 memsize; 521 | } XXH_istate32_t; 522 | 523 | typedef struct 524 | { 525 | U64 total_len; 526 | U64 seed; 527 | U64 v1; 528 | U64 v2; 529 | U64 v3; 530 | U64 v4; 531 | U64 mem64[4]; /* defined as U64 for alignment */ 532 | U32 memsize; 533 | } XXH_istate64_t; 534 | 535 | 536 | XXH32_state_t *XXH32_createState(void) 537 | { 538 | XXH_STATIC_ASSERT(sizeof(XXH32_state_t) >= sizeof( 539 | XXH_istate32_t)); // A compilation error here means XXH32_state_t is not large enough 540 | return (XXH32_state_t *)XXH_malloc(sizeof(XXH32_state_t)); 541 | } 542 | XXH_errorcode XXH32_freeState(XXH32_state_t *statePtr) 543 | { 544 | XXH_free(statePtr); 545 | return XXH_OK; 546 | }; 547 | 548 | XXH64_state_t *XXH64_createState(void) 549 | { 550 | XXH_STATIC_ASSERT(sizeof(XXH64_state_t) >= sizeof( 551 | XXH_istate64_t)); // A compilation error here means XXH64_state_t is not large enough 552 | return (XXH64_state_t *)XXH_malloc(sizeof(XXH64_state_t)); 553 | } 554 | XXH_errorcode XXH64_freeState(XXH64_state_t *statePtr) 555 | { 556 | XXH_free(statePtr); 557 | return XXH_OK; 558 | }; 559 | 560 | 561 | /*** Hash feed ***/ 562 | 563 | XXH_errorcode XXH32_reset(XXH32_state_t *state_in, U32 seed) 564 | { 565 | XXH_istate32_t *state = (XXH_istate32_t *) state_in; 566 | state->seed = seed; 567 | state->v1 = seed + PRIME32_1 + PRIME32_2; 568 | state->v2 = seed + PRIME32_2; 569 | state->v3 = seed + 0; 570 | state->v4 = seed - PRIME32_1; 571 | state->total_len = 0; 572 | state->memsize = 0; 573 | return XXH_OK; 574 | } 575 | 576 | XXH_errorcode XXH64_reset(XXH64_state_t *state_in, unsigned long long seed) 577 | { 578 | XXH_istate64_t *state = (XXH_istate64_t *) state_in; 579 | state->seed = seed; 580 | state->v1 = seed + PRIME64_1 + PRIME64_2; 581 | state->v2 = seed + PRIME64_2; 582 | state->v3 = seed + 0; 583 | state->v4 = seed - PRIME64_1; 584 | state->total_len = 0; 585 | state->memsize = 0; 586 | return XXH_OK; 587 | } 588 | 589 | 590 | FORCE_INLINE XXH_errorcode XXH32_update_endian(XXH32_state_t *state_in, 591 | const void *input, size_t len, XXH_endianess endian) 592 | { 593 | XXH_istate32_t *state = (XXH_istate32_t *) state_in; 594 | const BYTE *p = (const BYTE *)input; 595 | const BYTE *const bEnd = p + len; 596 | 597 | #ifdef XXH_ACCEPT_NULL_INPUT_POINTER 598 | if (input == NULL) return XXH_ERROR; 599 | #endif 600 | 601 | state->total_len += len; 602 | 603 | if (state->memsize + len < 16) // fill in tmp buffer 604 | { 605 | XXH_memcpy((BYTE *)(state->mem32) + state->memsize, input, len); 606 | state->memsize += (U32)len; 607 | return XXH_OK; 608 | } 609 | 610 | if (state->memsize) // some data left from previous update 611 | { 612 | XXH_memcpy((BYTE *)(state->mem32) + state->memsize, input, 613 | 16 - state->memsize); 614 | { 615 | const U32 *p32 = state->mem32; 616 | state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; 617 | state->v1 = XXH_rotl32(state->v1, 13); 618 | state->v1 *= PRIME32_1; 619 | p32++; 620 | state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; 621 | state->v2 = XXH_rotl32(state->v2, 13); 622 | state->v2 *= PRIME32_1; 623 | p32++; 624 | state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; 625 | state->v3 = XXH_rotl32(state->v3, 13); 626 | state->v3 *= PRIME32_1; 627 | p32++; 628 | state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; 629 | state->v4 = XXH_rotl32(state->v4, 13); 630 | state->v4 *= PRIME32_1; 631 | p32++; 632 | } 633 | p += 16 - state->memsize; 634 | state->memsize = 0; 635 | } 636 | 637 | if (p <= bEnd - 16) 638 | { 639 | const BYTE *const limit = bEnd - 16; 640 | U32 v1 = state->v1; 641 | U32 v2 = state->v2; 642 | U32 v3 = state->v3; 643 | U32 v4 = state->v4; 644 | 645 | do 646 | { 647 | v1 += XXH_readLE32(p, endian) * PRIME32_2; 648 | v1 = XXH_rotl32(v1, 13); 649 | v1 *= PRIME32_1; 650 | p += 4; 651 | v2 += XXH_readLE32(p, endian) * PRIME32_2; 652 | v2 = XXH_rotl32(v2, 13); 653 | v2 *= PRIME32_1; 654 | p += 4; 655 | v3 += XXH_readLE32(p, endian) * PRIME32_2; 656 | v3 = XXH_rotl32(v3, 13); 657 | v3 *= PRIME32_1; 658 | p += 4; 659 | v4 += XXH_readLE32(p, endian) * PRIME32_2; 660 | v4 = XXH_rotl32(v4, 13); 661 | v4 *= PRIME32_1; 662 | p += 4; 663 | } 664 | while (p <= limit); 665 | 666 | state->v1 = v1; 667 | state->v2 = v2; 668 | state->v3 = v3; 669 | state->v4 = v4; 670 | } 671 | 672 | if (p < bEnd) 673 | { 674 | XXH_memcpy(state->mem32, p, bEnd - p); 675 | state->memsize = (int)(bEnd - p); 676 | } 677 | 678 | return XXH_OK; 679 | } 680 | 681 | XXH_errorcode XXH32_update(XXH32_state_t *state_in, const void *input, 682 | size_t len) 683 | { 684 | XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; 685 | 686 | if ((endian_detected == XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) 687 | return XXH32_update_endian(state_in, input, len, XXH_littleEndian); 688 | else 689 | return XXH32_update_endian(state_in, input, len, XXH_bigEndian); 690 | } 691 | 692 | 693 | 694 | FORCE_INLINE U32 XXH32_digest_endian(const XXH32_state_t *state_in, 695 | XXH_endianess endian) 696 | { 697 | XXH_istate32_t *state = (XXH_istate32_t *) state_in; 698 | const BYTE *p = (const BYTE *)state->mem32; 699 | BYTE *bEnd = (BYTE *)(state->mem32) + state->memsize; 700 | U32 h32; 701 | 702 | if (state->total_len >= 16) 703 | h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 704 | 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); 705 | else 706 | h32 = state->seed + PRIME32_5; 707 | 708 | h32 += (U32) state->total_len; 709 | 710 | while (p + 4 <= bEnd) 711 | { 712 | h32 += XXH_readLE32(p, endian) * PRIME32_3; 713 | h32 = XXH_rotl32(h32, 17) * PRIME32_4; 714 | p += 4; 715 | } 716 | 717 | while (p < bEnd) 718 | { 719 | h32 += (*p) * PRIME32_5; 720 | h32 = XXH_rotl32(h32, 11) * PRIME32_1; 721 | p++; 722 | } 723 | 724 | h32 ^= h32 >> 15; 725 | h32 *= PRIME32_2; 726 | h32 ^= h32 >> 13; 727 | h32 *= PRIME32_3; 728 | h32 ^= h32 >> 16; 729 | 730 | return h32; 731 | } 732 | 733 | 734 | U32 XXH32_digest(const XXH32_state_t *state_in) 735 | { 736 | XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; 737 | 738 | if ((endian_detected == XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) 739 | return XXH32_digest_endian(state_in, XXH_littleEndian); 740 | else 741 | return XXH32_digest_endian(state_in, XXH_bigEndian); 742 | } 743 | 744 | 745 | FORCE_INLINE XXH_errorcode XXH64_update_endian(XXH64_state_t *state_in, 746 | const void *input, size_t len, XXH_endianess endian) 747 | { 748 | XXH_istate64_t *state = (XXH_istate64_t *) state_in; 749 | const BYTE *p = (const BYTE *)input; 750 | const BYTE *const bEnd = p + len; 751 | 752 | #ifdef XXH_ACCEPT_NULL_INPUT_POINTER 753 | if (input == NULL) return XXH_ERROR; 754 | #endif 755 | 756 | state->total_len += len; 757 | 758 | if (state->memsize + len < 32) // fill in tmp buffer 759 | { 760 | XXH_memcpy(((BYTE *)state->mem64) + state->memsize, input, len); 761 | state->memsize += (U32)len; 762 | return XXH_OK; 763 | } 764 | 765 | if (state->memsize) // some data left from previous update 766 | { 767 | XXH_memcpy(((BYTE *)state->mem64) + state->memsize, input, 768 | 32 - state->memsize); 769 | { 770 | const U64 *p64 = state->mem64; 771 | state->v1 += XXH_readLE64(p64, endian) * PRIME64_2; 772 | state->v1 = XXH_rotl64(state->v1, 31); 773 | state->v1 *= PRIME64_1; 774 | p64++; 775 | state->v2 += XXH_readLE64(p64, endian) * PRIME64_2; 776 | state->v2 = XXH_rotl64(state->v2, 31); 777 | state->v2 *= PRIME64_1; 778 | p64++; 779 | state->v3 += XXH_readLE64(p64, endian) * PRIME64_2; 780 | state->v3 = XXH_rotl64(state->v3, 31); 781 | state->v3 *= PRIME64_1; 782 | p64++; 783 | state->v4 += XXH_readLE64(p64, endian) * PRIME64_2; 784 | state->v4 = XXH_rotl64(state->v4, 31); 785 | state->v4 *= PRIME64_1; 786 | p64++; 787 | } 788 | p += 32 - state->memsize; 789 | state->memsize = 0; 790 | } 791 | 792 | if (p + 32 <= bEnd) 793 | { 794 | const BYTE *const limit = bEnd - 32; 795 | U64 v1 = state->v1; 796 | U64 v2 = state->v2; 797 | U64 v3 = state->v3; 798 | U64 v4 = state->v4; 799 | 800 | do 801 | { 802 | v1 += XXH_readLE64(p, endian) * PRIME64_2; 803 | v1 = XXH_rotl64(v1, 31); 804 | v1 *= PRIME64_1; 805 | p += 8; 806 | v2 += XXH_readLE64(p, endian) * PRIME64_2; 807 | v2 = XXH_rotl64(v2, 31); 808 | v2 *= PRIME64_1; 809 | p += 8; 810 | v3 += XXH_readLE64(p, endian) * PRIME64_2; 811 | v3 = XXH_rotl64(v3, 31); 812 | v3 *= PRIME64_1; 813 | p += 8; 814 | v4 += XXH_readLE64(p, endian) * PRIME64_2; 815 | v4 = XXH_rotl64(v4, 31); 816 | v4 *= PRIME64_1; 817 | p += 8; 818 | } 819 | while (p <= limit); 820 | 821 | state->v1 = v1; 822 | state->v2 = v2; 823 | state->v3 = v3; 824 | state->v4 = v4; 825 | } 826 | 827 | if (p < bEnd) 828 | { 829 | XXH_memcpy(state->mem64, p, bEnd - p); 830 | state->memsize = (int)(bEnd - p); 831 | } 832 | 833 | return XXH_OK; 834 | } 835 | 836 | XXH_errorcode XXH64_update(XXH64_state_t *state_in, const void *input, 837 | size_t len) 838 | { 839 | XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; 840 | 841 | if ((endian_detected == XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) 842 | return XXH64_update_endian(state_in, input, len, XXH_littleEndian); 843 | else 844 | return XXH64_update_endian(state_in, input, len, XXH_bigEndian); 845 | } 846 | 847 | 848 | 849 | FORCE_INLINE U64 XXH64_digest_endian(const XXH64_state_t *state_in, 850 | XXH_endianess endian) 851 | { 852 | XXH_istate64_t *state = (XXH_istate64_t *) state_in; 853 | const BYTE *p = (const BYTE *)state->mem64; 854 | BYTE *bEnd = (BYTE *)state->mem64 + state->memsize; 855 | U64 h64; 856 | 857 | if (state->total_len >= 32) 858 | { 859 | U64 v1 = state->v1; 860 | U64 v2 = state->v2; 861 | U64 v3 = state->v3; 862 | U64 v4 = state->v4; 863 | 864 | h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 865 | 12) + XXH_rotl64(v4, 18); 866 | 867 | v1 *= PRIME64_2; 868 | v1 = XXH_rotl64(v1, 31); 869 | v1 *= PRIME64_1; 870 | h64 ^= v1; 871 | h64 = h64 * PRIME64_1 + PRIME64_4; 872 | 873 | v2 *= PRIME64_2; 874 | v2 = XXH_rotl64(v2, 31); 875 | v2 *= PRIME64_1; 876 | h64 ^= v2; 877 | h64 = h64 * PRIME64_1 + PRIME64_4; 878 | 879 | v3 *= PRIME64_2; 880 | v3 = XXH_rotl64(v3, 31); 881 | v3 *= PRIME64_1; 882 | h64 ^= v3; 883 | h64 = h64 * PRIME64_1 + PRIME64_4; 884 | 885 | v4 *= PRIME64_2; 886 | v4 = XXH_rotl64(v4, 31); 887 | v4 *= PRIME64_1; 888 | h64 ^= v4; 889 | h64 = h64 * PRIME64_1 + PRIME64_4; 890 | } 891 | else 892 | h64 = state->seed + PRIME64_5; 893 | 894 | h64 += (U64) state->total_len; 895 | 896 | while (p + 8 <= bEnd) 897 | { 898 | U64 k1 = XXH_readLE64(p, endian); 899 | k1 *= PRIME64_2; 900 | k1 = XXH_rotl64(k1, 31); 901 | k1 *= PRIME64_1; 902 | h64 ^= k1; 903 | h64 = XXH_rotl64(h64, 27) * PRIME64_1 + PRIME64_4; 904 | p += 8; 905 | } 906 | 907 | if (p + 4 <= bEnd) 908 | { 909 | h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1; 910 | h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; 911 | p += 4; 912 | } 913 | 914 | while (p < bEnd) 915 | { 916 | h64 ^= (*p) * PRIME64_5; 917 | h64 = XXH_rotl64(h64, 11) * PRIME64_1; 918 | p++; 919 | } 920 | 921 | h64 ^= h64 >> 33; 922 | h64 *= PRIME64_2; 923 | h64 ^= h64 >> 29; 924 | h64 *= PRIME64_3; 925 | h64 ^= h64 >> 32; 926 | 927 | return h64; 928 | } 929 | 930 | 931 | unsigned long long XXH64_digest(const XXH64_state_t *state_in) 932 | { 933 | XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; 934 | 935 | if ((endian_detected == XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) 936 | return XXH64_digest_endian(state_in, XXH_littleEndian); 937 | else 938 | return XXH64_digest_endian(state_in, XXH_bigEndian); 939 | } 940 | 941 | 942 | -------------------------------------------------------------------------------- /deps/xxhash/xxhash.h: -------------------------------------------------------------------------------- 1 | /* 2 | xxHash - Extremely Fast Hash algorithm 3 | Header File 4 | Copyright (C) 2012-2014, Yann Collet. 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are 9 | met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the following disclaimer 15 | in the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | You can contact the author at : 31 | - xxHash source repository : http://code.google.com/p/xxhash/ 32 | */ 33 | 34 | /* Notice extracted from xxHash homepage : 35 | 36 | xxHash is an extremely fast Hash algorithm, running at RAM speed limits. 37 | It also successfully passes all tests from the SMHasher suite. 38 | 39 | Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) 40 | 41 | Name Speed Q.Score Author 42 | xxHash 5.4 GB/s 10 43 | CrapWow 3.2 GB/s 2 Andrew 44 | MumurHash 3a 2.7 GB/s 10 Austin Appleby 45 | SpookyHash 2.0 GB/s 10 Bob Jenkins 46 | SBox 1.4 GB/s 9 Bret Mulvey 47 | Lookup3 1.2 GB/s 9 Bob Jenkins 48 | SuperFastHash 1.2 GB/s 1 Paul Hsieh 49 | CityHash64 1.05 GB/s 10 Pike & Alakuijala 50 | FNV 0.55 GB/s 5 Fowler, Noll, Vo 51 | CRC32 0.43 GB/s 9 52 | MD5-32 0.33 GB/s 10 Ronald L. Rivest 53 | SHA1-32 0.28 GB/s 10 54 | 55 | Q.Score is a measure of quality of the hash function. 56 | It depends on successfully passing SMHasher test set. 57 | 10 is a perfect score. 58 | */ 59 | 60 | #pragma once 61 | 62 | #if defined (__cplusplus) 63 | extern "C" { 64 | #endif 65 | 66 | 67 | /***************************** 68 | Includes 69 | *****************************/ 70 | #include /* size_t */ 71 | 72 | 73 | /***************************** 74 | Type 75 | *****************************/ 76 | typedef enum { XXH_OK = 0, XXH_ERROR } XXH_errorcode; 77 | 78 | 79 | 80 | /***************************** 81 | Simple Hash Functions 82 | *****************************/ 83 | 84 | unsigned int XXH32(const void *input, size_t length, unsigned seed); 85 | unsigned long long XXH64(const void *input, size_t length, 86 | unsigned long long seed); 87 | 88 | /* 89 | XXH32() : 90 | Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input". 91 | The memory between input & input+length must be valid (allocated and read-accessible). 92 | "seed" can be used to alter the result predictably. 93 | This function successfully passes all SMHasher tests. 94 | Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s 95 | XXH64() : 96 | Calculate the 64-bits hash of sequence of length "len" stored at memory address "input". 97 | */ 98 | 99 | 100 | 101 | /***************************** 102 | Advanced Hash Functions 103 | *****************************/ 104 | typedef struct { long long ll[ 6]; } XXH32_state_t; 105 | typedef struct { long long ll[11]; } XXH64_state_t; 106 | 107 | /* 108 | These structures allow static allocation of XXH states. 109 | States must then be initialized using XXHnn_reset() before first use. 110 | 111 | If you prefer dynamic allocation, please refer to functions below. 112 | */ 113 | 114 | XXH32_state_t *XXH32_createState(void); 115 | XXH_errorcode XXH32_freeState(XXH32_state_t *statePtr); 116 | 117 | XXH64_state_t *XXH64_createState(void); 118 | XXH_errorcode XXH64_freeState(XXH64_state_t *statePtr); 119 | 120 | /* 121 | These functions create and release memory for XXH state. 122 | States must then be initialized using XXHnn_reset() before first use. 123 | */ 124 | 125 | 126 | XXH_errorcode XXH32_reset(XXH32_state_t *statePtr, unsigned seed); 127 | XXH_errorcode XXH32_update(XXH32_state_t *statePtr, const void *input, 128 | size_t length); 129 | unsigned int XXH32_digest(const XXH32_state_t *statePtr); 130 | 131 | XXH_errorcode XXH64_reset(XXH64_state_t *statePtr, 132 | unsigned long long seed); 133 | XXH_errorcode XXH64_update(XXH64_state_t *statePtr, const void *input, 134 | size_t length); 135 | unsigned long long XXH64_digest(const XXH64_state_t *statePtr); 136 | 137 | /* 138 | These functions calculate the xxHash of an input provided in multiple smaller packets, 139 | as opposed to an input provided as a single block. 140 | 141 | XXH state space must first be allocated, using either static or dynamic method provided above. 142 | 143 | Start a new hash by initializing state with a seed, using XXHnn_reset(). 144 | 145 | Then, feed the hash state by calling XXHnn_update() as many times as necessary. 146 | Obviously, input must be valid, meaning allocated and read accessible. 147 | The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. 148 | 149 | Finally, you can produce a hash anytime, by using XXHnn_digest(). 150 | This function returns the final nn-bits hash. 151 | You can nonetheless continue feeding the hash state with more input, 152 | and therefore get some new hashes, by calling again XXHnn_digest(). 153 | 154 | When you are done, don't forget to free XXH state space, using typically XXHnn_freeState(). 155 | */ 156 | 157 | 158 | #if defined (__cplusplus) 159 | } 160 | #endif 161 | -------------------------------------------------------------------------------- /fuzz/decode/a/README: -------------------------------------------------------------------------------- 1 | These are based on a first couple of blocks of encoded netbsd.qif 2 | 3 | Table size: 256 4 | -------------------------------------------------------------------------------- /fuzz/decode/a/preamble: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/preamble -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000000,sig_06,src_000390,op_havoc,rep_4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000000,sig_06,src_000390,op_havoc,rep_4 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000000,sig_06,src_000579,op_flip1,pos_14: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000000,sig_06,src_000579,op_flip1,pos_14 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000000,src_000000,op_flip2,pos_12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000000,src_000000,op_flip2,pos_12 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000001,sig_11,src_000579,op_havoc,rep_4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000001,sig_11,src_000579,op_havoc,rep_4 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000002,sig_11,src_000481,op_int16,pos_15,val_-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000002,sig_11,src_000481,op_int16,pos_15,val_-1 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000002,src_000000,op_havoc,rep_8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000002,src_000000,op_havoc,rep_8 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000006,src_000285,op_flip2,pos_14: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000006,src_000285,op_flip2,pos_14 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000008,src_000285,op_flip2,pos_20: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000008,src_000285,op_flip2,pos_20 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000010,src_000306,op_flip2,pos_75: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000010,src_000306,op_flip2,pos_75 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000011,src_000344,op_havoc,rep_2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/a/test-cases/id_000011,src_000344,op_havoc,rep_2 -------------------------------------------------------------------------------- /fuzz/decode/a/test-cases/id_000014,src_000366,op_flip2,pos_28: -------------------------------------------------------------------------------- 1 | ??????????????????`� -------------------------------------------------------------------------------- /fuzz/decode/b/README: -------------------------------------------------------------------------------- 1 | Table size: 4096 2 | -------------------------------------------------------------------------------- /fuzz/decode/b/preamble: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/b/preamble -------------------------------------------------------------------------------- /fuzz/decode/b/test-cases/seed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/b/test-cases/seed -------------------------------------------------------------------------------- /fuzz/decode/c/setup.sh: -------------------------------------------------------------------------------- 1 | # Source this file 2 | export USE_QPACK_05=1 3 | ARGS='interop-decode -i @@ -o /dev/null -s 100 -t 256' 4 | -------------------------------------------------------------------------------- /fuzz/decode/c/test-cases/fb-req.qif.proxygen.out.256.100.0-chopped: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/c/test-cases/fb-req.qif.proxygen.out.256.100.0-chopped -------------------------------------------------------------------------------- /fuzz/decode/d/preamble: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/d/preamble -------------------------------------------------------------------------------- /fuzz/decode/d/setup.sh: -------------------------------------------------------------------------------- 1 | # Source this file 2 | export USE_QPACK_05=0 3 | ARGS='fuzz-decode -i fuzz/decode/d/preamble -f @@ -s 100 -t 256' 4 | -------------------------------------------------------------------------------- /fuzz/decode/d/test-cases/fb-resp.minhq.256.128.0.ack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/decode/d/test-cases/fb-resp.minhq.256.128.0.ack -------------------------------------------------------------------------------- /fuzz/input/256.100.1/fb-req.out.256.100.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/input/256.100.1/fb-req.out.256.100.1 -------------------------------------------------------------------------------- /fuzz/input/256.100.1/fb-resp.out.256.100.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/input/256.100.1/fb-resp.out.256.100.1 -------------------------------------------------------------------------------- /fuzz/input/256.100.1/netbsd.out.256.100.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/fuzz/input/256.100.1/netbsd.out.256.100.1 -------------------------------------------------------------------------------- /ls-qpack-config.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT "@BUILD_SHARED_LIBS@" AND NOT "@LSQPACK_XXH@") 2 | include(CMakeFindDependencyMacro) 3 | if("@xxHash_FOUND@") 4 | find_dependency(xxHash CONFIG) 5 | else() 6 | find_dependency(PkgConfig) 7 | pkg_check_modules(XXH REQUIRED IMPORTED_TARGET libxxhash) 8 | endif() 9 | endif() 10 | include("${CMAKE_CURRENT_LIST_DIR}/ls-qpack-targets.cmake") 11 | -------------------------------------------------------------------------------- /lsqpack.pc.in: -------------------------------------------------------------------------------- 1 | prefix="@CMAKE_INSTALL_PREFIX@" 2 | exec_prefix="${prefix}" 3 | libdir="${prefix}/@CMAKE_INSTALL_LIBDIR@" 4 | includedir="${prefix}/@CMAKE_INSTALL_INCLUDEDIR@" 5 | 6 | Name: @PROJECT_NAME@ 7 | Description: @CMAKE_PROJECT_DESCRIPTION@ 8 | URL: @CMAKE_PROJECT_HOMEPAGE_URL@ 9 | Version: @PROJECT_VERSION@ 10 | Requires.private: @LSQPACK_DEPENDS@ 11 | Cflags: -I"${includedir}" 12 | Libs: -L"${libdir}" -lls-qpack 13 | -------------------------------------------------------------------------------- /lsxpack_header.h: -------------------------------------------------------------------------------- 1 | #ifndef LSXPACK_HEADER_H_v208 2 | #define LSXPACK_HEADER_H_v208 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | #ifndef LSXPACK_MAX_STRLEN 13 | #define LSXPACK_MAX_STRLEN UINT16_MAX 14 | #endif 15 | 16 | typedef int32_t lsxpack_offset_t; 17 | #if LSXPACK_MAX_STRLEN == UINT16_MAX 18 | typedef uint16_t lsxpack_strlen_t; 19 | #elif LSXPACK_MAX_STRLEN == UINT32_MAX 20 | typedef uint32_t lsxpack_strlen_t; 21 | #else 22 | #error unexpected LSXPACK_MAX_STRLEN 23 | #endif 24 | 25 | #define LSXPACK_DEL ((char *)NULL) 26 | 27 | enum lsxpack_flag 28 | { 29 | LSXPACK_HPACK_VAL_MATCHED = 1, 30 | LSXPACK_QPACK_IDX = 2, 31 | LSXPACK_APP_IDX = 4, 32 | LSXPACK_NAME_HASH = 8, 33 | LSXPACK_NAMEVAL_HASH = 16, 34 | LSXPACK_VAL_MATCHED = 32, 35 | LSXPACK_NEVER_INDEX = 64, 36 | }; 37 | 38 | /** 39 | * When header are decoded, it should be stored to @buf starting from @name_offset, 40 | * : \r\n 41 | * So, it can be used directly as HTTP/1.1 header. there are 4 extra characters 42 | * added. 43 | * 44 | * limitation: we currently does not support total header size > 64KB. 45 | */ 46 | 47 | struct lsxpack_header 48 | { 49 | char *buf; /* the buffer for headers */ 50 | uint32_t name_hash; /* hash value for name */ 51 | uint32_t nameval_hash; /* hash value for name + value */ 52 | lsxpack_offset_t name_offset; /* the offset for name in the buffer */ 53 | lsxpack_offset_t val_offset; /* the offset for value in the buffer */ 54 | lsxpack_strlen_t name_len; /* the length of name */ 55 | lsxpack_strlen_t val_len; /* the length of value */ 56 | uint16_t chain_next_idx; /* mainly for cookie value chain */ 57 | uint8_t hpack_index; /* HPACK static table index */ 58 | uint8_t qpack_index; /* QPACK static table index */ 59 | uint8_t app_index; /* APP header index */ 60 | enum lsxpack_flag flags:8; /* combination of lsxpack_flag */ 61 | uint8_t indexed_type; /* control to disable index or not */ 62 | uint8_t dec_overhead; /* num of extra bytes written to decoded buffer */ 63 | }; 64 | 65 | typedef struct lsxpack_header lsxpack_header_t; 66 | 67 | 68 | static inline void 69 | lsxpack_header_set_idx(lsxpack_header_t *hdr, int hpack_idx, 70 | const char *val, size_t val_len) 71 | { 72 | memset(hdr, 0, sizeof(*hdr)); 73 | hdr->buf = (char *)val; 74 | hdr->hpack_index = (uint8_t)hpack_idx; 75 | assert(hpack_idx != 0); 76 | assert(val_len <= LSXPACK_MAX_STRLEN); 77 | hdr->val_len = (lsxpack_strlen_t)val_len; 78 | } 79 | 80 | 81 | static inline void 82 | lsxpack_header_set_qpack_idx(lsxpack_header_t *hdr, int qpack_idx, 83 | const char *val, size_t val_len) 84 | { 85 | memset(hdr, 0, sizeof(*hdr)); 86 | hdr->buf = (char *)val; 87 | hdr->qpack_index = (uint8_t)qpack_idx; 88 | assert(qpack_idx != -1); 89 | hdr->flags = LSXPACK_QPACK_IDX; 90 | assert(val_len <= LSXPACK_MAX_STRLEN); 91 | hdr->val_len = (lsxpack_strlen_t)val_len; 92 | } 93 | 94 | 95 | static inline void 96 | lsxpack_header_set_offset(lsxpack_header_t *hdr, const char *buf, 97 | size_t name_offset, size_t name_len, 98 | size_t val_len) 99 | { 100 | memset(hdr, 0, sizeof(*hdr)); 101 | hdr->buf = (char *)buf; 102 | hdr->name_offset = (lsxpack_offset_t)name_offset; 103 | assert(name_len <= LSXPACK_MAX_STRLEN); 104 | hdr->name_len = (lsxpack_strlen_t)name_len; 105 | assert(name_offset + name_len + 2 <= LSXPACK_MAX_STRLEN); 106 | hdr->val_offset = (lsxpack_offset_t)(name_offset + name_len + 2); 107 | assert(val_len <= LSXPACK_MAX_STRLEN); 108 | hdr->val_len = (lsxpack_strlen_t)val_len; 109 | } 110 | 111 | 112 | static inline void 113 | lsxpack_header_set_offset2(lsxpack_header_t *hdr, const char *buf, 114 | size_t name_offset, size_t name_len, 115 | size_t val_offset, size_t val_len) 116 | { 117 | memset(hdr, 0, sizeof(*hdr)); 118 | hdr->buf = (char *)buf; 119 | hdr->name_offset = (lsxpack_offset_t)name_offset; 120 | assert(name_len <= LSXPACK_MAX_STRLEN); 121 | hdr->name_len = (lsxpack_strlen_t)name_len; 122 | assert(val_offset <= LSXPACK_MAX_STRLEN); 123 | hdr->val_offset = (lsxpack_offset_t)val_offset; 124 | assert(val_len <= LSXPACK_MAX_STRLEN); 125 | hdr->val_len = (lsxpack_strlen_t)val_len; 126 | } 127 | 128 | 129 | static inline void 130 | lsxpack_header_prepare_decode(lsxpack_header_t *hdr, 131 | char *out, size_t offset, size_t len) 132 | { 133 | memset(hdr, 0, sizeof(*hdr)); 134 | hdr->buf = out; 135 | assert(offset <= LSXPACK_MAX_STRLEN); 136 | hdr->name_offset = (lsxpack_offset_t)offset; 137 | if (len > LSXPACK_MAX_STRLEN) 138 | hdr->val_len = LSXPACK_MAX_STRLEN; 139 | else 140 | hdr->val_len = (lsxpack_strlen_t)len; 141 | } 142 | 143 | 144 | static inline const char * 145 | lsxpack_header_get_name(const lsxpack_header_t *hdr) 146 | { 147 | return (hdr->name_len)? hdr->buf + hdr->name_offset : NULL; 148 | } 149 | 150 | 151 | static inline const char * 152 | lsxpack_header_get_value(const lsxpack_header_t *hdr) 153 | { return hdr->buf + hdr->val_offset; } 154 | 155 | static inline size_t 156 | lsxpack_header_get_dec_size(const lsxpack_header_t *hdr) 157 | { return hdr->name_len + hdr->val_len + hdr->dec_overhead; } 158 | 159 | static inline void 160 | lsxpack_header_mark_val_changed(lsxpack_header_t *hdr) 161 | { 162 | hdr->flags = (enum lsxpack_flag)(hdr->flags & 163 | ~(LSXPACK_HPACK_VAL_MATCHED|LSXPACK_VAL_MATCHED|LSXPACK_NAMEVAL_HASH)); 164 | } 165 | #ifdef __cplusplus 166 | } 167 | #endif 168 | 169 | #endif //LSXPACK_HEADER_H_v208 170 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function(lsqpack_add_test TARGET) 2 | add_executable(test_${TARGET} "") 3 | target_sources(test_${TARGET} PRIVATE test_${TARGET}.c) 4 | target_link_libraries(test_${TARGET} ls-qpack) 5 | 6 | if(WIN32) 7 | target_include_directories(test_${TARGET} PRIVATE ../wincompat) 8 | target_link_libraries(test_${TARGET} ${GETOPT_LIB}) 9 | else() 10 | target_link_libraries(test_${TARGET} m) 11 | endif() 12 | 13 | add_test(${TARGET} test_${TARGET}) 14 | endfunction() 15 | 16 | lsqpack_add_test(int) 17 | lsqpack_add_test(enc_str) 18 | lsqpack_add_test(huff_dec) 19 | lsqpack_add_test(read_enc_stream) 20 | lsqpack_add_test(qpack) 21 | lsqpack_add_test(circ_list) 22 | lsqpack_add_test(dec_crash_case) 23 | lsqpack_add_test(dyn_table_cap_mismatch) 24 | 25 | if(WIN32) 26 | message(WARNING "Scenario tests are disabled on Windows (TODO)") 27 | else() 28 | file(GLOB SCENARIOS scenarios/*.sce) 29 | foreach(SCENARIO ${SCENARIOS}) 30 | get_filename_component(TEST_NAME ${SCENARIO} NAME) 31 | add_test( 32 | NAME scenario-${TEST_NAME} 33 | COMMAND bash run-scenario.sh ${SCENARIO} 34 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test 35 | ) 36 | set_tests_properties(scenario-${TEST_NAME} PROPERTIES 37 | ENVIRONMENT "PATH=$ENV{PATH}:${PROJECT_BINARY_DIR}/bin:${PROJECT_SOURCE_DIR}/tools" 38 | ) 39 | endforeach() 40 | endif() 41 | 42 | find_package(Perl) 43 | 44 | if(PERL_FOUND) 45 | message(STATUS "Found Perl interpreter: ${PERL_EXECUTABLE}") 46 | file(GLOB QIFS qifs/*.qif) 47 | 48 | foreach(QIF ${QIFS}) 49 | get_filename_component(FILE_NAME ${QIF} NAME) 50 | 51 | foreach(TABLE_SIZE 0 256 512 1024 4096) 52 | foreach(RISKED_STREAMS 0 100) 53 | foreach(IMMEDIATE_ACK 0 1) 54 | foreach(AGGRESSIVE 0 1) 55 | set(TEST_NAME qif-${FILE_NAME}-t${TABLE_SIZE}-s${RISKED_STREAMS}-a${IMMEDIATE_ACK}-A${AGGRESSIVE}) 56 | 57 | add_test( 58 | NAME ${TEST_NAME} 59 | COMMAND ${PERL_EXECUTABLE} run-qif.pl --table-size ${TABLE_SIZE} --risked-streams ${RISKED_STREAMS} --immed-ack ${IMMEDIATE_ACK} --aggressive ${AGGRESSIVE} ${QIF} 60 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test 61 | ) 62 | 63 | if(NOT WIN32) 64 | set_tests_properties(${TEST_NAME} PROPERTIES 65 | ENVIRONMENT "PATH=$ENV{PATH}:${PROJECT_BINARY_DIR}/bin:${PROJECT_SOURCE_DIR}/tools" 66 | ) 67 | endif() 68 | endforeach() 69 | endforeach() 70 | endforeach() 71 | endforeach() 72 | endforeach() 73 | else() 74 | message(WARNING "Perl not found: QIF tests won't be run") 75 | endif() 76 | -------------------------------------------------------------------------------- /test/lsqpack-test.h: -------------------------------------------------------------------------------- 1 | #ifndef LITESPEED_QPACK_TEST_H 2 | #define LITESPEED_QPACK_TEST_H 1 3 | 4 | #ifndef LS_QPACK_USE_LARGE_TABLES 5 | #define LS_QPACK_USE_LARGE_TABLES 1 6 | #endif 7 | 8 | #if !LS_QPACK_USE_LARGE_TABLES 9 | #define lsqpack_huff_decode_full lsqpack_huff_decode 10 | #endif 11 | 12 | int 13 | lsqpack_enc_enc_str (unsigned prefix_bits, unsigned char *const dst, 14 | size_t dst_len, const unsigned char *str, unsigned str_len); 15 | 16 | unsigned char * 17 | lsqpack_enc_int (unsigned char *dst, unsigned char *const end, uint64_t value, 18 | unsigned prefix_bits); 19 | 20 | int 21 | lsqpack_dec_int (const unsigned char **src_p, const unsigned char *src_end, 22 | unsigned prefix_bits, uint64_t *value_p, 23 | struct lsqpack_dec_int_state *state); 24 | 25 | struct huff_decode_retval 26 | { 27 | enum 28 | { 29 | HUFF_DEC_OK, 30 | HUFF_DEC_END_SRC, 31 | HUFF_DEC_END_DST, 32 | HUFF_DEC_ERROR, 33 | } status; 34 | unsigned n_dst; 35 | unsigned n_src; 36 | }; 37 | 38 | struct huff_decode_retval 39 | lsqpack_huff_decode_full (const unsigned char *src, int src_len, 40 | unsigned char *dst, int dst_len, 41 | struct lsqpack_huff_decode_state *state, int final); 42 | 43 | int 44 | lsqpack_find_in_static_headers (uint32_t name_hash, const char *name, 45 | unsigned name_len); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /test/qifs/long-codes.qif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/test/qifs/long-codes.qif -------------------------------------------------------------------------------- /test/qifs/netbsd.qif: -------------------------------------------------------------------------------- 1 | :method GET 2 | :scheme http 3 | :authority www.netbsd.org 4 | :path / 5 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 6 | accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 7 | accept-language en-US,en;q=0.5 8 | accept-encoding gzip, deflate 9 | connection keep-alive 10 | upgrade-insecure-requests 1 11 | pragma no-cache 12 | cache-control no-cache 13 | 14 | :method GET 15 | :scheme http 16 | :authority www.netbsd.org 17 | :path /global.css 18 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 19 | accept text/css,*/*;q=0.1 20 | accept-language en-US,en;q=0.5 21 | accept-encoding gzip, deflate 22 | referer http://www.netbsd.org/ 23 | connection keep-alive 24 | pragma no-cache 25 | cache-control no-cache 26 | 27 | :method GET 28 | :scheme http 29 | :authority www.netbsd.org 30 | :path /global.js 31 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 32 | accept */* 33 | accept-language en-US,en;q=0.5 34 | accept-encoding gzip, deflate 35 | referer http://www.netbsd.org/ 36 | connection keep-alive 37 | pragma no-cache 38 | cache-control no-cache 39 | 40 | :method GET 41 | :scheme http 42 | :authority www.netbsd.org 43 | :path /donations/donors.js 44 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 45 | accept */* 46 | accept-language en-US,en;q=0.5 47 | accept-encoding gzip, deflate 48 | referer http://www.netbsd.org/ 49 | connection keep-alive 50 | pragma no-cache 51 | cache-control no-cache 52 | 53 | :method GET 54 | :scheme http 55 | :authority www.netbsd.org 56 | :path /images/NetBSD-smaller.png 57 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 58 | accept */* 59 | accept-language en-US,en;q=0.5 60 | accept-encoding gzip, deflate 61 | referer http://www.netbsd.org/ 62 | connection keep-alive 63 | pragma no-cache 64 | cache-control no-cache 65 | 66 | :method GET 67 | :scheme http 68 | :authority www.netbsd.org 69 | :path /images/download-icon-orange.png 70 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 71 | accept */* 72 | accept-language en-US,en;q=0.5 73 | accept-encoding gzip, deflate 74 | referer http://www.netbsd.org/ 75 | connection keep-alive 76 | pragma no-cache 77 | cache-control no-cache 78 | 79 | :method GET 80 | :scheme http 81 | :authority www.netbsd.org 82 | :path /images/support-icon-orange.png 83 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 84 | accept */* 85 | accept-language en-US,en;q=0.5 86 | accept-encoding gzip, deflate 87 | referer http://www.netbsd.org/ 88 | connection keep-alive 89 | pragma no-cache 90 | cache-control no-cache 91 | 92 | :method GET 93 | :scheme http 94 | :authority www.netbsd.org 95 | :path /images/community-icon-orange.png 96 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 97 | accept */* 98 | accept-language en-US,en;q=0.5 99 | accept-encoding gzip, deflate 100 | referer http://www.netbsd.org/ 101 | connection keep-alive 102 | pragma no-cache 103 | cache-control no-cache 104 | 105 | :method GET 106 | :scheme http 107 | :authority www.netbsd.org 108 | :path /images/develop-icon-orange.png 109 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 110 | accept */* 111 | accept-language en-US,en;q=0.5 112 | accept-encoding gzip, deflate 113 | referer http://www.netbsd.org/ 114 | connection keep-alive 115 | pragma no-cache 116 | cache-control no-cache 117 | 118 | :method GET 119 | :scheme http 120 | :authority www.netbsd.org 121 | :path /images/donate-icon-orange.png 122 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 123 | accept */* 124 | accept-language en-US,en;q=0.5 125 | accept-encoding gzip, deflate 126 | referer http://www.netbsd.org/ 127 | connection keep-alive 128 | pragma no-cache 129 | cache-control no-cache 130 | 131 | :method GET 132 | :scheme http 133 | :authority www.netbsd.org 134 | :path /images/links/paypal.gif 135 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 136 | accept */* 137 | accept-language en-US,en;q=0.5 138 | accept-encoding gzip, deflate 139 | referer http://www.netbsd.org/ 140 | connection keep-alive 141 | pragma no-cache 142 | cache-control no-cache 143 | 144 | :method GET 145 | :scheme http 146 | :authority www.netbsd.org 147 | :path /images/links/stripe-black-donate-small.png 148 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 149 | accept */* 150 | accept-language en-US,en;q=0.5 151 | accept-encoding gzip, deflate 152 | referer http://www.netbsd.org/ 153 | connection keep-alive 154 | pragma no-cache 155 | cache-control no-cache 156 | 157 | :method GET 158 | :scheme http 159 | :authority www.netbsd.org 160 | :path /images/links/cafepress.png 161 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 162 | accept */* 163 | accept-language en-US,en;q=0.5 164 | accept-encoding gzip, deflate 165 | referer http://www.netbsd.org/ 166 | connection keep-alive 167 | pragma no-cache 168 | cache-control no-cache 169 | 170 | :method GET 171 | :scheme http 172 | :authority www.netbsd.org 173 | :path /images/westernlogo_sm.png 174 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 175 | accept */* 176 | accept-language en-US,en;q=0.5 177 | accept-encoding gzip, deflate 178 | referer http://www.netbsd.org/ 179 | connection keep-alive 180 | pragma no-cache 181 | cache-control no-cache 182 | 183 | :method GET 184 | :scheme http 185 | :authority www.netbsd.org 186 | :path /images/columbia.jpg 187 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 188 | accept */* 189 | accept-language en-US,en;q=0.5 190 | accept-encoding gzip, deflate 191 | referer http://www.netbsd.org/ 192 | connection keep-alive 193 | pragma no-cache 194 | cache-control no-cache 195 | 196 | :method GET 197 | :scheme http 198 | :authority www.netbsd.org 199 | :path /images/fastly.png 200 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 201 | accept */* 202 | accept-language en-US,en;q=0.5 203 | accept-encoding gzip, deflate 204 | referer http://www.netbsd.org/ 205 | connection keep-alive 206 | pragma no-cache 207 | cache-control no-cache 208 | 209 | :method GET 210 | :scheme http 211 | :authority www.netbsd.org 212 | :path /images/navBar-gradient.png 213 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 214 | accept */* 215 | accept-language en-US,en;q=0.5 216 | accept-encoding gzip, deflate 217 | referer http://www.netbsd.org/global.css 218 | connection keep-alive 219 | pragma no-cache 220 | cache-control no-cache 221 | 222 | :method GET 223 | :scheme https 224 | :authority www.paypalobjects.com 225 | :path /en_US/i/btn/btn_subscribe_SM.gif 226 | user-agent Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 227 | accept */* 228 | accept-language en-US,en;q=0.5 229 | accept-encoding gzip, deflate, br 230 | referer http://www.netbsd.org/ 231 | cookie PYPF=CT-2 232 | connection keep-alive 233 | pragma no-cache 234 | cache-control no-cache 235 | 236 | -------------------------------------------------------------------------------- /test/run-qif.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Script to run a QIF file. 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use File::Compare qw(compare); 9 | use File::Copy qw(copy); 10 | use File::Path qw(remove_tree); 11 | use File::Spec::Functions qw(catfile); 12 | use Getopt::Long qw(GetOptions); 13 | 14 | my $cleanup = 1; 15 | 16 | GetOptions( 17 | "aggressive=i" => \my $aggressive, 18 | "table-size=i" => \my $table_size, 19 | "immed-ack=i" => \my $immed_ack, 20 | "risked-streams=i" => \my $risked_streams, 21 | "no-cleanup" => sub { $cleanup = 0 }, 22 | ); 23 | 24 | # To reduce the number of tests from doubling, check HTTP/1.x mode in some 25 | # input, but not others. The value of this setting is easy to determine. 26 | my $http1x = ($aggressive + $table_size + $immed_ack + $risked_streams) & 1; 27 | 28 | my $dir = catfile(($ENV{TMP} || $ENV{TEMP} || "/tmp"), 29 | "run-qif-out-" . rand . $$); 30 | mkdir $dir or die "cannot create temp directory $dir"; 31 | if (!$cleanup) 32 | { 33 | print "created temp directory: $dir\n"; 34 | } 35 | 36 | END { 37 | if (defined($dir) && $cleanup) { 38 | remove_tree($dir); 39 | } 40 | } 41 | 42 | my $encode_log = catfile($dir, "encode.log"); 43 | my $qif_file = catfile($dir, "qif"); 44 | my $bin_file = catfile($dir, "out"); 45 | my $resulting_qif_file = catfile($dir, "qif-result"); 46 | 47 | my ($encode_args, $decode_args) = ('', ''); 48 | if ($aggressive) { 49 | $encode_args="$encode_args -A"; 50 | } 51 | 52 | if ($immed_ack) { 53 | $encode_args="$encode_args -a 1"; 54 | } 55 | 56 | if (defined $risked_streams) { 57 | $encode_args = "$encode_args -s $risked_streams"; 58 | $decode_args = "$decode_args -s $risked_streams"; 59 | } 60 | 61 | if (defined $table_size) { 62 | $encode_args = "$encode_args -t $table_size"; 63 | $decode_args = "$decode_args -t $table_size"; 64 | } 65 | 66 | copy($ARGV[0], $qif_file) or die "cannot copy original $ARGV[0] to $qif_file"; 67 | 68 | if ($^O eq 'MSWin32') { 69 | system('interop-encode', $encode_args, '-i', $qif_file, '-o', $bin_file) 70 | and die "interop-encode failed"; 71 | system('interop-decode', $decode_args, '-m', '1', '-i', $bin_file, '-o', $resulting_qif_file, '-H', $http1x) 72 | and die "interop-decode failed"; 73 | } else { 74 | system("interop-encode $encode_args -i $qif_file -o $bin_file") 75 | and die "interop-encode failed"; 76 | system("interop-decode $decode_args -m 1 -i $bin_file -o $resulting_qif_file -H $http1x") 77 | and die "interop-decode failed"; 78 | } 79 | 80 | sub sort_qif { 81 | no warnings 'uninitialized'; 82 | my ($in, $out) = @_; 83 | local $/ = "\n\n"; 84 | open F, $in or die "cannot open $in for reading: $!"; 85 | my @chunks = map $$_[1], 86 | sort { $$a[0] <=> $$b[0] } 87 | map { /^#\s*stream\s+(\d+)/; [ $1, $_ ] } 88 | ; 89 | close F; 90 | for (@chunks) { 91 | s/^#.*\n//mg; 92 | } 93 | open F, ">", $out or die "cannot open $out for writing: $!"; 94 | print F @chunks; 95 | close F; 96 | } 97 | 98 | sort_qif($qif_file, "$qif_file.canonical"); 99 | sort_qif($resulting_qif_file, "$resulting_qif_file.canonical"); 100 | 101 | exit compare "$qif_file.canonical", "$resulting_qif_file.canonical"; 102 | -------------------------------------------------------------------------------- /test/run-scenario.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Script to run a scenario file. A scenario file contains encoder parameters 4 | # as well as a list of headers in QIF format. The headers are encoded and 5 | # the output is then decoded and compared to the original. 6 | 7 | set -x 8 | set -e 9 | 10 | for NEED in interop-encode interop-decode sort-qif.pl; do 11 | type $NEED 12 | if [ $? -ne 0 ]; then 13 | echo $NEED is not in $PATH 1>&2 14 | exit 1 15 | fi 16 | done 17 | 18 | function cleanup { 19 | rm -vf $QIF_FILE 20 | rm -vf $BIN_FILE 21 | rm -vf $RESULTING_QIF_FILE 22 | rm -vf $ENCODE_LOG 23 | rmdir $DIR 24 | } 25 | 26 | if [ -z "$DO_CLEANUP" -o "$DO_CLEANUP" != 0 ]; then 27 | trap cleanup EXIT 28 | fi 29 | 30 | RECIPE=$1 31 | 32 | source $RECIPE 33 | 34 | DIR=/tmp/recipe-out-$$$RANDOM 35 | ENCODE_LOG=$DIR/encode.log 36 | QIF_FILE=$DIR/qif 37 | BIN_FILE=$DIR/out 38 | RESULTING_QIF_FILE=$DIR/qif-result 39 | mkdir -p $DIR 40 | 41 | if [ "$AGGRESSIVE" = 1 ]; then 42 | ENCODE_ARGS="$ENCODE_ARGS -A" 43 | fi 44 | 45 | if [ "$IMMEDIATE_ACK" = 1 ]; then 46 | ENCODE_ARGS="$ENCODE_ARGS -a 1" 47 | fi 48 | 49 | if [ "$ANNOTATIONS" = 1 ]; then 50 | ENCODE_ARGS="$ENCODE_ARGS -n" 51 | fi 52 | 53 | if [ -n "$RISKED_STREAMS" ]; then 54 | ENCODE_ARGS="$ENCODE_ARGS -s $RISKED_STREAMS" 55 | DECODE_ARGS="$DECODE_ARGS -s $RISKED_STREAMS" 56 | fi 57 | 58 | if [ -n "$TABLE_SIZE" ]; then 59 | ENCODE_ARGS="$ENCODE_ARGS -t $TABLE_SIZE" 60 | DECODE_ARGS="$DECODE_ARGS -t $TABLE_SIZE" 61 | fi 62 | 63 | echo -e "$QIF"\\n > $QIF_FILE 64 | 65 | interop-encode $ENCODE_ARGS -i $QIF_FILE -o $BIN_FILE 2>$ENCODE_LOG 66 | interop-decode $DECODE_ARGS -i $BIN_FILE -o $RESULTING_QIF_FILE 67 | diff <(sort-qif.pl --strip $QIF_FILE) \ 68 | <(sort-qif.pl --strip $RESULTING_QIF_FILE) 69 | -------------------------------------------------------------------------------- /test/scenarios/0.95-reset.sce: -------------------------------------------------------------------------------- 1 | TABLE_SIZE=256 2 | AGGRESSIVE=1 3 | RISKED_STREAMS=0 4 | QIF=$(cat<<'EOQ' 5 | dude nude 6 | dude nude 7 | dude where is my car? 8 | 9 | EOQ 10 | ) 11 | -------------------------------------------------------------------------------- /test/scenarios/cancel-stream.sce: -------------------------------------------------------------------------------- 1 | TABLE_SIZE=256 2 | ANNOTATIONS=1 3 | QIF=$(cat<<'EOQ' 4 | dude nude 5 | dude nude 6 | ## c 1 7 | dude where is my car? 8 | 9 | one fish 10 | two fish 11 | ## c 2 12 | red fish 13 | blue fish 14 | 15 | black fish 16 | blue fish 17 | ## c 3 18 | old fish 19 | new fish 20 | 21 | EOQ 22 | ) -------------------------------------------------------------------------------- /test/scenarios/drain-2.sce: -------------------------------------------------------------------------------- 1 | TABLE_SIZE=512 2 | ANNOTATIONS=1 3 | AGGRESSIVE=0 4 | RISKED_STREAMS=0 5 | IMMEDIATE_ACK=0 6 | QIF=$(cat<<'EOQ' 7 | notre dame translates to our lady of paris 8 | iconography poor peoples book 9 | spire must be repaired 10 | sainte-chapelle is known as the kingdom of light 11 | notre dame translates our to lady of paris 12 | iconography poor peoples book 13 | ## s 2 14 | 15 | notre dame translates to our lady of paris 16 | iconography poor peoples book 17 | 18 | victor hugo wrote the hunchback 19 | archdiocese of paris 20 | twelve million people visit annually 21 | coronation of emperor napoleon 22 | most famous gothic cathedral 23 | 24 | victor hugo wrote the hunchback 25 | archdiocese of paris 26 | twelve million people visit annually 27 | coronation of emperor napoleon 28 | most famous gothic cathedral 29 | 30 | notre dame translates to our lady of paris 31 | iconography poor peoples book 32 | 33 | notre dame translates to our lady of paris 34 | iconography poor peoples book 35 | 36 | EOQ 37 | ) -------------------------------------------------------------------------------- /test/scenarios/drain.sce: -------------------------------------------------------------------------------- 1 | TABLE_SIZE=512 2 | ANNOTATIONS=1 3 | AGGRESSIVE=0 4 | RISKED_STREAMS=0 5 | IMMEDIATE_ACK=0 6 | QIF=$(cat<<'EOQ' 7 | notre dame translates to our lady of paris 8 | iconography poor peoples book 9 | spire must be repaired 10 | sainte-chapelle is known as the kingdom of light 11 | notre dame translates to our lady of paris 12 | iconography poor peoples book 13 | ## s 2 14 | 15 | notre dame translates to our lady of paris 16 | iconography poor peoples book 17 | 18 | victor hugo wrote the hunchback 19 | archdiocese of paris 20 | twelve million people visit annually 21 | coronation of emperor napoleon 22 | most famous gothic cathedral 23 | 24 | victor hugo wrote the hunchback 25 | archdiocese of paris 26 | twelve million people visit annually 27 | coronation of emperor napoleon 28 | most famous gothic cathedral 29 | 30 | notre dame translates to our lady of paris 31 | iconography poor peoples book 32 | 33 | notre dame translates to our lady of paris 34 | iconography poor peoples book 35 | 36 | EOQ 37 | ) -------------------------------------------------------------------------------- /test/scenarios/end-dst-2.sce: -------------------------------------------------------------------------------- 1 | # This scenario is designed to reach the 'case HUFF_DEC_END_DST' 2 | # in lsqpack_dec_enc_in(). 3 | TABLE_SIZE=256 4 | AGGRESSIVE=0 5 | RISKED_STREAMS=0 6 | QIF=$(cat<<'EOQ' 7 | aaaaaaaaaaaaaaaaaaaaaaaaa aaaa 8 | bbbbbbbbbbbbbbbbbbbbbbbbb bbbb 9 | 10 | aaaaaaaaaaaaaaaaaaaaaaaaa aaaa 11 | bbbbbbbbbbbbbbbbbbbbbbbbb bbbb 12 | 13 | EOQ 14 | ) -------------------------------------------------------------------------------- /test/scenarios/end-dst.sce: -------------------------------------------------------------------------------- 1 | # This scenario is designed to reach the 'case HUFF_DEC_END_DST' 2 | # in parse_header_data() when Huffman encoding is turned on. 3 | TABLE_SIZE=256 4 | AGGRESSIVE=1 5 | RISKED_STREAMS=1 6 | QIF=$(cat<<'EOQ' 7 | aaaa aaa 8 | aaaa aaaaa 9 | aaaa aaaaaaaa 10 | aaaa aaaaaaaaaaaaa 11 | aaaa aaaaaaaaaaaaaaaaaaaaa 12 | 13 | EOQ 14 | ) 15 | -------------------------------------------------------------------------------- /test/scenarios/incl-name.sce: -------------------------------------------------------------------------------- 1 | # This scenario is designed to reach 'case DATA_STATE_READ_LFONR_NAME_PLAIN' 2 | # in parse_header_data(). It contains header names that are be too small 3 | # for Huffman coding. 4 | TABLE_SIZE=256 5 | AGGRESSIVE=1 6 | RISKED_STREAMS=0 7 | QIF=$(cat<<'EOQ' 8 | yo I 9 | no do not 10 | se know 11 | 12 | EOQ 13 | ) 14 | -------------------------------------------------------------------------------- /test/scenarios/multi-byte-int-dyn-ref-1.sce: -------------------------------------------------------------------------------- 1 | # This scenario is designed to reach the `case EEA_INS_NAMEREF_DYNAMIC` 2 | # in the `lsqpack_enc_encode` function. It is intended to fail the integer 3 | # encoding due to buffer length. To trigger this case, many names must be 4 | # inserted into the dynamic table so that the integer encoding requires 5 | # more than one byte. 6 | TABLE_SIZE=16384 7 | AGGRESSIVE=1 8 | IMMEDIATE_ACK=0 9 | RISKED_STREAMS=1000 10 | QIF=$(cat<<'EOQ' 11 | aaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaa 12 | bbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbb 13 | ccccccccccccc cccccccccccccccccccccccccc 14 | ddddddddddddd dddddddddddddddddddddddddd 15 | eeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeee 16 | fffffffffffff ffffffffffffffffffffffffff 17 | ggggggggggggg gggggggggggggggggggggggggg 18 | aaaa oneeeeeeeee 19 | bbbb oneeeeeeeee 20 | cccc oneeeeeeeee 21 | dddd oneeeeeeeee 22 | eeee oneeeeeeeee 23 | ffff oneeeeeeeee 24 | gggg oneeeeeeeee 25 | hhhh oneeeeeeeee 26 | iiii oneeeeeeeee 27 | jjjj oneeeeeeeee 28 | kkkk oneeeeeeeee 29 | llll oneeeeeeeee 30 | mmmm oneeeeeeeee 31 | nnnn oneeeeeeeee 32 | oooo oneeeeeeeee 33 | pppp oneeeeeeeee 34 | qqqq oneeeeeeeee 35 | rrrr oneeeeeeeee 36 | ssss oneeeeeeeee 37 | tttt oneeeeeeeee 38 | uuuu oneeeeeeeee 39 | vvvv oneeeeeeeee 40 | wwww oneeeeeeeee 41 | xxxx oneeeeeeeee 42 | yyyy oneeeeeeeee 43 | zzzz oneeeeeeeee 44 | aabb oneeeeeeeee 45 | bbcc oneeeeeeeee 46 | ccdd oneeeeeeeee 47 | ddee oneeeeeeeee 48 | eeff oneeeeeeeee 49 | ffgg oneeeeeeeee 50 | gghh oneeeeeeeee 51 | hhii oneeeeeeeee 52 | iijj oneeeeeeeee 53 | jjkk oneeeeeeeee 54 | kkll oneeeeeeeee 55 | llmm oneeeeeeeee 56 | mmnn oneeeeeeeee 57 | nnoo oneeeeeeeee 58 | oopp oneeeeeeeee 59 | ppqq oneeeeeeeee 60 | qqrr oneeeeeeeee 61 | rrss oneeeeeeeee 62 | sstt oneeeeeeeee 63 | ttuu oneeeeeeeee 64 | uuvv oneeeeeeeee 65 | vvww oneeeeeeeee 66 | wwxx oneeeeeeeee 67 | xxyy oneeeeeeeee 68 | yyzz oneeeeeeeee 69 | 70 | bbbb two 71 | cccc two 72 | dddd two 73 | eeee two 74 | ffff two 75 | gggg two 76 | hhhh two 77 | yyyy two 78 | zzzz two 79 | aabb two 80 | bbcc two 81 | ccdd two 82 | ddee two 83 | eeff two 84 | ffgg two 85 | gghh two 86 | hhii two 87 | iijj two 88 | jjkk two 89 | kkll two 90 | llmm two 91 | mmnn two 92 | nnoo two 93 | oopp two 94 | ppqq two 95 | qqrr two 96 | rrss two 97 | sstt two 98 | ttuu two 99 | uuvv two 100 | vvww two 101 | wwxx two 102 | xxyy two 103 | yyzz two 104 | aaaa two 105 | 106 | aaaa three 107 | bbbb three 108 | 109 | EOQ 110 | ) 111 | -------------------------------------------------------------------------------- /test/scenarios/multi-byte-int-dyn-ref-2.sce: -------------------------------------------------------------------------------- 1 | # This scenario is designed to reach the `case EHA_INDEXED_DYN` 2 | # in the `lsqpack_enc_encode` function. It is intended to fail the integer 3 | # encoding due to buffer length. To trigger this case, many names and values 4 | # must be inserted into the dynamic table so that the integer encoding requires 5 | # more than one byte. 6 | TABLE_SIZE=16384 7 | AGGRESSIVE=1 8 | IMMEDIATE_ACK=0 9 | RISKED_STREAMS=1000 10 | QIF=$(cat<<'EOQ' 11 | aaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaa 12 | bbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbb 13 | ccccccccccccc cccccccccccccccccccccccccc 14 | ddddddddddddd dddddddddddddddddddddddddd 15 | eeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeee 16 | fffffffffffff ffffffffffffffffffffffffff 17 | ggggggggggggg gggggggggggggggggggggggggg 18 | aaaa oneeeeeeeee 19 | bbbb oneeeeeeeee 20 | cccc oneeeeeeeee 21 | dddd oneeeeeeeee 22 | eeee oneeeeeeeee 23 | ffff oneeeeeeeee 24 | gggg oneeeeeeeee 25 | hhhh oneeeeeeeee 26 | iiii oneeeeeeeee 27 | jjjj oneeeeeeeee 28 | kkkk oneeeeeeeee 29 | llll oneeeeeeeee 30 | mmmm oneeeeeeeee 31 | nnnn oneeeeeeeee 32 | oooo oneeeeeeeee 33 | pppp oneeeeeeeee 34 | qqqq oneeeeeeeee 35 | rrrr oneeeeeeeee 36 | ssss oneeeeeeeee 37 | tttt oneeeeeeeee 38 | uuuu oneeeeeeeee 39 | vvvv oneeeeeeeee 40 | wwww oneeeeeeeee 41 | xxxx oneeeeeeeee 42 | yyyy oneeeeeeeee 43 | zzzz oneeeeeeeee 44 | aabb oneeeeeeeee 45 | bbcc oneeeeeeeee 46 | ccdd oneeeeeeeee 47 | ddee oneeeeeeeee 48 | eeff oneeeeeeeee 49 | ffgg oneeeeeeeee 50 | gghh oneeeeeeeee 51 | hhii oneeeeeeeee 52 | iijj oneeeeeeeee 53 | jjkk oneeeeeeeee 54 | kkll oneeeeeeeee 55 | llmm oneeeeeeeee 56 | mmnn oneeeeeeeee 57 | nnoo oneeeeeeeee 58 | oopp oneeeeeeeee 59 | ppqq oneeeeeeeee 60 | qqrr oneeeeeeeee 61 | rrss oneeeeeeeee 62 | sstt oneeeeeeeee 63 | ttuu oneeeeeeeee 64 | uuvv oneeeeeeeee 65 | vvww oneeeeeeeee 66 | wwxx oneeeeeeeee 67 | xxyy oneeeeeeeee 68 | yyzz oneeeeeeeee 69 | abcd oneeeeeeeee 70 | bcde oneeeeeeeee 71 | cdef oneeeeeeeee 72 | defg oneeeeeeeee 73 | efgh oneeeeeeeee 74 | fghi oneeeeeeeee 75 | ghij oneeeeeeeee 76 | hijk oneeeeeeeee 77 | ijkl oneeeeeeeee 78 | jklm oneeeeeeeee 79 | klmn oneeeeeeeee 80 | lmno oneeeeeeeee 81 | mnop oneeeeeeeee 82 | nopq oneeeeeeeee 83 | opqr oneeeeeeeee 84 | pqrs oneeeeeeeee 85 | qrst oneeeeeeeee 86 | rstu oneeeeeeeee 87 | 88 | aaaa oneeeeeeeee 89 | bbbb oneeeeeeeee 90 | cccc oneeeeeeeee 91 | dddd oneeeeeeeee 92 | eeee oneeeeeeeee 93 | ffff oneeeeeeeee 94 | gggg oneeeeeeeee 95 | hhhh oneeeeeeeee 96 | iiii oneeeeeeeee 97 | jjjj oneeeeeeeee 98 | kkkk oneeeeeeeee 99 | llll oneeeeeeeee 100 | mmmm oneeeeeeeee 101 | nnnn oneeeeeeeee 102 | oooo oneeeeeeeee 103 | pppp oneeeeeeeee 104 | qqqq oneeeeeeeee 105 | rrrr oneeeeeeeee 106 | ssss oneeeeeeeee 107 | tttt oneeeeeeeee 108 | uuuu oneeeeeeeee 109 | vvvv oneeeeeeeee 110 | wwww oneeeeeeeee 111 | xxxx oneeeeeeeee 112 | yyyy oneeeeeeeee 113 | zzzz oneeeeeeeee 114 | aabb oneeeeeeeee 115 | bbcc oneeeeeeeee 116 | ccdd oneeeeeeeee 117 | ddee oneeeeeeeee 118 | eeff oneeeeeeeee 119 | ffgg oneeeeeeeee 120 | gghh oneeeeeeeee 121 | hhii oneeeeeeeee 122 | iijj oneeeeeeeee 123 | jjkk oneeeeeeeee 124 | kkll oneeeeeeeee 125 | llmm oneeeeeeeee 126 | mmnn oneeeeeeeee 127 | nnoo oneeeeeeeee 128 | oopp oneeeeeeeee 129 | ppqq oneeeeeeeee 130 | qqrr oneeeeeeeee 131 | rrss oneeeeeeeee 132 | sstt oneeeeeeeee 133 | ttuu oneeeeeeeee 134 | uuvv oneeeeeeeee 135 | vvww oneeeeeeeee 136 | wwxx oneeeeeeeee 137 | xxyy oneeeeeeeee 138 | yyzz oneeeeeeeee 139 | aaaa oneeeeeeeee 140 | abcd oneeeeeeeee 141 | bcde oneeeeeeeee 142 | cdef oneeeeeeeee 143 | defg oneeeeeeeee 144 | efgh oneeeeeeeee 145 | fghi oneeeeeeeee 146 | ghij oneeeeeeeee 147 | hijk oneeeeeeeee 148 | ijkl oneeeeeeeee 149 | jklm oneeeeeeeee 150 | klmn oneeeeeeeee 151 | lmno oneeeeeeeee 152 | mnop oneeeeeeeee 153 | nopq oneeeeeeeee 154 | opqr oneeeeeeeee 155 | pqrs oneeeeeeeee 156 | qrst oneeeeeeeee 157 | rstu oneeeeeeeee 158 | aaaa oneeeeeeeee 159 | 160 | EOQ 161 | ) 162 | -------------------------------------------------------------------------------- /test/scenarios/post-base-1.sce: -------------------------------------------------------------------------------- 1 | TABLE_SIZE=256 2 | AGGRESSIVE=1 3 | RISKED_STREAMS=1 4 | QIF=$(cat<<'EOQ' 5 | dude nude 6 | dude nude 7 | dude where is my car? 8 | 9 | EOQ 10 | ) 11 | -------------------------------------------------------------------------------- /test/scenarios/post-base-2.sce: -------------------------------------------------------------------------------- 1 | # This scenario is designed to reach 'case DATA_STATE_LFPBNR_READ_VAL_PLAIN' 2 | # in parse_header_data(). It has a number of repeated names with values that 3 | # would be too small for Huffman coding. 4 | TABLE_SIZE=256 5 | AGGRESSIVE=1 6 | RISKED_STREAMS=1 7 | QIF=$(cat<<'EOQ' 8 | dude n 9 | dude nu 10 | dude where is my car? 11 | 12 | EOQ 13 | ) 14 | -------------------------------------------------------------------------------- /test/scenarios/post-base-nr.sce: -------------------------------------------------------------------------------- 1 | TABLE_SIZE=256 2 | AGGRESSIVE=0 3 | RISKED_STREAMS=1 4 | QIF=$(cat<<'EOQ' 5 | dude nude 6 | dude nude 7 | dude where is my car? 8 | 9 | EOQ 10 | ) 11 | -------------------------------------------------------------------------------- /test/scenarios/set-max-cap.sce: -------------------------------------------------------------------------------- 1 | TABLE_SIZE=256 2 | ANNOTATIONS=1 3 | QIF=$(cat<<'EOQ' 4 | dude nude 5 | dude nude 6 | dude where is my car? 7 | 8 | ## t 0 9 | one fish 10 | two fish 11 | red fish 12 | blue fish 13 | 14 | EOQ 15 | ) -------------------------------------------------------------------------------- /test/test_circ_list.c: -------------------------------------------------------------------------------- 1 | /* Bug report and test case contributed by Guoye Zhang -- thanks! */ 2 | 3 | #include "lsxpack_header.h" 4 | #include "lsqpack.h" 5 | 6 | static void _decoderUnblocked(void *context) { 7 | 8 | } 9 | 10 | static struct lsxpack_header *_decoderPrepareDecode(void *context, struct lsxpack_header *header, size_t space) { 11 | 12 | return NULL; 13 | 14 | } 15 | 16 | static int _decoderProcessHeader(void *context, struct lsxpack_header *header) { 17 | 18 | return 0; 19 | 20 | } 21 | 22 | int main(int argc, const char * argv[]) { 23 | 24 | struct lsqpack_enc qpackEncoder; 25 | 26 | struct lsqpack_dec qpackDecoder; 27 | 28 | static struct lsqpack_dec_hset_if callbacks = { 29 | 30 | .dhi_unblocked = _decoderUnblocked, 31 | 32 | .dhi_prepare_decode = _decoderPrepareDecode, 33 | 34 | .dhi_process_header = _decoderProcessHeader, 35 | 36 | }; 37 | 38 | uint8_t stdc[LSQPACK_LONGEST_SDTC]; 39 | 40 | size_t stdcSize = sizeof(stdc); 41 | 42 | lsqpack_enc_init(&qpackEncoder, NULL, 4096, 4096, 100, LSQPACK_ENC_OPT_SERVER, stdc, &stdcSize); 43 | 44 | lsqpack_dec_init(&qpackDecoder, stdout, 4096, 100, &callbacks, (enum lsqpack_dec_opts)0); 45 | 46 | lsqpack_dec_enc_in(&qpackDecoder, stdc, stdcSize); 47 | 48 | uint8_t iciBuffer[LSQPACK_LONGEST_ICI]; 49 | 50 | ssize_t iciSize = 0; 51 | 52 | for (unsigned i = 0; i < 6; i++) { 53 | 54 | uint8_t encoderBuffer[1024]; 55 | 56 | size_t usedEncoderSize = 0, encoderSize = sizeof(encoderBuffer); 57 | 58 | uint8_t headerBuffer[1024]; 59 | 60 | size_t usedHeaderSize = 0, headerSize = sizeof(headerBuffer); 61 | 62 | struct lsxpack_header header; 63 | memset(&header, 0, sizeof(header)); 64 | 65 | const char *name, *value; 66 | 67 | lsqpack_enc_start_header(&qpackEncoder, 0, i); 68 | 69 | name = ":authority" "localhost"; 70 | 71 | lsxpack_header_set_offset2(&header, name, 0, strlen(":authority"), strlen(":authority"), strlen("localhost")); 72 | 73 | enum lsqpack_enc_status status = lsqpack_enc_encode(&qpackEncoder, &encoderBuffer[usedEncoderSize], &encoderSize, &headerBuffer[usedHeaderSize], &headerSize, &header, (enum lsqpack_enc_flags)0); 74 | 75 | assert(status == LQES_OK); 76 | 77 | usedEncoderSize += encoderSize; usedHeaderSize += headerSize; 78 | 79 | encoderSize = sizeof(encoderBuffer) - usedEncoderSize; headerSize = sizeof(headerBuffer) - usedHeaderSize; 80 | 81 | uint8_t prefixBuffer[22]; 82 | 83 | lsqpack_enc_end_header(&qpackEncoder, prefixBuffer, sizeof(prefixBuffer), NULL); 84 | 85 | if (usedEncoderSize > 0) { 86 | 87 | lsqpack_dec_enc_in(&qpackDecoder, encoderBuffer, usedEncoderSize); 88 | 89 | if (lsqpack_dec_ici_pending(&qpackDecoder)) { 90 | 91 | iciSize = lsqpack_dec_write_ici(&qpackDecoder, iciBuffer, sizeof(iciBuffer)); 92 | 93 | } 94 | 95 | } 96 | 97 | } 98 | 99 | lsqpack_enc_decoder_in(&qpackEncoder, iciBuffer, iciSize); 100 | 101 | return 0; 102 | 103 | } 104 | 105 | -------------------------------------------------------------------------------- /test/test_dec_crash_case.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lsqpack.h" 4 | #include "lsxpack_header.h" 5 | 6 | static void _decoderUnblocked(void *context) { 7 | } 8 | 9 | static struct lsxpack_header *_decoderPrepareDecode(void *context, struct lsxpack_header *header, size_t space) 10 | { 11 | static struct lsxpack_header xpackHeader; 12 | char *xpackHeaderBuffer = (char *)malloc(space); 13 | lsxpack_header_prepare_decode(&xpackHeader, xpackHeaderBuffer, 0, space); 14 | return &xpackHeader; 15 | } 16 | 17 | static int _decoderProcessHeader(void *context, struct lsxpack_header *header) 18 | { 19 | printf("Decode header %.*s: %.*s\n", header->name_len, &header->buf[header->name_offset], header->val_len, &header->buf[header->val_offset]); 20 | return 0; 21 | } 22 | 23 | int main(int argc, const char * argv[]) { 24 | struct lsqpack_dec qpackDecoder; 25 | 26 | struct lsqpack_dec_hset_if callbacks = { 27 | .dhi_unblocked = _decoderUnblocked, 28 | .dhi_prepare_decode = _decoderPrepareDecode, 29 | .dhi_process_header = _decoderProcessHeader, 30 | }; 31 | lsqpack_dec_init(&qpackDecoder, stderr, 16384, 100, &callbacks, (enum lsqpack_dec_opts)0); 32 | 33 | size_t size = 50; 34 | uint8_t buffer[16384] = { 35 | 0x01, 0x32, 0x00, 0x00, 0x50, 0x91, 0x19, 0x08, 36 | 0x54, 0x21, 0x7a, 0x0e, 0x41, 0xd0, 0x92, 0xa1, 37 | 0x2b, 0xd2, 0x5b, 0x8f, 0xbe, 0xd3, 0x3f, 0xd1, 38 | 0x51, 0x85, 0x62, 0x53, 0x9d, 0x25, 0xb3, 0xd7, 39 | 0x5f, 0x10, 0x83, 0x9b, 0xd9, 0xab, 0x5f, 0x50, 40 | 0x8b, 0xed, 0x69, 0x88, 0xb4, 0xc7, 0x53, 0x1e, 41 | 0xfd, 0xfa, 42 | }; 43 | uint8_t decoderBuffer[LSQPACK_LONGEST_HEADER_ACK]; 44 | size_t decoderBufferSize = sizeof(decoderBuffer); 45 | const uint8_t *bufferRef = buffer; 46 | enum lsqpack_read_header_status status = lsqpack_dec_header_in(&qpackDecoder, NULL, 0, size, &bufferRef, size, decoderBuffer, &decoderBufferSize); 47 | printf("Decode status %d\n", status); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /test/test_dyn_table_cap_mismatch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lsqpack.h" 4 | #include "lsxpack_header.h" 5 | 6 | static void _decoderUnblocked(void *context) { 7 | } 8 | 9 | static struct lsxpack_header *_decoderPrepareDecode(void *context, struct lsxpack_header *header, size_t space) 10 | { 11 | static struct lsxpack_header xpackHeader; 12 | char *xpackHeaderBuffer = (char *)malloc(space); 13 | lsxpack_header_prepare_decode(&xpackHeader, xpackHeaderBuffer, 0, space); 14 | return &xpackHeader; 15 | } 16 | 17 | static int _decoderProcessHeader(void *context, struct lsxpack_header *header) 18 | { 19 | printf("Decode header %.*s: %.*s\n", header->name_len, &header->buf[header->name_offset], header->val_len, &header->buf[header->val_offset]); 20 | return 0; 21 | } 22 | 23 | int main(int argc, const char * argv[]) { 24 | struct lsqpack_dec qpackDecoder; 25 | 26 | struct lsqpack_dec_hset_if callbacks = { 27 | .dhi_unblocked = _decoderUnblocked, 28 | .dhi_prepare_decode = _decoderPrepareDecode, 29 | .dhi_process_header = _decoderProcessHeader, 30 | }; 31 | lsqpack_dec_init(&qpackDecoder, stderr, 16384, 100, &callbacks, (enum lsqpack_dec_opts)0); 32 | 33 | FILE *encoder_stream = fopen("testdata/encoder_stream", "r"); 34 | FILE *response = fopen("testdata/response", "r"); 35 | uint8_t buffer[16384]; 36 | size_t size = 0; 37 | if (!encoder_stream) 38 | { 39 | encoder_stream = fopen("../../test/testdata/encoder_stream", "r"); 40 | response = fopen("../../test/testdata/response", "r"); 41 | } 42 | while ((size = fread(buffer, 1, sizeof(buffer), encoder_stream)) > 0) { 43 | lsqpack_dec_enc_in(&qpackDecoder, buffer, size); 44 | } 45 | fclose(encoder_stream); 46 | 47 | size = fread(buffer, 1, sizeof(buffer), response); 48 | uint8_t decoderBuffer[LSQPACK_LONGEST_HEADER_ACK]; 49 | size_t decoderBufferSize = sizeof(decoderBuffer); 50 | const uint8_t *bufferRef = buffer; 51 | enum lsqpack_read_header_status status = lsqpack_dec_header_in(&qpackDecoder, NULL, 2092, size, &bufferRef, size, decoderBuffer, &decoderBufferSize); 52 | printf("Decode status %d\n", status); 53 | fclose(response); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /test/test_enc_str.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "lsqpack.h" 7 | #include "lsqpack-test.h" 8 | 9 | 10 | struct str_test 11 | { 12 | int strt_lineno; 13 | unsigned strt_prefix_bits; 14 | const unsigned char *strt_in_str; 15 | unsigned strt_in_len; 16 | const unsigned char *strt_out_buf; 17 | int strt_retval; 18 | }; 19 | 20 | 21 | static const struct str_test tests[] = 22 | { 23 | 24 | { 25 | __LINE__, 26 | 3, 27 | (unsigned char *) "", 28 | 0, 29 | (unsigned char *) "\x00", 30 | 1, 31 | }, 32 | 33 | { 34 | __LINE__, 35 | 3, 36 | (unsigned char *) "a", 37 | 1, 38 | (unsigned char *) "\x01" "a", 39 | 2, 40 | }, 41 | 42 | { 43 | __LINE__, 44 | 3, 45 | (unsigned char *) "aa", 46 | 2, 47 | (unsigned char *) "\x02" "aa", 48 | 3, 49 | }, 50 | 51 | { 52 | __LINE__, 53 | 3, 54 | (unsigned char *) "aaa", 55 | 3, 56 | (unsigned char *) "\x0A\x18\xc7", 57 | 3, 58 | }, 59 | 60 | { 61 | __LINE__, 62 | 3, 63 | (unsigned char *) "aaaaaaaaa", 64 | 9, 65 | (unsigned char *) "\x0E\x18\xc6\x31\x8c\x63\x1f", 66 | 7, 67 | }, 68 | 69 | { 70 | __LINE__, 71 | 3, 72 | (unsigned char *) "aaaaaaaaaa", 73 | 10, 74 | (unsigned char *) "\x0F\x00\x18\xc6\x31\x8c\x63\x18\xff", 75 | 9, 76 | }, 77 | 78 | { 79 | __LINE__, 80 | 3, 81 | (unsigned char *) "aaaaaaaaaabbb", 82 | 13, 83 | (unsigned char *) "\x0F\x02\x18\xc6\x31\x8c\x63\x18\xe3\x8e\x3f", 84 | 11, 85 | }, 86 | 87 | { 88 | __LINE__, 89 | 3, 90 | (unsigned char *) "\x80\x90\xA0\xBA", 91 | 4, 92 | (unsigned char *) "\x04\x80\x90\xA0\xBA", 93 | 5, 94 | }, 95 | 96 | { 97 | __LINE__, 98 | 3, 99 | (unsigned char *) "\x80\x90\xA0\xBA\x80\x90\xA0", 100 | 7, 101 | (unsigned char *) "\x07\x00\x80\x90\xA0\xBA\x80\x90\xA0", 102 | 9, 103 | }, 104 | 105 | { 106 | __LINE__, 107 | 3, 108 | (unsigned char *) "\x80\x90\xA0\xBA\x80\x90\xA0" "foo", 109 | 10, 110 | (unsigned char *) "\x07\x03\x80\x90\xA0\xBA\x80\x90\xA0" "foo", 111 | 12, 112 | }, 113 | 114 | }; 115 | 116 | 117 | int 118 | main (void) 119 | { 120 | const struct str_test *test; 121 | int r; 122 | unsigned char *out= malloc(0x1000); 123 | 124 | if (!out) 125 | return 1; 126 | 127 | for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test) 128 | { 129 | out[0] = 0; 130 | r = lsqpack_enc_enc_str(test->strt_prefix_bits, out, 0x1000, 131 | test->strt_in_str, test->strt_in_len); 132 | assert(r == test->strt_retval); 133 | if (r > 0) 134 | assert(0 == memcmp(test->strt_out_buf, out, r)); 135 | } 136 | 137 | free(out); 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /test/test_huff_dec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Usage: test_huff_dec [-v] [-e] 3 | * 4 | * -v Verbose 5 | * -e Run expensive tests 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #ifdef WIN32 13 | #include 14 | #else 15 | #include 16 | #endif 17 | 18 | #include "lsqpack.h" 19 | #include "lsqpack-test.h" 20 | 21 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 22 | 23 | static int verbose; 24 | 25 | struct test_huff_dec 26 | { 27 | int lineno; 28 | 29 | /* Input */ 30 | unsigned char *src; 31 | size_t src_sz; 32 | 33 | /* Output */ 34 | char *dst; 35 | size_t dst_sz; 36 | }; 37 | 38 | const struct test_huff_dec tests[] = 39 | { 40 | { __LINE__, 41 | (unsigned char *) 42 | "\xbf\x6c\x85\xfa\x53\xc4\xe5\xb0\xaa\x19\x0a\x53\xe9\x42\x0e\xcf\xf3", 43 | 17, 44 | "Dude, where is my car?", 45 | 22, 46 | }, 47 | { __LINE__, 48 | (unsigned char *) 49 | "\xd6\x32\x1a\xa9\x94\x8c\x64\xea\xa0\xea\x91\x49" 50 | "\x4f\x62\x67\x51\x33\x95\x4a\x0b\x54\xc9\x9d\x43\xca\xa2\x67\x2a" 51 | "\x93\x8d\x24\x9f\xc0\xfd\x21\x41\xd5\x22\x88\x1f\x71\xcc\x5a\x3d" 52 | "\x28\x49\xcb\xe0\xd5\x4d\xff\xff\xff\xe2\x67\x2a\x85\xaa\x45\x0f" 53 | "\x2a\x83\x50\x86\x98\x76\x7d\x29\xe0\x68\x51\x33\x95\x4a\x47\x52" 54 | "\x9e\x27\x3a\x0a\xa1\x06\x8b\x47\x42\x93\x50\x3a\x88\x55\x27\x1c" 55 | "\x8a\x58\x54\x12\xd2\xc8\xa5\x25\x52\x5b\x0f\x4f\xff\xff\xff\x89" 56 | "\x9c\xaa\x20\xa3\x5d\x45\x31\x99\x0a\x4e\x53\x4d\x3a\x54\xf0\x34" 57 | "\x29\x5d\x87\x8c\x71\xd1\xe9\x49\x4d\xdc\xaa\x4a\x52\xa5\x44\x96" 58 | "\xa5\x0d\x51\x27\x2a\x3e\x94\x3d\x8a\x24\xb5\x28\x3a\xa4\x50\x7f" 59 | "\xff\xff\xfe\x4e\x3a\x25\xfb\x52\x3b\x52\xa5\x3e\x94\x94\xd6\x21" 60 | "\x2a\x1a\x9a\xec\x2a\x10\x63\xd5\xf4\xa1\xec\x52\x52\x96\x85\x0f" 61 | "\x2a\x89\x9c\xaa\x52\x3a\xbe\x94\xf0\x34\x29\x51\xd2\xa1\xe5\x51" 62 | "\x33\x99\x1f\x4a\x47\x6a\x7f\xff\xff\xfc\x3c\xaa\x4e\x64\x28\x84" 63 | "\xd8\x5a\xa6\x4c\xeb\xa8\xa4\x35\x48\xa7\xa2\xa7\xe9\x4f\x13\x9a" 64 | "\x82\xa9\x39\x54\xf0\x34\x28\x79\x54\xa4\x68\x41\xbb\x95\x48\xed" 65 | "\x35\x12\x7d\x29\xe0\xc9\x9d\x48\xec\x38\xe4\xff\xff\xff\xf1\x13" 66 | "\x9e\xda\x24\x2d\x88\x50\x75\x48\xa4\x85\x2d\x6a\x12\x72\xa1\x3f" 67 | "\x4a\x64\x50\x87\xb6\x89\x14\xa8\xe9\x50\x93\x8e\xc1\x91\x25\xb0" 68 | "\xde\xca\xa4\xe6\x42\x88\x4d\x85\xaa\x64\xce\xa0\xd1\xff\xff\xff" 69 | "\xe5\x23\x42\x0d\xdc\xab\xa8\xa6\x44\xa9\xe0\x68\x53\xc4\xe3\x4a" 70 | "\x94\x9a\x69\xd2\xa4\x65\x51\x25\xb2\x92\xc8\xa0\xd4\x41\xaa\x2f" 71 | "\x8f\x5f\x4a\x75\xa8\xe9\x4f\xa5\x10\x9b\x0b\x54\xc9\x9f\xe9\x43" 72 | "\xca\xa2\x67\x2f\xff\xff\xff\x9d\x4d\x54\x8a\x78\x2a\x83\x41\x2c" 73 | "\x34\x65\x51\x27\x52\x82\x8e\xa5\x07\x54\x8a\x78\x35\x9e\x94\xa4" 74 | "\xb5\x7d\x29\x1d\xa9\x53\xc4\xe6\x24\xff\x4a\x1a\xa5\x27\x35\x3f" 75 | "\x4a\x46\x52\x0e\xd4\x15\x43\xca\xa4\xe6\x47\xff\xff\xff\x93\x94" 76 | "\x7d\xfd\x29\x1d\xa6\xa2\x4f\xa5\x2b\x1d\x89\x39\xfa\xa9\x49\xec" 77 | "\x2a\x87\x95\x44\xce\x55\x0b\x55\x03\xb2\x62\xc8\xa4\xc7\xb0\xd4" 78 | "\x50\x35\x0f\x64\x85\xb1\x75\x14\xd2\x74\xa8\x99\xc6\x95\x0d\x57" 79 | "\xff\xff\xff\x83\xae\xb2\x8e\xc1\xd4\x42\xa9\x39\x54\x41\x4b\x49" 80 | "\x64\x50\xd5\x28\x99\xca\xa5\x05\x1a\x12\xa4\xc7\xb0\xd4\x50\x35" 81 | "\xa8\x37\x52\xae\xa2\x9c\xa7\x1a\x54\xc8\xa0\xe9\x51\x09\xb0\xdd" 82 | "\xcd\x54\xca\x24\xff\xff\xff\xfe\x17\xcd\x76\x15\x08\x50\xc8\x51" 83 | "\x33\x99\x0a\x21\x36\x16\xa9\x93\x3a\x86\x4a\x0b\x44\xbf\x4a\x52" 84 | "\x7b\x0a\xa0\xd0\xa0\xd4\x4c\xe6\xaa\x65\x07\x58\xec\x4a\x92\xd8" 85 | "\x7a\x54\x9c\xc8\x52\xb9\xfd\x20\xc4\x1d\x1f\xff\xff\xfe\x20\xb4" 86 | "\xc7\x40\xea\x21\x57\x51\x4c\x89\x53\xc0\xd0\xa0\xd4\x42\x6c\x2d" 87 | "\x53\x26\x75\x3c\x15\x41\xd8\x55\x3c\x1e\xa4\xa8\x93\xa8\x34\x20" 88 | "\xe4\x30\xd2\x55\x3c\x19\x33\xa8\x99\xcd\x54\xc8\xff\xff\xff\xf2" 89 | "\xbb\x0d\x49\x92\x6e\xe5\xfa\x53\xc1\x93\x3a\x9e\x0d\x44\x8a\x0e" 90 | "\xa3\x52\x3a\x11\xf4\xa0\xea\x91\x44\xce\x55\x09\x61\x46\x9b\x6c" 91 | "\x2a\x14\xf0\x55\x0d\x48\xe6\x35\x45\x50\xf6\xd9\xff\xff\xff\xe2" 92 | "\x6c\x29\x56\x93\xc1\x68\xa0\xd5\x4c\xa5\x76\x1d\x27\x4f\xaa\xca" 93 | "\x85\x12\x75\x27\x1f\x72\xa9\x19\x4b\x57\xff\xf6\xff\xff\x37\xff" 94 | "\xff\x70\xd4\x42\x6c\x2d\x53\x26\x75\x10\x3e\xe3\x98\xbf\x4a\x4a" 95 | "\x5b\x0e\x43\x1e\xd4\x7d\x28\x3a\x0d\xdc\xbf\xff\xff\xfe\x1a\xa5" 96 | "\x0c\x94\x16\x89\x7e\x94\x4c\xe5\x50\xa8\x41\x6a\x21\x54\x3c\xaa" 97 | "\x50\x69\x4a\xa1\xaa\x51\x33\x8d\x2a\x19\x2a\x19\x0a\x26\x72\xa9" 98 | "\x59\xd2\x5a\x89\xe9\x43\xca\xa5\x27\x49\x8f\x57\xd2\x89\x9c\xbf" 99 | "\xff\xff\xfe\x16\x82\xd2\x5a\x92\x3a\x14\x42\x6d\x96\x55\x0c\x94" 100 | "\x16\x89\x54\x3d\xa9\x50\xf2\xa9\xe2\x73\x12\x75\x13\x39\x54\xa4" 101 | "\x75\x7a\x52\x53\xd9\x4a\x14\x3c\xaa\x50\x69\x4a\xa4\xe3\xee\x55" 102 | "\x23\x29\x6a\xff\xff\xff\xf2\x93\xdb\x44\x85\x93\xed\x43\x54\xa2" 103 | "\x27\x3d\x89\xfa\x51\x33\x8d\x2a\x78\x9c\xc4\x9d\x4f\x16\x19\x33" 104 | "\x95\x0a\x1a\xa5\x13\x39\x54\x8c\xf2\x7a\x50\xf2\xa8\x35\x11\x50" 105 | "\xfa\x95\x4f\x13\x96\xa5\x13\x39\x7f\xff\xff\xfc\x9c\xa3\x91\x43" 106 | "\x21\x42\x5a\x95\x0f\x2c\xbf\x4a\x0e\xa9\x14\x4c\xe5\x51\x15\x0f" 107 | "\xa9\x7e\x94\x1a\x14\x1a\x88\xa8\x7d\x4b\xf4\xa1\x90\xa4\x85\x1c" 108 | "\x9f\x4a\x1e\xc5\x3c\x4e\x62\x4e\xa5\x06\xaa\x62\xd8\x85\x0d\x57" 109 | "\xff\xff\xff\x89\x9c\xaa\x22\x71\xd6\x5a\x0a\x84\x29\x45\xb4\xd6" 110 | "\xa1\xe5\x51\x36\xd8\x9a\x0a\xb5\x25\x1a\x54\x1d\x52\x29\x61\x48" 111 | "\x73\x50\x85\x07\x54\x8a\x76\xb4\xdd\xcb\x62\x14\x96\xc3\xd2\xa2" 112 | "\x67\x2a\x95\xd8\x79\x3f\xff\xff\xfc\x3c\xaa\x0d\x49\x4d\x54\xc5" 113 | "\xb1\x7f\xff\xff\xff\x3f\xff\xff\xfc\xdd\x69\x27\x53\xc0\xd0\xa2" 114 | "\x67\x2a\x86\xa6\xbb\x0a\x84\x18\xf5\x28\x79\x54\x42\x6c\x2d\x53" 115 | "\x26\x75\x32\x29\x30\xd3\x39\x6c\x2c\x8a\x4b\x61\xe9\x51\x33\x99" 116 | "\x0a\x52\x3a\x94\xf1\x39\xd4\xac\x64\x2c\x8a\x5b\x5f\xff\xff\xff" 117 | "\x83\xaa\x45\x24\x3f\x8a\x97\x51\x4c\x65\x53\xc0\xd0\xa4\xa6\xb2" 118 | "\x9a\x3d\x29\x5d\x03\xa9\x25\x91\x43\xd4\xa4\xe6\x42\x94\x16\x64" 119 | "\x7d\xa9\x39\x90\xa4\xa5\x2a\x54\x42\x6c\xb4\x9d\x54\x4c\xe5\x52" 120 | "\x42\x93\xaf\xff\xff\xff\x88\xed\x68\xec\x2d\x1e\x94\x1d\x52\x29" 121 | "\xe0\xc9\x9d\x44\x5b\x61\x53\xeb\xed\x42\xf7\x2d\x9e\x94\xa4\xfd" 122 | "\xcb\x49\x6a\x4a\x87\x95\x41\xa9\x4d\xa8\x25\x05\xfa\x52\x5b\x0f" 123 | "\x4a\x89\x9c\xaa\x4e\x51\xf7\x2a\x87\x97\xff\xff\xff\xc4\xce\x55" 124 | "\x11\x39\xed\xa2\x42\xd8\x85\x12\x75\x13\x39\x54\x49\xa6\x9d\x25" 125 | "\xa8\xd5\x4c\xa1\xe5\x51\x33\x95\x4a\x0d\x5a\x14\x1c\x67\xb5\x2a" 126 | "\x26\x72\xa8\x43\x4c\x3b\x3e\x94\xf0\x34\x7f\xff\xff\xf9\x21\x48" 127 | "\x64\x1b\xb9\x7e\x94\x1d\x52\x28\x82\x96\x92\xc8\xa2\x4e\xa1\x0f" 128 | "\x49\x54\x3d\xa9\x50\xf2\xa8\x35\x10\x9b\x0b\x54\xc9\x9d\x44\xce" 129 | "\x34\xa9\xe0\x68\x50\xbe\x48\x54\x20\xdd\xca\xa0\xea\x93\xff\xff" 130 | "\xff\xc3\xf7\x2d\x9e\x27\x2d\x14\x9a\xa9\x97\x51\x4c\x95\x29\x28" 131 | "\xc8\x9f\xa5\x13\x39\xed\x9a\x75\x13\x39\x90\xa2\x13\x61\x6a\x99" 132 | "\x33\xa9\x59\x6c\xee\x39\x0b\x22\x85\xee\x5b\x3d\x28\x32\x24\xc7" 133 | "\xa9\x43\xcb\xff\xff\xff\xe4\xe6\x47\xd2\x86\x4a\x88\x29\x69\x2c" 134 | "\x8a\x47\x6a\x54\x4c\xe5\x50\x72\x77\x2d\x89\x32\x0b\x49\x6a\x4a" 135 | "\x87\x95\x41\xa9\x35\x85\x1a\x4b\x62\x88\x4d\x85\xaa\x64\xce\xa2" 136 | "\x67\x1a\x54\xa2\xdb\x3a\x96\x4f\xff\xff\xff\x3c\x19\x33\x9a\xaf" 137 | "\xa5\x13\x38\xd2\xa5\x03\xf4\xa4\x87\xb2\x91\xd4\x95\x07\x54\x8a" 138 | "\x54\x75\x29\x3d\x85\x51\x33\x8e\xa5\x10\x93\x59\x61\x64\x52\x5b" 139 | "\x0f\x4a\x89\x35\x25\x51\x27\x51\x26\xa4\xbf\x4a\x47\x6a\x7f\xff" 140 | "\xff\xfc\xf1\x39\x89\x3a\x94\x9a\x69\xd2\xa0\xec\x3d\xa8\x2f\xd2" 141 | "\x83\x4a\x83\xab\xd2\x94\x9e\x92\xd4\x9f\xa5\x12\x5b\x2c\x34\x74" 142 | "\x15\x41\xd5\x22\x84\x3d\x35\x96\x8a\x0d\x54\xdf\x4a\x50\x6e\xa5" 143 | "\x51\x33\x95\x4b\x07\x31\x7f\xff\xff\xfc\x3c\xaa\x0d\x4a\x0c\x7a" 144 | "\x94\x3d\x8a\x26\x72\xa9\xe2\xc1\xa6\x75\x0f\x2a\x83\x51\x09\x3d" 145 | "\x94\xaf\xff\xff\xff\xe7\xff\xff\xff\x9b\xe7\x2a\x84\x39\xfa\xa8" 146 | "\x84\xda\x4e\xaa\x4e\x64\x29\x39\x47\x22\x87\xb5\x2a\x1e\x55\x13" 147 | "\x39\x54\x98\x74\x50\x5f\x4a\x48\x73\xd8\xa0\xea\x91\x49\xac\x35" 148 | "\x55\x16\x45\x0b\x51\x0f\x6d\x83\x98\xd5\x4d\x47\xaf\xff\xff\xff" 149 | "\x06\x95\x29\x2f\xd2\x83\x4a\x89\x9c\xaa\x20\x74\x95\x44\x9a\x92" 150 | "\xa9\xd0\xb6\x75\x35\x53\x29\x39\x90\xa2\x67\xb6\x98\xd4\x35\x4a" 151 | "\x26\x72\xa9\x21\xac\x29\x12\x63\xd4\xa1\xe5\x51\x33\x95\x4a\x47" 152 | "\x52\x9e\x27\x3f\xff\xff\xff\x95\x8c\x85\x91\x4b\x6b\x50\x75\x48" 153 | "\xa4\x87\xf1\x52\x91\xfa\x51\x33\x95\x49\xc6\x92\x4f\xe0\x7e\x97" 154 | "\x51\x4d\xf3\xda\x85\x32\x29\xe0\x68\x52\x63\x77\x2d\x4a\x24\xea" 155 | "\x5b\x54\x85\xb1\x09\x1d\x52\x28\x99\xc6\x9f\xff\xff\xff\x27\x2a" 156 | "\x9e\x06\x85\x13\x39\x54\x20\xeb\x48\xcd\x57\xd2\x89\x9c\xaa\x7f" 157 | "\xff\x6f\xff\xf3\x7f\xff\xb3\xaa\x89\x14\xd0\x3a\xbe\xbf\xff\xb7" 158 | "\xff\xf9\xbf\xff\xf3\x28\x6a\x94\x4c\xe5\x50\x87\x3f\x5f\xff\xed" 159 | "\xff\xfe\x6f\xff\xee\x21\x4e\xe5\xb2\xa1\x92\xda\x07\x67\xd2\x89" 160 | "\x9c\xbf\xff\xff\xfe\x1a\xa9\x0d\xdc\xd2\x5a\x3a\x14\xf1\x39\xe9" 161 | "\x53\x22\x94\xda\x84\xa8\x6a\x92\x5b\x3b\x98\xbe\x14\x1d\x52\x29" 162 | "\x5d\xa9\x51\x27\x51\x33\x95\x44\xd8\x7b\x63\xa0\xaa\x1e\x55\x10" 163 | "\x7a\x4b\x39\xfc\x7f\xff\xff\xf9\x31\x52\x93\x55\x32\x94\x95\x41" 164 | "\xa2\x73\xd8\x55\xd4\x53\x22\x93\x8e\x45\x27\x1d\x12\xa8\x84\x8e" 165 | "\xc4\x96\x45\x25\x3d\x9e\x07\x64\x9f\x4a\x24\xea\x4c\x54\xa8\x7e" 166 | "\xe5\xb1\x4f\x06\x4c\xea\x78\x9c\x69\x53\x27\xff\xff\xff\x9e\x06" 167 | "\x85\x08\x5b\x12\x33\x54\xa7\x83\xdb\x44\x8a\x46\x55\x06\xa2\x12" 168 | "\x7b\x29\xf4\xa4\xa6\xee\x55\x29\x35\x56\xa4\xa8\xfa\x53\xc4\xe5" 169 | "\xa9\x41\xa9\x49\xec\x2a\x9d\xcc\x7a\x0b\x52\x7f\xff\xff\xfc\x45" 170 | "\xb2\xca\x72\x0d\x26\xaa\x65\x2b\x1d\x87\xf3\xe9\x14\xa8\x82\x9b" 171 | "\xd9\x64\x51\x33\x95\x4b\x6a\x94\xf6\x26\xda\x86\x92\xa9\x59\x6c" 172 | "\x41\xea\x53\xc4\xe7\x53\xc0\xd0\xa5\x1e\x8d\x54\xca\x1e\xa5\x27" 173 | "\x32\x3f\xff\xff\xfc\x8c\x64\xea\xba\x8a\x63\x2a\x9e\x2c\x2d\x44" 174 | "\x9c\xb2\x28\x3a\xa4\x53\xc5\x86\x4c\xe5\x91\x41\xc6\x7b\x52\xa1" 175 | "\x0f\x57\x7b\x68\x41\xbb\x96\x8f\x4b\xa8\xa6\xf9\xca\xa1\x27\x35" 176 | "\x5f\x4a\x78\x32\x67\x51\x33\x97\xff\xff\xff\xc9\x07\x4d\x6a\x47" 177 | "\x40\xc9\xd5\x48\xca\x3b\x24\xfa\x52\xb3\x9a\xa4\x96\x45\x27\x34" 178 | "\xd3\x96\xc5\x0d\x52\x89\x9c\xaa\x0c\xd6\x28\x34\x28\x99\xca\xa4" 179 | "\x63\x27\x55\x29\xb5\x04\xa0\xa8\x51\x09\x34\xb2\x96\xa2\xc9\xff" 180 | "\xff\xff\xe0\xea\x91\x44\xce\x55\x09\x39\x50\x95\x11\xe0\xb4\x50" 181 | "\x59\x14\x35\x4a\x0e\xa5\x2d\xa8\x87\xa9\x04\x31\xed\x42\x83\xaa" 182 | "\x45\x0d\x52\x12\x6a\x88\x93\x77\x2a\x85\x96\x53\xd8\x95\x12\x75" 183 | "\x26\x2a\x7f\xff\xff\xfc\xa4\xf6\x15\x41\x9a\xc5\xd4\x53\x85\x52" 184 | "\x16\xc5\x13\x39\x54\xf1\x39\x91\xd4\xb6\x23\xe9\x41\xd5\x22\x83" 185 | "\xa2\x85\x2d\xa9\x05\x2d\x5f\x4a\x64\x53\xad\x45\xf0\xa2\x67\x1a" 186 | "\x54\x4c\xe5\x51\x1d\x4d\x52\x9e\x06\x8f\xff\xff\xff\x12\x3e\xa6" 187 | "\xaa\x65\x0f\x52\x83\x52\xbb\x6c\xae\x83\x22\x75\x27\xb4\xab\xff" 188 | "\xff\xff\xf9\xff\xff\xff\xe6\xf9\xca\xa1\x07\x5a\x46\x6a\xbe\x94" 189 | "\x3d\x8a\x72\x3d\x12\xa9\x9c\x76\x20\xb5\x7d\x28\x34\x29\x49\x6a" 190 | "\x50\x83\xa2\x82\xc8\xa4\xe6\xa7\xe9\x42\x14\x68\x2c\x8a\x56\x32" 191 | "\x1a\xa9\x94\x1d\x52\x29\x30\xfd\x96\x4f\xff\xff\xff\x24\x3f\x8a" 192 | "\x94\x1a\x54\x4c\xe5\x52\x4f\x46\xaa\x65\x29\x1d\x4b\xa8\xa6\xe3" 193 | "\xa9\x29\x8b\x61\x0a\xa4\xe3\x91\x44\xce\x64\x29\x29\xaa\x1d\x0a" 194 | "\x21\x36\x5b\x34\xd4\x15\x48\xca\x43\xd2\x55\x13\x38\xd2\xa2\x67" 195 | "\x2f\xff\xff\xff\x88\x19\xa8\x3d\x8a\x56\x3b\x50\x59\x14\x35\x4a" 196 | "\x26\x72\xa8\x32\x25\x43\xca\xa4\xb4\x1a\xa9\x8d\x54\xca\x52\x7b" 197 | "\x0a\xa7\x81\xa4\xb6\x28\x7e\xe5\xb1\x49\xcd\x4a\x83\xaa\x45\x10" 198 | "\x91\xd8\x59\x3f\xff\xff\xfc\x25\xb6\x18\xf6\xa2\x8f\x5f\x4a\x26" 199 | "\x72\xa8\x41\xd5\xdc\x68\x52\x3b\x49\xd4\xa9\x52\xb1\xd8\x9a\x3d" 200 | "\x28\x93\x50\x92\xc8\xa0\xea\x91\x49\x2c\x35\x75\x9a\xa9\x94\x32" 201 | "\x50\xa1\x0f\x52\x4b\x52\x50\xa2\x4f\xff\xff\xff\xe2\x67\x2a\x92" 202 | "\x14\x9d\x57\x51\x4d\xf3\x95\x49\x3d\x1a\xa9\x94\xa4\x75\x29\x19" 203 | "\x46\x95\x06\xa2\x46\x94\x9c\xea\x1e\xa5\x13\x39\x54\x9c\x69\x24" 204 | "\xea\x78\x32\x67\x52\x73\x21\x49\xca\x5a\x11\xf5\xff\xff\xff\xe2" 205 | "\x13\x60\xcd\x34\xe9\x2d\x45\x91\x43\xda\x95\x27\x32\x14\xa0\xb3" 206 | "\x23\xe9\x41\xd5\x22\x88\x49\xa5\x94\xb5\x16\x45\x0d\x52\x87\xa8" 207 | "\xaa\x4d\x61\x46\x95\x12\x5a\x90\x55\x0b\x2c\xa7\xb1\x3f\x4a\x0e" 208 | "\xa9\x3f\xff\xff\xfc\xb0\xf4\x50\x59\x14\x9c\xc8\x52\x72\x8e\x45" 209 | "\x25\xb0\xf4\xa8\x83\x48\x55\x12\x75\x10\x69\x0a\xba\x8a\x6f\x9c" 210 | "\xb5\x28\x99\xca\xa5\x36\xa0\x94\x15\x0a\x58\x5a\x07\xe4\xb2\x7d" 211 | "\x28\x99\xca\xa4\xe5\x1c\x9f\xff\xff\xfe\x21\x27\xae\xb2\xc8\xa5" 212 | "\x87\xa2\x83\x55\x37\xd2\x83\xaa\x45\x06\xa2\x0d\x34\xff\x4a\x0d" 213 | "\x0a\x1e\x55\x2b\xb0\xf2\x9e\xda\xa4\x52\xc2\xd0\x62\xcb\xf4\xa4" 214 | "\xb4\x1c\x69\x2c\x8a\x5b\x5f\x81\xd9\x22\x92\xd8\x7a\x7f\xff\xff" 215 | "\xfc\x9c\xc8\x52\x83\x56\x85\xd4\x53\x7c\xe5\x53\xa0\xfc\x29\x25" 216 | "\x87\xae\xb2\xc9\xf4\xa2\x67\x2a\x96\xd7\x59\x6c\x52\x83\x56\xa5" 217 | "\x06\x95\x25\x93\xe9\x41\xd5\x22\x89\xf0\x75\x2c\x3f\x84\x28\x79" 218 | "\x7f\xff\xff\xfc\x49\xe3\x19\x08\x75\xa4\x32\x08\x7a\x0f\x6d\x85" 219 | "\x91\x44\x94\xa9\x9d\x41\xd7\x59\x47\x61\x64\x5d\x45\x32\x25\x44" 220 | "\x14\xb4\x96\x45\x06\x85\x13\x39\xed\x9a\x75\x27\x32\x14\x94\xa3" 221 | "\x4d\xb6\x15\x0a\x4e\x39\x3f\xff\xff\xfc\x96\xc3\xfb\x2d\x4a\x1a" 222 | "\xa4\x9d\x41\xa9\x21\x87\x19\xe8\x31\x07\x42\x93\x58\x6a\x94\x1a" 223 | "\x54\x4c\xe5\x53\xc1\xec\xa2\x45\x27\x2a\x93\x8e\x45\x28\x2c\xa9" 224 | "\x50\x75\x48\xa1\xed\x4f\x83\x25\x25\x91\x7f", 225 | 12 + 16 * 174 + 11, 226 | "Pacing back and forth the length of the hatchways and savagely chewing\n" 227 | "the end of a cigar, was the man whose casual glance had rescued me from\n" 228 | "the sea. His height was probably five feet ten inches, or ten and a\n" 229 | "half; but my first impression, or feel of the man, was not of this, but\n" 230 | "of his strength. And yet, while he was of massive build, with broad\n" 231 | "shoulders and deep chest, I could not characterize his strength as\n" 232 | "massive. It was what might be termed a sinewy, knotty strength, of the\n" 233 | "kind we ascribe to lean and wiry men, but which, in him, because of his\n" 234 | "heavy build, partook more of the enlarged gorilla order. Not that in\n" 235 | "appearance he seemed in the least gorilla-like. What I am striving to\n" 236 | "express is this strength itself, more as a thing apart from his physical\n" 237 | "semblance. It was a strength we are wont to associate with things\n" 238 | "primitive, with wild animals, and the creatures we imagine our\n" 239 | "tree-dwelling prototypes to have been—a strength savage, ferocious, alive\n" 240 | "in itself, the essence of life in that it is the potency of motion, the\n" 241 | "elemental stuff itself out of which the many forms of life have been\n" 242 | "moulded; in short, that which writhes in the body of a snake when the\n" 243 | "head is cut off, and the snake, as a snake, is dead, or which lingers in\n" 244 | "the shapeless lump of turtle-meat and recoils and quivers from the prod\n" 245 | "of a finger.\n" 246 | "\n" 247 | "Such was the impression of strength I gathered from this man who paced up\n" 248 | "and down. He was firmly planted on his legs; his feet struck the deck\n" 249 | "squarely and with surety; every movement of a muscle, from the heave of\n" 250 | "the shoulders to the tightening of the lips about the cigar, was\n" 251 | "decisive, and seemed to come out of a strength that was excessive and\n" 252 | "overwhelming. In fact, though this strength pervaded every action of\n" 253 | "his, it seemed but the advertisement of a greater strength that lurked\n" 254 | "within, that lay dormant and no more than stirred from time to time, but\n" 255 | "which might arouse, at any moment, terrible and compelling, like the rage\n" 256 | "of a lion or the wrath of a storm.\n" 257 | "\n" 258 | "The cook stuck his head out of the galley door and grinned encouragingly\n" 259 | "at me, at the same time jerking his thumb in the direction of the man who\n" 260 | "paced up and down by the hatchway. Thus I was given to understand that\n" 261 | "he was the captain, the “Old Man,” in the cook’s vernacular, the\n" 262 | "individual whom I must interview and put to the trouble of somehow\n" 263 | "getting me ashore. I had half started forward, to get over with what I\n" 264 | "was certain would be a stormy five minutes, when a more violent\n" 265 | "suffocating paroxysm seized the unfortunate person who was lying on his\n" 266 | "back. He wrenched and writhed about convulsively. The chin, with the\n" 267 | "damp black beard, pointed higher in the air as the back muscles stiffened\n" 268 | "and the chest swelled in an unconscious and instinctive effort to get\n" 269 | "more air. Under the whiskers, and all unseen, I knew that the skin was\n" 270 | "taking on a purplish hue.\n" 271 | "\n" 272 | "The captain, or Wolf Larsen, as men called him, ceased pacing and gazed\n" 273 | "down at the dying man. So fierce had this final struggle become that the\n" 274 | "sailor paused in the act of flinging more water over him and stared\n" 275 | "curiously, the canvas bucket partly tilted and dripping its contents to\n" 276 | "the deck. The dying man beat a tattoo on the hatch with his heels,\n" 277 | "straightened out his legs, and stiffened in one great tense effort, and\n" 278 | "rolled his head from side to side. Then the muscles relaxed, the head\n" 279 | "stopped rolling, and a sigh, as of profound relief, floated upward from\n" 280 | "his lips. The jaw dropped, the upper lip lifted, and two rows of\n" 281 | "tobacco-discoloured teeth appeared. It seemed as though his features had\n" 282 | "frozen into a diabolical grin at the world he had left and outwitted.", 283 | 3687, 284 | }, 285 | { __LINE__, 286 | (unsigned char *) 287 | "\xe5\x39\x6a\x50\xd5\x28\x99\xca\xa5\xe3\xdb\x62" 288 | "\x0a\xa1\xe5\x52\x7b\x69\x1d\x4a\x17\xb9\x6a\x4a\x14\x32\x54\x8c" 289 | "\xa4\x3d\x25\x42\x95\x14\x85\x42\x07\x67\xa5\x25\x3d\x8a\x1e\xa2" 290 | "\xa9\x59\x4f\x5d\x05\x51\x27\x52\x43\x21\x07\xa3\xb9\x54\x4c\xe5" 291 | "\x52\xb3\xd0\x64\x98\x83\xa1\x48\xc7\x54\x88\x53\xc4\xe6\x24\xea" 292 | "\x4e\x3e\xe5\x50\x87\xaa\xa2\x91\x25\x91\x44\xce\x5a\x54\xf0\x64" 293 | "\xce\xa0\xea\x3a\x67\x2d\x8a\x0e\xa9\x14\x49\xd4\x1a\x11\x6d\x25" 294 | "\x50\x74\x9e\xa9\x94\x4c\xe5\x52\xb3\xf8\x2d\x88\x50\xf2\xa8\x99" 295 | "\xca\xa1\x47\x62\x67\xfa\x51\x33\x95\x44\x16\xb1\xd8\x34\x95\x41" 296 | "\xd5\x22\x85\xed\x68\xe8\x51\x09\x1a\x4c\x7a\x94\x49\xd4\xf1\x39" 297 | "\x89\x3a\x89\x9c\xaa\x67\x1f\x84\x28\x79\x54\xd2\x34\xdb\x61\x54" 298 | "\x1d\x52\x28\x79\x54\xd2\x34\xdb\x61\x7f\xd2\x14\xc4\x79\x14\x2d" 299 | "\x49\x32\x68\x2a\x89\x9c\xb4\xfd\x28\x35\x24\x29\x0b\x52\x54\xb0" 300 | "\xa8\xac\xa4\x4a\x89\x3a\x89\x9c\xaa\x1e\xb3\x54\x63\xd4\x85\x0f" 301 | "\x2a\x94\x8e\xae\xa6\xaa\x45\x2c\x2f\x6b\x4d\x61\x50\xa2\x67\x1a" 302 | "\x54\x4c\xe5\xf4\xa2\x27\x3d\xb4\x48\xa4\x85\x25\x03\xb0\xaa\x26" 303 | "\x72\xa8\x41\xda\x82\xa1\x4f\x13\x98\x93\xa8\x6a\x6b\x2d\x0a\x26" 304 | "\x72\xd2\xa2\x4e\xa2\x67\x2a\x88\x2d\x63\xb0\x69\x31\xea\x5f", 305 | 12 + 16 * 16 + 15, 306 | "When in the Course of human events it becomes necessary for one " 307 | "people to dissolve the political bands which have connected them " 308 | "with another and to assume among the powers of the earth, the " 309 | "separate and equal station to which the Laws of Nature and of " 310 | "Nature's God entitle them, a decent respect to the opinions of " 311 | "mankind requires that they should declare the causes which impel " 312 | "them to the separation.", 313 | 404, 314 | }, 315 | }; 316 | 317 | void 318 | run_test (const struct test_huff_dec *test) 319 | { 320 | struct huff_decode_retval retval; 321 | struct lsqpack_huff_decode_state state; 322 | unsigned in_chunk_sz, out_chunk_sz, in_off, out_off, n_to_read, n_to_write; 323 | char output[0x1000]; 324 | 325 | if (verbose) 326 | fprintf(stderr, "Run test on line %u\n", test->lineno); 327 | 328 | for (in_chunk_sz = 1; in_chunk_sz <= test->src_sz; ++in_chunk_sz) 329 | { 330 | if (verbose) 331 | fprintf(stderr, "chunk %u out of %zu\n", in_chunk_sz, test->src_sz); 332 | for (out_chunk_sz = 1; out_chunk_sz <= test->dst_sz; ++out_chunk_sz) 333 | { 334 | in_off = 0; 335 | out_off = 0; 336 | state.resume = 0; 337 | n_to_read = MIN(test->src_sz, in_chunk_sz); 338 | n_to_write = MIN(test->dst_sz, out_chunk_sz); 339 | 340 | do 341 | { 342 | assert(in_off + n_to_read <= test->src_sz); /* self-test */ 343 | retval = lsqpack_huff_decode_full(test->src + in_off, n_to_read, 344 | (unsigned char *) output + out_off, n_to_write, &state, 345 | test->src_sz == in_off + n_to_read); 346 | switch (retval.status) 347 | { 348 | case HUFF_DEC_OK: 349 | in_off += retval.n_src; 350 | out_off += retval.n_dst; 351 | assert(test->dst_sz == out_off); 352 | assert(test->src_sz == in_off); 353 | assert(0 == memcmp(test->dst, output, out_off)); 354 | break; 355 | case HUFF_DEC_END_SRC: 356 | case HUFF_DEC_END_DST: 357 | in_off += retval.n_src; 358 | out_off += retval.n_dst; 359 | n_to_write = MIN(sizeof(output) - out_off, out_chunk_sz); 360 | n_to_read = MIN(test->src_sz - in_off, in_chunk_sz); 361 | break; 362 | default: 363 | assert(retval.status == HUFF_DEC_ERROR); 364 | assert(0); 365 | } 366 | } 367 | while (in_off < test->src_sz); 368 | assert(retval.status == HUFF_DEC_OK); 369 | } 370 | } 371 | } 372 | 373 | int 374 | main (int argc, char **argv) 375 | { 376 | const struct test_huff_dec *test; 377 | int opt, run_expensive = 0; 378 | 379 | while (-1 != (opt = getopt(argc, argv, "ve"))) 380 | { 381 | switch (opt) 382 | { 383 | case 'v': 384 | verbose = 1; 385 | break; 386 | case 'e': 387 | run_expensive = 1; 388 | break; 389 | default: 390 | exit(EXIT_FAILURE); 391 | } 392 | } 393 | 394 | for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test) 395 | if (run_expensive || test->src_sz * test->dst_sz < 150000) 396 | run_test(test); 397 | 398 | return 0; 399 | } 400 | -------------------------------------------------------------------------------- /test/test_int.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "lsqpack.h" 7 | #include "lsqpack-test.h" 8 | 9 | struct int_test 10 | { 11 | int it_lineno; 12 | unsigned it_prefix_bits; 13 | unsigned char it_encoded[20]; 14 | size_t it_enc_sz; 15 | uint64_t it_decoded; 16 | int it_dec_retval; 17 | }; 18 | 19 | static const struct int_test tests[] = 20 | { 21 | 22 | { .it_lineno = __LINE__, 23 | .it_prefix_bits = 7, 24 | .it_encoded = { 0x7F, 0x02, }, 25 | .it_enc_sz = 2, 26 | .it_decoded = 0x81, 27 | .it_dec_retval = 0, 28 | }, 29 | 30 | /* RFC 7541, Appendinx C.1.1 */ 31 | { .it_lineno = __LINE__, 32 | .it_prefix_bits = 5, 33 | .it_encoded = { 0b1010, 0x02, }, 34 | .it_enc_sz = 1, 35 | .it_decoded = 10, 36 | .it_dec_retval = 0, 37 | }, 38 | 39 | /* RFC 7541, Appendinx C.1.2 */ 40 | { .it_lineno = __LINE__, 41 | .it_prefix_bits = 5, 42 | .it_encoded = { 0b11111, 0b10011010, 0b00001010, }, 43 | .it_enc_sz = 3, 44 | .it_decoded = 1337, 45 | .it_dec_retval = 0, 46 | }, 47 | 48 | /* RFC 7541, Appendinx C.1.3 */ 49 | { .it_lineno = __LINE__, 50 | .it_prefix_bits = 8, 51 | .it_encoded = { 0b101010, }, 52 | .it_enc_sz = 1, 53 | .it_decoded = 42, 54 | .it_dec_retval = 0, 55 | }, 56 | 57 | { .it_lineno = __LINE__, 58 | .it_prefix_bits = 7, 59 | .it_encoded = { 0b01111111, 0b10000001, 0b10000010, 0b00000011, }, 60 | .it_enc_sz = 4, 61 | /* 01234560123456 */ 62 | .it_decoded = 0b1100000100000001 + 0b1111111, 63 | .it_dec_retval = 0, 64 | }, 65 | 66 | { .it_lineno = __LINE__, 67 | .it_prefix_bits = 7, 68 | .it_encoded = { 0b01111111, 0b10000001, 0b10000010, 0b10000011, 69 | 0b00000011, }, 70 | .it_enc_sz = 5, 71 | /* 012345601234560123456 */ 72 | .it_decoded = 0b11000001100000100000001 + 0b1111111, 73 | .it_dec_retval = 0, 74 | }, 75 | 76 | { .it_lineno = __LINE__, 77 | .it_prefix_bits = 7, 78 | .it_encoded = { 0b01111111, 0b10000001, 0b10000010, 0b10000011, 79 | 0b10000100, 0b10000101, 0b10000110, 0b10000111, 80 | 0b10001000, 81 | 0b00000011, }, 82 | .it_enc_sz = 10, 83 | /* 01234560123456012345601234560123456012345601234560123456 */ 84 | .it_decoded = 0b1100010000000111000011000001010000100000001100000100000001 85 | + 0b1111111, 86 | .it_dec_retval = 0, 87 | }, 88 | 89 | { .it_lineno = __LINE__, 90 | .it_prefix_bits = 7, 91 | .it_encoded = { 0b01111111, 0b10000001, 0b10000010, 0b10000011, 92 | 0b10000100, 0b10000101, 0b10000110, 0b10000111, 93 | 0b10001000, 0b10001001, 94 | 0b00000001, }, 95 | .it_enc_sz = 11, 96 | /* 012345601234560123456012345601234560123456012345601234560123456 */ 97 | .it_decoded = 0b1000100100010000000111000011000001010000100000001100000100000001 98 | + 0b1111111, 99 | .it_dec_retval = 0, 100 | }, 101 | 102 | { .it_lineno = __LINE__, 103 | .it_prefix_bits = 7, 104 | .it_encoded = { 0b01111111, 0b10000000, 0b11111111, 0b11111111, 105 | 0b11111111, 0b11111111, 0b11111111, 0b11111111, 106 | 0b11111111, 0b11111111, 107 | 0b00000001, }, 108 | .it_enc_sz = 11, 109 | .it_decoded = UINT64_MAX, 110 | .it_dec_retval = 0, 111 | }, 112 | 113 | { .it_lineno = __LINE__, 114 | .it_prefix_bits = 7, 115 | /* Same as above, but with extra bit that overflows it */ 116 | /* ----v---- */ 117 | .it_encoded = { 0b01111111, 0b10010000, 0b11111111, 0b11111111, 118 | 0b11111111, 0b11111111, 0b11111111, 0b11111111, 119 | 0b11111111, 0b11111111, 120 | 0b00000001, }, 121 | .it_enc_sz = 11, 122 | .it_dec_retval = -2, 123 | }, 124 | 125 | { .it_lineno = __LINE__, 126 | .it_prefix_bits = 8, 127 | .it_encoded = { 0b11111111, 0b10000001, 0b10000010, 0b10000011, 128 | 0b10000100, 0b10000101, 0b10000110, 0b10000111, 129 | 0b10001000, 0b10001001, 0b00000001, }, 130 | .it_enc_sz = 11, 131 | /* 012345601234560123456012345601234560123456012345601234560123456 */ 132 | .it_decoded = 0b1000100100010000000111000011000001010000100000001100000100000001 133 | + 0b11111111, 134 | .it_dec_retval = 0, 135 | }, 136 | 137 | { .it_lineno = __LINE__, 138 | .it_prefix_bits = 7, 139 | .it_encoded = { 0b01111111, 0b11101111, 0b11111111, 0b11111111, 140 | 0b11111111, 0b11111111, 0b11111111, 0b11111111, 141 | 0b11111111, 0b11111111, 142 | 0b00000001, }, 143 | .it_enc_sz = 11, 144 | .it_dec_retval = -2, 145 | }, 146 | 147 | { .it_lineno = __LINE__, 148 | .it_prefix_bits = 7, 149 | .it_encoded = { 0b01111111, 0b10000001, 0b10000010, 0b10000011, 150 | 0b10000100, 0b10000101, 0b10000110, 0b10000111, 151 | 0b10001000, 0b10001001, 152 | 0b00000011, }, 153 | .it_enc_sz = 11, 154 | .it_dec_retval = -2, 155 | }, 156 | 157 | { .it_lineno = __LINE__, 158 | .it_prefix_bits = 7, 159 | .it_encoded = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 160 | 0xFF, 0xFF, 0xFF, }, 161 | .it_enc_sz = 11, 162 | .it_dec_retval = -2, 163 | }, 164 | 165 | }; 166 | 167 | int 168 | main (void) 169 | { 170 | const struct int_test *test; 171 | const unsigned char *src; 172 | unsigned char *dst; 173 | unsigned char buf[ sizeof(((struct int_test *) NULL)->it_encoded) ]; 174 | uint64_t val; 175 | size_t sz; 176 | int rv; 177 | 178 | /* Test the decoder */ 179 | for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test) 180 | { 181 | struct lsqpack_dec_int_state state; 182 | state.resume = 0; 183 | for (sz = 0; sz < test->it_enc_sz - 1; ++sz) 184 | { 185 | src = test->it_encoded + sz; 186 | rv = lsqpack_dec_int(&src, src + 1, test->it_prefix_bits, 187 | &val, &state); 188 | assert(-1 == rv); 189 | } 190 | src = test->it_encoded + sz; 191 | rv = lsqpack_dec_int(&src, src + 1, test->it_prefix_bits, 192 | &val, &state); 193 | assert(rv == test->it_dec_retval); 194 | if (0 == rv) 195 | assert(val == test->it_decoded); 196 | } 197 | 198 | /* Test the encoder */ 199 | for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test) 200 | { 201 | if (test->it_dec_retval != 0) 202 | continue; 203 | for (sz = 1; sz < test->it_enc_sz; ++sz) 204 | { 205 | dst = lsqpack_enc_int(buf, buf + sz, test->it_decoded, 206 | test->it_prefix_bits); 207 | assert(dst == buf); /* Not enough room */ 208 | } 209 | for (; sz <= sizeof(buf); ++sz) 210 | { 211 | buf[0] = '\0'; 212 | dst = lsqpack_enc_int(buf, buf + sz, test->it_decoded, 213 | test->it_prefix_bits); 214 | assert(dst - buf == (intptr_t) test->it_enc_sz); 215 | assert(0 == memcmp(buf, test->it_encoded, test->it_enc_sz)); 216 | } 217 | } 218 | 219 | return 0; 220 | } 221 | -------------------------------------------------------------------------------- /test/test_read_enc_stream.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Read encoder stream 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "lsqpack.h" 10 | 11 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 12 | 13 | /* Dynamic table entry: */ 14 | struct lsqpack_dec_table_entry 15 | { 16 | unsigned dte_name_len; 17 | unsigned dte_val_len; 18 | unsigned dte_refcnt; 19 | unsigned dte_pad[4]; /* name hash, nameval hash, name idx, flags */ 20 | char dte_buf[0]; /* Contains both name and value */ 21 | }; 22 | 23 | #define DTE_NAME(dte) ((dte)->dte_buf) 24 | #define DTE_VALUE(dte) (&(dte)->dte_buf[(dte)->dte_name_len]) 25 | 26 | 27 | struct test_read_encoder_stream 28 | { 29 | int lineno; 30 | 31 | /* Input */ 32 | unsigned char input[0x100]; 33 | size_t input_sz; 34 | 35 | /* Output */ 36 | unsigned n_entries; 37 | struct { 38 | const char *name, *value; 39 | } dyn_table[10]; 40 | }; 41 | 42 | 43 | static const struct test_read_encoder_stream tests[] = 44 | { 45 | 46 | { __LINE__, 47 | "\xc0\x8b\xf1\xe3\xc2\xf5\x15\x31\xa2\x45\xcf\x64\xdf", 48 | 13, 49 | 1, 50 | { 51 | { ":authority", "www.netbsd.org", }, 52 | }, 53 | }, 54 | 55 | { __LINE__, 56 | "\xc0\x00", 57 | 2, 58 | 1, 59 | { 60 | { ":authority", "", }, 61 | }, 62 | }, 63 | 64 | { __LINE__, 65 | "\xc0\x8b\xf1\xe3\xc2\xf5\x15\x31\xa2\x45\xcf\x64\xdf\x00", 66 | 14, 67 | 2, 68 | { 69 | { ":authority", "www.netbsd.org", }, 70 | { ":authority", "www.netbsd.org", }, 71 | }, 72 | }, 73 | 74 | { __LINE__, 75 | "\xc0\x8b\xf1\xe3\xc2\xf5\x15\x31\xa2\x45\xcf\x64\xdf\x00\x20", 76 | 15, 77 | 0, 78 | { 79 | { NULL, NULL, }, 80 | }, 81 | }, 82 | 83 | { __LINE__, 84 | "\xc0\x15" "Respect my authorata!", 85 | 2 + sizeof("Respect my authorata!") - 1, 86 | 1, 87 | { 88 | { ":authority", "Respect my authorata!", }, 89 | }, 90 | }, 91 | 92 | { __LINE__, 93 | "\x63\x92\xd9\x0b\x8c\xe5\x39\x6c\x2a\x86\x42\x94\xfa\x50\x83\xb3" 94 | "\xfc", 95 | 17, 96 | 1, 97 | { 98 | { "dude", "Where is my car?", }, 99 | }, 100 | }, 101 | 102 | { __LINE__, 103 | "\x63\x92\xd9\x0b\x03\x61\x61\x7a", 104 | 8, 105 | 1, 106 | { 107 | { "dude", "aaz", }, 108 | }, 109 | }, 110 | 111 | { __LINE__, 112 | "\x43\x7a\x7a\x7a\x88\xcc\x6a\x0d\x48\xea\xe8\x3b\x0f", 113 | 13, 114 | 1, 115 | { 116 | { "zzz", "Kilimanjaro", }, 117 | }, 118 | }, 119 | 120 | { __LINE__, 121 | "\x43\x7a\x7a\x7a\x03\x61\x61\x7a", 122 | 8, 123 | 1, 124 | { 125 | { "zzz", "aaz", }, 126 | }, 127 | }, 128 | 129 | { __LINE__, 130 | "\x43\x7a\x7a\x7a\x03\x61\x61\x61\x80\x03\x61\x61\x7a", 131 | 13, 132 | 2, 133 | { 134 | { "zzz", "aaa", }, 135 | { "zzz", "aaz", }, 136 | }, 137 | }, 138 | 139 | { __LINE__, 140 | "\x40\x88\xcc\x6a\x0d\x48\xea\xe8\x3b\x0f", 141 | 10, 142 | 1, 143 | { 144 | { "", "Kilimanjaro", }, 145 | }, 146 | }, 147 | 148 | { __LINE__, 149 | "\x43\x7a\x7a\x7a\x00", 150 | 5, 151 | 1, 152 | { 153 | { "zzz", "", }, 154 | }, 155 | }, 156 | 157 | { __LINE__, 158 | "\x40\x00", 159 | 2, 160 | 1, 161 | { 162 | { "", "", }, 163 | }, 164 | }, 165 | 166 | }; 167 | 168 | 169 | struct ringbuf_iter 170 | { 171 | const struct lsqpack_ringbuf *rbuf; 172 | unsigned next, end; 173 | }; 174 | 175 | 176 | #if LSQPACK_DEVEL_MODE 177 | unsigned 178 | ringbuf_count (const struct lsqpack_ringbuf *rbuf); 179 | 180 | void * 181 | ringbuf_iter_next (struct ringbuf_iter *iter); 182 | 183 | void * 184 | ringbuf_iter_first (struct ringbuf_iter *iter, 185 | const struct lsqpack_ringbuf *rbuf); 186 | 187 | static void 188 | verify_dyn_table (const struct lsqpack_dec *dec, 189 | const struct test_read_encoder_stream *test) 190 | { 191 | const struct lsqpack_dec_table_entry *entry; 192 | unsigned n; 193 | struct ringbuf_iter riter; 194 | 195 | assert(ringbuf_count(&dec->qpd_dyn_table) == test->n_entries); 196 | 197 | for (n = 0; n < test->n_entries; ++n) 198 | { 199 | if (n == 0) 200 | entry = ringbuf_iter_first(&riter, &dec->qpd_dyn_table); 201 | else 202 | entry = ringbuf_iter_next(&riter); 203 | assert(entry); 204 | assert(entry->dte_name_len == strlen(test->dyn_table[n].name)); 205 | assert(entry->dte_val_len == strlen(test->dyn_table[n].value)); 206 | assert(0 == memcmp(DTE_NAME(entry), test->dyn_table[n].name, entry->dte_name_len)); 207 | assert(0 == memcmp(DTE_VALUE(entry), test->dyn_table[n].value, entry->dte_val_len)); 208 | } 209 | } 210 | #else 211 | static void 212 | verify_dyn_table (const struct lsqpack_dec *dec, 213 | const struct test_read_encoder_stream *test) 214 | { 215 | fprintf(stderr, "LSQPACK_DEVEL_MODE is not compiled: cannot verify dynamic table\n"); 216 | } 217 | #endif 218 | 219 | 220 | static void 221 | run_test (const struct test_read_encoder_stream *test) 222 | { 223 | struct lsqpack_dec dec; 224 | size_t chunk_sz, off; 225 | int r; 226 | 227 | for (chunk_sz = 1; chunk_sz <= test->input_sz; ++chunk_sz) 228 | { 229 | lsqpack_dec_init(&dec, NULL, 0x1000, 100, (void *) 1 /* hset */, 230 | LSQPACK_DEC_OPT_HTTP1X); 231 | 232 | off = 0; 233 | do 234 | { 235 | r = lsqpack_dec_enc_in(&dec, test->input + off, 236 | MIN(chunk_sz, test->input_sz - off)); 237 | assert(r == 0); 238 | off += MIN(chunk_sz, test->input_sz - off); 239 | } 240 | while (off < test->input_sz); 241 | 242 | verify_dyn_table(&dec, test); 243 | 244 | lsqpack_dec_cleanup(&dec); 245 | } 246 | } 247 | 248 | 249 | int 250 | main (void) 251 | { 252 | const struct test_read_encoder_stream *test; 253 | 254 | for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test) 255 | run_test(test); 256 | 257 | return 0; 258 | } 259 | -------------------------------------------------------------------------------- /test/testdata/encoder_stream: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/test/testdata/encoder_stream -------------------------------------------------------------------------------- /test/testdata/response: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litespeedtech/ls-qpack/771a794d7dfcc6132576136f62db3065493d876f/test/testdata/response -------------------------------------------------------------------------------- /tools/gen-enums.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Generate static table enums 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my @table = ( 8 | ":authority", "", 9 | ":path", "/", 10 | "age", "0", 11 | "content-disposition", "", 12 | "content-length", "0", 13 | "cookie", "", 14 | "date", "", 15 | "etag", "", 16 | "if-modified-since", "", 17 | "if-none-match", "", 18 | "last-modified", "", 19 | "link", "", 20 | "location", "", 21 | "referer", "", 22 | "set-cookie", "", 23 | ":method", "CONNECT", 24 | ":method", "DELETE", 25 | ":method", "GET", 26 | ":method", "HEAD", 27 | ":method", "OPTIONS", 28 | ":method", "POST", 29 | ":method", "PUT", 30 | ":scheme", "http", 31 | ":scheme", "https", 32 | ":status", "103", 33 | ":status", "200", 34 | ":status", "304", 35 | ":status", "404", 36 | ":status", "503", 37 | "accept", "*/*", 38 | "accept", "application/dns-message", 39 | "accept-encoding", "gzip, deflate, br", 40 | "accept-ranges", "bytes", 41 | "access-control-allow-headers", "cache-control", 42 | "access-control-allow-headers", "content-type", 43 | "access-control-allow-origin", "*", 44 | "cache-control", "max-age=0", 45 | "cache-control", "max-age=2592000", 46 | "cache-control", "max-age=604800", 47 | "cache-control", "no-cache", 48 | "cache-control", "no-store", 49 | "cache-control", "public, max-age=31536000", 50 | "content-encoding", "br", 51 | "content-encoding", "gzip", 52 | "content-type", "application/dns-message", 53 | "content-type", "application/javascript", 54 | "content-type", "application/json", 55 | "content-type", "application/x-www-form-urlencoded", 56 | "content-type", "image/gif", 57 | "content-type", "image/jpeg", 58 | "content-type", "image/png", 59 | "content-type", "text/css", 60 | "content-type", "text/html; charset=utf-8", 61 | "content-type", "text/plain", 62 | "content-type", "text/plain;charset=utf-8", 63 | "range", "bytes=0-", 64 | "strict-transport-security", "max-age=31536000", 65 | "strict-transport-security", "max-age=31536000; includesubdomains", 66 | "strict-transport-security", "max-age=31536000; includesubdomains; preload", 67 | "vary", "accept-encoding", 68 | "vary", "origin", 69 | "x-content-type-options", "nosniff", 70 | "x-xss-protection", "1; mode=block", 71 | ":status", "100", 72 | ":status", "204", 73 | ":status", "206", 74 | ":status", "302", 75 | ":status", "400", 76 | ":status", "403", 77 | ":status", "421", 78 | ":status", "425", 79 | ":status", "500", 80 | "accept-language", "", 81 | "access-control-allow-credentials", "FALSE", 82 | "access-control-allow-credentials", "TRUE", 83 | "access-control-allow-headers", "*", 84 | "access-control-allow-methods", "get", 85 | "access-control-allow-methods", "get, post, options", 86 | "access-control-allow-methods", "options", 87 | "access-control-expose-headers", "content-length", 88 | "access-control-request-headers", "content-type", 89 | "access-control-request-method", "get", 90 | "access-control-request-method", "post", 91 | "alt-svc", "clear", 92 | "authorization", "", 93 | "content-security-policy", "script-src 'none'; object-src 'none'; base-uri 'none'", 94 | "early-data", "1", 95 | "expect-ct", "", 96 | "forwarded", "", 97 | "if-range", "", 98 | "origin", "", 99 | "purpose", "prefetch", 100 | "server", "", 101 | "timing-allow-origin", "*", 102 | "upgrade-insecure-requests", "1", 103 | "user-agent", "", 104 | "x-forwarded-for", "", 105 | "x-frame-options", "deny", 106 | "x-frame-options", "sameorigin", 107 | ); 108 | 109 | 110 | my $idx = 0; 111 | print "enum lsqpack_tnam {\n"; 112 | while (my ($name, $value) = splice(@table, 0, 2)) { 113 | my $enum = "$name-$value"; 114 | $enum =~ tr/a-z/A-Z/; 115 | $enum =~ tr/-/_/; 116 | $enum =~ s~[^A-Z0-9_]~_~g; 117 | $enum =~ s/_+/_/g; 118 | $enum =~ s/_+$//; 119 | $enum =~ s/^_+//; 120 | print " LSQPACK_TNV_$enum = $idx, /* \"$name\" \"$value\" */\n"; 121 | ++$idx; 122 | } 123 | print "};\n\n"; 124 | -------------------------------------------------------------------------------- /tools/har2qif.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # har2qif.pl - Convert HAR file to a QIF file. 4 | # 5 | # QIF stands for "QPACK Interop Format." It is meant to be easy to parse, 6 | # write, and compare. The idea is that the QIF input to an encoder can 7 | # be compared to QIF output produced by the decoder using `diff(1)': 8 | # 9 | # sh$ har2qif -requests my.har > in.qif 10 | # sh$ ./qpack-encoder-A in.qif > binary-format # See wiki 11 | # sh$ ./qpack-decoder-B binary-format > out.qif 12 | # sh$ diff in.qif out.qif && echo "Success!" 13 | # 14 | # The QIF format is plain text. Each header field in on a separate line, 15 | # with name and value separated by the TAB character. An empty line 16 | # signifies the end of a header set. Lines beginning with '#' are ignored. 17 | # 18 | # HTTP/2 header sets are left untouched, while non-HTTP/2 header sets are 19 | # transformed to resemble HTTP/2: 20 | # - Header names are lowercased 21 | # - `Host' header name is removed from requests 22 | # - Requests get :method, :scheme, :authority, and :path pseudo-headers 23 | # - Responses get :status pseudo-headers 24 | 25 | use strict; 26 | use warnings; 27 | 28 | use Getopt::Long; 29 | use JSON qw(decode_json); 30 | use URI; 31 | 32 | my $key = 'request'; 33 | my ($limit, $split_cookie); 34 | 35 | GetOptions( "help" => sub { 36 | print < file.qif 38 | 39 | Options: 40 | -requests Print headers from requests. This is the default. 41 | -responses Print headers from responses. 42 | -split-cookie Split the Cookie: header. 43 | -limit N Limit number of header sets to N. The default 44 | is no limit. 45 | 46 | If file.har is not specified, the HAR is read from stdin 47 | USAGE 48 | 49 | exit; 50 | }, 51 | "requests" => sub { $key = "request", }, 52 | "responses" => sub { $key = "response", }, 53 | "split-cookie" => \$split_cookie, 54 | "limit=i" => \$limit, 55 | ); 56 | 57 | my $json = decode_json( do { undef $/; <> }); 58 | my @messages = map $$_{$key}, @{ $json->{log}{entries} }; 59 | if (defined($limit)) 60 | { 61 | splice @messages, $limit; 62 | } 63 | 64 | my @header_sets = do { 65 | if ($key eq 'request') { 66 | map req_header_set($_), @messages 67 | } else { 68 | map resp_header_set($_), @messages 69 | } 70 | }; 71 | 72 | for (@header_sets) { 73 | no warnings 'uninitialized'; 74 | print map "$$_[0]\t$$_[1]\n", @$_; 75 | print "\n"; 76 | } 77 | 78 | exit; 79 | 80 | # Looking at capitalization of the first header is a more reliable means 81 | # of determining HTTP version than relying on httpVersion field. 82 | # 83 | sub is_http2 { 84 | my $message = shift; 85 | if (defined($$message{headers}[0]) 86 | && defined($$message{headers}[0]{name})) { 87 | return $$message{headers}[0]{name} =~ /^[a-z:]/; 88 | } elsif (defined($$message{httpVersion})) { 89 | return $$message{httpVersion} =~ m~HTTP/2~i; 90 | } else { 91 | return; 92 | } 93 | } 94 | 95 | sub req_header_set { 96 | my $message = shift; 97 | my @set; 98 | if (!is_http2($message)) { 99 | my @headers = map [ lc($$_{name}), $$_{value}, ], 100 | grep $$_{name} ne 'Host', @{ $$message{headers} }; 101 | my $uri = URI->new($$message{url}); 102 | @set = ( 103 | [ ':method', $$message{method}, ], 104 | [ ':scheme', $uri->scheme, ], 105 | [ ':authority', $uri->authority, ], 106 | [ ':path', $uri->path_query, ], 107 | @headers, 108 | ); 109 | } else { 110 | @set = map [ $$_{name}, $$_{value}, ], @{ $$message{headers} }; 111 | } 112 | if ($split_cookie) { 113 | return [ map { 114 | if ('cookie' eq $$_[0]) { 115 | map [ 'cookie', $_, ], split /;\s+/, $$_[1] 116 | } else { 117 | $_ 118 | } 119 | } @set ]; 120 | } else { 121 | return \@set; 122 | } 123 | } 124 | 125 | sub resp_header_set { 126 | my $message = shift; 127 | no warnings 'uninitialized'; 128 | if (!is_http2($message)) { 129 | my @headers = map [ lc($$_{name}), $$_{value}, ], 130 | @{ $$message{headers} }; 131 | my $uri = URI->new($$message{url}); 132 | return [ 133 | [ ':status', $$message{status}, ], 134 | @headers, 135 | ]; 136 | } else { 137 | return [ map [ $$_{name}, $$_{value}, ], @{ $$message{headers} } ] 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tools/randomize-cookies.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # Given a QIF file, randomize cookie headers 4 | # 5 | # Usage: randomize-cookies.pl input.qif > output.qif 6 | 7 | @chars = ('0' .. '9', 'a' .. 'z', 'A' .. 'Z', split '', '/+*'); 8 | 9 | sub randomify { 10 | $val = shift; 11 | 12 | if (exists($cached_vals{$val})) { 13 | return $cached_vals{$val}; 14 | } 15 | 16 | @val = split '', $val; 17 | $seen_eq = 0; 18 | for ($i = 0; $i < @val; ++$i) 19 | { 20 | if ($val[$i] ne '=' or $seen_eq++) 21 | { 22 | $rand = $rand[$i] //= int(rand(@chars)); 23 | # Output is a function of both position and value: 24 | $val[$i] = $chars[ ($rand + ord($val[$i])) % @chars ]; 25 | } 26 | } 27 | 28 | return $cached_vals{$val} = join '', @val; 29 | } 30 | 31 | while (<>) { 32 | chomp; 33 | if (m/^cookie\t(.*)/) { 34 | print "cookie\t", randomify($1), "\n"; 35 | } elsif (m/^set-cookie\t(.*)/) { 36 | print "set-cookie\t", join('; ', map randomify($_), 37 | split /;\s+/, $1), "\n"; 38 | } else { 39 | print $_, "\n"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tools/sort-qif.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # sort-qif.pl -- sort QIF file 4 | # 5 | # Sort QIF file. This assumes that the header lists in the QIF file have a 6 | # leading comment that looks like '# stream $number'. 7 | # 8 | # Usage: sort-qif.pl [--strip-comments] [files] [--output FILE] 9 | 10 | use Getopt::Long; 11 | 12 | GetOptions("strip-comments" => \$strip_comments, output => \$output); 13 | 14 | if (defined($output)) { 15 | open STDOUT, ">", $output or die "cannot open $output for writing: $!"; 16 | } 17 | 18 | $/ = "\n\n"; 19 | 20 | @chunks = map $$_[1], 21 | sort { $$a[0] <=> $$b[0] } 22 | map { /^*#\s*stream\s+(\d+)/; [ $1, $_ ] } 23 | <>; 24 | 25 | if ($strip_comments) { 26 | for (@chunks) { 27 | s/^#.*\n//mg; 28 | } 29 | } 30 | 31 | print @chunks; 32 | --------------------------------------------------------------------------------