├── .gitignore ├── CMakeLists.txt ├── ChangeLog ├── Debug ├── makefile ├── objects.mk ├── sources.mk └── subdir.mk ├── LICENSE ├── README.md ├── Release ├── makefile ├── objects.mk ├── sources.mk └── subdir.mk ├── example └── main.c ├── makefile.targets ├── qpack-config.cmake.in ├── qpack.c ├── qpack.h ├── qpack.h.in └── test └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Libraries 15 | *.lib 16 | *.a 17 | *.la 18 | *.lo 19 | 20 | # Shared objects (inc. Windows DLLs) 21 | *.dll 22 | *.so 23 | *.so.* 24 | *.dylib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | *.i*86 31 | *.x86_64 32 | *.hex 33 | 34 | # Debug files 35 | *.dSYM/ 36 | *.su 37 | 38 | # Eclipse 39 | .settings/ 40 | .project 41 | .cproject 42 | 43 | # VSCode 44 | .vscode/ 45 | 46 | # Folder for build artifacts 47 | [Bb]uild/ 48 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0 FATAL_ERROR) 2 | project(qpack VERSION 0.10.7) 3 | 4 | option(BUILD_EXAMPLES "Build examples" ON) 5 | option(BUILD_TESTS "Build tests" ON) 6 | 7 | if (NOT DEFINED CMAKE_BUILD_TYPE) 8 | set (CMAKE_BUILD_TYPE Release) 9 | endif() 10 | 11 | set(CMAKE_DEBUG_POSTFIX _d) 12 | 13 | configure_file( 14 | "${CMAKE_SOURCE_DIR}/qpack.h.in" 15 | "${CMAKE_SOURCE_DIR}/qpack.h" 16 | ) 17 | 18 | set(qpack_header ${CMAKE_SOURCE_DIR}/qpack.h) 19 | 20 | add_library(qpack STATIC ${CMAKE_SOURCE_DIR}/qpack.c) 21 | target_include_directories(qpack PRIVATE ${CMAKE_SOURCE_DIR}) 22 | set_target_properties(qpack PROPERTIES SUFFIX ".so") 23 | 24 | if (BUILD_EXAMPLES) 25 | add_executable(example ${CMAKE_SOURCE_DIR}/example/main.c) 26 | target_include_directories(example PRIVATE ${CMAKE_SOURCE_DIR}) 27 | target_link_libraries(example PRIVATE qpack) 28 | endif() 29 | 30 | if (BUILD_TESTS) 31 | add_executable(test_ ${CMAKE_SOURCE_DIR}/test/main.c) 32 | target_include_directories(test_ PRIVATE ${CMAKE_SOURCE_DIR}) 33 | target_link_libraries(test_ PRIVATE qpack) 34 | endif() 35 | 36 | install( 37 | TARGETS qpack 38 | EXPORT qpack-targets 39 | ARCHIVE DESTINATION lib 40 | ) 41 | 42 | install( 43 | FILES ${qpack_header} 44 | DESTINATION include 45 | ) 46 | 47 | include(CMakePackageConfigHelpers) 48 | include(GNUInstallDirs) 49 | 50 | set(CONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/cmake/${CMAKE_PROJECT_NAME}") 51 | 52 | write_basic_package_version_file( 53 | "${CMAKE_BINARY_DIR}/qpack-config-version.cmake" 54 | COMPATIBILITY AnyNewerVersion 55 | ) 56 | 57 | export(EXPORT qpack-targets FILE ${CMAKE_BINARY_DIR}/qpack-targets.cmake) 58 | 59 | configure_package_config_file( 60 | "${CMAKE_SOURCE_DIR}/qpack-config.cmake.in" 61 | "${CMAKE_BINARY_DIR}/qpack-config.cmake" 62 | INSTALL_DESTINATION ${CONF_INSTALL_DIR} 63 | PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR CMAKE_INSTALL_FULL_LIBDIR 64 | ) 65 | 66 | install(EXPORT qpack-targets FILE qpack-targets.cmake DESTINATION ${CONF_INSTALL_DIR}) 67 | 68 | install(FILES ${CMAKE_BINARY_DIR}/qpack-config.cmake ${CMAKE_BINARY_DIR}/qpack-config-version.cmake DESTINATION ${CONF_INSTALL_DIR}) 69 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | libqpack (0.10.7) 2 | 3 | * De not reset unpacker on qp_unpacker_res(). 4 | 5 | -- Jeroen van der Heijden 17 Okt 2017 6 | 7 | libqpack (0.10.6) 8 | 9 | * Fixed bug in length check for unpacking strings. 10 | 11 | -- Jeroen van der Heijden 08 Sep 2017 12 | 13 | libqpack (0.10.5) 14 | 15 | * Inline functions for checks and fixed compile warnings. 16 | 17 | -- Jeroen van der Heijden 01 Sep 2017 18 | 19 | libqpack (0.10.4) 20 | 21 | * Added CFLAGS. 22 | 23 | -- Jeroen van der Heijden 31 Aug 2017 24 | 25 | libqpack (0.10.3) 26 | 27 | * Make build script platform independent. 28 | 29 | -- Jeroen van der Heijden 31 Aug 2017 30 | 31 | libqpack (0.10.2) 32 | 33 | * Added qp_sprint() for printing qp data to a string. Like qp_fprint() the 34 | output is made JSON compatible if possible. 35 | 36 | -- Jeroen van der Heijden 31 Aug 2017 37 | 38 | libqpack (0.10.1) 39 | 40 | * Added qp_fprint() for printing qp data to a stream. As far as possible 41 | the output is made JSON compatible. 42 | 43 | -- Jeroen van der Heijden 28 Aug 2017 44 | 45 | libqpack (0.10.0) 46 | 47 | * Added __cplusplus macro for c++ compatibility. 48 | 49 | * Renamed qp_res_via_t member bool to boolean. 50 | 51 | -- Jeroen van der Heijden 25 Aug 2017 52 | 53 | libqpack (0.9.2) 54 | 55 | * Added QP_ERR_WRITE_STREAM error code. 56 | 57 | * Added function "int qp_res_fprint(qp_res_t * res, FILE * stream)" for 58 | printing a qp_res_t type to a stream. 59 | 60 | -- Jeroen van der Heijden 15 Jun 2017 61 | 62 | libqpack (0.9.1) 63 | 64 | * Initial version for the C program language. 65 | 66 | -- Jeroen van der Heijden 14 Jun 2017 67 | -------------------------------------------------------------------------------- /Debug/makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | -include ../makefile.init 6 | 7 | RM := rm -rf 8 | 9 | # All of the sources participating in the build are defined here 10 | -include sources.mk 11 | -include subdir.mk 12 | -include objects.mk 13 | 14 | ifneq ($(MAKECMDGOALS),clean) 15 | ifneq ($(strip $(C_DEPS)),) 16 | -include $(C_DEPS) 17 | endif 18 | endif 19 | 20 | -include ../makefile.defs 21 | 22 | OS := $(shell uname) 23 | ifeq ($(OS),Darwin) 24 | FN := libqpack.dylib 25 | INSTALL_PATH := /usr/local 26 | else 27 | FN := libqpack.so 28 | INSTALL_PATH := /usr 29 | endif 30 | 31 | # Add inputs and outputs from these tool invocations to the build variables 32 | 33 | # All Target 34 | all: libqpack 35 | 36 | # Tool invocations 37 | libqpack: $(OBJS) $(USER_OBJS) 38 | @echo 'Building target: $@' 39 | @echo 'Invoking: Cross GCC Linker' 40 | gcc -shared -o $(FN) $(OBJS) $(USER_OBJS) $(LIBS) $(LDFLAGS) 41 | @echo 'Finished building target: $@' 42 | @echo ' ' 43 | 44 | # Other Targets 45 | clean: 46 | -$(RM) $(LIBRARIES)$(OBJS)$(C_DEPS) $(FN) 47 | -@echo ' ' 48 | 49 | .PHONY: all clean dependents 50 | .SECONDARY: 51 | 52 | -include ../makefile.targets 53 | -------------------------------------------------------------------------------- /Debug/objects.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | USER_OBJS := 6 | 7 | LIBS := 8 | 9 | -------------------------------------------------------------------------------- /Debug/sources.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | OBJ_SRCS := 6 | ASM_SRCS := 7 | C_SRCS := 8 | O_SRCS := 9 | S_UPPER_SRCS := 10 | LIBRARIES := 11 | OBJS := 12 | C_DEPS := 13 | 14 | # Every subdirectory with source files must be described here 15 | SUBDIRS := \ 16 | . \ 17 | 18 | -------------------------------------------------------------------------------- /Debug/subdir.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | # Add inputs and outputs from these tool invocations to the build variables 6 | C_SRCS += \ 7 | ../qpack.c 8 | 9 | OBJS += \ 10 | ./qpack.o 11 | 12 | C_DEPS += \ 13 | ./qpack.d 14 | 15 | 16 | # Each subdirectory must supply rules for building sources it contributes 17 | %.o: ../%.c 18 | @echo 'Building file: $<' 19 | @echo 'Invoking: Cross GCC Compiler' 20 | gcc -I../ -O0 -g3 -Wall $(CFLAGS) -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" 21 | @echo 'Finished building: $<' 22 | @echo ' ' 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Transceptor Technology 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 | # QPack for the C programming language (libqpack) 2 | Fast and efficient data serializer for the C/C++ programming language. QPack is originally created for [SiriDB](https://github.com/SiriDB/siridb-server) but is now used by a lot of other projects too. 3 | 4 | --------------------------------------- 5 | * [Installation](#installation) 6 | * [Usage](#usage) 7 | * [Packer](#packer) 8 | * [Unpacker](#unpacker) 9 | * [API](#api) 10 | * [qp_packer_t](#qp_packer_t) 11 | * [qp_unpacker_t](#qp_unpacker_t) 12 | * [qp_res_t](#qp_res_t) 13 | * [qp_map_t](#qp_map_t) 14 | * [qp_array_t](#qp_array_t) 15 | * [Miscellaneous functions](#miscellaneous-functions) 16 | 17 | --------------------------------------- 18 | 19 | ## Installation 20 | Install debug or release version, in this example we will install the release version. 21 | ``` 22 | $ cd Release 23 | ``` 24 | 25 | Compile qpack 26 | ``` 27 | $ make all 28 | ``` 29 | 30 | Install qpack 31 | ``` 32 | $ sudo make install 33 | ``` 34 | 35 | > Note: run `sudo make uninstall` for removal. 36 | 37 | ## Usage 38 | The following data structure will be packed and unpacked to illustrate how 39 | qpack can be used: 40 | 41 | ```json 42 | { 43 | "What is the answer to life the universe and everything?": 42 44 | } 45 | ``` 46 | ### Packer 47 | First we start to pack the data. 48 | ```c 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | const char * q = "What is the answer to life the universe and everything?"; 57 | const int a = 42; 58 | 59 | int main(void) 60 | { 61 | qp_packer_t * packer = qp_packer_create(512); 62 | if (packer == NULL) { 63 | abort(); 64 | } 65 | 66 | /* Open a map. */ 67 | qp_add_map(&packer); 68 | 69 | /* Add a key. We will add the question. Note that the question will be 70 | * packed without the terminator (null) character. */ 71 | qp_add_raw(packer, q, strlen(q)); 72 | 73 | /* Add a value. QPack only supports signed integers. */ 74 | qp_add_int64(packer, (int64_t) a); 75 | 76 | /* Close the map. Note that in a map an even number of items must be placed 77 | * which represent key/value pairs. */ 78 | qp_close_map(packer); 79 | 80 | /* Print the packed data */ 81 | qp_packer_print(packer); 82 | 83 | /* cleanup the packer */ 84 | qp_packer_destroy(packer); 85 | 86 | return 0; 87 | } 88 | ``` 89 | 90 | ### Unpacker 91 | Now we create some code to unpack the data. 92 | ```c 93 | void print_qa(const unsigned char * data, size_t len) 94 | { 95 | qp_unpacker_t unpacker; 96 | qp_res_t * res; 97 | int rc; 98 | 99 | /* Initialize the unpacker */ 100 | qp_unpacker_init(&unpacker, data, len); 101 | 102 | /* We can get the data from the unpacker in two ways. One is to step over 103 | * the data using qp_next() and the other is to unpack all to a qp_res_t 104 | * object. The last option is probably easier and is what we will use in 105 | * this example but note that it consumes more memory and is potentially 106 | * slower compared to qp_next() for some use cases. One more important 107 | * difference is that qp_res_t contains null terminated copies for raw data 108 | * which means that this function cannot be used in case raw data contains 109 | * null characters by themself. */ 110 | 111 | res = qp_unpacker_res(&unpacker, &rc); 112 | if (rc) { 113 | fprintf(stderr, "error: %s\n", qp_strerror(rc)); 114 | } else { 115 | /* Usually you should check your response not with assertions */ 116 | assert (res->tp == QP_RES_MAP); 117 | assert (res->via.map->n == 1); /* one key/value pair */ 118 | assert (res->via.map->keys[0].tp == QP_RES_STR); /* key */ 119 | assert (res->via.map->values[0].tp == QP_RES_INT64); /* val */ 120 | 121 | printf( 122 | "Unpacked:\n" 123 | " Question: %s\n" 124 | " Answer : %" PRId64 "\n", 125 | res->via.map->keys[0].via.str, 126 | res->via.map->values[0].via.int64); 127 | 128 | /* cleanup res */ 129 | qp_res_destroy(res); 130 | } 131 | } 132 | 133 | /* Call the function from main (see packer example for full code) */ 134 | int main(void) 135 | { 136 | // ... see packer example for full code 137 | 138 | /* Call print_qa() before destroying the packer using member property 139 | * qp_packer_t.buffer which contains the packer data and qp_packer_t.len 140 | * for the size of the data */ 141 | print_qa(packer->buffer, packer->len); 142 | 143 | /* cleanup the packer */ 144 | qp_packer_destroy(packer); 145 | 146 | return 0; 147 | } 148 | ``` 149 | 150 | ## API 151 | 152 | ### `qp_packer_t` 153 | Object which is used to pack data. 154 | 155 | *Public members* 156 | - `unsigned char * qp_packer_t.buffer`: contains the packed data (readonly) 157 | - `size_t qp_packer_t.len`: the length of the data (readonly) 158 | 159 | #### `qp_packer_t * qp_packer_create(size_t alloc_size)` 160 | Create and return a new packer instance. Argument `alloc_size` should be at 161 | least greather than 0 since this value is used to allocate memory. When more 162 | memory is required while packing data the size will grow in steps of 163 | `alloc_size`. 164 | 165 | #### `void qp_packer_destroy(qp_packer_t * packer)` 166 | Cleaunup packer instance. 167 | 168 | #### `int qp_add_raw(qp_packer_t * packer, const char * raw, size_t len)` 169 | Add raw data to the packer. 170 | 171 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 172 | cannot be allocated. 173 | 174 | Example: 175 | ```c 176 | int rc; 177 | const char * s = "some string"; 178 | /* We use strlen(s) so the string will be packed without the terminator (null) 179 | * character. */ 180 | if ((rc = qp_add_raw(packer, s, strlen(s)))) { 181 | fprintf(stderr, "error: %s\n", qp_strerror(rc)); 182 | } 183 | ``` 184 | 185 | #### `int qp_add_int64(qp_packer_t * packer, int64_t i)` 186 | Add an integer value to the packer. Note that only signed integers are 187 | supported by QPack so unsigned integers should be casted. This function 188 | accepts only `int64_t` but tries to pack the value as small as possible. 189 | 190 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 191 | cannot be allocated. 192 | 193 | #### `int qp_add_double(qp_packer_t * packer, double d)` 194 | Add a double value to the packer. 195 | 196 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 197 | cannot be allocated. 198 | 199 | #### `int qp_add_true(qp_packer_t * packer)` 200 | Add boolean value `TRUE` to the packer. 201 | 202 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 203 | cannot be allocated. 204 | 205 | #### `int qp_add_false(qp_packer_t * packer)` 206 | Add boolean value `FALSE` to the packer. 207 | 208 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 209 | cannot be allocated. 210 | 211 | #### `int qp_add_null(qp_packer_t * packer)` 212 | Add `NULL` to the packer. 213 | 214 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 215 | cannot be allocated. 216 | 217 | #### `int qp_add_array(qp_packer_t ** packaddr)` 218 | Add an `ARRAY` to the packer. Do not forget to close the array using 219 | `qp_close_array()` although closing is not required by qpack in case 220 | no other values need to be added after the array. 221 | 222 | >Note: this function needs a pointer-pointer to a packer since the function 223 | >might re-allocate memory for the packer. 224 | 225 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 226 | cannot be allocated. 227 | 228 | #### `int qp_add_map(qp_packer_t ** packaddr)` 229 | Add a `MAP` to the packer for storing key/value pairs. Do not forget to close 230 | the map using `qp_close_map()` although closing is not required by qpack in case 231 | no other values need to be added after the map. Always add an even number of 232 | items which represent key/value pairs. 233 | 234 | >Note: this function needs a pointer-pointer to a packer since the function 235 | >might re-allocate memory for the packer. 236 | 237 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 238 | cannot be allocated. 239 | 240 | #### `int qp_close_array(qp_packer_t * packer)` 241 | Close an array. 242 | 243 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 244 | cannot be allocated. `QP_ERR_NO_OPEN_ARRAY` can be returned in case no *open* 245 | array was found. 246 | 247 | #### `int qp_close_map(qp_packer_t * packer)` 248 | Close a map. 249 | 250 | Returns 0 if successful or `QP_ERR_ALLOC` in case more memory is required which 251 | cannot be allocated. `QP_ERR_NO_OPEN_MAP` can be returned in case no *open* 252 | map was found or `QP_ERR_MISSING_VALUE` is returned (and the map will *not* be 253 | closed) if the map contains a key without value. 254 | 255 | #### `void qp_packer_print(qp_packer_t * packer)` 256 | Macro function for printing packer content to stdout. 257 | 258 | ### `qp_unpacker_t` 259 | Object which is used to unpack data. 260 | 261 | *Public members* 262 | - `const unsigned char * qp_unpacker_t.pt`: 263 | pointer to the current position in the data 264 | - `const unsigned char * qp_unpacker_t.start`: 265 | pointer to data start (readonly) 266 | - `const unsigned char * qp_unpacker_t.end`: 267 | pointer to data end (readonly) 268 | 269 | #### `void qp_unpacker_init(qp_unpacker_t * unpacker, const unsigned char * pt, size_t len)` 270 | Initialize a `qp_unpacker_t` instance. No cleanup after the initialization is required. It is 271 | allowed to run this function multiple times to bring the unpacker back to its initial state. 272 | 273 | #### `qp_types_t qp_next(qp_unpacker_t * unpacker, qp_obj_t * qp_obj)` 274 | Can be used to walk over an unpacker instance step-by-step. After calling the function, 275 | `qp_obj` points to the current item in the unpacker. `qp_obj.tp` is always equal 276 | to the return value of this function but note that `qp_obj` is allowed to be 277 | `NULL` in which case you only have the return value. Because a `qp_obj_t` 278 | instance *can* point to the raw data in the unpacker, the unpacker/data must 279 | not be destroyed as long as the `qp_obj_t` is in use. 280 | 281 | Example: 282 | >Note: this code is similar to the`print_qa()` code in the [unpacker example](#unpacker) 283 | >but uses `qp_next()` instead of `qp_unpacker_res()` 284 | ```c 285 | qp_obj_t question, answer; 286 | 287 | assert (qp_is_map(qp_next(&unpacker, NULL))); 288 | assert (qp_is_raw(qp_next(&unpacker, &question))); 289 | assert (qp_is_int(qp_next(&unpacker, &answer))); 290 | 291 | printf( 292 | "Unpacked:\n" 293 | " Question: %.*s\n" 294 | " Answer : %" PRId64 "\n", 295 | (int) question.len, question.via.raw, 296 | answer.via.int64); 297 | ``` 298 | #### `qp_res_t * qp_unpacker_res(qp_unpacker_t * unpacker, int * rc)` 299 | Returns a pointer to a [qp_res_t](#qp_res_t) instance or `NULL` in case of an 300 | error. When successful the original data (unpacker) is no longer required and 301 | can be destroyed. 302 | 303 | Argument `rc` is 0 when successful or `QP_ERR_ALLOC` in case required memory 304 | cannot be allocated. The value of `rc` is `QP_ERR_CORRUPT` when the unpacker 305 | contains data which cannot be unpacked. `rc` is not required and is allowed to 306 | be `NULL`. 307 | 308 | >Note: Each raw value will be copied to a NULL terminated string which means 309 | >that this function cannot be used in case your raw data contains NULL 310 | >characters. In the latter case you need to use `qp_next()` to unpack the data. 311 | 312 | #### `void qp_unpacker_print(qp_unpacker_t * unpacker)` 313 | Macro function for printing unpacker content to stdout. 314 | 315 | ### `qp_res_t` 316 | Object which contains unpacked data. An instance of `qp_res_t` is created with 317 | the [qp_unpacker_t](#qp_unpacker_t) function `qp_unpacker_res()`. 318 | 319 | *Public members* 320 | - `qp_res_tp qp_res_t.tp`: Type of the result (readonly) 321 | - `QP_RES_MAP` 322 | - `QP_RES_ARRAY` 323 | - `QP_RES_INT64` 324 | - `QP_RES_REAL` 325 | - `QP_RES_STR` 326 | - `QP_RES_BOOL` 327 | - `QP_RES_NULL` 328 | - `qp_res_via_t qp_res_t.via`: Union containing the actual data (readonly) 329 | - `qp_map_t * map` 330 | - `qp_array_t * array` 331 | - `char * str` (null terminated string) 332 | - `int64_t int64` 333 | - `double real` 334 | - `int boolean` (0=FALSE or 1=TRUE) 335 | - `void * null` (set to the actual `NULL` pointer) 336 | 337 | #### `void qp_res_destroy(qp_res_t * res)` 338 | Cleanup result instance. 339 | 340 | #### `int qp_res_fprint(qp_res_t * res, FILE * stream)` 341 | Print a `qp_res_t` to a given stream. Returns 0 if successful. The return value 342 | is `QP_ERR_WRITE_STREAM` when writing to the stream fails or `QP_ERR_CORRUPT` 343 | when `res` contains invalid data. 344 | 345 | ### `qp_map_t` 346 | Object is accesible via [qp_res_t.via.map](#qp_res_t). 347 | 348 | *Public members* 349 | - `size_t qp_map_t.n`: Number of keys and values in the map (readonly) 350 | - `qp_res_t * qp_map_t.keys`: Keys (readonly) 351 | - `qp_res_t * qp_map_t.values`: Values (readonly) 352 | 353 | ### `qp_array_t` 354 | Object is accesible via [qp_res_t.via.array](#qp_res_t). 355 | 356 | *Public members* 357 | - `size_t qp_map_t.n`: Number of values in the array (readonly) 358 | - `qp_res_t * qp_map_t.values`: Values (readonly) 359 | 360 | ### `qp_obj_t` 361 | Object is used together with the `qp_next()` function and *can* point to 362 | data from an unpacker object. 363 | 364 | *Public members* 365 | - `uint8_t qp_obj_t.tp`: Type for the object (readonly) 366 | - `QP_END ` *(equal to 0 and indicates end of unpacker)* 367 | - `QP_ERR ` *(error while reading from unpacker)* 368 | - `QP_RAW ` *(raw data)* 369 | - `QP_HOOK ` *(hook for custom implementations)* 370 | - `QP_INT64 ` *(signed 64bit interger)* 371 | - `QP_DOUBLE ` *(double value)* 372 | - `QP_ARRAY0 ` *(empty array)* 373 | - `QP_ARRAY1 ` *(array with 1 item)* 374 | - `QP_ARRAY2 ` *(array with 2 items)* 375 | - `QP_ARRAY3 ` *(array with 3 items)* 376 | - `QP_ARRAY4 ` *(array with 4 items)* 377 | - `QP_ARRAY5 ` *(array with 5 items)* 378 | - `QP_MAP0 ` *(empty map)* 379 | - `QP_MAP1 ` *(map with 1 item)* 380 | - `QP_MAP2 ` *(map with 2 items)* 381 | - `QP_MAP3 ` *(map with 3 items)* 382 | - `QP_MAP4 ` *(map with 4 items)* 383 | - `QP_MAP5 ` *(map with 5 items)* 384 | - `QP_TRUE ` *(boolean true)* 385 | - `QP_FALSE ` *(boolean false)* 386 | - `QP_NULL ` *(null)* 387 | - `QP_ARRAY_OPEN ` *(open a new array)* 388 | - `QP_MAP_OPEN ` *(open a new map)* 389 | - `QP_ARRAY_CLOSE ` *(close array)* 390 | - `QP_MAP_CLOSE ` *(close map)* 391 | - `size_t qp_obj_t.len`: Length of data. (readonly, only used when tp=QP_RAW) 392 | - `qp_via_t qp_obj_t.via`: Union type (readonly, only used when tp=QP_RAW, 393 | QP_INT64 or QP_DOUBLE) 394 | - `int64_t int64` 395 | - `double real` 396 | - `const char * raw` 397 | 398 | ### Miscellaneous functions 399 | #### `const char * qp_strerror(int err_code)` 400 | Returns the error message for a given error code. 401 | 402 | #### `const char * qp_version(void)` 403 | Returns the version of qpack. 404 | -------------------------------------------------------------------------------- /Release/makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | -include ../makefile.init 6 | 7 | RM := rm -rf 8 | 9 | # All of the sources participating in the build are defined here 10 | -include sources.mk 11 | -include subdir.mk 12 | -include objects.mk 13 | 14 | ifneq ($(MAKECMDGOALS),clean) 15 | ifneq ($(strip $(C_DEPS)),) 16 | -include $(C_DEPS) 17 | endif 18 | endif 19 | 20 | -include ../makefile.defs 21 | 22 | OS := $(shell uname) 23 | ifeq ($(OS),Darwin) 24 | FN := libqpack.dylib 25 | INSTALL_PATH := /usr/local 26 | else 27 | FN := libqpack.so 28 | INSTALL_PATH := /usr 29 | endif 30 | 31 | # Add inputs and outputs from these tool invocations to the build variables 32 | 33 | # All Target 34 | all: libqpack 35 | 36 | # Tool invocations 37 | libqpack: $(OBJS) $(USER_OBJS) 38 | @echo 'Building target: $@' 39 | @echo 'Invoking: Cross GCC Linker' 40 | gcc -shared -o $(FN) $(OBJS) $(USER_OBJS) $(LIBS) $(LDFLAGS) 41 | @echo 'Finished building target: $@' 42 | @echo ' ' 43 | 44 | # Other Targets 45 | clean: 46 | -$(RM) $(LIBRARIES)$(OBJS)$(C_DEPS) $(FN) 47 | -@echo ' ' 48 | 49 | .PHONY: all clean dependents 50 | .SECONDARY: 51 | 52 | -include ../makefile.targets 53 | -------------------------------------------------------------------------------- /Release/objects.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | USER_OBJS := 6 | 7 | LIBS := 8 | 9 | -------------------------------------------------------------------------------- /Release/sources.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | OBJ_SRCS := 6 | ASM_SRCS := 7 | C_SRCS := 8 | O_SRCS := 9 | S_UPPER_SRCS := 10 | LIBRARIES := 11 | OBJS := 12 | C_DEPS := 13 | 14 | # Every subdirectory with source files must be described here 15 | SUBDIRS := \ 16 | . \ 17 | 18 | -------------------------------------------------------------------------------- /Release/subdir.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | # Add inputs and outputs from these tool invocations to the build variables 6 | C_SRCS += \ 7 | ../qpack.c 8 | 9 | OBJS += \ 10 | ./qpack.o 11 | 12 | C_DEPS += \ 13 | ./qpack.d 14 | 15 | 16 | # Each subdirectory must supply rules for building sources it contributes 17 | %.o: ../%.c 18 | @echo 'Building file: $<' 19 | @echo 'Invoking: Cross GCC Compiler' 20 | gcc -I../ -O3 -Wall $(CFLAGS) -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" 21 | @echo 'Finished building: $<' 22 | @echo ' ' 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * main.c - Example used in the README.md, compile with: 3 | * 4 | * gcc main.c -lqpack -o example.out 5 | * 6 | * Created on: Jun 14, 2017 7 | * Author: Jeroen van der Heijden 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | const char * q = "What is the answer to life the universe and everything?"; 17 | const int a = 42; 18 | 19 | void print_qa(const unsigned char * data, size_t len) 20 | { 21 | qp_unpacker_t unpacker; 22 | qp_res_t * res; 23 | int rc; 24 | 25 | /* Initialize the unpacker */ 26 | qp_unpacker_init(&unpacker, data, len); 27 | 28 | /* We can get the data from the unpacker in two ways. One is to step over 29 | * the data using qp_next() and the other is to unpack all to a qp_res_t 30 | * object. The last option is probably easier and is what we will use in 31 | * this example but note that it consumes more memory and is potentially 32 | * slower compared to qp_next() for some use cases. An advantage might be 33 | * that the original data/unpacker is no longer required by qp_res_t. 34 | * 35 | * One more important difference is that qp_res_t contains null terminated 36 | * copies for raw data which mean that this method cannot be used in case 37 | * raw data contains null characters by themself. */ 38 | 39 | res = qp_unpacker_res(&unpacker, &rc); 40 | if (rc) { 41 | fprintf(stderr, "error: %s\n", qp_strerror(rc)); 42 | } else { 43 | /* Usually you should check your response not with assertions */ 44 | assert (res->tp == QP_RES_MAP); 45 | assert (res->via.map->n == 1); /* one key/value pair */ 46 | assert (res->via.map->keys[0].tp == QP_RES_STR); /* key */ 47 | assert (res->via.map->values[0].tp == QP_RES_INT64); /* val */ 48 | 49 | printf( 50 | "Unpacked:\n" 51 | " Question: %s\n" 52 | " Answer : %" PRId64 "\n", 53 | res->via.map->keys[0].via.str, 54 | res->via.map->values[0].via.int64); 55 | 56 | /* cleanup res */ 57 | qp_res_destroy(res); 58 | } 59 | } 60 | 61 | int main(void) 62 | { 63 | printf("Example running with qpack version: %s\n\n", qp_version()); 64 | qp_packer_t * packer = qp_packer_create(512); 65 | if (packer == NULL) { 66 | abort(); 67 | } 68 | 69 | /* Open a map. */ 70 | qp_add_map(&packer); 71 | 72 | /* Add a key. We will add the question. Note that the question will be 73 | * packed without the teminator (null) character. */ 74 | qp_add_raw(packer, q, strlen(q)); 75 | 76 | /* Add a value. QPack only supports signed intergers. */ 77 | qp_add_int64(packer, (int64_t) a); 78 | 79 | /* Close the map. Note that in a map an even number of items must be placed 80 | * which represent key/value pairs. */ 81 | qp_close_map(packer); 82 | 83 | /* Print the packed data */ 84 | qp_packer_print(packer); 85 | 86 | /* Unpack example */ 87 | print_qa(packer->buffer, packer->len); 88 | 89 | /* cleanup the packer */ 90 | qp_packer_destroy(packer); 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /makefile.targets: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | install: 3 | @cp ../qpack.h $(INSTALL_PATH)/include/qpack.h 4 | @cp $(FN) $(INSTALL_PATH)/lib/$(FN) 5 | 6 | .PHONY: uninstall 7 | uninstall: 8 | @rm -f $(INSTALL_PATH)/include/qpack.h 9 | @rm -f $(INSTALL_PATH)/lib/$(FN) 10 | -------------------------------------------------------------------------------- /qpack-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set_and_check(qpack_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@") 4 | set_and_check(qpack_LIBRARY_DIRS "@CMAKE_INSTALL_FULL_LIBDIR@") 5 | 6 | message(STATUS "Found libqpack: ${qpack_INCLUDE_DIRS} (found version ${qpack_VERSION})") 7 | 8 | include("${CMAKE_CURRENT_LIST_DIR}/qpack-targets.cmake") 9 | -------------------------------------------------------------------------------- /qpack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * qpack.c 3 | * 4 | * Created on: Mar 22, 2017 5 | * Author: Jeroen van der Heijden 6 | */ 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define QP__INITIAL_NEST_SIZE 2 15 | #define QP__INITIAL_ALLOC_SZ 8 16 | #define QP__CHKN if (n < 0) return QP_ERR; *pt += n; 17 | #define QP__CHKT if (tp == QP_ERR) return QP_ERR; 18 | #define QP__MINSZ 16 19 | #define QP__FACTOR 1.4 20 | 21 | #define QP__RETURN_INC_C \ 22 | if (packer->depth) packer->nesting[packer->depth - 1].n++; \ 23 | return 0; 24 | 25 | #define QP_RESIZE(LEN) \ 26 | if (packer->len + LEN > packer->buffer_size) \ 27 | { \ 28 | unsigned char * tmp; \ 29 | packer->buffer_size = \ 30 | ((packer->len + LEN) / packer->alloc_size + 1) \ 31 | * packer->alloc_size; \ 32 | tmp = (unsigned char *) realloc( \ 33 | packer->buffer, \ 34 | packer->buffer_size); \ 35 | if (tmp == NULL) \ 36 | { \ 37 | packer->buffer_size = packer->len; \ 38 | return QP_ERR_ALLOC; \ 39 | } \ 40 | packer->buffer = tmp; \ 41 | } 42 | 43 | #define QP_PLAIN_OBJ(QP__TYPE) \ 44 | { \ 45 | QP_RESIZE(1) \ 46 | packer->buffer[packer->len++] = QP__TYPE; \ 47 | QP__RETURN_INC_C \ 48 | } 49 | 50 | #define QP_ADD_INT8(packer, integer) \ 51 | QP_RESIZE(2) \ 52 | if (integer >= 0 && integer < 64) \ 53 | { \ 54 | packer->buffer[packer->len++] = integer; \ 55 | } \ 56 | else if (integer >= -60 && integer < 0) \ 57 | { \ 58 | packer->buffer[packer->len++] = 63 - integer; \ 59 | } \ 60 | else \ 61 | { \ 62 | packer->buffer[packer->len++] = QP__INT8; \ 63 | packer->buffer[packer->len++] = integer; \ 64 | } 65 | 66 | #define QP_ADD_INT16(packer, integer) \ 67 | QP_RESIZE(3) \ 68 | packer->buffer[packer->len++] = QP__INT16; \ 69 | memcpy(packer->buffer + packer->len, &integer, sizeof(int16_t)); \ 70 | packer->len += sizeof(int16_t); 71 | 72 | #define QP_ADD_INT32(packer, integer) \ 73 | QP_RESIZE(5) \ 74 | packer->buffer[packer->len++] = QP__INT32; \ 75 | memcpy(packer->buffer + packer->len, &integer, sizeof(int32_t)); \ 76 | packer->len += sizeof(int32_t); 77 | 78 | #define QP_UNPACK_RAW(uintx_t) \ 79 | { \ 80 | QP_UNPACK_CHECK_SZ(sizeof(uintx_t)) \ 81 | size_t sz = (size_t) *((uintx_t *) unpacker->pt); \ 82 | unpacker->pt += sizeof(uintx_t); \ 83 | QP_UNPACK_CHECK_SZ(sz) \ 84 | if (qp_obj != NULL) \ 85 | { \ 86 | qp_obj->tp = QP_RAW; \ 87 | qp_obj->via.raw = (char *) unpacker->pt; \ 88 | qp_obj->len = sz; \ 89 | } \ 90 | unpacker->pt += sz; \ 91 | return QP_RAW; \ 92 | } 93 | 94 | #define QP_UNPACK_INT(intx_t) \ 95 | { \ 96 | QP_UNPACK_CHECK_SZ(sizeof(intx_t)) \ 97 | int64_t val = (int64_t) *((intx_t *) unpacker->pt); \ 98 | if (qp_obj != NULL) \ 99 | { \ 100 | qp_obj->tp = QP_INT64; \ 101 | qp_obj->via.int64 = val; \ 102 | } \ 103 | unpacker->pt += sizeof(intx_t); \ 104 | return QP_INT64; \ 105 | } 106 | 107 | #define QP_UNPACK_CHECK_SZ(size) \ 108 | if (unpacker->pt + size > unpacker->end) \ 109 | { \ 110 | if (qp_obj != NULL) \ 111 | { \ 112 | qp_obj->tp = QP_ERR; \ 113 | } \ 114 | return QP_ERR; \ 115 | } 116 | 117 | static qp_types_t QP_print_unpacker( 118 | FILE * stream, 119 | qp_types_t tp, 120 | qp_unpacker_t * unpacker, 121 | qp_obj_t * qp_obj); 122 | static void QP_fprint_raw(FILE * stream, const char * s, size_t n); 123 | static int QP_res(qp_unpacker_t * unpacker, qp_res_t * res, qp_obj_t * val); 124 | static void QP_res_destroy(qp_res_t * res); 125 | static qp_types_t QP_sprint_unpacker( 126 | char ** s, 127 | size_t * sz, 128 | char ** pt, 129 | qp_types_t tp, 130 | qp_unpacker_t * unpacker, 131 | qp_obj_t * qp_obj); 132 | static int QP_sprint_raw( 133 | char ** s, 134 | size_t * sz, 135 | size_t p, 136 | const char * d, 137 | size_t n); 138 | 139 | qp_packer_t * qp_packer_create(size_t alloc_size) 140 | { 141 | assert (alloc_size); /* alloc_size should not be 0 */ 142 | 143 | qp_packer_t * packer; 144 | packer = (qp_packer_t *) malloc( 145 | sizeof(qp_packer_t) + 146 | QP__INITIAL_NEST_SIZE * sizeof(struct qp__nest_s)); 147 | if (packer != NULL) 148 | { 149 | packer->alloc_size = alloc_size; 150 | packer->buffer_size = alloc_size; 151 | packer->len = 0; 152 | packer->depth = 0; 153 | packer->nest_sz = QP__INITIAL_NEST_SIZE; 154 | packer->buffer = (unsigned char *) malloc( 155 | sizeof(unsigned char) * alloc_size); 156 | 157 | if (packer->buffer == NULL) 158 | { 159 | free(packer); 160 | packer = NULL; 161 | } 162 | } 163 | return packer; 164 | } 165 | 166 | void qp_packer_destroy(qp_packer_t * packer) 167 | { 168 | free(packer->buffer); 169 | free(packer); 170 | } 171 | 172 | const char * qp_strerror(int err_code) 173 | { 174 | switch (err_code) 175 | { 176 | case 0: return "success"; 177 | case QP_ERR_ALLOC: return "memory allocation error"; 178 | case QP_ERR_WRITE_STREAM: return "cannot write to stream"; 179 | case QP_ERR_CORRUPT: return "data is invalid or corrupt"; 180 | case QP_ERR_NO_OPEN_ARRAY: return "no array found to close"; 181 | case QP_ERR_NO_OPEN_MAP: return "no map found to close"; 182 | case QP_ERR_MISSING_VALUE: return "value is missing for the last key"; 183 | default: return "unknown error code"; 184 | } 185 | } 186 | 187 | const char * qp_version(void) 188 | { 189 | return QP_VERSION; 190 | } 191 | 192 | int qp_add_raw(qp_packer_t * packer, const char * raw, size_t len) 193 | { 194 | size_t required_size = len + 9; 195 | 196 | QP_RESIZE(required_size) 197 | 198 | if (len < 100) 199 | { 200 | packer->buffer[packer->len++] = 128 + len; 201 | } 202 | else if (len < 256) 203 | { 204 | uint8_t length = (uint8_t) len; 205 | packer->buffer[packer->len++] = QP__RAW8; 206 | packer->buffer[packer->len++] = length; 207 | } 208 | else if (len < 65536) 209 | { 210 | uint16_t length = (uint16_t) len; 211 | packer->buffer[packer->len++] = QP__RAW16; 212 | memcpy(packer->buffer + packer->len, &length, 2); 213 | packer->len += 2; 214 | } 215 | else if (len < 4294967296) 216 | { 217 | uint32_t length = (uint32_t) len; 218 | packer->buffer[packer->len++] = QP__RAW32; 219 | memcpy(packer->buffer + packer->len, &length, 4); 220 | packer->len += 4; 221 | } 222 | else 223 | { 224 | uint64_t length = (uint64_t) len; 225 | packer->buffer[packer->len++] = QP__RAW64; 226 | memcpy(packer->buffer + packer->len, &length, 8); 227 | packer->len += 8; 228 | } 229 | memcpy(packer->buffer + packer->len, raw, len); 230 | packer->len += len; 231 | 232 | QP__RETURN_INC_C 233 | } 234 | 235 | int qp_add_array(qp_packer_t ** packaddr) 236 | { 237 | qp_packer_t * packer = *packaddr; 238 | QP_RESIZE(1) 239 | 240 | size_t i = packer->depth; 241 | 242 | packer->depth++; 243 | 244 | if (packer->depth == packer->nest_sz) 245 | { 246 | qp_packer_t * tmp; 247 | packer->nest_sz *= 2; 248 | tmp = (qp_packer_t *) realloc( 249 | packer, 250 | sizeof(qp_packer_t) + 251 | packer->nest_sz * (sizeof(struct qp__nest_s))); 252 | if (tmp == NULL) 253 | { 254 | packer->nest_sz /= 2; 255 | return QP_ERR_ALLOC; 256 | } 257 | 258 | (*packaddr) = packer = tmp; 259 | } 260 | packer->nesting[i].n = 0; 261 | packer->nesting[i].pos = packer->len; 262 | packer->nesting[i].tp = NEST_ARRAY; 263 | 264 | packer->buffer[packer->len++] = QP__ARRAY_OPEN; 265 | 266 | if (i) 267 | { 268 | packer->nesting[i - 1].n++; 269 | } 270 | return 0; 271 | } 272 | 273 | int qp_close_array(qp_packer_t * packer) 274 | { 275 | struct qp__nest_s nesting; 276 | if ( !packer->depth || 277 | (nesting = packer->nesting[packer->depth - 1]).tp != NEST_ARRAY) 278 | { 279 | return QP_ERR_NO_OPEN_ARRAY; 280 | } 281 | 282 | if (nesting.n > 5) 283 | { 284 | QP_RESIZE(1) 285 | packer->buffer[packer->len++] = QP__ARRAY_CLOSE; 286 | } 287 | else 288 | { 289 | packer->buffer[nesting.pos] = QP__ARRAY0 + nesting.n; 290 | } 291 | packer->depth--; 292 | return 0; 293 | } 294 | 295 | int qp_add_map(qp_packer_t ** packaddr) 296 | { 297 | qp_packer_t * packer = *packaddr; 298 | QP_RESIZE(1) 299 | 300 | size_t i = packer->depth; 301 | 302 | packer->depth++; 303 | 304 | if (packer->depth == packer->nest_sz) 305 | { 306 | qp_packer_t * tmp; 307 | packer->nest_sz *= 2; 308 | tmp = (qp_packer_t *) realloc( 309 | packer, 310 | sizeof(qp_packer_t) + 311 | packer->nest_sz * (sizeof(struct qp__nest_s))); 312 | if (tmp == NULL) 313 | { 314 | packer->nest_sz /= 2; 315 | return QP_ERR_ALLOC; 316 | } 317 | 318 | (*packaddr) = packer = tmp; 319 | } 320 | packer->nesting[i].n = 0; 321 | packer->nesting[i].pos = packer->len; 322 | packer->nesting[i].tp = NEST_MAP; 323 | 324 | packer->buffer[packer->len++] = QP__MAP_OPEN; 325 | 326 | if (i) 327 | { 328 | packer->nesting[i - 1].n++; 329 | } 330 | return 0; 331 | } 332 | 333 | int qp_close_map(qp_packer_t * packer) 334 | { 335 | struct qp__nest_s nesting; 336 | if ( !packer->depth || 337 | (nesting = packer->nesting[packer->depth - 1]).tp != NEST_MAP) 338 | { 339 | return QP_ERR_NO_OPEN_MAP; 340 | } 341 | 342 | if (nesting.n % 2) 343 | { 344 | return QP_ERR_MISSING_VALUE; 345 | } 346 | 347 | if (nesting.n > 10) 348 | { 349 | QP_RESIZE(1) 350 | packer->buffer[packer->len++] = QP__MAP_CLOSE; 351 | } 352 | else 353 | { 354 | packer->buffer[nesting.pos] = QP__MAP0 + nesting.n / 2; 355 | } 356 | packer->depth--; 357 | return 0; 358 | } 359 | 360 | int qp_add_int64(qp_packer_t * packer, int64_t i) 361 | { 362 | int8_t i8; 363 | int16_t i16; 364 | int32_t i32; 365 | 366 | if ((i8 = (int8_t) i) == i) 367 | { 368 | QP_ADD_INT8(packer, i8) 369 | } 370 | else if ((i16 = (int16_t) i) == i) 371 | { 372 | QP_ADD_INT16(packer, i16) 373 | } 374 | else if ((i32 = (int32_t) i) == i) 375 | { 376 | QP_ADD_INT32(packer, i32) 377 | } 378 | else 379 | { 380 | QP_RESIZE(9) 381 | packer->buffer[packer->len++] = QP__INT64; 382 | memcpy(packer->buffer + packer->len, &i, sizeof(int64_t)); 383 | packer->len += sizeof(int64_t); 384 | } 385 | QP__RETURN_INC_C 386 | } 387 | 388 | int qp_add_double(qp_packer_t * packer, double d) 389 | { 390 | QP_RESIZE(9) 391 | if (d == 0.0) 392 | { 393 | packer->buffer[packer->len++] = QP__DOUBLE_0; 394 | } 395 | else if (d == 1.0) 396 | { 397 | packer->buffer[packer->len++] = QP__DOUBLE_1; 398 | } 399 | else if (d == -1.0) 400 | { 401 | packer->buffer[packer->len++] = QP__DOUBLE_N1; 402 | } 403 | else 404 | { 405 | packer->buffer[packer->len++] = QP__DOUBLE; 406 | memcpy(packer->buffer + packer->len, &d, sizeof(double)); 407 | packer->len += sizeof(double); 408 | } 409 | QP__RETURN_INC_C 410 | } 411 | 412 | int qp_add_true(qp_packer_t * packer) QP_PLAIN_OBJ(QP__TRUE) 413 | int qp_add_false(qp_packer_t * packer) QP_PLAIN_OBJ(QP__FALSE) 414 | int qp_add_null(qp_packer_t * packer) QP_PLAIN_OBJ(QP__NULL) 415 | 416 | void qp_unpacker_init( 417 | qp_unpacker_t * unpacker, 418 | const unsigned char * pt, 419 | size_t len) 420 | { 421 | unpacker->start = pt; 422 | unpacker->pt = pt; 423 | unpacker->end = pt + len; 424 | } 425 | 426 | qp_types_t qp_next(qp_unpacker_t * unpacker, qp_obj_t * qp_obj) 427 | { 428 | uint8_t tp; 429 | 430 | if (unpacker->pt >= unpacker->end) 431 | { 432 | if (qp_obj != NULL) 433 | { 434 | qp_obj->tp = QP_END; 435 | } 436 | return QP_END; 437 | } 438 | 439 | tp = *unpacker->pt; 440 | unpacker->pt++; 441 | 442 | switch(tp) 443 | { 444 | case 0: 445 | case 1: 446 | case 2: 447 | case 3: 448 | case 4: 449 | case 5: 450 | case 6: 451 | case 7: 452 | case 8: 453 | case 9: 454 | case 10: 455 | case 11: 456 | case 12: 457 | case 13: 458 | case 14: 459 | case 15: 460 | case 16: 461 | case 17: 462 | case 18: 463 | case 19: 464 | case 20: 465 | case 21: 466 | case 22: 467 | case 23: 468 | case 24: 469 | case 25: 470 | case 26: 471 | case 27: 472 | case 28: 473 | case 29: 474 | case 30: 475 | case 31: 476 | case 32: 477 | case 33: 478 | case 34: 479 | case 35: 480 | case 36: 481 | case 37: 482 | case 38: 483 | case 39: 484 | case 40: 485 | case 41: 486 | case 42: 487 | case 43: 488 | case 44: 489 | case 45: 490 | case 46: 491 | case 47: 492 | case 48: 493 | case 49: 494 | case 50: 495 | case 51: 496 | case 52: 497 | case 53: 498 | case 54: 499 | case 55: 500 | case 56: 501 | case 57: 502 | case 58: 503 | case 59: 504 | case 60: 505 | case 61: 506 | case 62: 507 | case 63: 508 | if (qp_obj != NULL) 509 | { 510 | qp_obj->tp = QP_INT64; 511 | qp_obj->via.int64 = (int64_t) tp; 512 | } 513 | return QP_INT64; 514 | 515 | case 64: 516 | case 65: 517 | case 66: 518 | case 67: 519 | case 68: 520 | case 69: 521 | case 70: 522 | case 71: 523 | case 72: 524 | case 73: 525 | case 74: 526 | case 75: 527 | case 76: 528 | case 77: 529 | case 78: 530 | case 79: 531 | case 80: 532 | case 81: 533 | case 82: 534 | case 83: 535 | case 84: 536 | case 85: 537 | case 86: 538 | case 87: 539 | case 88: 540 | case 89: 541 | case 90: 542 | case 91: 543 | case 92: 544 | case 93: 545 | case 94: 546 | case 95: 547 | case 96: 548 | case 97: 549 | case 98: 550 | case 99: 551 | case 100: 552 | case 101: 553 | case 102: 554 | case 103: 555 | case 104: 556 | case 105: 557 | case 106: 558 | case 107: 559 | case 108: 560 | case 109: 561 | case 110: 562 | case 111: 563 | case 112: 564 | case 113: 565 | case 114: 566 | case 115: 567 | case 116: 568 | case 117: 569 | case 118: 570 | case 119: 571 | case 120: 572 | case 121: 573 | case 122: 574 | case 123: 575 | if (qp_obj != NULL) 576 | { 577 | qp_obj->tp = QP_INT64; 578 | qp_obj->via.int64 = (int64_t) 63 - tp; 579 | } 580 | return QP_INT64; 581 | 582 | case 124: 583 | if (qp_obj != NULL) 584 | { 585 | qp_obj->tp = QP_HOOK; 586 | } 587 | return QP_HOOK; 588 | 589 | case 125: 590 | case 126: 591 | case 127: 592 | /* unpack fixed doubles -1.0, 0.0 or 1.0 */ 593 | if (qp_obj != NULL) 594 | { 595 | qp_obj->tp = QP_DOUBLE; 596 | qp_obj->via.real = (double) (tp - 126); 597 | } 598 | return QP_DOUBLE; 599 | 600 | case 128: 601 | case 129: 602 | case 130: 603 | case 131: 604 | case 132: 605 | case 133: 606 | case 134: 607 | case 135: 608 | case 136: 609 | case 137: 610 | case 138: 611 | case 139: 612 | case 140: 613 | case 141: 614 | case 142: 615 | case 143: 616 | case 144: 617 | case 145: 618 | case 146: 619 | case 147: 620 | case 148: 621 | case 149: 622 | case 150: 623 | case 151: 624 | case 152: 625 | case 153: 626 | case 154: 627 | case 155: 628 | case 156: 629 | case 157: 630 | case 158: 631 | case 159: 632 | case 160: 633 | case 161: 634 | case 162: 635 | case 163: 636 | case 164: 637 | case 165: 638 | case 166: 639 | case 167: 640 | case 168: 641 | case 169: 642 | case 170: 643 | case 171: 644 | case 172: 645 | case 173: 646 | case 174: 647 | case 175: 648 | case 176: 649 | case 177: 650 | case 178: 651 | case 179: 652 | case 180: 653 | case 181: 654 | case 182: 655 | case 183: 656 | case 184: 657 | case 185: 658 | case 186: 659 | case 187: 660 | case 188: 661 | case 189: 662 | case 190: 663 | case 191: 664 | case 192: 665 | case 193: 666 | case 194: 667 | case 195: 668 | case 196: 669 | case 197: 670 | case 198: 671 | case 199: 672 | case 200: 673 | case 201: 674 | case 202: 675 | case 203: 676 | case 204: 677 | case 205: 678 | case 206: 679 | case 207: 680 | case 208: 681 | case 209: 682 | case 210: 683 | case 211: 684 | case 212: 685 | case 213: 686 | case 214: 687 | case 215: 688 | case 216: 689 | case 217: 690 | case 218: 691 | case 219: 692 | case 220: 693 | case 221: 694 | case 222: 695 | case 223: 696 | case 224: 697 | case 225: 698 | case 226: 699 | case 227: 700 | /* unpack fixed sized raw strings */ 701 | { 702 | size_t size = tp - 128; 703 | QP_UNPACK_CHECK_SZ(size) 704 | 705 | if (qp_obj != NULL) 706 | { 707 | qp_obj->tp = QP_RAW; 708 | qp_obj->via.raw = (char *) unpacker->pt; 709 | qp_obj->len = size; 710 | } 711 | unpacker->pt += size; 712 | return QP_RAW; 713 | } 714 | case 228: 715 | QP_UNPACK_RAW(uint8_t) 716 | case 229: 717 | QP_UNPACK_RAW(uint16_t) 718 | case 230: 719 | QP_UNPACK_RAW(uint32_t) 720 | case 231: 721 | QP_UNPACK_RAW(uint64_t) 722 | 723 | case 232: 724 | QP_UNPACK_INT(int8_t) 725 | case 233: 726 | QP_UNPACK_INT(int16_t) 727 | case 234: 728 | QP_UNPACK_INT(int32_t) 729 | case 235: 730 | QP_UNPACK_INT(int64_t) 731 | 732 | case 236: 733 | QP_UNPACK_CHECK_SZ(sizeof(double)) 734 | if (qp_obj != NULL) 735 | { 736 | qp_obj->tp = QP_DOUBLE; 737 | memcpy(&qp_obj->via.real, unpacker->pt, sizeof(double)); 738 | } 739 | unpacker->pt += sizeof(double); 740 | return QP_DOUBLE; 741 | 742 | case 237: 743 | case 238: 744 | case 239: 745 | case 240: 746 | case 241: 747 | case 242: 748 | case 243: 749 | case 244: 750 | case 245: 751 | case 246: 752 | case 247: 753 | case 248: 754 | case 249: 755 | case 250: 756 | case 251: 757 | case 252: 758 | case 253: 759 | case 254: 760 | case 255: 761 | if (qp_obj != NULL) 762 | { 763 | qp_obj->tp = tp; 764 | } 765 | return tp; 766 | } 767 | 768 | return QP_ERR; 769 | } 770 | 771 | int qp_raw_is_equal(qp_obj_t * obj, const char * str) 772 | { 773 | return 774 | strlen(str) == obj->len && 775 | strncmp(obj->via.raw, str, obj->len) == 0; 776 | } 777 | 778 | 779 | void qp_res_destroy(qp_res_t * res) 780 | { 781 | QP_res_destroy(res); 782 | free(res); 783 | } 784 | 785 | int qp_res_fprint(qp_res_t * res, FILE * stream) 786 | { 787 | size_t i; 788 | int rc = 0; 789 | switch(res->tp) 790 | { 791 | case QP_RES_ARRAY: 792 | if (fprintf(stream, "[") < 0) 793 | { 794 | return QP_ERR_WRITE_STREAM; 795 | } 796 | for (i = 0; i < res->via.array->n; i++) 797 | { 798 | if ((i && fprintf(stream, ",") < 0) || 799 | (rc = qp_res_fprint(res->via.array->values + i, stream))) 800 | { 801 | return (rc) ? rc : QP_ERR_WRITE_STREAM; 802 | } 803 | } 804 | if (fprintf(stream, "]") < 0) 805 | { 806 | return QP_ERR_WRITE_STREAM; 807 | } 808 | break; 809 | case QP_RES_STR: 810 | if (fprintf(stream, "%s", res->via.str) < 0) 811 | { 812 | return QP_ERR_WRITE_STREAM; 813 | } 814 | break; 815 | case QP_RES_BOOL: 816 | if (fprintf(stream, "%s", (res->via.boolean) ? "TRUE": "FALSE") < 0) 817 | { 818 | return QP_ERR_WRITE_STREAM; 819 | } 820 | break; 821 | case QP_RES_NULL: 822 | if (fprintf(stream, "NULL") < 0) 823 | { 824 | return QP_ERR_WRITE_STREAM; 825 | } 826 | break; 827 | case QP_RES_INT64: 828 | if (fprintf(stream, "%" PRId64, res->via.int64) < 0) 829 | { 830 | return QP_ERR_WRITE_STREAM; 831 | } 832 | break; 833 | case QP_RES_REAL: 834 | if (fprintf(stream, "%f", res->via.real) < 0) 835 | { 836 | return QP_ERR_WRITE_STREAM; 837 | } 838 | break; 839 | case QP_RES_MAP: 840 | if (fprintf(stream, "{") < 0) 841 | { 842 | return QP_ERR_WRITE_STREAM; 843 | } 844 | 845 | for (i = 0; i < res->via.map->n; i++) 846 | { 847 | if ((i && fprintf(stream, ",") < 0) || ( 848 | (rc = qp_res_fprint(res->via.map->keys + i, stream)) || 849 | fprintf(stream, ":") < 0 || 850 | (rc = qp_res_fprint(res->via.map->values + i, stream)))) 851 | { 852 | return (rc) ? rc : QP_ERR_WRITE_STREAM; 853 | } 854 | } 855 | if (fprintf(stream, "}") < 0) 856 | { 857 | return QP_ERR_WRITE_STREAM; 858 | } 859 | break; 860 | default: 861 | return QP_ERR_CORRUPT; 862 | } 863 | return rc; 864 | } 865 | 866 | qp_res_t * qp_unpacker_res(qp_unpacker_t * unpacker, int * rc) 867 | { 868 | int fallb; 869 | int * rcode = (rc == NULL) ? &fallb : rc; 870 | qp_res_t * res; 871 | qp_types_t tp; 872 | qp_obj_t val; 873 | 874 | tp = qp_next(unpacker, &val); 875 | if (tp == QP_END || 876 | tp == QP_ERR || 877 | tp == QP_HOOK || 878 | tp == QP_ARRAY_CLOSE || 879 | tp == QP_MAP_CLOSE) 880 | { 881 | *rcode = QP_ERR_CORRUPT; 882 | return NULL; 883 | } 884 | 885 | res = (qp_res_t *) malloc(sizeof(qp_res_t)); 886 | 887 | if (res == NULL) 888 | { 889 | *rcode = QP_ERR_ALLOC; 890 | } 891 | else 892 | { 893 | *rcode = QP_res(unpacker, res, &val); 894 | 895 | if (*rcode) 896 | { 897 | qp_res_destroy(res); 898 | res = NULL; 899 | } 900 | } 901 | return res; 902 | } 903 | 904 | void qp_print(const unsigned char * data, size_t len) 905 | { 906 | qp_fprint(stdout, data, len); 907 | printf("\n"); 908 | } 909 | 910 | void qp_fprint(FILE * stream, const unsigned char * data, size_t len) 911 | { 912 | qp_obj_t qp_obj; 913 | qp_unpacker_t unpacker; 914 | qp_unpacker_init(&unpacker, data, len); 915 | QP_print_unpacker(stream, qp_next(&unpacker, &qp_obj), &unpacker, &qp_obj); 916 | } 917 | 918 | char * qp_sprint(const unsigned char * data, size_t len) 919 | { 920 | if (!len) 921 | { 922 | return NULL; 923 | } 924 | size_t sz = QP__MINSZ + len * QP__FACTOR; 925 | char * s = (char *) malloc(sz); 926 | if (s != NULL) 927 | { 928 | char * pt; 929 | qp_obj_t qp_obj; 930 | qp_unpacker_t unpacker; 931 | qp_unpacker_init(&unpacker, data, len); 932 | pt = s; 933 | if (QP_sprint_unpacker( 934 | &s, 935 | &sz, 936 | &pt, 937 | qp_next(&unpacker, &qp_obj), 938 | &unpacker, &qp_obj) != QP_ERR) 939 | { 940 | *pt = '\0'; 941 | sz = pt - s + 1; 942 | char * tmp = (char *) realloc(s, sz); 943 | if (tmp == NULL) 944 | { 945 | free(s); 946 | } 947 | s = tmp; 948 | } 949 | else 950 | { 951 | free(s); 952 | s = NULL; 953 | } 954 | } 955 | return s; 956 | } 957 | 958 | static void QP_fprint_raw(FILE * stream, const char * s, size_t n) 959 | { 960 | size_t i; 961 | fputc('"', stream); 962 | for (i = 0; i < n; i++) 963 | { 964 | char c = s[i]; 965 | switch (c) 966 | { 967 | case '"': 968 | case '\\': 969 | fputc('\\', stream); 970 | } 971 | fputc(c, stream); 972 | } 973 | fputc('"', stream); 974 | } 975 | 976 | static qp_types_t QP_print_unpacker( 977 | FILE * stream, 978 | qp_types_t tp, 979 | qp_unpacker_t * unpacker, 980 | qp_obj_t * qp_obj) 981 | { 982 | int count; 983 | int found; 984 | switch (tp) 985 | { 986 | case QP_INT64: 987 | fprintf(stream, "%" PRId64, qp_obj->via.int64); 988 | break; 989 | case QP_DOUBLE: 990 | fprintf(stream, "%f", qp_obj->via.real); 991 | break; 992 | case QP_RAW: 993 | QP_fprint_raw(stream, qp_obj->via.raw, qp_obj->len); 994 | break; 995 | case QP_TRUE: 996 | fprintf(stream, "true"); 997 | break; 998 | case QP_FALSE: 999 | fprintf(stream, "false"); 1000 | break; 1001 | case QP_NULL: 1002 | fprintf(stream, "null"); 1003 | break; 1004 | case QP_ARRAY0: 1005 | case QP_ARRAY1: 1006 | case QP_ARRAY2: 1007 | case QP_ARRAY3: 1008 | case QP_ARRAY4: 1009 | case QP_ARRAY5: 1010 | fprintf(stream, "["); 1011 | count = tp - QP_ARRAY0; 1012 | tp = qp_next(unpacker, qp_obj); 1013 | for (found = 0; count-- && tp; found = 1) 1014 | { 1015 | if (found ) 1016 | { 1017 | fprintf(stream, ","); 1018 | } 1019 | tp = QP_print_unpacker(stream, tp, unpacker, qp_obj); 1020 | } 1021 | fprintf(stream, "]"); 1022 | return tp; 1023 | case QP_MAP0: 1024 | case QP_MAP1: 1025 | case QP_MAP2: 1026 | case QP_MAP3: 1027 | case QP_MAP4: 1028 | case QP_MAP5: 1029 | fprintf(stream, "{"); 1030 | count = tp - QP_MAP0; 1031 | tp = qp_next(unpacker, qp_obj); 1032 | for (found = 0; count-- && tp; found = 1) 1033 | { 1034 | if (found ) 1035 | { 1036 | fprintf(stream, ","); 1037 | } 1038 | tp = QP_print_unpacker(stream, tp, unpacker, qp_obj); 1039 | fprintf(stream, ":"); 1040 | tp = QP_print_unpacker(stream, tp, unpacker, qp_obj); 1041 | } 1042 | fprintf(stream, "}"); 1043 | return tp; 1044 | case QP_ARRAY_OPEN: 1045 | fprintf(stream, "["); 1046 | tp = qp_next(unpacker, qp_obj); 1047 | for (count = 0; tp && tp != QP_ARRAY_CLOSE; count = 1) 1048 | { 1049 | if (count) 1050 | { 1051 | fprintf(stream, ","); 1052 | } 1053 | tp = QP_print_unpacker(stream, tp, unpacker, qp_obj); 1054 | } 1055 | fprintf(stream, "]"); 1056 | break; 1057 | case QP_MAP_OPEN: 1058 | fprintf(stream, "{"); 1059 | tp = qp_next(unpacker, qp_obj); 1060 | for (count = 0; tp && tp != QP_MAP_CLOSE; count = 1) 1061 | { 1062 | if (count) 1063 | { 1064 | fprintf(stream, ","); 1065 | } 1066 | tp = QP_print_unpacker(stream, tp, unpacker, qp_obj); 1067 | fprintf(stream, ":"); 1068 | tp = QP_print_unpacker(stream, tp, unpacker, qp_obj); 1069 | } 1070 | fprintf(stream, "}"); 1071 | break; 1072 | default: 1073 | break; 1074 | } 1075 | return qp_next(unpacker, qp_obj); 1076 | } 1077 | 1078 | static int QP_sprint_raw( 1079 | char ** s, 1080 | size_t * sz, 1081 | size_t p, 1082 | const char * d, 1083 | size_t n) 1084 | { 1085 | size_t i; 1086 | size_t pos = p; 1087 | (*s)[pos++] = '"'; 1088 | for (i = 0; i < n; i++) 1089 | { 1090 | char c = d[i]; 1091 | 1092 | if (pos >= (*sz) - 3) 1093 | { 1094 | char * tmp; 1095 | *sz += QP__MINSZ + (n - i) * QP__FACTOR; 1096 | tmp = (char *) realloc(*s, *sz); 1097 | if (tmp == NULL) 1098 | { 1099 | return -1; 1100 | } 1101 | *s = tmp; 1102 | } 1103 | 1104 | switch (c) 1105 | { 1106 | case '"': 1107 | case '\\': 1108 | (*s)[pos++] = '\\'; 1109 | } 1110 | (*s)[pos++] = c; 1111 | } 1112 | (*s)[pos++] = '"'; 1113 | return pos - p; 1114 | } 1115 | 1116 | static qp_types_t QP_sprint_unpacker( 1117 | char ** s, 1118 | size_t * sz, 1119 | char ** pt, 1120 | qp_types_t tp, 1121 | qp_unpacker_t * unpacker, 1122 | qp_obj_t * qp_obj) 1123 | { 1124 | int n, count, found; 1125 | size_t pos = (*pt) - (*s); 1126 | if ((*sz) - pos < QP__MINSZ) 1127 | { 1128 | char * tmp; 1129 | *sz += QP__MINSZ + (unpacker->end - unpacker->pt) * QP__FACTOR; 1130 | tmp = (char *) realloc(*s, *sz); 1131 | if (tmp == NULL) 1132 | { 1133 | return QP_ERR; 1134 | } 1135 | *s = tmp; 1136 | *pt = (*s) + pos; 1137 | } 1138 | switch (tp) 1139 | { 1140 | case QP_INT64: 1141 | n = sprintf(*pt, "%" PRId64, qp_obj->via.int64); QP__CHKN 1142 | break; 1143 | case QP_DOUBLE: 1144 | n = sprintf(*pt, "%f", qp_obj->via.real); QP__CHKN 1145 | break; 1146 | case QP_RAW: 1147 | n = QP_sprint_raw(s, sz, pos, qp_obj->via.raw, qp_obj->len); QP__CHKN 1148 | break; 1149 | case QP_TRUE: 1150 | n = sprintf(*pt, "true"); QP__CHKN 1151 | break; 1152 | case QP_FALSE: 1153 | n = sprintf(*pt, "false"); QP__CHKN 1154 | break; 1155 | case QP_NULL: 1156 | n = sprintf(*pt, "null"); QP__CHKN 1157 | break; 1158 | case QP_ARRAY0: 1159 | case QP_ARRAY1: 1160 | case QP_ARRAY2: 1161 | case QP_ARRAY3: 1162 | case QP_ARRAY4: 1163 | case QP_ARRAY5: 1164 | n = sprintf(*pt, "["); QP__CHKN 1165 | count = tp - QP_ARRAY0; 1166 | tp = qp_next(unpacker, qp_obj); QP__CHKT 1167 | for (found = 0; count-- && tp; found = 1) 1168 | { 1169 | if (found ) 1170 | { 1171 | n = sprintf(*pt, ","); QP__CHKN 1172 | } 1173 | tp = QP_sprint_unpacker(s, sz, pt, tp, unpacker, qp_obj); QP__CHKT 1174 | } 1175 | n = sprintf(*pt, "]"); QP__CHKN 1176 | return tp; 1177 | case QP_MAP0: 1178 | case QP_MAP1: 1179 | case QP_MAP2: 1180 | case QP_MAP3: 1181 | case QP_MAP4: 1182 | case QP_MAP5: 1183 | n = sprintf(*pt, "{"); QP__CHKN 1184 | count = tp - QP_MAP0; 1185 | tp = qp_next(unpacker, qp_obj); QP__CHKT 1186 | for (found = 0; count-- && tp; found = 1) 1187 | { 1188 | if (found ) 1189 | { 1190 | n = sprintf(*pt, ","); QP__CHKN 1191 | } 1192 | tp = QP_sprint_unpacker(s, sz, pt, tp, unpacker, qp_obj); QP__CHKT 1193 | n = sprintf(*pt, ":"); QP__CHKN 1194 | tp = QP_sprint_unpacker(s, sz, pt, tp, unpacker, qp_obj); QP__CHKT 1195 | } 1196 | n = sprintf(*pt, "}"); QP__CHKN 1197 | return tp; 1198 | case QP_ARRAY_OPEN: 1199 | n = sprintf(*pt, "["); QP__CHKN 1200 | tp = qp_next(unpacker, qp_obj); QP__CHKT 1201 | for (count = 0; tp && tp != QP_ARRAY_CLOSE; count = 1) 1202 | { 1203 | if (count) 1204 | { 1205 | n = sprintf(*pt, ","); QP__CHKN 1206 | } 1207 | tp = QP_sprint_unpacker(s, sz, pt, tp, unpacker, qp_obj); QP__CHKT 1208 | } 1209 | n = sprintf(*pt, "]"); QP__CHKN 1210 | break; 1211 | case QP_MAP_OPEN: 1212 | n = sprintf(*pt, "{"); QP__CHKN 1213 | tp = qp_next(unpacker, qp_obj); QP__CHKT 1214 | for (count = 0; tp && tp != QP_MAP_CLOSE; count = 1) 1215 | { 1216 | if (count) 1217 | { 1218 | n = sprintf(*pt, ","); QP__CHKN 1219 | } 1220 | tp = QP_sprint_unpacker(s, sz, pt, tp, unpacker, qp_obj); QP__CHKT 1221 | n = sprintf(*pt, ":"); QP__CHKN 1222 | tp = QP_sprint_unpacker(s, sz, pt, tp, unpacker, qp_obj); QP__CHKT 1223 | } 1224 | n = sprintf(*pt, "}"); QP__CHKN 1225 | break; 1226 | default: 1227 | break; 1228 | } 1229 | 1230 | return qp_next(unpacker, qp_obj); 1231 | } 1232 | 1233 | static int QP_res(qp_unpacker_t * unpacker, qp_res_t * res, qp_obj_t * val) 1234 | { 1235 | int rc; 1236 | size_t i, n, m; 1237 | qp_types_t tp; 1238 | qp_res_t * tmp; 1239 | 1240 | switch((qp_types_t) val->tp) 1241 | { 1242 | case QP_RAW: 1243 | res->tp = QP_RES_STR; 1244 | res->via.str = strndup(val->via.raw, val->len); 1245 | return (res->via.str == NULL) ? QP_ERR_ALLOC : 0; 1246 | case QP_HOOK: 1247 | /* hooks are not implemented yet */ 1248 | return QP_ERR_CORRUPT; 1249 | case QP_INT64: 1250 | res->tp = QP_RES_INT64; 1251 | res->via.int64 = val->via.int64; 1252 | return 0; 1253 | case QP_DOUBLE: 1254 | res->tp = QP_RES_REAL; 1255 | res->via.real = val->via.real; 1256 | return 0; 1257 | case QP_ARRAY0: 1258 | case QP_ARRAY1: 1259 | case QP_ARRAY2: 1260 | case QP_ARRAY3: 1261 | case QP_ARRAY4: 1262 | case QP_ARRAY5: 1263 | res->tp = QP_RES_ARRAY; 1264 | n = val->tp - QP_ARRAY0; 1265 | res->via.array = (qp_array_t *) malloc(sizeof(qp_array_t)); 1266 | if (res->via.array == NULL) 1267 | { 1268 | return QP_ERR_ALLOC; 1269 | } 1270 | res->via.array->n = n; 1271 | res->via.array->values = (qp_res_t *) malloc(sizeof(qp_res_t) * n); 1272 | 1273 | if (!n) 1274 | { 1275 | return 0; 1276 | } 1277 | 1278 | if (res->via.array->values == NULL) 1279 | { 1280 | res->via.array->n = 0; 1281 | return QP_ERR_ALLOC; 1282 | } 1283 | 1284 | for (i = 0; i < n; i++) 1285 | { 1286 | tp = qp_next(unpacker, val); 1287 | 1288 | if (tp == QP_END || 1289 | tp == QP_ERR || 1290 | tp == QP_HOOK || 1291 | tp == QP_ARRAY_CLOSE || 1292 | tp == QP_MAP_CLOSE) 1293 | { 1294 | res->via.array->n = i; 1295 | return QP_ERR_CORRUPT; 1296 | } 1297 | 1298 | if ((rc = QP_res(unpacker, res->via.array->values + i, val))) 1299 | { 1300 | res->via.array->n = i + 1; 1301 | return rc; 1302 | } 1303 | } 1304 | return 0; 1305 | case QP_MAP0: 1306 | case QP_MAP1: 1307 | case QP_MAP2: 1308 | case QP_MAP3: 1309 | case QP_MAP4: 1310 | case QP_MAP5: 1311 | res->tp = QP_RES_MAP; 1312 | n = val->tp - QP_MAP0; 1313 | res->via.map = (qp_map_t *) malloc(sizeof(qp_map_t)); 1314 | if (res->via.map == NULL) 1315 | { 1316 | return QP_ERR_ALLOC; 1317 | } 1318 | res->via.map->n = n; 1319 | res->via.map->keys = (qp_res_t *) malloc(sizeof(qp_res_t) * n); 1320 | res->via.map->values = (qp_res_t *) malloc(sizeof(qp_res_t) * n); 1321 | 1322 | if (!n) 1323 | { 1324 | return 0; 1325 | } 1326 | 1327 | if (res->via.map->keys == NULL || res->via.map->values == NULL) 1328 | { 1329 | res->via.map->n = 0; 1330 | return QP_ERR_ALLOC; 1331 | } 1332 | 1333 | for (i = 0; i < n; i++) 1334 | { 1335 | int kv = 2; 1336 | 1337 | while (kv--) 1338 | { 1339 | tp = qp_next(unpacker, val); 1340 | 1341 | if (tp == QP_END || 1342 | tp == QP_ERR || 1343 | tp == QP_HOOK || 1344 | tp == QP_ARRAY_CLOSE || 1345 | tp == QP_MAP_CLOSE) 1346 | { 1347 | res->via.map->n = i; 1348 | if (!kv) 1349 | { 1350 | QP_res_destroy(res->via.map->keys + i); 1351 | } 1352 | return QP_ERR_CORRUPT; 1353 | } 1354 | if ((rc = QP_res( 1355 | unpacker, 1356 | (kv) ? res->via.map->keys + i : res->via.map->values + i, 1357 | val))) 1358 | { 1359 | res->via.map->n = i + 1; 1360 | return rc; 1361 | } 1362 | } 1363 | } 1364 | return 0; 1365 | case QP_TRUE: 1366 | case QP_FALSE: 1367 | res->tp = QP_RES_BOOL; 1368 | res->via.boolean = (val->tp == QP_TRUE); 1369 | return 0; 1370 | case QP_NULL: 1371 | res->tp = QP_RES_NULL; 1372 | res->via.null = NULL; 1373 | return 0; 1374 | case QP_ARRAY_OPEN: 1375 | res->tp = QP_RES_ARRAY; 1376 | 1377 | n = 0; 1378 | 1379 | res->via.array = (qp_array_t *) malloc(sizeof(qp_array_t)); 1380 | if (res->via.array == NULL) 1381 | { 1382 | return QP_ERR_ALLOC; 1383 | } 1384 | 1385 | res->via.array->n = 0; 1386 | res->via.array->values = NULL; 1387 | 1388 | for (i = 0;; i++) 1389 | { 1390 | tp = qp_next(unpacker, val); 1391 | 1392 | if (tp == QP_END || tp == QP_ARRAY_CLOSE) 1393 | { 1394 | break; 1395 | } 1396 | 1397 | if (tp == QP_ERR || tp == QP_HOOK || tp == QP_MAP_CLOSE) 1398 | { 1399 | return QP_ERR_CORRUPT; 1400 | } 1401 | 1402 | res->via.array->n++; 1403 | 1404 | if (res->via.array->n > n) 1405 | { 1406 | n = (n) ? n * 2: QP__INITIAL_ALLOC_SZ; 1407 | 1408 | tmp = (qp_res_t *) realloc( 1409 | res->via.array->values, 1410 | sizeof(qp_res_t) * n); 1411 | 1412 | if (tmp == NULL) 1413 | { 1414 | res->via.array->n--; 1415 | return QP_ERR_ALLOC; 1416 | } 1417 | 1418 | res->via.array->values = tmp; 1419 | } 1420 | 1421 | if ((rc = QP_res(unpacker, res->via.array->values + i, val))) 1422 | { 1423 | return rc; 1424 | } 1425 | } 1426 | 1427 | if (n > res->via.array->n) 1428 | { 1429 | tmp = (qp_res_t *) realloc( 1430 | res->via.array->values, 1431 | sizeof(qp_res_t) * res->via.array->n); 1432 | 1433 | if (tmp == NULL) 1434 | { 1435 | return QP_ERR_ALLOC; 1436 | } 1437 | 1438 | res->via.array->values = tmp; 1439 | } 1440 | 1441 | return 0; 1442 | case QP_MAP_OPEN: 1443 | res->tp = QP_RES_MAP; 1444 | 1445 | tp = QP_MAP_OPEN; 1446 | n = 0; 1447 | m = 0; 1448 | res->via.map = (qp_map_t *) malloc(sizeof(qp_map_t)); 1449 | if (res->via.map == NULL) 1450 | { 1451 | return QP_ERR_ALLOC; 1452 | } 1453 | 1454 | res->via.map->n = 0; 1455 | res->via.map->keys = NULL; 1456 | res->via.map->values = NULL; 1457 | 1458 | for (i = 0; tp != QP_END && tp != QP_MAP_CLOSE; i++) 1459 | { 1460 | int kv = 2; 1461 | 1462 | while (kv--) 1463 | { 1464 | qp_res_t ** rs; 1465 | size_t * sz; 1466 | tp = qp_next(unpacker, val); 1467 | 1468 | if (kv) 1469 | { 1470 | if (tp == QP_END || tp == QP_MAP_CLOSE) 1471 | { 1472 | break; 1473 | } 1474 | 1475 | if (tp == QP_ERR || 1476 | tp == QP_HOOK || 1477 | tp == QP_ARRAY_CLOSE) 1478 | { 1479 | return QP_ERR_CORRUPT; 1480 | } 1481 | res->via.map->n++; 1482 | rs = &res->via.map->keys; 1483 | sz = &n; 1484 | } 1485 | else 1486 | { 1487 | if (tp == QP_END || 1488 | tp == QP_ERR || 1489 | tp == QP_HOOK || 1490 | tp == QP_ARRAY_CLOSE || 1491 | tp == QP_MAP_CLOSE) 1492 | { 1493 | res->via.map->n--; 1494 | QP_res_destroy(res->via.map->keys + i); 1495 | return QP_ERR_CORRUPT; 1496 | } 1497 | rs = &res->via.map->values; 1498 | sz = &m; 1499 | } 1500 | 1501 | if (res->via.map->n > *sz) 1502 | { 1503 | *sz = (*sz) ? (*sz) * 2: QP__INITIAL_ALLOC_SZ; 1504 | 1505 | tmp = (qp_res_t *) realloc(*rs, sizeof(qp_res_t) * (*sz)); 1506 | 1507 | if (tmp == NULL) 1508 | { 1509 | res->via.map->n--; 1510 | if (!kv) 1511 | { 1512 | QP_res_destroy(res->via.map->keys + i); 1513 | } 1514 | return QP_ERR_ALLOC; 1515 | } 1516 | 1517 | *rs = tmp; 1518 | } 1519 | 1520 | if ((rc = QP_res(unpacker, (*rs) + i, val))) 1521 | { 1522 | if (kv) 1523 | { 1524 | res->via.map->n--; 1525 | QP_res_destroy(res->via.map->keys + i); 1526 | } 1527 | return rc; 1528 | } 1529 | } 1530 | } 1531 | 1532 | if (n > res->via.map->n) 1533 | { 1534 | tmp = (qp_res_t *) realloc( 1535 | res->via.map->keys, 1536 | sizeof(qp_res_t) * res->via.map->n); 1537 | if (tmp == NULL) 1538 | { 1539 | return QP_ERR_ALLOC; 1540 | } 1541 | res->via.map->keys = tmp; 1542 | 1543 | tmp = (qp_res_t *) realloc( 1544 | res->via.map->values, 1545 | sizeof(qp_res_t) * res->via.map->n); 1546 | if (tmp == NULL) 1547 | { 1548 | return QP_ERR_ALLOC; 1549 | } 1550 | res->via.map->values = tmp; 1551 | } 1552 | 1553 | return 0; 1554 | default: 1555 | res->tp = QP_RES_NULL; 1556 | return QP_ERR_CORRUPT; 1557 | } 1558 | } 1559 | 1560 | static void QP_res_destroy(qp_res_t * res) 1561 | { 1562 | size_t i; 1563 | switch(res->tp) 1564 | { 1565 | case QP_RES_ARRAY: 1566 | for (i = 0; i < res->via.array->n; i++) 1567 | { 1568 | QP_res_destroy(res->via.array->values + i); 1569 | } 1570 | free(res->via.array->values); 1571 | free(res->via.array); 1572 | break; 1573 | case QP_RES_STR: 1574 | free(res->via.str); 1575 | break; 1576 | case QP_RES_MAP: 1577 | for (i = 0; i < res->via.map->n; i++) 1578 | { 1579 | QP_res_destroy(res->via.map->keys + i); 1580 | QP_res_destroy(res->via.map->values + i); 1581 | } 1582 | free(res->via.map->keys); 1583 | free(res->via.map->values); 1584 | free(res->via.map); 1585 | break; 1586 | default: 1587 | break; 1588 | } 1589 | } 1590 | -------------------------------------------------------------------------------- /qpack.h: -------------------------------------------------------------------------------- 1 | /* 2 | * qpack.h 3 | * 4 | * Created on: Mar 22, 2017 5 | * Author: Jeroen van der Heijden 6 | */ 7 | 8 | #ifndef QPACK_H_ 9 | #define QPACK_H_ 10 | 11 | // Version numbers are configured with CMake. 12 | #define QP_VERSION_MAJOR 0 13 | #define QP_VERSION_MINOR 10 14 | #define QP_VERSION_PATCH 7 15 | 16 | #define QP_VERSION "0.10.7" 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | typedef union qp_via_u qp_via_t; 23 | typedef union qp_res_u qp_res_via_t; 24 | 25 | typedef struct qp_obj_s qp_obj_t; 26 | typedef struct qp_packer_s qp_packer_t; 27 | typedef struct qp_unpacker_s qp_unpacker_t; 28 | typedef struct qp_res_s qp_res_t; 29 | typedef struct qp_map_s qp_map_t; 30 | typedef struct qp_array_s qp_array_t; 31 | 32 | /* private typemap */ 33 | enum 34 | { 35 | QP__END, /* at the end while unpacking */ 36 | QP__ERR, /* error */ 37 | QP__RAW, /* raw string */ 38 | /* 39 | * Both END and RAW are never actually packed but 0 and 1 are reserved 40 | * for positive signed integers. 41 | * 42 | * Fixed positive integers from 0 till 63 [ 0...63 ] 43 | * 44 | * Fixed negative integers from -60 till -1 [ 64...123 ] 45 | * 46 | */ 47 | QP__HOOK=124, /* Hook is not used by SiriDB */ 48 | QP__DOUBLE_N1=125, /* ## double value -1.0 */ 49 | QP__DOUBLE_0, /* ## double value 0.0 */ 50 | QP__DOUBLE_1, /* ## double value 1.0 */ 51 | /* 52 | * Fixed raw strings lengths from 0 till 99 [ 128...227 ] 53 | */ 54 | QP__RAW8=228, /* ## raw string with length < 1 byte */ 55 | QP__RAW16, /* ## raw string with length < 1 byte */ 56 | QP__RAW32, /* ## raw string with length < 1 byte */ 57 | QP__RAW64, /* ## raw string with length < 1 byte */ 58 | QP__INT8, /* ## 1 byte signed integer */ 59 | QP__INT16, /* ## 2 byte signed integer */ 60 | QP__INT32, /* ## 4 byte signed integer */ 61 | QP__INT64, /* 8 bytes signed integer */ 62 | QP__DOUBLE, /* 8 bytes double */ 63 | QP__ARRAY0, /* empty array */ 64 | QP__ARRAY1, /* array with 1 item */ 65 | QP__ARRAY2, /* array with 2 items */ 66 | QP__ARRAY3, /* array with 3 items */ 67 | QP__ARRAY4, /* array with 4 items */ 68 | QP__ARRAY5, /* array with 5 items */ 69 | QP__MAP0, /* empty map */ 70 | QP__MAP1, /* map with 1 item */ 71 | QP__MAP2, /* map with 2 items */ 72 | QP__MAP3, /* map with 3 items */ 73 | QP__MAP4, /* map with 4 items */ 74 | QP__MAP5, /* map with 5 items */ 75 | QP__TRUE, /* boolean true */ 76 | QP__FALSE, /* boolean false */ 77 | QP__NULL, /* null (none, nil) */ 78 | QP__ARRAY_OPEN, /* open a new array */ 79 | QP__MAP_OPEN, /* open a new map */ 80 | QP__ARRAY_CLOSE, /* close array */ 81 | QP__MAP_CLOSE, /* close map */ 82 | }; 83 | 84 | /* enums */ 85 | typedef enum qp_err_e 86 | { 87 | QP_ERR_WRITE_STREAM=-6, 88 | QP_ERR_NO_OPEN_ARRAY, 89 | QP_ERR_NO_OPEN_MAP, 90 | QP_ERR_MISSING_VALUE, 91 | QP_ERR_CORRUPT, 92 | QP_ERR_ALLOC, 93 | } qp_err_t; 94 | 95 | typedef enum qp_res_e 96 | { 97 | QP_RES_MAP, 98 | QP_RES_ARRAY, 99 | QP_RES_INT64, 100 | QP_RES_REAL, 101 | QP_RES_STR, 102 | QP_RES_BOOL, 103 | QP_RES_NULL, 104 | } qp_res_tp; 105 | 106 | typedef enum qp_types_e 107 | { 108 | QP_END=QP__END, 109 | QP_ERR, 110 | QP_RAW, 111 | QP_HOOK=QP__HOOK, 112 | QP_INT64=QP__INT64, /* 8 bytes signed integer */ 113 | QP_DOUBLE, /* 8 bytes double */ 114 | QP_ARRAY0, /* empty array */ 115 | QP_ARRAY1, /* array with 1 item */ 116 | QP_ARRAY2, /* array with 2 items */ 117 | QP_ARRAY3, /* array with 3 items */ 118 | QP_ARRAY4, /* array with 4 items */ 119 | QP_ARRAY5, /* array with 5 items */ 120 | QP_MAP0, /* empty map */ 121 | QP_MAP1, /* map with 1 item */ 122 | QP_MAP2, /* map with 2 items */ 123 | QP_MAP3, /* map with 3 items */ 124 | QP_MAP4, /* map with 4 items */ 125 | QP_MAP5, /* map with 5 items */ 126 | QP_TRUE, /* boolean true */ 127 | QP_FALSE, /* boolean false */ 128 | QP_NULL, /* null (none, nil) */ 129 | QP_ARRAY_OPEN, /* open a new array */ 130 | QP_MAP_OPEN, /* open a new map */ 131 | QP_ARRAY_CLOSE, /* close array */ 132 | QP_MAP_CLOSE, /* close map */ 133 | } qp_types_t; 134 | 135 | enum qp__nest_types 136 | { 137 | NEST_ARRAY, 138 | NEST_MAP 139 | }; 140 | 141 | union qp_via_u 142 | { 143 | int64_t int64; 144 | double real; 145 | const char * raw; 146 | }; 147 | 148 | struct qp_obj_s 149 | { 150 | uint8_t tp; 151 | size_t len; 152 | qp_via_t via; 153 | }; 154 | 155 | struct qp_map_s 156 | { 157 | size_t n; 158 | qp_res_t * keys; 159 | qp_res_t * values; 160 | }; 161 | 162 | struct qp_array_s 163 | { 164 | size_t n; 165 | qp_res_t * values; 166 | }; 167 | 168 | union qp_res_u 169 | { 170 | qp_map_t * map; 171 | qp_array_t * array; 172 | char * str; 173 | int64_t int64; 174 | double real; 175 | int boolean; 176 | void * null; 177 | }; 178 | 179 | struct qp_res_s 180 | { 181 | qp_res_tp tp; 182 | qp_res_via_t via; 183 | }; 184 | 185 | struct qp__nest_s 186 | { 187 | enum qp__nest_types tp; 188 | size_t n; 189 | size_t pos; 190 | }; 191 | 192 | struct qp_packer_s 193 | { 194 | unsigned char * buffer; 195 | size_t len; 196 | size_t buffer_size; 197 | size_t alloc_size; 198 | size_t depth; 199 | size_t nest_sz; 200 | struct qp__nest_s nesting[]; 201 | }; 202 | 203 | struct qp_unpacker_s 204 | { 205 | const unsigned char * start; 206 | const unsigned char * pt; 207 | const unsigned char * end; 208 | }; 209 | 210 | #ifdef __cplusplus 211 | extern "C" { 212 | #endif 213 | 214 | /* create and destroy functions */ 215 | qp_packer_t * qp_packer_create(size_t alloc_size); 216 | void qp_packer_destroy(qp_packer_t * packer); 217 | 218 | /* add to packer functions */ 219 | int qp_add_raw(qp_packer_t * packer, const char * raw, size_t len); 220 | int qp_add_int64(qp_packer_t * packer, int64_t i); 221 | int qp_add_double(qp_packer_t * packer, double d); 222 | int qp_add_true(qp_packer_t * packer); 223 | int qp_add_false(qp_packer_t * packer); 224 | int qp_add_null(qp_packer_t * packer); 225 | int qp_add_array(qp_packer_t ** packaddr); 226 | int qp_add_map(qp_packer_t ** packaddr); 227 | 228 | /* close array/map functions */ 229 | int qp_close_array(qp_packer_t * packer); 230 | int qp_close_map(qp_packer_t * packer); 231 | 232 | /* initialize unpacker */ 233 | void qp_unpacker_init( 234 | qp_unpacker_t * unpacker, 235 | const unsigned char * pt, 236 | size_t len); 237 | 238 | /* step over an unpacker */ 239 | qp_types_t qp_next(qp_unpacker_t * unpacker, qp_obj_t * qp_obj); 240 | 241 | /* unpack all */ 242 | qp_res_t * qp_unpacker_res(qp_unpacker_t * unpacker, int * rc); 243 | void qp_res_destroy(qp_res_t * res); 244 | int qp_res_fprint(qp_res_t * res, FILE * stream); 245 | 246 | /* test functions for qp_obj_t */ 247 | static inline int qp_is_array(qp_types_t tp) 248 | { 249 | return tp == QP_ARRAY_OPEN || (tp >= QP_ARRAY0 && tp <= QP_ARRAY5); 250 | } 251 | 252 | static inline int qp_is_map(qp_types_t tp) 253 | { 254 | return tp == QP_MAP_OPEN || (tp >= QP_MAP0 && tp <= QP_MAP5); 255 | } 256 | 257 | static inline int qp_is_close(qp_types_t tp) 258 | { 259 | return tp == QP_ARRAY_CLOSE || tp == QP_MAP_CLOSE; 260 | } 261 | 262 | static inline int qp_is_int(qp_types_t tp) 263 | { 264 | return tp == QP_INT64; 265 | } 266 | 267 | static inline int qp_is_double(qp_types_t tp) 268 | { 269 | return tp == QP_DOUBLE; 270 | } 271 | 272 | static inline int qp_is_bool(qp_types_t tp) 273 | { 274 | return tp == QP_TRUE || tp == QP_FALSE; 275 | } 276 | 277 | static inline int qp_is_null(qp_types_t tp) 278 | { 279 | return tp == QP_NULL; 280 | } 281 | 282 | static inline int qp_is_raw(qp_types_t tp) 283 | { 284 | return tp == QP_RAW; 285 | } 286 | 287 | static inline int qp_is_raw_term(qp_obj_t * qp_obj) 288 | { 289 | return (qp_obj->tp == QP_RAW && 290 | qp_obj->len && 291 | qp_obj->via.raw[qp_obj->len - 1] == '\0'); 292 | } 293 | int qp_raw_is_equal(qp_obj_t * obj, const char * str); 294 | 295 | /* print */ 296 | void qp_print(const unsigned char * data, size_t len); 297 | void qp_fprint(FILE * stream, const unsigned char * data, size_t len); 298 | char * qp_sprint(const unsigned char * data, size_t len); 299 | 300 | #define qp_packer_print(packer) \ 301 | qp_print(packer->buffer, packer->len) 302 | #define qp_packer_fprint(stream, packer) \ 303 | qp_fprint(stream, packer->buffer, packer->len) 304 | #define qp_packer_sprint(packer) \ 305 | qp_sprint(packer->buffer, packer->len) 306 | #define qp_unpacker_print(unpacker) \ 307 | qp_print(unpacker->start, unpacker->end - unpacker->start) 308 | #define qp_unpacker_fprint(stream, unpacker) \ 309 | qp_fprint(stream, unpacker->start, unpacker->end - unpacker->start) 310 | #define qp_unpacker_sprint(unpacker) \ 311 | qp_sprint(unpacker->start, unpacker->end - unpacker->start) 312 | 313 | /* misc functions */ 314 | const char * qp_strerror(int err_code); 315 | const char * qp_version(void); 316 | 317 | #ifdef __cplusplus 318 | } 319 | #endif 320 | 321 | #endif /* QPACK_H_ */ 322 | -------------------------------------------------------------------------------- /qpack.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * qpack.h 3 | * 4 | * Created on: Mar 22, 2017 5 | * Author: Jeroen van der Heijden 6 | */ 7 | 8 | #ifndef QPACK_H_ 9 | #define QPACK_H_ 10 | 11 | // Version numbers are configured with CMake. 12 | #define QP_VERSION_MAJOR @qpack_VERSION_MAJOR@ 13 | #define QP_VERSION_MINOR @qpack_VERSION_MINOR@ 14 | #define QP_VERSION_PATCH @qpack_VERSION_PATCH@ 15 | 16 | #define QP_VERSION "@qpack_VERSION@" 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | typedef union qp_via_u qp_via_t; 23 | typedef union qp_res_u qp_res_via_t; 24 | 25 | typedef struct qp_obj_s qp_obj_t; 26 | typedef struct qp_packer_s qp_packer_t; 27 | typedef struct qp_unpacker_s qp_unpacker_t; 28 | typedef struct qp_res_s qp_res_t; 29 | typedef struct qp_map_s qp_map_t; 30 | typedef struct qp_array_s qp_array_t; 31 | 32 | /* private typemap */ 33 | enum 34 | { 35 | QP__END, /* at the end while unpacking */ 36 | QP__ERR, /* error */ 37 | QP__RAW, /* raw string */ 38 | /* 39 | * Both END and RAW are never actually packed but 0 and 1 are reserved 40 | * for positive signed integers. 41 | * 42 | * Fixed positive integers from 0 till 63 [ 0...63 ] 43 | * 44 | * Fixed negative integers from -60 till -1 [ 64...123 ] 45 | * 46 | */ 47 | QP__HOOK=124, /* Hook is not used by SiriDB */ 48 | QP__DOUBLE_N1=125, /* ## double value -1.0 */ 49 | QP__DOUBLE_0, /* ## double value 0.0 */ 50 | QP__DOUBLE_1, /* ## double value 1.0 */ 51 | /* 52 | * Fixed raw strings lengths from 0 till 99 [ 128...227 ] 53 | */ 54 | QP__RAW8=228, /* ## raw string with length < 1 byte */ 55 | QP__RAW16, /* ## raw string with length < 1 byte */ 56 | QP__RAW32, /* ## raw string with length < 1 byte */ 57 | QP__RAW64, /* ## raw string with length < 1 byte */ 58 | QP__INT8, /* ## 1 byte signed integer */ 59 | QP__INT16, /* ## 2 byte signed integer */ 60 | QP__INT32, /* ## 4 byte signed integer */ 61 | QP__INT64, /* 8 bytes signed integer */ 62 | QP__DOUBLE, /* 8 bytes double */ 63 | QP__ARRAY0, /* empty array */ 64 | QP__ARRAY1, /* array with 1 item */ 65 | QP__ARRAY2, /* array with 2 items */ 66 | QP__ARRAY3, /* array with 3 items */ 67 | QP__ARRAY4, /* array with 4 items */ 68 | QP__ARRAY5, /* array with 5 items */ 69 | QP__MAP0, /* empty map */ 70 | QP__MAP1, /* map with 1 item */ 71 | QP__MAP2, /* map with 2 items */ 72 | QP__MAP3, /* map with 3 items */ 73 | QP__MAP4, /* map with 4 items */ 74 | QP__MAP5, /* map with 5 items */ 75 | QP__TRUE, /* boolean true */ 76 | QP__FALSE, /* boolean false */ 77 | QP__NULL, /* null (none, nil) */ 78 | QP__ARRAY_OPEN, /* open a new array */ 79 | QP__MAP_OPEN, /* open a new map */ 80 | QP__ARRAY_CLOSE, /* close array */ 81 | QP__MAP_CLOSE, /* close map */ 82 | }; 83 | 84 | /* enums */ 85 | typedef enum qp_err_e 86 | { 87 | QP_ERR_WRITE_STREAM=-6, 88 | QP_ERR_NO_OPEN_ARRAY, 89 | QP_ERR_NO_OPEN_MAP, 90 | QP_ERR_MISSING_VALUE, 91 | QP_ERR_CORRUPT, 92 | QP_ERR_ALLOC, 93 | } qp_err_t; 94 | 95 | typedef enum qp_res_e 96 | { 97 | QP_RES_MAP, 98 | QP_RES_ARRAY, 99 | QP_RES_INT64, 100 | QP_RES_REAL, 101 | QP_RES_STR, 102 | QP_RES_BOOL, 103 | QP_RES_NULL, 104 | } qp_res_tp; 105 | 106 | typedef enum qp_types_e 107 | { 108 | QP_END=QP__END, 109 | QP_ERR, 110 | QP_RAW, 111 | QP_HOOK=QP__HOOK, 112 | QP_INT64=QP__INT64, /* 8 bytes signed integer */ 113 | QP_DOUBLE, /* 8 bytes double */ 114 | QP_ARRAY0, /* empty array */ 115 | QP_ARRAY1, /* array with 1 item */ 116 | QP_ARRAY2, /* array with 2 items */ 117 | QP_ARRAY3, /* array with 3 items */ 118 | QP_ARRAY4, /* array with 4 items */ 119 | QP_ARRAY5, /* array with 5 items */ 120 | QP_MAP0, /* empty map */ 121 | QP_MAP1, /* map with 1 item */ 122 | QP_MAP2, /* map with 2 items */ 123 | QP_MAP3, /* map with 3 items */ 124 | QP_MAP4, /* map with 4 items */ 125 | QP_MAP5, /* map with 5 items */ 126 | QP_TRUE, /* boolean true */ 127 | QP_FALSE, /* boolean false */ 128 | QP_NULL, /* null (none, nil) */ 129 | QP_ARRAY_OPEN, /* open a new array */ 130 | QP_MAP_OPEN, /* open a new map */ 131 | QP_ARRAY_CLOSE, /* close array */ 132 | QP_MAP_CLOSE, /* close map */ 133 | } qp_types_t; 134 | 135 | enum qp__nest_types 136 | { 137 | NEST_ARRAY, 138 | NEST_MAP 139 | }; 140 | 141 | union qp_via_u 142 | { 143 | int64_t int64; 144 | double real; 145 | const char * raw; 146 | }; 147 | 148 | struct qp_obj_s 149 | { 150 | uint8_t tp; 151 | size_t len; 152 | qp_via_t via; 153 | }; 154 | 155 | struct qp_map_s 156 | { 157 | size_t n; 158 | qp_res_t * keys; 159 | qp_res_t * values; 160 | }; 161 | 162 | struct qp_array_s 163 | { 164 | size_t n; 165 | qp_res_t * values; 166 | }; 167 | 168 | union qp_res_u 169 | { 170 | qp_map_t * map; 171 | qp_array_t * array; 172 | char * str; 173 | int64_t int64; 174 | double real; 175 | int boolean; 176 | void * null; 177 | }; 178 | 179 | struct qp_res_s 180 | { 181 | qp_res_tp tp; 182 | qp_res_via_t via; 183 | }; 184 | 185 | struct qp__nest_s 186 | { 187 | enum qp__nest_types tp; 188 | size_t n; 189 | size_t pos; 190 | }; 191 | 192 | struct qp_packer_s 193 | { 194 | unsigned char * buffer; 195 | size_t len; 196 | size_t buffer_size; 197 | size_t alloc_size; 198 | size_t depth; 199 | size_t nest_sz; 200 | struct qp__nest_s nesting[]; 201 | }; 202 | 203 | struct qp_unpacker_s 204 | { 205 | const unsigned char * start; 206 | const unsigned char * pt; 207 | const unsigned char * end; 208 | }; 209 | 210 | #ifdef __cplusplus 211 | extern "C" { 212 | #endif 213 | 214 | /* create and destroy functions */ 215 | qp_packer_t * qp_packer_create(size_t alloc_size); 216 | void qp_packer_destroy(qp_packer_t * packer); 217 | 218 | /* add to packer functions */ 219 | int qp_add_raw(qp_packer_t * packer, const char * raw, size_t len); 220 | int qp_add_int64(qp_packer_t * packer, int64_t i); 221 | int qp_add_double(qp_packer_t * packer, double d); 222 | int qp_add_true(qp_packer_t * packer); 223 | int qp_add_false(qp_packer_t * packer); 224 | int qp_add_null(qp_packer_t * packer); 225 | int qp_add_array(qp_packer_t ** packaddr); 226 | int qp_add_map(qp_packer_t ** packaddr); 227 | 228 | /* close array/map functions */ 229 | int qp_close_array(qp_packer_t * packer); 230 | int qp_close_map(qp_packer_t * packer); 231 | 232 | /* initialize unpacker */ 233 | void qp_unpacker_init( 234 | qp_unpacker_t * unpacker, 235 | const unsigned char * pt, 236 | size_t len); 237 | 238 | /* step over an unpacker */ 239 | qp_types_t qp_next(qp_unpacker_t * unpacker, qp_obj_t * qp_obj); 240 | 241 | /* unpack all */ 242 | qp_res_t * qp_unpacker_res(qp_unpacker_t * unpacker, int * rc); 243 | void qp_res_destroy(qp_res_t * res); 244 | int qp_res_fprint(qp_res_t * res, FILE * stream); 245 | 246 | /* test functions for qp_obj_t */ 247 | static inline int qp_is_array(qp_types_t tp) 248 | { 249 | return tp == QP_ARRAY_OPEN || (tp >= QP_ARRAY0 && tp <= QP_ARRAY5); 250 | } 251 | 252 | static inline int qp_is_map(qp_types_t tp) 253 | { 254 | return tp == QP_MAP_OPEN || (tp >= QP_MAP0 && tp <= QP_MAP5); 255 | } 256 | 257 | static inline int qp_is_close(qp_types_t tp) 258 | { 259 | return tp == QP_ARRAY_CLOSE || tp == QP_MAP_CLOSE; 260 | } 261 | 262 | static inline int qp_is_int(qp_types_t tp) 263 | { 264 | return tp == QP_INT64; 265 | } 266 | 267 | static inline int qp_is_double(qp_types_t tp) 268 | { 269 | return tp == QP_DOUBLE; 270 | } 271 | 272 | static inline int qp_is_bool(qp_types_t tp) 273 | { 274 | return tp == QP_TRUE || tp == QP_FALSE; 275 | } 276 | 277 | static inline int qp_is_null(qp_types_t tp) 278 | { 279 | return tp == QP_NULL; 280 | } 281 | 282 | static inline int qp_is_raw(qp_types_t tp) 283 | { 284 | return tp == QP_RAW; 285 | } 286 | 287 | static inline int qp_is_raw_term(qp_obj_t * qp_obj) 288 | { 289 | return (qp_obj->tp == QP_RAW && 290 | qp_obj->len && 291 | qp_obj->via.raw[qp_obj->len - 1] == '\0'); 292 | } 293 | int qp_raw_is_equal(qp_obj_t * obj, const char * str); 294 | 295 | /* print */ 296 | void qp_print(const unsigned char * data, size_t len); 297 | void qp_fprint(FILE * stream, const unsigned char * data, size_t len); 298 | char * qp_sprint(const unsigned char * data, size_t len); 299 | 300 | #define qp_packer_print(packer) \ 301 | qp_print(packer->buffer, packer->len) 302 | #define qp_packer_fprint(stream, packer) \ 303 | qp_fprint(stream, packer->buffer, packer->len) 304 | #define qp_packer_sprint(packer) \ 305 | qp_sprint(packer->buffer, packer->len) 306 | #define qp_unpacker_print(unpacker) \ 307 | qp_print(unpacker->start, unpacker->end - unpacker->start) 308 | #define qp_unpacker_fprint(stream, unpacker) \ 309 | qp_fprint(stream, unpacker->start, unpacker->end - unpacker->start) 310 | #define qp_unpacker_sprint(unpacker) \ 311 | qp_sprint(unpacker->start, unpacker->end - unpacker->start) 312 | 313 | /* misc functions */ 314 | const char * qp_strerror(int err_code); 315 | const char * qp_version(void); 316 | 317 | #ifdef __cplusplus 318 | } 319 | #endif 320 | 321 | #endif /* QPACK_H_ */ 322 | -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * main.c 3 | * 4 | * Created on: Mar 22, 2017 5 | * Author: joente 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main(void) 14 | { 15 | qp_packer_t * packer; 16 | qp_unpacker_t unpacker; 17 | qp_res_t * res = NULL; 18 | int rc; 19 | 20 | puts("Start QPack library test..."); 21 | 22 | packer = qp_packer_create(512); 23 | qp_add_map(&packer); 24 | qp_add_raw(packer, "Hi \"Qpack\"", strlen("Hi \"Qpack\"")); 25 | qp_add_array(&packer); 26 | qp_add_array(&packer); 27 | qp_add_array(&packer); 28 | qp_add_int64(packer, 9); 29 | qp_add_int64(packer, -79); 30 | qp_add_int64(packer, -1); 31 | qp_add_int64(packer, 123456789); 32 | qp_add_double(packer, 123.456); 33 | qp_close_array(packer); 34 | qp_close_array(packer); 35 | qp_close_array(packer); 36 | qp_close_map(packer); 37 | qp_packer_print(packer); 38 | 39 | char * tmp = qp_packer_sprint(packer); 40 | if (tmp != NULL) 41 | { 42 | printf("As string: %s\n", tmp); 43 | free(tmp); 44 | } 45 | 46 | qp_unpacker_init(&unpacker, packer->buffer, packer->len); 47 | 48 | res = qp_unpacker_res(&unpacker, &rc); 49 | 50 | if (rc == 0) 51 | { 52 | qp_res_fprint(res, stdout); 53 | qp_res_destroy(res); 54 | } 55 | 56 | qp_packer_destroy(packer); 57 | 58 | puts("\n\nFinished QPack library test!"); 59 | 60 | return 0; 61 | } 62 | --------------------------------------------------------------------------------