├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── bon ├── bon.c └── util.1 ├── bon2json ├── bon2json.1 └── bon2json.c ├── build └── BUILD.txt ├── examples ├── hello.bon ├── hello.c └── read.c ├── jansson ├── dump.c ├── error.c ├── hashtable.c ├── hashtable.h ├── jansson.h ├── jansson_config.h ├── jansson_private.h ├── load.c ├── memory.c ├── pack_unpack.c ├── strbuffer.c ├── strbuffer.h ├── strconv.c ├── utf.c ├── utf.h └── value.c ├── json2bon ├── json2bon.1 └── json2bon.c ├── libbon └── bon │ ├── bon.c │ ├── bon.h │ ├── crc32.c │ ├── crc32.h │ ├── inline.h │ ├── private.h │ ├── read.c │ ├── read_inline.h │ ├── type.c │ ├── write.c │ └── write_inline.h └── test ├── bench.cpp ├── catch.hpp ├── test.1 └── test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeFiles 2 | CMakeScripts 3 | *.a 4 | bin 5 | *.out 6 | examples/hello 7 | examples/read 8 | BON.build 9 | install_manifest.txt 10 | BON.xcodeproj 11 | test/*.bon 12 | test/test 13 | 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(BON) 3 | 4 | if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") 6 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++") 8 | else() 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") 10 | endif() 11 | endif() 12 | 13 | SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin) 14 | 15 | add_library(libbon 16 | libbon/bon/bon.c 17 | libbon/bon/bon.h 18 | libbon/bon/crc32.c 19 | libbon/bon/crc32.h 20 | libbon/bon/inline.h 21 | libbon/bon/private.h 22 | libbon/bon/read.c 23 | libbon/bon/read_inline.h 24 | libbon/bon/type.c 25 | libbon/bon/write.c 26 | libbon/bon/write_inline.h 27 | jansson/utf.c 28 | jansson/utf.h) 29 | SET_TARGET_PROPERTIES(libbon 30 | PROPERTIES OUTPUT_NAME bon) 31 | 32 | add_library(jansson STATIC 33 | jansson/dump.c 34 | jansson/error.c 35 | jansson/hashtable.c 36 | jansson/hashtable.h 37 | jansson/jansson_config.h 38 | jansson/jansson.h 39 | jansson/jansson_private.h 40 | jansson/load.c 41 | jansson/memory.c 42 | jansson/pack_unpack.c 43 | jansson/strbuffer.c 44 | jansson/strbuffer.h 45 | jansson/strconv.c 46 | jansson/utf.c 47 | jansson/utf.h 48 | jansson/value.c) 49 | 50 | include_directories(${BON_SOURCE_DIR}/libbon) 51 | include_directories(${BON_SOURCE_DIR}/jansson) 52 | 53 | add_executable(json2bon 54 | json2bon/json2bon.c) 55 | target_link_libraries(json2bon libbon jansson) 56 | 57 | add_executable(bon2json 58 | bon2json/bon2json.c) 59 | target_link_libraries(bon2json libbon jansson) 60 | 61 | add_executable(bon 62 | bon/bon.c) 63 | target_link_libraries(bon libbon) 64 | 65 | add_executable(test-core 66 | test/test.cpp) 67 | target_link_libraries(test-core libbon) 68 | 69 | add_test(core bin/test-core) 70 | 71 | install(TARGETS 72 | libbon 73 | json2bon 74 | bon2json 75 | bon 76 | RUNTIME DESTINATION bin 77 | LIBRARY DESTINATION lib 78 | ARCHIVE DESTINATION lib) 79 | 80 | install(FILES 81 | libbon/bon/bon.h 82 | libbon/bon/crc32.h 83 | libbon/bon/private.h 84 | libbon/bon/inline.h 85 | libbon/bon/read_inline.h 86 | libbon/bon/write_inline.h 87 | jansson/utf.h 88 | DESTINATION include/bon) 89 | 90 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | bon, bon2json and json2bon i free software under the MIT License (MIT). 2 | 3 | bon2json and json2bon uses a modified version of jansson, as JSON parser for C. jansson is also free software under the MIT License (MIT). 4 | 5 | 6 | * BON: Copyright (c) 2013 Emil Ernerfeldt . 7 | * jansson: Copyright (c) 2009-2012 Petri Lehtinen 8 | 9 | 10 | 11 | The MIT License (MIT) 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BON - Binary Object Notation 2 | ============================ 3 | BON is a fast, flexible, general purpose binary serialization format. 4 | 5 | ***Key features:*** 6 | 7 | * Fully JSON compatible (both ways) 8 | * Self-identifying four-byte header 9 | * Always Unicode 10 | * Supports streaming 11 | * Supports quick browsing of huge files 12 | * Optional CRC-32 for every file 13 | * Zero-overhead read and write of '[packed](https://github.com/emilk/bon/wiki/Packed-data)' data (e.g. an array of floats). In fact, this is [*10-50x*](https://github.com/emilk/bon/wiki/Packed-data) faster than MessagePack! 14 | 15 | This last point makes BON perfect for things that has lots of numeric data, like 3D models, sound, and scientific data. 16 | 17 | [Read more about the BON format here](https://github.com/emilk/bon/wiki/BON-format). 18 | 19 | ### What's wrong with MessagePack/BSON/protobuffers/...? 20 | They all fail in one or more of the key features listed above. 21 | 22 | ### I like it - can I help? 23 | Please do! The BON format is still work in progress, and so I'm looking for any and all feedback. 24 | 25 | The code is all open source (under the MIT license) and implementations in other languages is most welcome. 26 | 27 | # Licence 28 | The format and code is free (as in beer and speech) under the MIT license. 29 | 30 | # Building 31 | 32 | > git clone https://github.com/emilk/bon.git 33 | > cd bon/build 34 | > cmake .. && make && make install 35 | 36 | # Implementation 37 | I have written a C library implementation of BON which you can [read about here](https://github.com/emilk/bon/wiki/Lib). 38 | 39 | In addition, I have written [some tools](https://github.com/emilk/bon/wiki/bon2json-and-json2bon) for inspecting .bon files, as well as converting between BON and JSON. -------------------------------------------------------------------------------- /bon/bon.c: -------------------------------------------------------------------------------- 1 | // 2 | // util.c 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | // 9 | // Util app named 'bon' for inspecting bon files. 10 | 11 | #include 12 | #include // bon_stats 13 | #include 14 | #include 15 | #include 16 | #include // inf, nan etc 17 | #include 18 | 19 | typedef struct elem_t elem_t; 20 | 21 | #define MAX_PATH_COMP_LEN 32 22 | 23 | struct elem_t 24 | { 25 | bon_value* val; 26 | elem_t* prev; 27 | elem_t* next; 28 | 29 | char descr[MAX_PATH_COMP_LEN+1]; // "how did we get here?" 30 | }; 31 | 32 | 33 | // Globals: 34 | bon_r_doc* B = NULL; 35 | elem_t* g_stack_root = NULL; 36 | elem_t* g_stack_top = NULL; 37 | 38 | bon_value* top() { 39 | return g_stack_top->val; 40 | } 41 | 42 | void pwd() 43 | { 44 | elem_t* e = g_stack_root; 45 | while (e) { 46 | printf("%s/", e->descr); 47 | e = e->next; 48 | } 49 | printf("\n"); 50 | } 51 | 52 | 53 | bon_bool open_file(const char* path) { 54 | bon_size size; 55 | const uint8_t* data = bon_read_file(&size, path); 56 | if (!data) { 57 | fprintf(stderr, "Failed to read .bon file at %s\n", path); 58 | return BON_FALSE; 59 | } 60 | 61 | B = bon_r_open(data, size, BON_R_FLAG_DEFAULT); 62 | if (bon_r_error(B)) { 63 | fprintf(stderr, "Failed to parse .bon file at %s: %s\n", path, bon_r_err_str(B)); 64 | bon_r_close(B); 65 | return BON_FALSE; 66 | } 67 | 68 | g_stack_root = (elem_t*)calloc(1, sizeof(elem_t)); 69 | g_stack_root->val = bon_r_root(B); 70 | //strcpy(g_stack_root->descr, "/"); 71 | g_stack_top = g_stack_root; 72 | return BON_TRUE; 73 | } 74 | 75 | 76 | void print_commands() 77 | { 78 | printf( 79 | "cd key - enter an element in an object. 'key' can also be a list index.\n" 80 | "exit - exit bon\n" 81 | "help - prints this help\n" 82 | "ls - list members of current object\n" 83 | "stats - print statistics about the entire bon file\n" 84 | "pwd - print path of current object\n" 85 | "print key - full print of the value associated with the given key. 'key' can also be a list index.\\n" 86 | ); 87 | } 88 | 89 | 90 | void print_summary(bon_value* v) 91 | { 92 | switch (bon_r_value_type(B, v)) 93 | { 94 | case BON_LOGICAL_LIST: 95 | printf("[ ... ] (list with %d elements)", (int)bon_r_list_size(B, v)); 96 | break; 97 | 98 | case BON_LOGICAL_OBJECT: 99 | printf("{ ... } (object with %d keys)", (int)bon_r_obj_size(B, v)); 100 | break; 101 | 102 | default: 103 | bon_print(B, v, stdout, 0); 104 | break; 105 | } 106 | } 107 | 108 | 109 | void ls_obj(bon_value* obj) 110 | { 111 | size_t size = bon_r_obj_size(B, obj); 112 | 113 | const size_t KeyValDist = 32; 114 | printf("KEYS"); 115 | for (size_t si=0; sival; } 179 | 180 | bon_value* val = bon_r_get_key(B, dir, name); 181 | if (val) { return val; } 182 | 183 | // Try using as a number: 184 | if (all_int(name)) { 185 | bon_size ix = (bon_size)atoi(name); 186 | 187 | if (bon_r_is_object(B, dir)) { 188 | return bon_r_obj_value(B, dir, ix); 189 | } else { 190 | return bon_r_list_elem(B, dir, ix); 191 | } 192 | } 193 | 194 | return NULL; 195 | } 196 | 197 | void cd(const char* key) 198 | { 199 | if (strcmp(key, ".")==0) { 200 | return; 201 | } 202 | 203 | if (strcmp(key, "..")==0) { 204 | elem_t* oldTop = g_stack_top; 205 | g_stack_top = oldTop->prev; 206 | free(oldTop); 207 | g_stack_top->next = NULL; 208 | return; 209 | } 210 | 211 | bon_value* val = val_by_name_or_num(key); 212 | 213 | if (val) { 214 | //bon_value* old = top(); 215 | 216 | g_stack_top->next = (elem_t*)calloc(1, sizeof(elem_t)); 217 | g_stack_top->next->val = val; 218 | g_stack_top->next->prev = g_stack_top; 219 | g_stack_top = g_stack_top->next; 220 | 221 | size_t len = strlen(key); 222 | if (len > MAX_PATH_COMP_LEN) { 223 | len = MAX_PATH_COMP_LEN; 224 | } 225 | strncpy(g_stack_top->descr, key, len); 226 | g_stack_top->descr[len] = 0; 227 | } else { 228 | fprintf(stderr, "No such key or index \"%s\" (try ls)\n", key); 229 | } 230 | } 231 | 232 | void stats() 233 | { 234 | bon_stats* stats = &B->stats; 235 | printf("-----------------\n"); 236 | printf("%8d bytes in total\n", (int)stats->bytes_file); 237 | //printf("%8d bytes in %d strings (incl header)\n", (int)stats->bytes_string_wet, (int)stats->count_string); 238 | printf("%8d bytes in %d strings (excl header)\n", (int)stats->bytes_string_dry, (int)stats->count_string); 239 | //printf("%8d bytes in %d aggrs (incl header)\n", (int)stats->bytes_aggr_wet, (int)stats->count_aggr); 240 | printf("%8d bytes in %d packed format (excl header)\n", (int)stats->bytes_aggr_dry, (int)stats->count_aggr); 241 | printf("-----------------\n"); 242 | } 243 | 244 | 245 | void run() 246 | { 247 | char line[256]; 248 | 249 | //printf("> "); 250 | 251 | while (fgets(line, sizeof(line), stdin)) { 252 | bon_value* dir = top(); // current dir 253 | 254 | // Remove trailing endline: 255 | line[strlen(line) - 1] = 0; 256 | 257 | if (strlen(line)==0) { continue; } // Enter 258 | 259 | if (strcmp(line, "help")==0) { 260 | print_commands(); 261 | } else if (strcmp(line, "exit")==0) { 262 | break; 263 | } else if (strcmp(line, "ls")==0) { 264 | ls(dir); 265 | } else if (strcmp(line, "pwd")==0) { 266 | pwd(); 267 | } else if (strncmp(line, "print ", 6)==0) { 268 | const char* key = line+6; 269 | bon_value* val = val_by_name_or_num(key); 270 | 271 | if (val) { 272 | bon_print(B, val, stdout, 0); 273 | printf("\n"); 274 | } else { 275 | fprintf(stderr, "No such key or index \"%s\" (try ls)\n", key); 276 | } 277 | } else if (strncmp(line, "cd ", 3)==0) { 278 | const char* key = line+3; 279 | cd(key); 280 | } else if (strncmp(line, "stats", 6)==0) { 281 | stats(); 282 | } else { 283 | printf("Unknown commands (try typing \"help\")\n"); 284 | } 285 | 286 | //printf("> "); 287 | } 288 | } 289 | 290 | 291 | int main(int argc, const char * argv[]) 292 | { 293 | /* 294 | usage: 295 | 296 | bon foo.bon 297 | ls - list keys and what they map to (unless too long) 298 | cd - open a child object 299 | print key - output a value/list/object as json 300 | exit 301 | 302 | or: 303 | 304 | bon -json -block0 foo.bon 305 | 306 | cat | json2bon > bar.bon 307 | { "foo": 12 }^D 308 | bon bar.bon 309 | 310 | 311 | // Print root block as json (i.e. skip trailing, big blocks) 312 | 313 | */ 314 | 315 | for (int i=1; i 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | json_t* uint64_2_json(uint64_t u64) 15 | { 16 | if (u64 <= 0x7fffffffffffffffULL) { 17 | return json_integer( (int64_t)u64 ); 18 | } else { 19 | fprintf(stderr, "Uint64 too large for jansson - converting to double"); 20 | return json_real( (double)u64 ); 21 | } 22 | } 23 | 24 | json_t* bon2json(bon_r_doc* B, bon_value* v) 25 | { 26 | switch (bon_r_value_type(B, v)) 27 | { 28 | case BON_LOGICAL_NIL: 29 | return json_null(); 30 | 31 | case BON_LOGICAL_BOOL: 32 | return json_boolean( bon_r_bool(B, v) ); 33 | 34 | 35 | case BON_LOGICAL_UINT: 36 | return uint64_2_json( bon_r_uint(B, v) ); 37 | 38 | 39 | case BON_LOGICAL_SINT: 40 | return json_integer( bon_r_int(B, v) ); 41 | 42 | 43 | case BON_LOGICAL_DOUBLE: 44 | return json_real( bon_r_double(B, v) ); 45 | 46 | 47 | case BON_LOGICAL_STRING: 48 | return json_string( bon_r_cstr(B, v) ); 49 | 50 | 51 | case BON_LOGICAL_LIST: { 52 | json_t* jarray = json_array(); 53 | bon_size size = bon_r_list_size(B, v); 54 | for (bon_size ix=0; ix git clone https://github.com/emilk/bon.git 2 | > cd bon/build 3 | > cmake .. && make && make install 4 | 5 | To make an Xcode project: 6 | > cmake -G Xcode .. 7 | 8 | -------------------------------------------------------------------------------- /examples/hello.bon: -------------------------------------------------------------------------------- 1 | BON0{#msg,Hello world!}F -------------------------------------------------------------------------------- /examples/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | FILE* fp = fopen("hello.bon", "wb"); 6 | bon_w_doc* B = bon_w_new(&bon_file_writer, fp, BON_W_FLAG_DEFAULT ); 7 | 8 | bon_w_obj_begin(B); // The root object 9 | bon_w_key(B, "msg"); 10 | bon_w_cstring(B, "Hello world!"); 11 | bon_w_obj_end(B); 12 | 13 | bon_error err = bon_w_close( B ); 14 | if (err != BON_SUCCESS) { 15 | fprintf(stderr, "Failed to write to hello.bon: %s", bon_err_str(err)); 16 | } 17 | fclose( fp ); 18 | } 19 | -------------------------------------------------------------------------------- /examples/read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | int main(int argc, char* argv[]) { 7 | const char* path = (argc < 2 ? "hello.bon" : argv[1]); 8 | 9 | // Read file contents: 10 | size_t file_size; 11 | uint8_t* file_data = bon_read_file(&file_size, path); 12 | 13 | if (!file_data) { 14 | fprintf(stderr, "Failed to read %s\n", path); 15 | return 1; 16 | } 17 | 18 | bon_r_doc* B = bon_r_open(file_data, file_size, BON_R_FLAG_DEFAULT); 19 | 20 | if (bon_r_error(B) != BON_SUCCESS) { 21 | fprintf(stderr, "Failed to parse BON file: %s\n", bon_r_err_str(B)); 22 | bon_r_close(B); 23 | return 2; 24 | } 25 | 26 | // Get root object: 27 | bon_value* root = bon_r_root(B); 28 | 29 | // Retreive the key named 'msg': 30 | bon_value* msg = bon_r_get_key(B, root, "msg"); 31 | 32 | if (bon_r_is_string(B, msg)) { 33 | // Print it out: 34 | printf("%s\n", bon_r_cstr(B, msg)); 35 | } 36 | 37 | bon_r_close(B); 38 | free(file_data); // must be freed AFTER we close B! 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /jansson/dump.c: -------------------------------------------------------------------------------- 1 | #pragma clang diagnostic ignored "-Wconversion" 2 | 3 | /* 4 | * Copyright (c) 2009-2012 Petri Lehtinen 5 | * 6 | * Jansson is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See LICENSE for details. 8 | */ 9 | 10 | #define _GNU_SOURCE 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "jansson.h" 17 | #include "jansson_private.h" 18 | #include "strbuffer.h" 19 | #include "utf.h" 20 | 21 | #define MAX_INTEGER_STR_LENGTH 100 22 | #define MAX_REAL_STR_LENGTH 100 23 | 24 | struct object_key { 25 | size_t serial; 26 | const char *key; 27 | }; 28 | 29 | static int dump_to_strbuffer(const char *buffer, size_t size, void *data) 30 | { 31 | return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); 32 | } 33 | 34 | static int dump_to_file(const char *buffer, size_t size, void *data) 35 | { 36 | FILE *dest = (FILE *)data; 37 | if(fwrite(buffer, size, 1, dest) != 1) 38 | return -1; 39 | return 0; 40 | } 41 | 42 | /* 32 spaces (the maximum indentation size) */ 43 | static const char whitespace[] = " "; 44 | 45 | static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) 46 | { 47 | if(JSON_INDENT(flags) > 0) 48 | { 49 | int i, ws_count = JSON_INDENT(flags); 50 | 51 | if(dump("\n", 1, data)) 52 | return -1; 53 | 54 | for(i = 0; i < depth; i++) 55 | { 56 | if(dump(whitespace, ws_count, data)) 57 | return -1; 58 | } 59 | } 60 | else if(space && !(flags & JSON_COMPACT)) 61 | { 62 | return dump(" ", 1, data); 63 | } 64 | return 0; 65 | } 66 | 67 | static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags) 68 | { 69 | const char *pos, *end; 70 | int32_t codepoint; 71 | 72 | if(dump("\"", 1, data)) 73 | return -1; 74 | 75 | end = pos = str; 76 | while(1) 77 | { 78 | const char *text; 79 | char seq[13]; 80 | int length; 81 | 82 | while(*end) 83 | { 84 | end = utf8_iterate(pos, &codepoint); 85 | if(!end) 86 | return -1; 87 | 88 | /* mandatory escape or control char */ 89 | if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20) 90 | break; 91 | 92 | /* slash */ 93 | if((flags & JSON_ESCAPE_SLASH) && codepoint == '/') 94 | break; 95 | 96 | /* non-ASCII */ 97 | if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) 98 | break; 99 | 100 | pos = end; 101 | } 102 | 103 | if(pos != str) { 104 | if(dump(str, pos - str, data)) 105 | return -1; 106 | } 107 | 108 | if(end == pos) 109 | break; 110 | 111 | /* handle \, /, ", and control codes */ 112 | length = 2; 113 | switch(codepoint) 114 | { 115 | case '\\': text = "\\\\"; break; 116 | case '\"': text = "\\\""; break; 117 | case '\b': text = "\\b"; break; 118 | case '\f': text = "\\f"; break; 119 | case '\n': text = "\\n"; break; 120 | case '\r': text = "\\r"; break; 121 | case '\t': text = "\\t"; break; 122 | case '/': text = "\\/"; break; 123 | default: 124 | { 125 | /* codepoint is in BMP */ 126 | if(codepoint < 0x10000) 127 | { 128 | sprintf(seq, "\\u%04x", codepoint); 129 | length = 6; 130 | } 131 | 132 | /* not in BMP -> construct a UTF-16 surrogate pair */ 133 | else 134 | { 135 | int32_t first, last; 136 | 137 | codepoint -= 0x10000; 138 | first = 0xD800 | ((codepoint & 0xffc00) >> 10); 139 | last = 0xDC00 | (codepoint & 0x003ff); 140 | 141 | sprintf(seq, "\\u%04x\\u%04x", first, last); 142 | length = 12; 143 | } 144 | 145 | text = seq; 146 | break; 147 | } 148 | } 149 | 150 | if(dump(text, length, data)) 151 | return -1; 152 | 153 | str = pos = end; 154 | } 155 | 156 | return dump("\"", 1, data); 157 | } 158 | 159 | static int object_key_compare_keys(const void *key1, const void *key2) 160 | { 161 | return strcmp(((const struct object_key *)key1)->key, 162 | ((const struct object_key *)key2)->key); 163 | } 164 | 165 | static int object_key_compare_serials(const void *key1, const void *key2) 166 | { 167 | size_t a = ((const struct object_key *)key1)->serial; 168 | size_t b = ((const struct object_key *)key2)->serial; 169 | 170 | return a < b ? -1 : a == b ? 0 : 1; 171 | } 172 | 173 | static int do_dump(const json_t *json, size_t flags, int depth, 174 | json_dump_callback_t dump, void *data) 175 | { 176 | switch(json_typeof(json)) { 177 | case JSON_NULL: 178 | return dump("null", 4, data); 179 | 180 | case JSON_TRUE: 181 | return dump("true", 4, data); 182 | 183 | case JSON_FALSE: 184 | return dump("false", 5, data); 185 | 186 | case JSON_INTEGER: 187 | { 188 | char buffer[MAX_INTEGER_STR_LENGTH]; 189 | int size; 190 | 191 | size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, 192 | "%" JSON_INTEGER_FORMAT, 193 | json_integer_value(json)); 194 | if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) 195 | return -1; 196 | 197 | return dump(buffer, size, data); 198 | } 199 | 200 | case JSON_REAL: 201 | { 202 | char buffer[MAX_REAL_STR_LENGTH]; 203 | int size; 204 | double value = json_real_value(json); 205 | 206 | size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value); 207 | if(size < 0) 208 | return -1; 209 | 210 | return dump(buffer, size, data); 211 | } 212 | 213 | case JSON_STRING: 214 | return dump_string(json_string_value(json), dump, data, flags); 215 | 216 | case JSON_ARRAY: 217 | { 218 | int i; 219 | int n; 220 | json_array_t *array; 221 | 222 | /* detect circular references */ 223 | array = json_to_array(json); 224 | if(array->visited) 225 | goto array_error; 226 | array->visited = 1; 227 | 228 | n = json_array_size(json); 229 | 230 | if(dump("[", 1, data)) 231 | goto array_error; 232 | if(n == 0) { 233 | array->visited = 0; 234 | return dump("]", 1, data); 235 | } 236 | if(dump_indent(flags, depth + 1, 0, dump, data)) 237 | goto array_error; 238 | 239 | for(i = 0; i < n; ++i) { 240 | if(do_dump(json_array_get(json, i), flags, depth + 1, 241 | dump, data)) 242 | goto array_error; 243 | 244 | if(i < n - 1) 245 | { 246 | if(dump(",", 1, data) || 247 | dump_indent(flags, depth + 1, 1, dump, data)) 248 | goto array_error; 249 | } 250 | else 251 | { 252 | if(dump_indent(flags, depth, 0, dump, data)) 253 | goto array_error; 254 | } 255 | } 256 | 257 | array->visited = 0; 258 | return dump("]", 1, data); 259 | 260 | array_error: 261 | array->visited = 0; 262 | return -1; 263 | } 264 | 265 | case JSON_OBJECT: 266 | { 267 | json_object_t *object; 268 | void *iter; 269 | const char *separator; 270 | int separator_length; 271 | 272 | if(flags & JSON_COMPACT) { 273 | separator = ":"; 274 | separator_length = 1; 275 | } 276 | else { 277 | separator = ": "; 278 | separator_length = 2; 279 | } 280 | 281 | /* detect circular references */ 282 | object = json_to_object(json); 283 | if(object->visited) 284 | goto object_error; 285 | object->visited = 1; 286 | 287 | iter = json_object_iter((json_t *)json); 288 | 289 | if(dump("{", 1, data)) 290 | goto object_error; 291 | if(!iter) { 292 | object->visited = 0; 293 | return dump("}", 1, data); 294 | } 295 | if(dump_indent(flags, depth + 1, 0, dump, data)) 296 | goto object_error; 297 | 298 | if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER) 299 | { 300 | struct object_key *keys; 301 | size_t size, i; 302 | int (*cmp_func)(const void *, const void *); 303 | 304 | size = json_object_size(json); 305 | keys = jsonp_malloc(size * sizeof(struct object_key)); 306 | if(!keys) 307 | goto object_error; 308 | 309 | i = 0; 310 | while(iter) 311 | { 312 | keys[i].serial = hashtable_iter_serial(iter); 313 | keys[i].key = json_object_iter_key(iter); 314 | iter = json_object_iter_next((json_t *)json, iter); 315 | i++; 316 | } 317 | assert(i == size); 318 | 319 | if(flags & JSON_SORT_KEYS) 320 | cmp_func = object_key_compare_keys; 321 | else 322 | cmp_func = object_key_compare_serials; 323 | 324 | qsort(keys, size, sizeof(struct object_key), cmp_func); 325 | 326 | for(i = 0; i < size; i++) 327 | { 328 | const char *key; 329 | json_t *value; 330 | 331 | key = keys[i].key; 332 | value = json_object_get(json, key); 333 | assert(value); 334 | 335 | dump_string(key, dump, data, flags); 336 | if(dump(separator, separator_length, data) || 337 | do_dump(value, flags, depth + 1, dump, data)) 338 | { 339 | jsonp_free(keys); 340 | goto object_error; 341 | } 342 | 343 | if(i < size - 1) 344 | { 345 | if(dump(",", 1, data) || 346 | dump_indent(flags, depth + 1, 1, dump, data)) 347 | { 348 | jsonp_free(keys); 349 | goto object_error; 350 | } 351 | } 352 | else 353 | { 354 | if(dump_indent(flags, depth, 0, dump, data)) 355 | { 356 | jsonp_free(keys); 357 | goto object_error; 358 | } 359 | } 360 | } 361 | 362 | jsonp_free(keys); 363 | } 364 | else 365 | { 366 | /* Don't sort keys */ 367 | 368 | while(iter) 369 | { 370 | void *next = json_object_iter_next((json_t *)json, iter); 371 | 372 | dump_string(json_object_iter_key(iter), dump, data, flags); 373 | if(dump(separator, separator_length, data) || 374 | do_dump(json_object_iter_value(iter), flags, depth + 1, 375 | dump, data)) 376 | goto object_error; 377 | 378 | if(next) 379 | { 380 | if(dump(",", 1, data) || 381 | dump_indent(flags, depth + 1, 1, dump, data)) 382 | goto object_error; 383 | } 384 | else 385 | { 386 | if(dump_indent(flags, depth, 0, dump, data)) 387 | goto object_error; 388 | } 389 | 390 | iter = next; 391 | } 392 | } 393 | 394 | object->visited = 0; 395 | return dump("}", 1, data); 396 | 397 | object_error: 398 | object->visited = 0; 399 | return -1; 400 | } 401 | 402 | default: 403 | /* not reached */ 404 | return -1; 405 | } 406 | } 407 | 408 | char *json_dumps(const json_t *json, size_t flags) 409 | { 410 | strbuffer_t strbuff; 411 | char *result; 412 | 413 | if(strbuffer_init(&strbuff)) 414 | return NULL; 415 | 416 | if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) 417 | result = NULL; 418 | else 419 | result = jsonp_strdup(strbuffer_value(&strbuff)); 420 | 421 | strbuffer_close(&strbuff); 422 | return result; 423 | } 424 | 425 | int json_dumpf(const json_t *json, FILE *output, size_t flags) 426 | { 427 | return json_dump_callback(json, dump_to_file, (void *)output, flags); 428 | } 429 | 430 | int json_dump_file(const json_t *json, const char *path, size_t flags) 431 | { 432 | int result; 433 | 434 | FILE *output = fopen(path, "w"); 435 | if(!output) 436 | return -1; 437 | 438 | result = json_dumpf(json, output, flags); 439 | 440 | fclose(output); 441 | return result; 442 | } 443 | 444 | int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) 445 | { 446 | if(!(flags & JSON_ENCODE_ANY)) { 447 | if(!json_is_array(json) && !json_is_object(json)) 448 | return -1; 449 | } 450 | 451 | return do_dump(json, flags, 0, callback, data); 452 | } 453 | -------------------------------------------------------------------------------- /jansson/error.c: -------------------------------------------------------------------------------- 1 | #pragma clang diagnostic ignored "-Wconversion" 2 | 3 | #include 4 | #include "jansson_private.h" 5 | 6 | void jsonp_error_init(json_error_t *error, const char *source) 7 | { 8 | if(error) 9 | { 10 | error->text[0] = '\0'; 11 | error->line = -1; 12 | error->column = -1; 13 | error->position = 0; 14 | if(source) 15 | jsonp_error_set_source(error, source); 16 | else 17 | error->source[0] = '\0'; 18 | } 19 | } 20 | 21 | void jsonp_error_set_source(json_error_t *error, const char *source) 22 | { 23 | size_t length; 24 | 25 | if(!error || !source) 26 | return; 27 | 28 | length = strlen(source); 29 | if(length < JSON_ERROR_SOURCE_LENGTH) 30 | strcpy(error->source, source); 31 | else { 32 | size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; 33 | strcpy(error->source, "..."); 34 | strcpy(error->source + 3, source + extra); 35 | } 36 | } 37 | 38 | void jsonp_error_set(json_error_t *error, int line, int column, 39 | size_t position, const char *msg, ...) 40 | { 41 | va_list ap; 42 | 43 | va_start(ap, msg); 44 | jsonp_error_vset(error, line, column, position, msg, ap); 45 | va_end(ap); 46 | } 47 | 48 | void jsonp_error_vset(json_error_t *error, int line, int column, 49 | size_t position, const char *msg, va_list ap) 50 | { 51 | if(!error) 52 | return; 53 | 54 | if(error->text[0] != '\0') { 55 | /* error already set */ 56 | return; 57 | } 58 | 59 | error->line = line; 60 | error->column = column; 61 | error->position = position; 62 | 63 | vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap); 64 | error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; 65 | } 66 | -------------------------------------------------------------------------------- /jansson/hashtable.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * 4 | * This library is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #include 9 | #include 10 | #include "jansson_config.h" /* for JSON_INLINE */ 11 | #include "jansson_private.h" /* for container_of() */ 12 | #include "hashtable.h" 13 | 14 | typedef struct hashtable_list list_t; 15 | typedef struct hashtable_pair pair_t; 16 | typedef struct hashtable_bucket bucket_t; 17 | 18 | #define list_to_pair(list_) container_of(list_, pair_t, list) 19 | 20 | /* From http://www.cse.yorku.ca/~oz/hash.html */ 21 | static size_t hash_str(const void *ptr) 22 | { 23 | const char *str = (const char *)ptr; 24 | 25 | size_t hash = 5381; 26 | size_t c; 27 | 28 | while((c = (size_t)*str)) 29 | { 30 | hash = ((hash << 5) + hash) + c; 31 | str++; 32 | } 33 | 34 | return hash; 35 | } 36 | 37 | static JSON_INLINE void list_init(list_t *list) 38 | { 39 | list->next = list; 40 | list->prev = list; 41 | } 42 | 43 | static JSON_INLINE void list_insert(list_t *list, list_t *node) 44 | { 45 | node->next = list; 46 | node->prev = list->prev; 47 | list->prev->next = node; 48 | list->prev = node; 49 | } 50 | 51 | static JSON_INLINE void list_remove(list_t *list) 52 | { 53 | list->prev->next = list->next; 54 | list->next->prev = list->prev; 55 | } 56 | 57 | static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket) 58 | { 59 | return bucket->first == &hashtable->list && bucket->first == bucket->last; 60 | } 61 | 62 | static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, 63 | list_t *list) 64 | { 65 | if(bucket_is_empty(hashtable, bucket)) 66 | { 67 | list_insert(&hashtable->list, list); 68 | bucket->first = bucket->last = list; 69 | } 70 | else 71 | { 72 | list_insert(bucket->first, list); 73 | bucket->first = list; 74 | } 75 | } 76 | 77 | static const size_t primes[] = { 78 | 5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 79 | 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 80 | 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 81 | 805306457, 1610612741 82 | }; 83 | 84 | static JSON_INLINE size_t num_buckets(hashtable_t *hashtable) 85 | { 86 | return primes[hashtable->num_buckets]; 87 | } 88 | 89 | 90 | static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, 91 | const char *key, size_t hash) 92 | { 93 | list_t *list; 94 | pair_t *pair; 95 | 96 | if(bucket_is_empty(hashtable, bucket)) 97 | return NULL; 98 | 99 | list = bucket->first; 100 | while(1) 101 | { 102 | pair = list_to_pair(list); 103 | if(pair->hash == hash && strcmp(pair->key, key) == 0) 104 | return pair; 105 | 106 | if(list == bucket->last) 107 | break; 108 | 109 | list = list->next; 110 | } 111 | 112 | return NULL; 113 | } 114 | 115 | /* returns 0 on success, -1 if key was not found */ 116 | static int hashtable_do_del(hashtable_t *hashtable, 117 | const char *key, size_t hash) 118 | { 119 | pair_t *pair; 120 | bucket_t *bucket; 121 | size_t index; 122 | 123 | index = hash % num_buckets(hashtable); 124 | bucket = &hashtable->buckets[index]; 125 | 126 | pair = hashtable_find_pair(hashtable, bucket, key, hash); 127 | if(!pair) 128 | return -1; 129 | 130 | if(&pair->list == bucket->first && &pair->list == bucket->last) 131 | bucket->first = bucket->last = &hashtable->list; 132 | 133 | else if(&pair->list == bucket->first) 134 | bucket->first = pair->list.next; 135 | 136 | else if(&pair->list == bucket->last) 137 | bucket->last = pair->list.prev; 138 | 139 | list_remove(&pair->list); 140 | json_decref(pair->value); 141 | 142 | jsonp_free(pair); 143 | hashtable->size--; 144 | 145 | return 0; 146 | } 147 | 148 | static void hashtable_do_clear(hashtable_t *hashtable) 149 | { 150 | list_t *list, *next; 151 | pair_t *pair; 152 | 153 | for(list = hashtable->list.next; list != &hashtable->list; list = next) 154 | { 155 | next = list->next; 156 | pair = list_to_pair(list); 157 | json_decref(pair->value); 158 | jsonp_free(pair); 159 | } 160 | } 161 | 162 | static int hashtable_do_rehash(hashtable_t *hashtable) 163 | { 164 | list_t *list, *next; 165 | pair_t *pair; 166 | size_t i, index, new_size; 167 | 168 | jsonp_free(hashtable->buckets); 169 | 170 | hashtable->num_buckets++; 171 | new_size = num_buckets(hashtable); 172 | 173 | hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t)); 174 | if(!hashtable->buckets) 175 | return -1; 176 | 177 | for(i = 0; i < num_buckets(hashtable); i++) 178 | { 179 | hashtable->buckets[i].first = hashtable->buckets[i].last = 180 | &hashtable->list; 181 | } 182 | 183 | list = hashtable->list.next; 184 | list_init(&hashtable->list); 185 | 186 | for(; list != &hashtable->list; list = next) { 187 | next = list->next; 188 | pair = list_to_pair(list); 189 | index = pair->hash % new_size; 190 | insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list); 191 | } 192 | 193 | return 0; 194 | } 195 | 196 | 197 | int hashtable_init(hashtable_t *hashtable) 198 | { 199 | size_t i; 200 | 201 | hashtable->size = 0; 202 | hashtable->num_buckets = 0; /* index to primes[] */ 203 | hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t)); 204 | if(!hashtable->buckets) 205 | return -1; 206 | 207 | list_init(&hashtable->list); 208 | 209 | for(i = 0; i < num_buckets(hashtable); i++) 210 | { 211 | hashtable->buckets[i].first = hashtable->buckets[i].last = 212 | &hashtable->list; 213 | } 214 | 215 | return 0; 216 | } 217 | 218 | void hashtable_close(hashtable_t *hashtable) 219 | { 220 | hashtable_do_clear(hashtable); 221 | jsonp_free(hashtable->buckets); 222 | } 223 | 224 | int hashtable_set(hashtable_t *hashtable, 225 | const char *key, size_t serial, 226 | json_t *value) 227 | { 228 | pair_t *pair; 229 | bucket_t *bucket; 230 | size_t hash, index; 231 | 232 | /* rehash if the load ratio exceeds 1 */ 233 | if(hashtable->size >= num_buckets(hashtable)) 234 | if(hashtable_do_rehash(hashtable)) 235 | return -1; 236 | 237 | hash = hash_str(key); 238 | index = hash % num_buckets(hashtable); 239 | bucket = &hashtable->buckets[index]; 240 | pair = hashtable_find_pair(hashtable, bucket, key, hash); 241 | 242 | if(pair) 243 | { 244 | json_decref(pair->value); 245 | pair->value = value; 246 | } 247 | else 248 | { 249 | /* offsetof(...) returns the size of pair_t without the last, 250 | flexible member. This way, the correct amount is 251 | allocated. */ 252 | pair = jsonp_malloc(offsetof(pair_t, key) + strlen(key) + 1); 253 | if(!pair) 254 | return -1; 255 | 256 | pair->hash = hash; 257 | pair->serial = serial; 258 | strcpy(pair->key, key); 259 | pair->value = value; 260 | list_init(&pair->list); 261 | 262 | insert_to_bucket(hashtable, bucket, &pair->list); 263 | 264 | hashtable->size++; 265 | } 266 | return 0; 267 | } 268 | 269 | void *hashtable_get(hashtable_t *hashtable, const char *key) 270 | { 271 | pair_t *pair; 272 | size_t hash; 273 | bucket_t *bucket; 274 | 275 | hash = hash_str(key); 276 | bucket = &hashtable->buckets[hash % num_buckets(hashtable)]; 277 | 278 | pair = hashtable_find_pair(hashtable, bucket, key, hash); 279 | if(!pair) 280 | return NULL; 281 | 282 | return pair->value; 283 | } 284 | 285 | int hashtable_del(hashtable_t *hashtable, const char *key) 286 | { 287 | size_t hash = hash_str(key); 288 | return hashtable_do_del(hashtable, key, hash); 289 | } 290 | 291 | void hashtable_clear(hashtable_t *hashtable) 292 | { 293 | size_t i; 294 | 295 | hashtable_do_clear(hashtable); 296 | 297 | for(i = 0; i < num_buckets(hashtable); i++) 298 | { 299 | hashtable->buckets[i].first = hashtable->buckets[i].last = 300 | &hashtable->list; 301 | } 302 | 303 | list_init(&hashtable->list); 304 | hashtable->size = 0; 305 | } 306 | 307 | void *hashtable_iter(hashtable_t *hashtable) 308 | { 309 | return hashtable_iter_next(hashtable, &hashtable->list); 310 | } 311 | 312 | void *hashtable_iter_at(hashtable_t *hashtable, const char *key) 313 | { 314 | pair_t *pair; 315 | size_t hash; 316 | bucket_t *bucket; 317 | 318 | hash = hash_str(key); 319 | bucket = &hashtable->buckets[hash % num_buckets(hashtable)]; 320 | 321 | pair = hashtable_find_pair(hashtable, bucket, key, hash); 322 | if(!pair) 323 | return NULL; 324 | 325 | return &pair->list; 326 | } 327 | 328 | void *hashtable_iter_next(hashtable_t *hashtable, void *iter) 329 | { 330 | list_t *list = (list_t *)iter; 331 | if(list->next == &hashtable->list) 332 | return NULL; 333 | return list->next; 334 | } 335 | 336 | void *hashtable_iter_key(void *iter) 337 | { 338 | pair_t *pair = list_to_pair((list_t *)iter); 339 | return pair->key; 340 | } 341 | 342 | size_t hashtable_iter_serial(void *iter) 343 | { 344 | pair_t *pair = list_to_pair((list_t *)iter); 345 | return pair->serial; 346 | } 347 | 348 | void *hashtable_iter_value(void *iter) 349 | { 350 | pair_t *pair = list_to_pair((list_t *)iter); 351 | return pair->value; 352 | } 353 | 354 | void hashtable_iter_set(void *iter, json_t *value) 355 | { 356 | pair_t *pair = list_to_pair((list_t *)iter); 357 | 358 | json_decref(pair->value); 359 | pair->value = value; 360 | } 361 | -------------------------------------------------------------------------------- /jansson/hashtable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * 4 | * This library is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #ifndef HASHTABLE_H 9 | #define HASHTABLE_H 10 | 11 | struct hashtable_list { 12 | struct hashtable_list *prev; 13 | struct hashtable_list *next; 14 | }; 15 | 16 | /* "pair" may be a bit confusing a name, but think of it as a 17 | key-value pair. In this case, it just encodes some extra data, 18 | too */ 19 | struct hashtable_pair { 20 | size_t hash; 21 | struct hashtable_list list; 22 | json_t *value; 23 | size_t serial; 24 | char key[1]; 25 | }; 26 | 27 | struct hashtable_bucket { 28 | struct hashtable_list *first; 29 | struct hashtable_list *last; 30 | }; 31 | 32 | typedef struct hashtable { 33 | size_t size; 34 | struct hashtable_bucket *buckets; 35 | size_t num_buckets; /* index to primes[] */ 36 | struct hashtable_list list; 37 | } hashtable_t; 38 | 39 | 40 | #define hashtable_key_to_iter(key_) \ 41 | (&(container_of(key_, struct hashtable_pair, key)->list)) 42 | 43 | /** 44 | * hashtable_init - Initialize a hashtable object 45 | * 46 | * @hashtable: The (statically allocated) hashtable object 47 | * 48 | * Initializes a statically allocated hashtable object. The object 49 | * should be cleared with hashtable_close when it's no longer used. 50 | * 51 | * Returns 0 on success, -1 on error (out of memory). 52 | */ 53 | int hashtable_init(hashtable_t *hashtable); 54 | 55 | /** 56 | * hashtable_close - Release all resources used by a hashtable object 57 | * 58 | * @hashtable: The hashtable 59 | * 60 | * Destroys a statically allocated hashtable object. 61 | */ 62 | void hashtable_close(hashtable_t *hashtable); 63 | 64 | /** 65 | * hashtable_set - Add/modify value in hashtable 66 | * 67 | * @hashtable: The hashtable object 68 | * @key: The key 69 | * @serial: For addition order of keys 70 | * @value: The value 71 | * 72 | * If a value with the given key already exists, its value is replaced 73 | * with the new value. Value is "stealed" in the sense that hashtable 74 | * doesn't increment its refcount but decreases the refcount when the 75 | * value is no longer needed. 76 | * 77 | * Returns 0 on success, -1 on failure (out of memory). 78 | */ 79 | int hashtable_set(hashtable_t *hashtable, 80 | const char *key, size_t serial, 81 | json_t *value); 82 | 83 | /** 84 | * hashtable_get - Get a value associated with a key 85 | * 86 | * @hashtable: The hashtable object 87 | * @key: The key 88 | * 89 | * Returns value if it is found, or NULL otherwise. 90 | */ 91 | void *hashtable_get(hashtable_t *hashtable, const char *key); 92 | 93 | /** 94 | * hashtable_del - Remove a value from the hashtable 95 | * 96 | * @hashtable: The hashtable object 97 | * @key: The key 98 | * 99 | * Returns 0 on success, or -1 if the key was not found. 100 | */ 101 | int hashtable_del(hashtable_t *hashtable, const char *key); 102 | 103 | /** 104 | * hashtable_clear - Clear hashtable 105 | * 106 | * @hashtable: The hashtable object 107 | * 108 | * Removes all items from the hashtable. 109 | */ 110 | void hashtable_clear(hashtable_t *hashtable); 111 | 112 | /** 113 | * hashtable_iter - Iterate over hashtable 114 | * 115 | * @hashtable: The hashtable object 116 | * 117 | * Returns an opaque iterator to the first element in the hashtable. 118 | * The iterator should be passed to hashtable_iter_* functions. 119 | * The hashtable items are not iterated over in any particular order. 120 | * 121 | * There's no need to free the iterator in any way. The iterator is 122 | * valid as long as the item that is referenced by the iterator is not 123 | * deleted. Other values may be added or deleted. In particular, 124 | * hashtable_iter_next() may be called on an iterator, and after that 125 | * the key/value pair pointed by the old iterator may be deleted. 126 | */ 127 | void *hashtable_iter(hashtable_t *hashtable); 128 | 129 | /** 130 | * hashtable_iter_at - Return an iterator at a specific key 131 | * 132 | * @hashtable: The hashtable object 133 | * @key: The key that the iterator should point to 134 | * 135 | * Like hashtable_iter() but returns an iterator pointing to a 136 | * specific key. 137 | */ 138 | void *hashtable_iter_at(hashtable_t *hashtable, const char *key); 139 | 140 | /** 141 | * hashtable_iter_next - Advance an iterator 142 | * 143 | * @hashtable: The hashtable object 144 | * @iter: The iterator 145 | * 146 | * Returns a new iterator pointing to the next element in the 147 | * hashtable or NULL if the whole hastable has been iterated over. 148 | */ 149 | void *hashtable_iter_next(hashtable_t *hashtable, void *iter); 150 | 151 | /** 152 | * hashtable_iter_key - Retrieve the key pointed by an iterator 153 | * 154 | * @iter: The iterator 155 | */ 156 | void *hashtable_iter_key(void *iter); 157 | 158 | /** 159 | * hashtable_iter_serial - Retrieve the serial number pointed to by an iterator 160 | * 161 | * @iter: The iterator 162 | */ 163 | size_t hashtable_iter_serial(void *iter); 164 | 165 | /** 166 | * hashtable_iter_value - Retrieve the value pointed by an iterator 167 | * 168 | * @iter: The iterator 169 | */ 170 | void *hashtable_iter_value(void *iter); 171 | 172 | /** 173 | * hashtable_iter_set - Set the value pointed by an iterator 174 | * 175 | * @iter: The iterator 176 | * @value: The value to set 177 | */ 178 | void hashtable_iter_set(void *iter, json_t *value); 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /jansson/jansson.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * 4 | * Jansson is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #ifndef JANSSON_H 9 | #define JANSSON_H 10 | 11 | #include 12 | #include /* for size_t */ 13 | #include 14 | 15 | #include "jansson_config.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | /* version */ 22 | 23 | #define JANSSON_MAJOR_VERSION 2 24 | #define JANSSON_MINOR_VERSION 4 25 | #define JANSSON_MICRO_VERSION 99 26 | 27 | /* Micro version is omitted if it's 0 */ 28 | #define JANSSON_VERSION "2.5-dev" 29 | 30 | /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this 31 | for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ 32 | #define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \ 33 | (JANSSON_MINOR_VERSION << 8) | \ 34 | (JANSSON_MICRO_VERSION << 0)) 35 | 36 | 37 | /* types */ 38 | 39 | typedef enum { 40 | JSON_OBJECT, 41 | JSON_ARRAY, 42 | JSON_STRING, 43 | JSON_INTEGER, 44 | JSON_REAL, 45 | JSON_TRUE, 46 | JSON_FALSE, 47 | JSON_NULL 48 | } json_type; 49 | 50 | typedef struct json_t { 51 | json_type type; 52 | size_t refcount; 53 | } json_t; 54 | 55 | #if JSON_INTEGER_IS_LONG_LONG 56 | #ifdef _WIN32 57 | #define JSON_INTEGER_FORMAT "I64d" 58 | #else 59 | #define JSON_INTEGER_FORMAT "lld" 60 | #endif 61 | typedef long long json_int_t; 62 | #else 63 | #define JSON_INTEGER_FORMAT "ld" 64 | typedef long json_int_t; 65 | #endif /* JSON_INTEGER_IS_LONG_LONG */ 66 | 67 | #define json_typeof(json) ((json)->type) 68 | #define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT) 69 | #define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY) 70 | #define json_is_string(json) (json && json_typeof(json) == JSON_STRING) 71 | #define json_is_integer(json) (json && json_typeof(json) == JSON_INTEGER) 72 | #define json_is_real(json) (json && json_typeof(json) == JSON_REAL) 73 | #define json_is_number(json) (json_is_integer(json) || json_is_real(json)) 74 | #define json_is_true(json) (json && json_typeof(json) == JSON_TRUE) 75 | #define json_is_false(json) (json && json_typeof(json) == JSON_FALSE) 76 | #define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) 77 | #define json_is_null(json) (json && json_typeof(json) == JSON_NULL) 78 | 79 | /* construction, destruction, reference counting */ 80 | 81 | json_t *json_object(void); 82 | json_t *json_array(void); 83 | json_t *json_string(const char *value); 84 | json_t *json_string_nocheck(const char *value); 85 | json_t *json_integer(json_int_t value); 86 | json_t *json_real(double value); 87 | json_t *json_true(void); 88 | json_t *json_false(void); 89 | #define json_boolean(val) ((val) ? json_true() : json_false()) 90 | json_t *json_null(void); 91 | 92 | static JSON_INLINE 93 | json_t *json_incref(json_t *json) 94 | { 95 | if(json && json->refcount != (size_t)-1) 96 | ++json->refcount; 97 | return json; 98 | } 99 | 100 | /* do not call json_delete directly */ 101 | void json_delete(json_t *json); 102 | 103 | static JSON_INLINE 104 | void json_decref(json_t *json) 105 | { 106 | if(json && json->refcount != (size_t)-1 && --json->refcount == 0) 107 | json_delete(json); 108 | } 109 | 110 | 111 | /* error reporting */ 112 | 113 | #define JSON_ERROR_TEXT_LENGTH 160 114 | #define JSON_ERROR_SOURCE_LENGTH 80 115 | 116 | typedef struct { 117 | int line; 118 | int column; 119 | int position; 120 | char source[JSON_ERROR_SOURCE_LENGTH]; 121 | char text[JSON_ERROR_TEXT_LENGTH]; 122 | } json_error_t; 123 | 124 | 125 | /* getters, setters, manipulation */ 126 | 127 | size_t json_object_size(const json_t *object); 128 | json_t *json_object_get(const json_t *object, const char *key); 129 | int json_object_set_new(json_t *object, const char *key, json_t *value); 130 | int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); 131 | int json_object_del(json_t *object, const char *key); 132 | int json_object_clear(json_t *object); 133 | int json_object_update(json_t *object, json_t *other); 134 | int json_object_update_existing(json_t *object, json_t *other); 135 | int json_object_update_missing(json_t *object, json_t *other); 136 | void *json_object_iter(json_t *object); 137 | void *json_object_iter_at(json_t *object, const char *key); 138 | void *json_object_key_to_iter(const char *key); 139 | void *json_object_iter_next(json_t *object, void *iter); 140 | const char *json_object_iter_key(void *iter); 141 | json_t *json_object_iter_value(void *iter); 142 | int json_object_iter_set_new(json_t *object, void *iter, json_t *value); 143 | 144 | #define json_object_foreach(object, key, value) \ 145 | for(key = json_object_iter_key(json_object_iter(object)); \ 146 | key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ 147 | key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) 148 | 149 | static JSON_INLINE 150 | int json_object_set(json_t *object, const char *key, json_t *value) 151 | { 152 | return json_object_set_new(object, key, json_incref(value)); 153 | } 154 | 155 | static JSON_INLINE 156 | int json_object_set_nocheck(json_t *object, const char *key, json_t *value) 157 | { 158 | return json_object_set_new_nocheck(object, key, json_incref(value)); 159 | } 160 | 161 | static JSON_INLINE 162 | int json_object_iter_set(json_t *object, void *iter, json_t *value) 163 | { 164 | return json_object_iter_set_new(object, iter, json_incref(value)); 165 | } 166 | 167 | size_t json_array_size(const json_t *array); 168 | json_t *json_array_get(const json_t *array, size_t index); 169 | int json_array_set_new(json_t *array, size_t index, json_t *value); 170 | int json_array_append_new(json_t *array, json_t *value); 171 | int json_array_insert_new(json_t *array, size_t index, json_t *value); 172 | int json_array_remove(json_t *array, size_t index); 173 | int json_array_clear(json_t *array); 174 | int json_array_extend(json_t *array, json_t *other); 175 | 176 | static JSON_INLINE 177 | int json_array_set(json_t *array, size_t index, json_t *value) 178 | { 179 | return json_array_set_new(array, index, json_incref(value)); 180 | } 181 | 182 | static JSON_INLINE 183 | int json_array_append(json_t *array, json_t *value) 184 | { 185 | return json_array_append_new(array, json_incref(value)); 186 | } 187 | 188 | static JSON_INLINE 189 | int json_array_insert(json_t *array, size_t index, json_t *value) 190 | { 191 | return json_array_insert_new(array, index, json_incref(value)); 192 | } 193 | 194 | const char *json_string_value(const json_t *string); 195 | json_int_t json_integer_value(const json_t *integer); 196 | double json_real_value(const json_t *real); 197 | double json_number_value(const json_t *json); 198 | 199 | int json_string_set(json_t *string, const char *value); 200 | int json_string_set_nocheck(json_t *string, const char *value); 201 | int json_integer_set(json_t *integer, json_int_t value); 202 | int json_real_set(json_t *real, double value); 203 | 204 | 205 | /* pack, unpack */ 206 | 207 | json_t *json_pack(const char *fmt, ...); 208 | json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...); 209 | json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap); 210 | 211 | #define JSON_VALIDATE_ONLY 0x1 212 | #define JSON_STRICT 0x2 213 | 214 | int json_unpack(json_t *root, const char *fmt, ...); 215 | int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); 216 | int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap); 217 | 218 | 219 | /* equality */ 220 | 221 | int json_equal(json_t *value1, json_t *value2); 222 | 223 | 224 | /* copying */ 225 | 226 | json_t *json_copy(json_t *value); 227 | json_t *json_deep_copy(json_t *value); 228 | 229 | 230 | /* decoding */ 231 | 232 | #define JSON_REJECT_DUPLICATES 0x1 233 | #define JSON_DISABLE_EOF_CHECK 0x2 234 | #define JSON_DECODE_ANY 0x4 235 | 236 | typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); 237 | 238 | json_t *json_loads(const char *input, size_t flags, json_error_t *error); 239 | json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error); 240 | json_t *json_loadf(FILE *input, size_t flags, json_error_t *error); 241 | json_t *json_load_file(const char *path, size_t flags, json_error_t *error); 242 | json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error); 243 | 244 | 245 | /* encoding */ 246 | 247 | #define JSON_INDENT(n) (n & 0x1F) 248 | #define JSON_COMPACT 0x20 249 | #define JSON_ENSURE_ASCII 0x40 250 | #define JSON_SORT_KEYS 0x80 251 | #define JSON_PRESERVE_ORDER 0x100 252 | #define JSON_ENCODE_ANY 0x200 253 | #define JSON_ESCAPE_SLASH 0x400 254 | 255 | typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); 256 | 257 | char *json_dumps(const json_t *json, size_t flags); 258 | int json_dumpf(const json_t *json, FILE *output, size_t flags); 259 | int json_dump_file(const json_t *json, const char *path, size_t flags); 260 | int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags); 261 | 262 | /* custom memory allocation */ 263 | 264 | typedef void *(*json_malloc_t)(size_t); 265 | typedef void (*json_free_t)(void *); 266 | 267 | void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn); 268 | 269 | #ifdef __cplusplus 270 | } 271 | #endif 272 | 273 | #endif 274 | -------------------------------------------------------------------------------- /jansson/jansson_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2012 Petri Lehtinen 3 | * 4 | * Jansson is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | * 7 | * 8 | * This file specifies a part of the site-specific configuration for 9 | * Jansson, namely those things that affect the public API in 10 | * jansson.h. 11 | * 12 | * The configure script copies this file to jansson_config.h and 13 | * replaces @var@ substitutions by values that fit your system. If you 14 | * cannot run the configure script, you can do the value substitution 15 | * by hand. 16 | */ 17 | 18 | #ifndef JANSSON_CONFIG_H 19 | #define JANSSON_CONFIG_H 20 | 21 | /* If your compiler supports the inline keyword in C, JSON_INLINE is 22 | defined to `inline', otherwise empty. In C++, the inline is always 23 | supported. */ 24 | #ifdef __cplusplus 25 | #define JSON_INLINE inline 26 | #else 27 | #define JSON_INLINE inline 28 | #endif 29 | 30 | /* If your compiler supports the `long long` type and the strtoll() 31 | library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, 32 | otherwise to 0. */ 33 | #define JSON_INTEGER_IS_LONG_LONG 1 34 | 35 | /* If locale.h and localeconv() are available, define to 1, 36 | otherwise to 0. */ 37 | #define JSON_HAVE_LOCALECONV 1 38 | 39 | 40 | 41 | 42 | /* Added by Emil Ernerfeldt in 2013 to allow printing and parsing NaN and Inf values. 43 | */ 44 | #define JSON_ALLOW_NAN_INF 1 45 | 46 | #if JSON_ALLOW_NAN_INF 47 | # define JSON_NAN_STR "0e666" // I doubt anyone else would encode zero like this. 48 | # define JSON_POS_INF_STR "1e99999" // Big enough to overflow a 128 bit float 49 | # define JSON_NEG_INF_STR "-1e99999" 50 | #endif 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /jansson/jansson_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * 4 | * Jansson is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #ifndef JANSSON_PRIVATE_H 9 | #define JANSSON_PRIVATE_H 10 | 11 | #include 12 | #include "jansson.h" 13 | #include "hashtable.h" 14 | #include "strbuffer.h" 15 | 16 | #define container_of(ptr_, type_, member_) \ 17 | ((type_ *)((char *)ptr_ - offsetof(type_, member_))) 18 | 19 | /* On some platforms, max() may already be defined */ 20 | #ifndef max 21 | #define max(a, b) ((a) > (b) ? (a) : (b)) 22 | #endif 23 | 24 | /* va_copy is a C99 feature. In C89 implementations, it's sometimes 25 | available as __va_copy. If not, memcpy() should do the trick. */ 26 | #ifndef va_copy 27 | #ifdef __va_copy 28 | #define va_copy __va_copy 29 | #else 30 | #define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list)) 31 | #endif 32 | #endif 33 | 34 | typedef struct { 35 | json_t json; 36 | hashtable_t hashtable; 37 | size_t serial; 38 | int visited; 39 | } json_object_t; 40 | 41 | typedef struct { 42 | json_t json; 43 | size_t size; 44 | size_t entries; 45 | json_t **table; 46 | int visited; 47 | } json_array_t; 48 | 49 | typedef struct { 50 | json_t json; 51 | char *value; 52 | } json_string_t; 53 | 54 | typedef struct { 55 | json_t json; 56 | double value; 57 | } json_real_t; 58 | 59 | typedef struct { 60 | json_t json; 61 | json_int_t value; 62 | } json_integer_t; 63 | 64 | #define json_to_object(json_) container_of(json_, json_object_t, json) 65 | #define json_to_array(json_) container_of(json_, json_array_t, json) 66 | #define json_to_string(json_) container_of(json_, json_string_t, json) 67 | #define json_to_real(json_) container_of(json_, json_real_t, json) 68 | #define json_to_integer(json_) container_of(json_, json_integer_t, json) 69 | 70 | void jsonp_error_init(json_error_t *error, const char *source); 71 | void jsonp_error_set_source(json_error_t *error, const char *source); 72 | void jsonp_error_set(json_error_t *error, int line, int column, 73 | size_t position, const char *msg, ...); 74 | void jsonp_error_vset(json_error_t *error, int line, int column, 75 | size_t position, const char *msg, va_list ap); 76 | 77 | /* Locale independent string<->double conversions */ 78 | int jsonp_strtod(strbuffer_t *strbuffer, double *out); 79 | int jsonp_dtostr(char *buffer, size_t size, double value); 80 | 81 | /* Wrappers for custom memory functions */ 82 | void* jsonp_malloc(size_t size); 83 | void jsonp_free(void *ptr); 84 | char *jsonp_strdup(const char *str); 85 | 86 | /* Windows compatibility */ 87 | #ifdef _WIN32 88 | #define snprintf _snprintf 89 | #define vsnprintf _vsnprintf 90 | #endif 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /jansson/memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * Copyright (c) 2011-2012 Basile Starynkevitch 4 | * 5 | * Jansson is free software; you can redistribute it and/or modify it 6 | * under the terms of the MIT license. See LICENSE for details. 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include "jansson.h" 13 | #include "jansson_private.h" 14 | 15 | /* memory function pointers */ 16 | static json_malloc_t do_malloc = malloc; 17 | static json_free_t do_free = free; 18 | 19 | void *jsonp_malloc(size_t size) 20 | { 21 | if(!size) 22 | return NULL; 23 | 24 | return (*do_malloc)(size); 25 | } 26 | 27 | void jsonp_free(void *ptr) 28 | { 29 | if(!ptr) 30 | return; 31 | 32 | (*do_free)(ptr); 33 | } 34 | 35 | char *jsonp_strdup(const char *str) 36 | { 37 | char *new_str; 38 | 39 | new_str = jsonp_malloc(strlen(str) + 1); 40 | if(!new_str) 41 | return NULL; 42 | 43 | strcpy(new_str, str); 44 | return new_str; 45 | } 46 | 47 | void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) 48 | { 49 | do_malloc = malloc_fn; 50 | do_free = free_fn; 51 | } 52 | -------------------------------------------------------------------------------- /jansson/pack_unpack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * Copyright (c) 2011-2012 Graeme Smecher 4 | * 5 | * Jansson is free software; you can redistribute it and/or modify 6 | * it under the terms of the MIT license. See LICENSE for details. 7 | */ 8 | 9 | #include 10 | #include "jansson.h" 11 | #include "jansson_private.h" 12 | #include "utf.h" 13 | 14 | typedef struct { 15 | const char *start; 16 | const char *fmt; 17 | char token; 18 | json_error_t *error; 19 | size_t flags; 20 | int line; 21 | int column; 22 | } scanner_t; 23 | 24 | static const char * const type_names[] = { 25 | "object", 26 | "array", 27 | "string", 28 | "integer", 29 | "real", 30 | "true", 31 | "false", 32 | "null" 33 | }; 34 | 35 | #define type_name(x) type_names[json_typeof(x)] 36 | 37 | static const char unpack_value_starters[] = "{[siIbfFOon"; 38 | 39 | 40 | static void scanner_init(scanner_t *s, json_error_t *error, 41 | size_t flags, const char *fmt) 42 | { 43 | s->error = error; 44 | s->flags = flags; 45 | s->fmt = s->start = fmt; 46 | s->line = 1; 47 | s->column = 0; 48 | } 49 | 50 | static void next_token(scanner_t *s) 51 | { 52 | const char *t = s->fmt; 53 | s->column++; 54 | 55 | /* skip space and ignored chars */ 56 | while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { 57 | if(*t == '\n') { 58 | s->line++; 59 | s->column = 1; 60 | } 61 | else 62 | s->column++; 63 | 64 | t++; 65 | } 66 | 67 | s->token = *t; 68 | 69 | t++; 70 | s->fmt = t; 71 | } 72 | 73 | static void set_error(scanner_t *s, const char *source, const char *fmt, ...) 74 | { 75 | va_list ap; 76 | size_t pos; 77 | va_start(ap, fmt); 78 | 79 | pos = (size_t)(s->fmt - s->start); 80 | jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap); 81 | 82 | jsonp_error_set_source(s->error, source); 83 | 84 | va_end(ap); 85 | } 86 | 87 | static json_t *pack(scanner_t *s, va_list *ap); 88 | 89 | static json_t *pack_object(scanner_t *s, va_list *ap) 90 | { 91 | json_t *object = json_object(); 92 | next_token(s); 93 | 94 | while(s->token != '}') { 95 | const char *key; 96 | json_t *value; 97 | 98 | if(!s->token) { 99 | set_error(s, "", "Unexpected end of format string"); 100 | goto error; 101 | } 102 | 103 | if(s->token != 's') { 104 | set_error(s, "", "Expected format 's', got '%c'", s->token); 105 | goto error; 106 | } 107 | 108 | key = va_arg(*ap, const char *); 109 | if(!key) { 110 | set_error(s, "", "NULL object key"); 111 | goto error; 112 | } 113 | 114 | if(!utf8_check_string(key, -1)) { 115 | set_error(s, "", "Invalid UTF-8 in object key"); 116 | goto error; 117 | } 118 | 119 | next_token(s); 120 | 121 | value = pack(s, ap); 122 | if(!value) 123 | goto error; 124 | 125 | if(json_object_set_new_nocheck(object, key, value)) { 126 | set_error(s, "", "Unable to add key \"%s\"", key); 127 | goto error; 128 | } 129 | 130 | next_token(s); 131 | } 132 | 133 | return object; 134 | 135 | error: 136 | json_decref(object); 137 | return NULL; 138 | } 139 | 140 | static json_t *pack_array(scanner_t *s, va_list *ap) 141 | { 142 | json_t *array = json_array(); 143 | next_token(s); 144 | 145 | while(s->token != ']') { 146 | json_t *value; 147 | 148 | if(!s->token) { 149 | set_error(s, "", "Unexpected end of format string"); 150 | goto error; 151 | } 152 | 153 | value = pack(s, ap); 154 | if(!value) 155 | goto error; 156 | 157 | if(json_array_append_new(array, value)) { 158 | set_error(s, "", "Unable to append to array"); 159 | goto error; 160 | } 161 | 162 | next_token(s); 163 | } 164 | return array; 165 | 166 | error: 167 | json_decref(array); 168 | return NULL; 169 | } 170 | 171 | static json_t *pack(scanner_t *s, va_list *ap) 172 | { 173 | switch(s->token) { 174 | case '{': 175 | return pack_object(s, ap); 176 | 177 | case '[': 178 | return pack_array(s, ap); 179 | 180 | case 's': /* string */ 181 | { 182 | const char *str = va_arg(*ap, const char *); 183 | if(!str) { 184 | set_error(s, "", "NULL string argument"); 185 | return NULL; 186 | } 187 | if(!utf8_check_string(str, -1)) { 188 | set_error(s, "", "Invalid UTF-8 string"); 189 | return NULL; 190 | } 191 | return json_string_nocheck(str); 192 | } 193 | 194 | case 'n': /* null */ 195 | return json_null(); 196 | 197 | case 'b': /* boolean */ 198 | return va_arg(*ap, int) ? json_true() : json_false(); 199 | 200 | case 'i': /* integer from int */ 201 | return json_integer(va_arg(*ap, int)); 202 | 203 | case 'I': /* integer from json_int_t */ 204 | return json_integer(va_arg(*ap, json_int_t)); 205 | 206 | case 'f': /* real */ 207 | return json_real(va_arg(*ap, double)); 208 | 209 | case 'O': /* a json_t object; increments refcount */ 210 | return json_incref(va_arg(*ap, json_t *)); 211 | 212 | case 'o': /* a json_t object; doesn't increment refcount */ 213 | return va_arg(*ap, json_t *); 214 | 215 | default: 216 | set_error(s, "", "Unexpected format character '%c'", 217 | s->token); 218 | return NULL; 219 | } 220 | } 221 | 222 | static int unpack(scanner_t *s, json_t *root, va_list *ap); 223 | 224 | static int unpack_object(scanner_t *s, json_t *root, va_list *ap) 225 | { 226 | int ret = -1; 227 | int strict = 0; 228 | 229 | /* Use a set (emulated by a hashtable) to check that all object 230 | keys are accessed. Checking that the correct number of keys 231 | were accessed is not enough, as the same key can be unpacked 232 | multiple times. 233 | */ 234 | hashtable_t key_set; 235 | 236 | if(hashtable_init(&key_set)) { 237 | set_error(s, "", "Out of memory"); 238 | return -1; 239 | } 240 | 241 | if(root && !json_is_object(root)) { 242 | set_error(s, "", "Expected object, got %s", 243 | type_name(root)); 244 | goto out; 245 | } 246 | next_token(s); 247 | 248 | while(s->token != '}') { 249 | const char *key; 250 | json_t *value; 251 | int opt = 0; 252 | 253 | if(strict != 0) { 254 | set_error(s, "", "Expected '}' after '%c', got '%c'", 255 | (strict == 1 ? '!' : '*'), s->token); 256 | goto out; 257 | } 258 | 259 | if(!s->token) { 260 | set_error(s, "", "Unexpected end of format string"); 261 | goto out; 262 | } 263 | 264 | if(s->token == '!' || s->token == '*') { 265 | strict = (s->token == '!' ? 1 : -1); 266 | next_token(s); 267 | continue; 268 | } 269 | 270 | if(s->token != 's') { 271 | set_error(s, "", "Expected format 's', got '%c'", s->token); 272 | goto out; 273 | } 274 | 275 | key = va_arg(*ap, const char *); 276 | if(!key) { 277 | set_error(s, "", "NULL object key"); 278 | goto out; 279 | } 280 | 281 | next_token(s); 282 | 283 | if(s->token == '?') { 284 | opt = 1; 285 | next_token(s); 286 | } 287 | 288 | if(!root) { 289 | /* skipping */ 290 | value = NULL; 291 | } 292 | else { 293 | value = json_object_get(root, key); 294 | if(!value && !opt) { 295 | set_error(s, "", "Object item not found: %s", key); 296 | goto out; 297 | } 298 | } 299 | 300 | if(unpack(s, value, ap)) 301 | goto out; 302 | 303 | hashtable_set(&key_set, key, 0, json_null()); 304 | next_token(s); 305 | } 306 | 307 | if(strict == 0 && (s->flags & JSON_STRICT)) 308 | strict = 1; 309 | 310 | if(root && strict == 1 && key_set.size != json_object_size(root)) { 311 | long diff = (long)json_object_size(root) - (long)key_set.size; 312 | set_error(s, "", "%li object item(s) left unpacked", diff); 313 | goto out; 314 | } 315 | 316 | ret = 0; 317 | 318 | out: 319 | hashtable_close(&key_set); 320 | return ret; 321 | } 322 | 323 | static int unpack_array(scanner_t *s, json_t *root, va_list *ap) 324 | { 325 | size_t i = 0; 326 | int strict = 0; 327 | 328 | if(root && !json_is_array(root)) { 329 | set_error(s, "", "Expected array, got %s", type_name(root)); 330 | return -1; 331 | } 332 | next_token(s); 333 | 334 | while(s->token != ']') { 335 | json_t *value; 336 | 337 | if(strict != 0) { 338 | set_error(s, "", "Expected ']' after '%c', got '%c'", 339 | (strict == 1 ? '!' : '*'), 340 | s->token); 341 | return -1; 342 | } 343 | 344 | if(!s->token) { 345 | set_error(s, "", "Unexpected end of format string"); 346 | return -1; 347 | } 348 | 349 | if(s->token == '!' || s->token == '*') { 350 | strict = (s->token == '!' ? 1 : -1); 351 | next_token(s); 352 | continue; 353 | } 354 | 355 | if(!strchr(unpack_value_starters, s->token)) { 356 | set_error(s, "", "Unexpected format character '%c'", 357 | s->token); 358 | return -1; 359 | } 360 | 361 | if(!root) { 362 | /* skipping */ 363 | value = NULL; 364 | } 365 | else { 366 | value = json_array_get(root, i); 367 | if(!value) { 368 | set_error(s, "", "Array index %lu out of range", 369 | (unsigned long)i); 370 | return -1; 371 | } 372 | } 373 | 374 | if(unpack(s, value, ap)) 375 | return -1; 376 | 377 | next_token(s); 378 | i++; 379 | } 380 | 381 | if(strict == 0 && (s->flags & JSON_STRICT)) 382 | strict = 1; 383 | 384 | if(root && strict == 1 && i != json_array_size(root)) { 385 | long diff = (long)json_array_size(root) - (long)i; 386 | set_error(s, "", "%li array item(s) left unpacked", diff); 387 | return -1; 388 | } 389 | 390 | return 0; 391 | } 392 | 393 | static int unpack(scanner_t *s, json_t *root, va_list *ap) 394 | { 395 | switch(s->token) 396 | { 397 | case '{': 398 | return unpack_object(s, root, ap); 399 | 400 | case '[': 401 | return unpack_array(s, root, ap); 402 | 403 | case 's': 404 | if(root && !json_is_string(root)) { 405 | set_error(s, "", "Expected string, got %s", 406 | type_name(root)); 407 | return -1; 408 | } 409 | 410 | if(!(s->flags & JSON_VALIDATE_ONLY)) { 411 | const char **target; 412 | 413 | target = va_arg(*ap, const char **); 414 | if(!target) { 415 | set_error(s, "", "NULL string argument"); 416 | return -1; 417 | } 418 | 419 | if(root) 420 | *target = json_string_value(root); 421 | } 422 | return 0; 423 | 424 | case 'i': 425 | if(root && !json_is_integer(root)) { 426 | set_error(s, "", "Expected integer, got %s", 427 | type_name(root)); 428 | return -1; 429 | } 430 | 431 | if(!(s->flags & JSON_VALIDATE_ONLY)) { 432 | int *target = va_arg(*ap, int*); 433 | if(root) 434 | *target = (int)json_integer_value(root); 435 | } 436 | 437 | return 0; 438 | 439 | case 'I': 440 | if(root && !json_is_integer(root)) { 441 | set_error(s, "", "Expected integer, got %s", 442 | type_name(root)); 443 | return -1; 444 | } 445 | 446 | if(!(s->flags & JSON_VALIDATE_ONLY)) { 447 | json_int_t *target = va_arg(*ap, json_int_t*); 448 | if(root) 449 | *target = json_integer_value(root); 450 | } 451 | 452 | return 0; 453 | 454 | case 'b': 455 | if(root && !json_is_boolean(root)) { 456 | set_error(s, "", "Expected true or false, got %s", 457 | type_name(root)); 458 | return -1; 459 | } 460 | 461 | if(!(s->flags & JSON_VALIDATE_ONLY)) { 462 | int *target = va_arg(*ap, int*); 463 | if(root) 464 | *target = json_is_true(root); 465 | } 466 | 467 | return 0; 468 | 469 | case 'f': 470 | if(root && !json_is_real(root)) { 471 | set_error(s, "", "Expected real, got %s", 472 | type_name(root)); 473 | return -1; 474 | } 475 | 476 | if(!(s->flags & JSON_VALIDATE_ONLY)) { 477 | double *target = va_arg(*ap, double*); 478 | if(root) 479 | *target = json_real_value(root); 480 | } 481 | 482 | return 0; 483 | 484 | case 'F': 485 | if(root && !json_is_number(root)) { 486 | set_error(s, "", "Expected real or integer, got %s", 487 | type_name(root)); 488 | return -1; 489 | } 490 | 491 | if(!(s->flags & JSON_VALIDATE_ONLY)) { 492 | double *target = va_arg(*ap, double*); 493 | if(root) 494 | *target = json_number_value(root); 495 | } 496 | 497 | return 0; 498 | 499 | case 'O': 500 | if(root && !(s->flags & JSON_VALIDATE_ONLY)) 501 | json_incref(root); 502 | /* Fall through */ 503 | 504 | case 'o': 505 | if(!(s->flags & JSON_VALIDATE_ONLY)) { 506 | json_t **target = va_arg(*ap, json_t**); 507 | if(root) 508 | *target = root; 509 | } 510 | 511 | return 0; 512 | 513 | case 'n': 514 | /* Never assign, just validate */ 515 | if(root && !json_is_null(root)) { 516 | set_error(s, "", "Expected null, got %s", 517 | type_name(root)); 518 | return -1; 519 | } 520 | return 0; 521 | 522 | default: 523 | set_error(s, "", "Unexpected format character '%c'", 524 | s->token); 525 | return -1; 526 | } 527 | } 528 | 529 | json_t *json_vpack_ex(json_error_t *error, size_t flags, 530 | const char *fmt, va_list ap) 531 | { 532 | scanner_t s; 533 | va_list ap_copy; 534 | json_t *value; 535 | 536 | if(!fmt || !*fmt) { 537 | jsonp_error_init(error, ""); 538 | jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); 539 | return NULL; 540 | } 541 | jsonp_error_init(error, NULL); 542 | 543 | scanner_init(&s, error, flags, fmt); 544 | next_token(&s); 545 | 546 | va_copy(ap_copy, ap); 547 | value = pack(&s, &ap_copy); 548 | va_end(ap_copy); 549 | 550 | if(!value) 551 | return NULL; 552 | 553 | next_token(&s); 554 | if(s.token) { 555 | json_decref(value); 556 | set_error(&s, "", "Garbage after format string"); 557 | return NULL; 558 | } 559 | 560 | return value; 561 | } 562 | 563 | json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) 564 | { 565 | json_t *value; 566 | va_list ap; 567 | 568 | va_start(ap, fmt); 569 | value = json_vpack_ex(error, flags, fmt, ap); 570 | va_end(ap); 571 | 572 | return value; 573 | } 574 | 575 | json_t *json_pack(const char *fmt, ...) 576 | { 577 | json_t *value; 578 | va_list ap; 579 | 580 | va_start(ap, fmt); 581 | value = json_vpack_ex(NULL, 0, fmt, ap); 582 | va_end(ap); 583 | 584 | return value; 585 | } 586 | 587 | int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, 588 | const char *fmt, va_list ap) 589 | { 590 | scanner_t s; 591 | va_list ap_copy; 592 | 593 | if(!root) { 594 | jsonp_error_init(error, ""); 595 | jsonp_error_set(error, -1, -1, 0, "NULL root value"); 596 | return -1; 597 | } 598 | 599 | if(!fmt || !*fmt) { 600 | jsonp_error_init(error, ""); 601 | jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); 602 | return -1; 603 | } 604 | jsonp_error_init(error, NULL); 605 | 606 | scanner_init(&s, error, flags, fmt); 607 | next_token(&s); 608 | 609 | va_copy(ap_copy, ap); 610 | if(unpack(&s, root, &ap_copy)) { 611 | va_end(ap_copy); 612 | return -1; 613 | } 614 | va_end(ap_copy); 615 | 616 | next_token(&s); 617 | if(s.token) { 618 | set_error(&s, "", "Garbage after format string"); 619 | return -1; 620 | } 621 | 622 | return 0; 623 | } 624 | 625 | int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) 626 | { 627 | int ret; 628 | va_list ap; 629 | 630 | va_start(ap, fmt); 631 | ret = json_vunpack_ex(root, error, flags, fmt, ap); 632 | va_end(ap); 633 | 634 | return ret; 635 | } 636 | 637 | int json_unpack(json_t *root, const char *fmt, ...) 638 | { 639 | int ret; 640 | va_list ap; 641 | 642 | va_start(ap, fmt); 643 | ret = json_vunpack_ex(root, NULL, 0, fmt, ap); 644 | va_end(ap); 645 | 646 | return ret; 647 | } 648 | -------------------------------------------------------------------------------- /jansson/strbuffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * 4 | * Jansson is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include "jansson_private.h" 12 | #include "strbuffer.h" 13 | 14 | #define STRBUFFER_MIN_SIZE 16 15 | #define STRBUFFER_FACTOR 2 16 | #define STRBUFFER_SIZE_MAX ((size_t)-1) 17 | 18 | int strbuffer_init(strbuffer_t *strbuff) 19 | { 20 | strbuff->size = STRBUFFER_MIN_SIZE; 21 | strbuff->length = 0; 22 | 23 | strbuff->value = jsonp_malloc(strbuff->size); 24 | if(!strbuff->value) 25 | return -1; 26 | 27 | /* initialize to empty */ 28 | strbuff->value[0] = '\0'; 29 | return 0; 30 | } 31 | 32 | void strbuffer_close(strbuffer_t *strbuff) 33 | { 34 | jsonp_free(strbuff->value); 35 | strbuff->size = 0; 36 | strbuff->length = 0; 37 | strbuff->value = NULL; 38 | } 39 | 40 | void strbuffer_clear(strbuffer_t *strbuff) 41 | { 42 | strbuff->length = 0; 43 | strbuff->value[0] = '\0'; 44 | } 45 | 46 | const char *strbuffer_value(const strbuffer_t *strbuff) 47 | { 48 | return strbuff->value; 49 | } 50 | 51 | char *strbuffer_steal_value(strbuffer_t *strbuff) 52 | { 53 | char *result = strbuff->value; 54 | strbuffer_init(strbuff); 55 | return result; 56 | } 57 | 58 | int strbuffer_append(strbuffer_t *strbuff, const char *string) 59 | { 60 | return strbuffer_append_bytes(strbuff, string, strlen(string)); 61 | } 62 | 63 | int strbuffer_append_byte(strbuffer_t *strbuff, char byte) 64 | { 65 | return strbuffer_append_bytes(strbuff, &byte, 1); 66 | } 67 | 68 | int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) 69 | { 70 | if(size >= strbuff->size - strbuff->length) 71 | { 72 | size_t new_size; 73 | char *new_value; 74 | 75 | /* avoid integer overflow */ 76 | if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR 77 | || size > STRBUFFER_SIZE_MAX - 1 78 | || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size) 79 | return -1; 80 | 81 | new_size = max(strbuff->size * STRBUFFER_FACTOR, 82 | strbuff->length + size + 1); 83 | 84 | new_value = jsonp_malloc(new_size); 85 | if(!new_value) 86 | return -1; 87 | 88 | memcpy(new_value, strbuff->value, strbuff->length); 89 | 90 | jsonp_free(strbuff->value); 91 | strbuff->value = new_value; 92 | strbuff->size = new_size; 93 | } 94 | 95 | memcpy(strbuff->value + strbuff->length, data, size); 96 | strbuff->length += size; 97 | strbuff->value[strbuff->length] = '\0'; 98 | 99 | return 0; 100 | } 101 | 102 | char strbuffer_pop(strbuffer_t *strbuff) 103 | { 104 | if(strbuff->length > 0) { 105 | char c = strbuff->value[--strbuff->length]; 106 | strbuff->value[strbuff->length] = '\0'; 107 | return c; 108 | } 109 | else 110 | return '\0'; 111 | } 112 | -------------------------------------------------------------------------------- /jansson/strbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * 4 | * Jansson is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #ifndef STRBUFFER_H 9 | #define STRBUFFER_H 10 | 11 | typedef struct { 12 | char *value; 13 | size_t length; /* bytes used */ 14 | size_t size; /* bytes allocated */ 15 | } strbuffer_t; 16 | 17 | int strbuffer_init(strbuffer_t *strbuff); 18 | void strbuffer_close(strbuffer_t *strbuff); 19 | 20 | void strbuffer_clear(strbuffer_t *strbuff); 21 | 22 | const char *strbuffer_value(const strbuffer_t *strbuff); 23 | char *strbuffer_steal_value(strbuffer_t *strbuff); 24 | 25 | int strbuffer_append(strbuffer_t *strbuff, const char *string); 26 | int strbuffer_append_byte(strbuffer_t *strbuff, char byte); 27 | int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size); 28 | 29 | char strbuffer_pop(strbuffer_t *strbuff); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /jansson/strconv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "jansson_private.h" 6 | #include "strbuffer.h" 7 | 8 | #if JSON_ALLOW_NAN_INF 9 | #include 10 | 11 | /* Work around nonstandard isnan() and isinf() implementations */ 12 | #ifndef isnan 13 | static JSON_INLINE int isnan(double x) { return x != x; } 14 | #endif 15 | #ifndef isinf 16 | static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } 17 | #endif 18 | #endif // JSON_ALLOW_NAN_INF 19 | 20 | 21 | #if JSON_HAVE_LOCALECONV 22 | #include 23 | 24 | /* 25 | - This code assumes that the decimal separator is exactly one 26 | character. 27 | 28 | - If setlocale() is called by another thread between the call to 29 | localeconv() and the call to sprintf() or strtod(), the result may 30 | be wrong. setlocale() is not thread-safe and should not be used 31 | this way. Multi-threaded programs should use uselocale() instead. 32 | */ 33 | 34 | static void to_locale(strbuffer_t *strbuffer) 35 | { 36 | const char *point; 37 | char *pos; 38 | 39 | point = localeconv()->decimal_point; 40 | if(*point == '.') { 41 | /* No conversion needed */ 42 | return; 43 | } 44 | 45 | pos = strchr(strbuffer->value, '.'); 46 | if(pos) 47 | *pos = *point; 48 | } 49 | 50 | static void from_locale(char *buffer) 51 | { 52 | const char *point; 53 | char *pos; 54 | 55 | point = localeconv()->decimal_point; 56 | if(*point == '.') { 57 | /* No conversion needed */ 58 | return; 59 | } 60 | 61 | pos = strchr(buffer, *point); 62 | if(pos) 63 | *pos = '.'; 64 | } 65 | #endif 66 | 67 | 68 | int jsonp_strmatch(strbuffer_t *strbuffer, const char* str) 69 | { 70 | size_t len = strlen(str); 71 | if (strbuffer->length != len) 72 | return 0; 73 | if (memcmp(strbuffer->value, str, len)==0) 74 | return 1; 75 | else 76 | return 0; 77 | } 78 | 79 | 80 | int jsonp_strtod(strbuffer_t *strbuffer, double *out) 81 | { 82 | #if JSON_ALLOW_NAN_INF 83 | if (jsonp_strmatch(strbuffer, JSON_NAN_STR)) { 84 | *out = NAN; 85 | return 0; 86 | } else if (jsonp_strmatch(strbuffer, JSON_POS_INF_STR)) { 87 | *out = INFINITY; 88 | return 0; 89 | } else if (jsonp_strmatch(strbuffer, JSON_NEG_INF_STR)) { 90 | *out = -INFINITY; 91 | return 0; 92 | } 93 | #endif 94 | 95 | 96 | double value; 97 | char *end; 98 | 99 | #if JSON_HAVE_LOCALECONV 100 | to_locale(strbuffer); 101 | #endif 102 | 103 | errno = 0; 104 | value = strtod(strbuffer->value, &end); 105 | assert(end == strbuffer->value + strbuffer->length); 106 | 107 | if(errno == ERANGE && value != 0) { 108 | /* Overflow */ 109 | return -1; 110 | } 111 | 112 | *out = value; 113 | return 0; 114 | } 115 | 116 | int jsonp_dump_special(char *buffer, size_t size, const char* str) 117 | { 118 | size_t len = strlen(str); 119 | if (len >= size) return -1; 120 | memcpy(buffer, str, len); 121 | return (int)len; 122 | } 123 | 124 | int jsonp_dtostr(char *buffer, size_t size, double value) 125 | { 126 | #if JSON_ALLOW_NAN_INF 127 | if (isnan(value)) { 128 | return jsonp_dump_special(buffer, size, JSON_NAN_STR); 129 | } else if (isinf(value)) { 130 | if (value < 0) 131 | return jsonp_dump_special(buffer, size, JSON_NEG_INF_STR); 132 | else 133 | return jsonp_dump_special(buffer, size, JSON_POS_INF_STR); 134 | } 135 | #endif // JSON_ALLOW_NAN_INF 136 | 137 | int ret; 138 | char *start, *end; 139 | size_t length; 140 | 141 | ret = snprintf(buffer, size, "%.17g", value); 142 | if(ret < 0) 143 | return -1; 144 | 145 | length = (size_t)ret; 146 | if(length >= size) 147 | return -1; 148 | 149 | #if JSON_HAVE_LOCALECONV 150 | from_locale(buffer); 151 | #endif 152 | 153 | /* Make sure there's a dot or 'e' in the output. Otherwise 154 | a real is converted to an integer when decoding */ 155 | if(strchr(buffer, '.') == NULL && 156 | strchr(buffer, 'e') == NULL) 157 | { 158 | if(length + 3 >= size) { 159 | /* No space to append ".0" */ 160 | return -1; 161 | } 162 | buffer[length] = '.'; 163 | buffer[length + 1] = '0'; 164 | buffer[length + 2] = '\0'; 165 | length += 2; 166 | } 167 | 168 | /* Remove leading '+' from positive exponent. Also remove leading 169 | zeros from exponents (added by some printf() implementations) */ 170 | start = strchr(buffer, 'e'); 171 | if(start) { 172 | start++; 173 | end = start + 1; 174 | 175 | if(*start == '-') 176 | start++; 177 | 178 | while(*end == '0') 179 | end++; 180 | 181 | if(end != start) { 182 | memmove(start, end, length - (size_t)(end - buffer)); 183 | length -= (size_t)(end - start); 184 | } 185 | } 186 | 187 | return (int)length; 188 | } 189 | -------------------------------------------------------------------------------- /jansson/utf.c: -------------------------------------------------------------------------------- 1 | #pragma clang diagnostic ignored "-Wconversion" 2 | 3 | /* 4 | * Copyright (c) 2009-2012 Petri Lehtinen 5 | * 6 | * Jansson is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See LICENSE for details. 8 | */ 9 | 10 | #include 11 | #include "utf.h" 12 | 13 | int utf8_encode(int32_t codepoint, char *buffer, int *size) 14 | { 15 | if(codepoint < 0) 16 | return -1; 17 | else if(codepoint < 0x80) 18 | { 19 | buffer[0] = (char)codepoint; 20 | *size = 1; 21 | } 22 | else if(codepoint < 0x800) 23 | { 24 | buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6); 25 | buffer[1] = 0x80 + ((codepoint & 0x03F)); 26 | *size = 2; 27 | } 28 | else if(codepoint < 0x10000) 29 | { 30 | buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12); 31 | buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6); 32 | buffer[2] = 0x80 + ((codepoint & 0x003F)); 33 | *size = 3; 34 | } 35 | else if(codepoint <= 0x10FFFF) 36 | { 37 | buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18); 38 | buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12); 39 | buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6); 40 | buffer[3] = 0x80 + ((codepoint & 0x00003F)); 41 | *size = 4; 42 | } 43 | else 44 | return -1; 45 | 46 | return 0; 47 | } 48 | 49 | int utf8_check_first(char byte) 50 | { 51 | unsigned char u = (unsigned char)byte; 52 | 53 | if(u < 0x80) 54 | return 1; 55 | 56 | if(0x80 <= u && u <= 0xBF) { 57 | /* second, third or fourth byte of a multi-byte 58 | sequence, i.e. a "continuation byte" */ 59 | return 0; 60 | } 61 | else if(u == 0xC0 || u == 0xC1) { 62 | /* overlong encoding of an ASCII byte */ 63 | return 0; 64 | } 65 | else if(0xC2 <= u && u <= 0xDF) { 66 | /* 2-byte sequence */ 67 | return 2; 68 | } 69 | 70 | else if(0xE0 <= u && u <= 0xEF) { 71 | /* 3-byte sequence */ 72 | return 3; 73 | } 74 | else if(0xF0 <= u && u <= 0xF4) { 75 | /* 4-byte sequence */ 76 | return 4; 77 | } 78 | else { /* u >= 0xF5 */ 79 | /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid 80 | UTF-8 */ 81 | return 0; 82 | } 83 | } 84 | 85 | int utf8_check_full(const char *buffer, int size, int32_t *codepoint) 86 | { 87 | int i; 88 | int32_t value = 0; 89 | unsigned char u = (unsigned char)buffer[0]; 90 | 91 | if(size == 2) 92 | { 93 | value = u & 0x1F; 94 | } 95 | else if(size == 3) 96 | { 97 | value = u & 0xF; 98 | } 99 | else if(size == 4) 100 | { 101 | value = u & 0x7; 102 | } 103 | else 104 | return 0; 105 | 106 | for(i = 1; i < size; i++) 107 | { 108 | u = (unsigned char)buffer[i]; 109 | 110 | if(u < 0x80 || u > 0xBF) { 111 | /* not a continuation byte */ 112 | return 0; 113 | } 114 | 115 | value = (value << 6) + (u & 0x3F); 116 | } 117 | 118 | if(value > 0x10FFFF) { 119 | /* not in Unicode range */ 120 | return 0; 121 | } 122 | 123 | else if(0xD800 <= value && value <= 0xDFFF) { 124 | /* invalid code point (UTF-16 surrogate halves) */ 125 | return 0; 126 | } 127 | 128 | else if((size == 2 && value < 0x80) || 129 | (size == 3 && value < 0x800) || 130 | (size == 4 && value < 0x10000)) { 131 | /* overlong encoding */ 132 | return 0; 133 | } 134 | 135 | if(codepoint) 136 | *codepoint = value; 137 | 138 | return 1; 139 | } 140 | 141 | const char *utf8_iterate(const char *buffer, int32_t *codepoint) 142 | { 143 | int count; 144 | int32_t value; 145 | 146 | if(!*buffer) 147 | return buffer; 148 | 149 | count = utf8_check_first(buffer[0]); 150 | if(count <= 0) 151 | return NULL; 152 | 153 | if(count == 1) 154 | value = (unsigned char)buffer[0]; 155 | else 156 | { 157 | if(!utf8_check_full(buffer, count, &value)) 158 | return NULL; 159 | } 160 | 161 | if(codepoint) 162 | *codepoint = value; 163 | 164 | return buffer + count; 165 | } 166 | 167 | int utf8_check_string(const char *string, int length) 168 | { 169 | int i; 170 | 171 | if(length == -1) 172 | length = strlen(string); 173 | 174 | for(i = 0; i < length; i++) 175 | { 176 | int count = utf8_check_first(string[i]); 177 | if(count == 0) 178 | return 0; 179 | else if(count > 1) 180 | { 181 | if(i + count > length) 182 | return 0; 183 | 184 | if(!utf8_check_full(&string[i], count, NULL)) 185 | return 0; 186 | 187 | i += count - 1; 188 | } 189 | } 190 | 191 | return 1; 192 | } 193 | -------------------------------------------------------------------------------- /jansson/utf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012 Petri Lehtinen 3 | * 4 | * Jansson is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #ifndef UTF_H 9 | #define UTF_H 10 | 11 | #ifdef HAVE_CONFIG_H 12 | #include 13 | 14 | #ifdef HAVE_INTTYPES_H 15 | /* inttypes.h includes stdint.h in a standard environment, so there's 16 | no need to include stdint.h separately. If inttypes.h doesn't define 17 | int32_t, it's defined in config.h. */ 18 | #include 19 | #endif /* HAVE_INTTYPES_H */ 20 | 21 | #else /* !HAVE_CONFIG_H */ 22 | #ifdef _WIN32 23 | typedef int int32_t; 24 | #else /* !_WIN32 */ 25 | /* Assume a standard environment */ 26 | #include 27 | #endif /* _WIN32 */ 28 | 29 | #endif /* HAVE_CONFIG_H */ 30 | 31 | int utf8_encode(int codepoint, char *buffer, int *size); 32 | 33 | int utf8_check_first(char byte); 34 | int utf8_check_full(const char *buffer, int size, int32_t *codepoint); 35 | const char *utf8_iterate(const char *buffer, int32_t *codepoint); 36 | 37 | /* 38 | 1 iff valid utf8, 0 else. 39 | */ 40 | int utf8_check_string(const char *string, int length); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /json2bon/json2bon.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 2013-02-04 \" DATE 7 | .Dt json2bon 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm json2bon, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /json2bon/json2bon.c: -------------------------------------------------------------------------------- 1 | // 2 | // json2bon.c 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #include 10 | #include 11 | #include 12 | #include "hashtable.h" // hashtable_iter_serial 13 | #include 14 | 15 | #define PRESERVE_ORDER 1 16 | #define ATTEMPT_RECOVERY 1 17 | 18 | bon_bool write_json(json_t* json, bon_w_doc* B); 19 | 20 | 21 | struct object_key { 22 | size_t serial; 23 | const char *key; 24 | }; 25 | 26 | static int object_key_compare_serials(const void *key1, const void *key2) 27 | { 28 | size_t a = ((const struct object_key *)key1)->serial; 29 | size_t b = ((const struct object_key *)key2)->serial; 30 | 31 | return a < b ? -1 : a == b ? 0 : 1; 32 | } 33 | 34 | bon_bool write_obj(json_t* json, bon_w_doc* B) 35 | { 36 | bon_bool success = BON_TRUE; 37 | 38 | #if PRESERVE_ORDER 39 | size_t size = json_object_size(json); 40 | struct object_key* keys = malloc(size * sizeof(struct object_key)); 41 | 42 | void* iter = json_object_iter((json_t *)json); 43 | 44 | size_t i = 0; 45 | while(iter) 46 | { 47 | keys[i].serial = hashtable_iter_serial(iter); 48 | keys[i].key = json_object_iter_key(iter); 49 | iter = json_object_iter_next((json_t *)json, iter); 50 | ++i; 51 | } 52 | assert(i == size); 53 | 54 | qsort(keys, size, sizeof(struct object_key), object_key_compare_serials); 55 | 56 | bon_w_obj_begin(B); 57 | for (i = 0; i < size; ++i) 58 | { 59 | const char* key = keys[i].key; 60 | json_t* value = json_object_get(json, key); 61 | bon_w_key(B, key); 62 | if (!write_json(value, B)) { 63 | success = BON_FALSE; 64 | if (!ATTEMPT_RECOVERY) 65 | break; 66 | } 67 | } 68 | bon_w_obj_end(B); 69 | 70 | free(keys); 71 | #else 72 | const char* key; 73 | json_t* value; 74 | 75 | bon_w_obj_begin(B); 76 | json_object_foreach(json, key, value) { 77 | bon_w_key(B, key); 78 | if (!write_json(value, B)) { 79 | success = BON_FALSE; 80 | if (!ATTEMPT_RECOVERY) 81 | break; 82 | } 83 | } 84 | bon_w_obj_end(B); 85 | #endif 86 | 87 | return success; 88 | } 89 | 90 | 91 | bon_bool write_json(json_t* json, bon_w_doc* B) 92 | { 93 | if (!json) { 94 | fprintf(stderr, "write_json got NULL\n"); 95 | return BON_FALSE; 96 | } 97 | 98 | if (bon_w_error(B) != BON_SUCCESS) { 99 | if (ATTEMPT_RECOVERY) { 100 | bon_w_set_error(B, BON_SUCCESS); 101 | } 102 | } 103 | 104 | bon_bool success = BON_TRUE; 105 | 106 | switch (json_typeof(json)) 107 | { 108 | case JSON_OBJECT: { 109 | return write_obj(json, B); 110 | } break; 111 | 112 | 113 | case JSON_ARRAY: { 114 | size_t size = json_array_size(json); 115 | 116 | bon_w_list_begin(B); 117 | for (size_t ix=0; ixtext[0] != '\0') { 170 | fprintf(stderr, "%s, line: %d, col: %d\n", err->text, err->line, err->column); 171 | return BON_FALSE; 172 | } 173 | 174 | fprintf(stderr, "Unknown JSON parse error at line: %d, col: %d\n", err->line, err->column); 175 | return BON_FALSE; 176 | } 177 | 178 | bon_w_doc* B = bon_w_new(&bon_file_writer, out, BON_W_FLAG_DEFAULT); 179 | 180 | if (!B) { 181 | return BON_FALSE; 182 | } 183 | 184 | bon_bool success = write_json(json, B); 185 | 186 | if (bon_w_close(B) != BON_SUCCESS) { 187 | success = BON_FALSE; 188 | } 189 | 190 | return success; 191 | } 192 | 193 | 194 | int main(int argc, const char * argv[]) 195 | { 196 | bon_bool didParseFile = BON_FALSE; 197 | FILE* out = stdout; 198 | size_t flags = JSON_DECODE_ANY; 199 | /* Flags: 200 | JSON_REJECT_DUPLICATES 201 | JSON_DECODE_ANY 202 | JSON_DISABLE_EOF_CHECK 203 | */ 204 | 205 | for (int i=1; i 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | 10 | #include "bon.h" 11 | #include "private.h" 12 | #include // isnan, isinf 13 | #include // malloc 14 | 15 | 16 | // For fprintf:ing int64 17 | #define __STDC_FORMAT_MACROS 18 | #include 19 | 20 | 21 | // File size (stat): 22 | #include 23 | #include 24 | 25 | 26 | uint8_t* bon_read_file(bon_size* out_size, const char* path) 27 | { 28 | *out_size = 0; 29 | 30 | struct stat info; 31 | if (stat(path, &info) != 0) { 32 | fprintf(stderr, "Failed to stat file %s\n", path); 33 | return NULL; 34 | } 35 | 36 | size_t fileSize = (size_t)info.st_size; 37 | 38 | uint8_t* data = (uint8_t*)malloc(fileSize); 39 | 40 | FILE* fp = fopen(path, "rb"); 41 | 42 | size_t blocks_read = fread(data, fileSize, 1, fp); 43 | if (blocks_read != 1) { 44 | fprintf(stderr, "Failed to read file %s\n", path); 45 | return NULL ; 46 | } 47 | 48 | fclose(fp); 49 | 50 | *out_size = fileSize; 51 | return data; 52 | } 53 | 54 | 55 | //------------------------------------------------------------------------------ 56 | 57 | 58 | void bon_onError(const char* msg) 59 | { 60 | #ifndef NDEBUG 61 | fprintf(stderr, "BON error: %s\n", msg); 62 | 63 | //__asm__("int $3\n" : : ); // breakpoint 64 | #endif 65 | } 66 | 67 | 68 | 69 | //------------------------------------------------------------------------------ 70 | 71 | 72 | const char* bon_err_str(bon_error err) 73 | { 74 | if (BON_NUM_ERR <= err) 75 | return "BON_UNKNOWN_ERROR"; 76 | 77 | const char* err_str[BON_NUM_ERR] = { 78 | "BON_SUCCESS", 79 | 80 | "BON_ERR_WRITE_ERROR", 81 | "BON_ERR_BAD_AGGREGATE_SIZE", 82 | 83 | "BON_ERR_MISSING_CRC", 84 | "BON_ERR_WRONG_CRC", 85 | "BON_ERR_TOO_SHORT", 86 | "BON_ERR_BAD_HEADER", 87 | "BON_ERR_BAD_FOOTER", 88 | "BON_ERR_BAD_VLQ", 89 | "BON_ERR_BAD_CTRL", 90 | "BON_ERR_BAD_KEY", 91 | "BON_ERR_BAD_PACKED_TYPE", 92 | "BON_ERR_BAD_TYPE", 93 | "BON_ERR_BAD_VALUE", 94 | "BON_ERR_STRING_NOT_ZERO_ENDED", 95 | "BON_ERR_MISSING_TOKEN", 96 | 97 | "BON_ERR_TRAILING_DATA", 98 | "BON_ERR_BAD_BLOCK", 99 | "BON_ERR_BAD_BLOCK_REF", 100 | 101 | "BON_ERR_NARROWING", 102 | "BON_ERR_NULL_OBJ", 103 | 104 | "BON_ERR_NOT_UTF8" 105 | }; 106 | 107 | return err_str[err]; 108 | } 109 | 110 | 111 | //------------------------------------------------------------------------------ 112 | // Debug printing 113 | 114 | 115 | void bon_print_float(FILE* out, double dbl) 116 | { 117 | if (isnan(dbl)) fprintf(out, "NaN"); 118 | else if (isinf(dbl)) fprintf(out, dbl < 0 ? "-Inf" : "+Inf"); 119 | else fprintf(out, "%f", dbl); 120 | } 121 | 122 | void bon_print_aggr(bon_r_doc* B, const bon_type* aggType, bon_reader* br, FILE* out) 123 | { 124 | uint8_t id = aggType->id; 125 | 126 | switch (id) { 127 | case BON_TYPE_ARRAY: { 128 | fprintf(out, "["); 129 | bon_type_array* arr = aggType->u.array; 130 | for (uint64_t ti=0; tisize; ++ti) { 131 | bon_print_aggr(B, arr->type, br, out); 132 | if (ti != arr->size-1) 133 | fprintf(out, ", "); 134 | } 135 | fprintf(out, "]"); 136 | } break; 137 | 138 | 139 | case BON_TYPE_STRUCT: { 140 | fprintf(out, "{"); 141 | bon_type_struct* strct = aggType->u.strct; 142 | for (uint64_t ti=0; tisize; ++ti) { 143 | bon_kt* kt = &strct->kts[ti]; 144 | fprintf(out, "\"%s\": ", kt->key); 145 | bon_print_aggr(B, &kt->type, br, out); 146 | if (ti != strct->size-1) 147 | fprintf(out, ", "); 148 | } 149 | fprintf(out, "}"); 150 | } break; 151 | 152 | case BON_CTRL_SINT8: 153 | case BON_CTRL_SINT16_LE: 154 | case BON_CTRL_SINT16_BE: 155 | case BON_CTRL_SINT32_LE: 156 | case BON_CTRL_SINT32_BE: 157 | case BON_CTRL_SINT64_LE: 158 | case BON_CTRL_SINT64_BE: { 159 | int64_t s64 = br_read_sint64(br, id); 160 | fprintf(out, "%"PRIi64, s64); 161 | } 162 | 163 | 164 | case BON_CTRL_UINT8: 165 | case BON_CTRL_UINT16_LE: 166 | case BON_CTRL_UINT16_BE: 167 | case BON_CTRL_UINT32_LE: 168 | case BON_CTRL_UINT32_BE: 169 | case BON_CTRL_UINT64_LE: 170 | case BON_CTRL_UINT64_BE: { 171 | uint64_t u64 = br_read_uint64(br, id); 172 | fprintf(out, "%"PRIu64, u64); 173 | } break; 174 | 175 | 176 | case BON_CTRL_FLOAT_LE: 177 | case BON_CTRL_FLOAT_BE: 178 | case BON_CTRL_DOUBLE_LE: 179 | case BON_CTRL_DOUBLE_BE: { 180 | double dbl = br_read_double(br, id); 181 | bon_print_float(out, dbl); 182 | } break; 183 | 184 | 185 | default: { 186 | fprintf(out, "2JSON_ERROR"); 187 | } 188 | } 189 | } 190 | 191 | // Will print in json format. 192 | void bon_print(bon_r_doc* B, bon_value* v, FILE* out, size_t indent) 193 | { 194 | #define INDENT "\t" 195 | #define PRINT_NEWLINE_INDENT fprintf(out, "\n"); for (size_t iix=0; iixu.obj; 205 | bon_size size = kvs->size; 206 | 207 | if (size==0) { 208 | fprintf(out, "{ }"); 209 | } else { 210 | bon_bool multiline = (size > 1); 211 | 212 | if (multiline) { 213 | fprintf(out, "{"); 214 | indent++; 215 | PRINT_NEWLINE_INDENT 216 | } else { 217 | fprintf(out, "{ "); 218 | } 219 | 220 | for (bon_size i=0; idata[i]; 222 | 223 | fprintf(out, "\""); 224 | fprintf(out, "\"%s\": ", kv->key); 225 | fprintf(out, "\": "); 226 | bon_print(B, &kv->val, out, indent); 227 | 228 | if (i != size-1) { 229 | fprintf(out, ","); 230 | 231 | if (multiline) { 232 | PRINT_NEWLINE_INDENT 233 | } 234 | } 235 | } 236 | 237 | if (multiline) { 238 | indent--; 239 | PRINT_NEWLINE_INDENT 240 | fprintf(out, "}"); 241 | } else { 242 | fprintf(out, " }"); 243 | } 244 | } 245 | return; 246 | } 247 | 248 | 249 | switch (v->type) { 250 | case BON_VALUE_NIL: 251 | fprintf(out, "nil"); 252 | break; 253 | 254 | 255 | case BON_VALUE_BOOL: 256 | fprintf(out, v->u.boolean ? "true" : "false"); 257 | break; 258 | 259 | 260 | case BON_VALUE_UINT64: 261 | fprintf(out, "%"PRIu64, v->u.u64); 262 | break; 263 | 264 | 265 | case BON_VALUE_SINT64: 266 | fprintf(out, "%"PRIi64, v->u.s64); 267 | break; 268 | 269 | 270 | case BON_VALUE_DOUBLE: 271 | bon_print_float(out, v->u.dbl); 272 | break; 273 | 274 | 275 | case BON_VALUE_STRING: 276 | fprintf(out, "\""); 277 | fwrite(v->u.str.ptr, v->u.str.size, 1, out); 278 | fprintf(out, "\""); 279 | break; 280 | 281 | 282 | case BON_VALUE_LIST: { 283 | fprintf(out, "[ "); 284 | const bon_list* vals = &v->u.list; 285 | bon_size size = vals->size; 286 | for (bon_size i=0; idata + i, out, indent); 288 | if (i != size-1) 289 | fprintf(out, ", "); 290 | } 291 | fprintf(out, " ]"); 292 | } break; 293 | 294 | 295 | case BON_VALUE_AGGREGATE: { 296 | bon_value_agg* agg = v->u.agg; 297 | bon_size byteSize = bon_aggregate_payload_size(&agg->type); 298 | bon_reader br = make_br(NULL, agg->data, byteSize, BON_BAD_BLOCK_ID); 299 | bon_print_aggr(B, &agg->type, &br, out); 300 | if (br.nbytes!=0) { 301 | fprintf(stderr, "Bad aggregate\n"); 302 | } 303 | } break; 304 | 305 | 306 | case BON_VALUE_BLOCK_REF: { 307 | fprintf(out, "@%"PRIu64, v->u.blockRefId); 308 | } 309 | 310 | default: 311 | fprintf(out, "ERROR"); 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /libbon/bon/bon.h: -------------------------------------------------------------------------------- 1 | // 2 | // bon.h 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #ifndef BON_bon_w_h 10 | #define BON_bon_w_h 11 | 12 | #include 13 | #include 14 | 15 | #if defined(__linux__) 16 | # include 17 | # if __BYTE_ORDER == __LITTLE_ENDIAN 18 | # define __LITTLE_ENDIAN__ 1 19 | # elif __BYTE_ORDER == __BIG_ENDIAN 20 | # define __BIG_ENDIAN__ 1 21 | # endif 22 | #endif 23 | 24 | #if !__LITTLE_ENDIAN__ && !__BIG_ENDIAN__ 25 | # error "BON lib doesn't work on mixed endian machines!" 26 | #elif __LITTLE_ENDIAN__ && __BIG_ENDIAN__ 27 | # error "BON lib needs __LITTLE_ENDIAN__ or __BIG_ENDIAN__ defined - not both!" 28 | #endif 29 | 30 | 31 | //------------------------------------------------------------------------------ 32 | // BON common 33 | 34 | typedef char bon_bool; 35 | 36 | #define BON_TRUE ((bon_bool)1) 37 | #define BON_FALSE ((bon_bool)0) 38 | 39 | typedef size_t bon_size; 40 | 41 | #define BON_ZERO_ENDED (bon_size)(-1) // Valid as argument of string size 42 | 43 | typedef uint64_t bon_block_id; 44 | #define BON_BAD_BLOCK_ID (bon_block_id)(-1) 45 | 46 | typedef struct bon_value bon_value; 47 | 48 | //------------------------------------------------------------------------------ 49 | 50 | 51 | typedef enum { 52 | BON_SUCCESS = 0, 53 | 54 | // Write errors 55 | BON_ERR_WRITE_ERROR, // Writer refused 56 | BON_ERR_BAD_AGGREGATE_SIZE, // bon_w_pack got data of the wrong size 57 | 58 | /* 59 | bon_r: 60 | Detailed errors when reading. 61 | All read errors are due to corrupt BON file. 62 | These can help debug a BON encoder. 63 | */ 64 | BON_ERR_MISSING_CRC, 65 | BON_ERR_WRONG_CRC, 66 | BON_ERR_TOO_SHORT, // Premature end of file 67 | BON_ERR_BAD_HEADER, // Missing or corrupt 68 | BON_ERR_BAD_FOOTER, // Missing or corrupt 69 | BON_ERR_BAD_VLQ, 70 | BON_ERR_BAD_CTRL, 71 | BON_ERR_BAD_KEY, // Not a string, or string with zeros 72 | BON_ERR_BAD_PACKED_TYPE, 73 | BON_ERR_BAD_TYPE, // Never triggers 74 | BON_ERR_BAD_VALUE, 75 | BON_ERR_STRING_NOT_ZERO_ENDED, 76 | BON_ERR_MISSING_TOKEN, 77 | BON_ERR_TRAILING_DATA, // Data trailing the document 78 | BON_ERR_BAD_BLOCK, 79 | BON_ERR_BAD_BLOCK_REF, // Referring a block with an ID smaller or equal to own 80 | 81 | // bw_read_aggregate etc: 82 | BON_ERR_NARROWING, 83 | BON_ERR_NULL_OBJ, 84 | 85 | BON_ERR_NOT_UTF8, // Key or string not UTF8 when reading OR writing 86 | 87 | BON_NUM_ERR 88 | } bon_error; 89 | 90 | 91 | // Plain text versions of the above 92 | const char* bon_err_str(bon_error err); 93 | 94 | 95 | //------------------------------------------------------------------------------ 96 | 97 | 98 | /* The control codes are all in [0x40, 0x80) 99 | */ 100 | typedef enum { 101 | BON_CTRL_BLOCK_REF = 0x40, BON_CTRL_STRING_VLQ = 0x60, // @ ` 102 | BON_CTRL_ARRAY_VLQ = 'A', 103 | BON_CTRL_HEADER = 'B', 104 | BON_CTRL_STRUCT_VLQ = 'C', 105 | BON_CTRL_BLOCK_BEGIN = 'D', BON_CTRL_BLOCK_END = 'd', 106 | BON_CTRL_TRUE = 'E', BON_CTRL_FALSE = 'e', 107 | BON_CTRL_FOOTER = 'F', BON_CTRL_FOOTER_CRC = 'f', 108 | 109 | BON_CTRL_LIST_VLQ = 'L', 110 | BON_CTRL_NIL = 'N', 111 | BON_CTRL_OBJ_VLQ = 'O', 112 | 113 | BON_CTRL_SINT8 = 'P', 114 | BON_CTRL_UINT8 = 'Q', 115 | BON_CTRL_SINT16_LE = 'R', BON_CTRL_SINT16_BE = 'r', 116 | BON_CTRL_UINT16_LE = 'S', BON_CTRL_UINT16_BE = 's', 117 | BON_CTRL_SINT32_LE = 'T', BON_CTRL_SINT32_BE = 't', 118 | BON_CTRL_UINT32_LE = 'U', BON_CTRL_UINT32_BE = 'u', 119 | BON_CTRL_SINT64_LE = 'V', BON_CTRL_SINT64_BE = 'v', 120 | BON_CTRL_UINT64_LE = 'W', BON_CTRL_UINT64_BE = 'w', 121 | 122 | BON_CTRL_FLOAT_LE = 'X', BON_CTRL_FLOAT_BE = 'x', 123 | BON_CTRL_DOUBLE_LE = 'Y', BON_CTRL_DOUBLE_BE = 'y', 124 | 125 | // Open-ended list and object 126 | BON_CTRL_LIST_BEGIN = '[', BON_CTRL_LIST_END = ']', // 0x5B 0x5D 127 | BON_CTRL_OBJ_BEGIN = '{', BON_CTRL_OBJ_END = '}', // 0x7B 0x7D 128 | 129 | 130 | #if __LITTLE_ENDIAN__ 131 | BON_CTRL_SINT16 = BON_CTRL_SINT16_LE, 132 | BON_CTRL_UINT16 = BON_CTRL_UINT16_LE, 133 | BON_CTRL_SINT32 = BON_CTRL_SINT32_LE, 134 | BON_CTRL_UINT32 = BON_CTRL_UINT32_LE, 135 | BON_CTRL_SINT64 = BON_CTRL_SINT64_LE, 136 | BON_CTRL_UINT64 = BON_CTRL_UINT64_LE, 137 | BON_CTRL_FLOAT = BON_CTRL_FLOAT_LE, 138 | BON_CTRL_DOUBLE = BON_CTRL_DOUBLE_LE, 139 | #else 140 | BON_CTRL_SINT16 = BON_CTRL_SINT16_BE, 141 | BON_CTRL_UINT16 = BON_CTRL_UINT16_BE, 142 | BON_CTRL_SINT32 = BON_CTRL_SINT32_BE, 143 | BON_CTRL_UINT32 = BON_CTRL_UINT32_BE, 144 | BON_CTRL_SINT64 = BON_CTRL_SINT64_BE, 145 | BON_CTRL_UINT64 = BON_CTRL_UINT64_BE, 146 | BON_CTRL_FLOAT = BON_CTRL_FLOAT_BE, 147 | BON_CTRL_DOUBLE = BON_CTRL_DOUBLE_BE 148 | #endif 149 | } bon_ctrl; 150 | 151 | 152 | //------------------------------------------------------------------------------ 153 | 154 | typedef enum { 155 | BON_TYPE_BOOL, // target: bon_bool 156 | BON_TYPE_STRING = BON_CTRL_STRING_VLQ, // target: const char* 157 | 158 | 159 | //BON_TYPE_OBJECT = BON_CTRL_OBJ_BEGIN, 160 | //BON_TYPE_LIST = BON_CTRL_LIST_BEGIN, 161 | 162 | BON_TYPE_ARRAY = BON_CTRL_ARRAY_VLQ, 163 | BON_TYPE_STRUCT = BON_CTRL_STRUCT_VLQ, 164 | 165 | BON_TYPE_SINT8 = BON_CTRL_SINT8, 166 | BON_TYPE_UINT8 = BON_CTRL_UINT8, 167 | 168 | BON_TYPE_SINT16_LE = BON_CTRL_SINT16_LE, 169 | BON_TYPE_SINT16_BE = BON_CTRL_SINT16_BE, 170 | BON_TYPE_UINT16_LE = BON_CTRL_UINT16_LE, 171 | BON_TYPE_UINT16_BE = BON_CTRL_UINT16_BE, 172 | 173 | BON_TYPE_SINT32_LE = BON_CTRL_SINT32_LE, 174 | BON_TYPE_SINT32_BE = BON_CTRL_SINT32_BE, 175 | BON_TYPE_UINT32_LE = BON_CTRL_UINT32_LE, 176 | BON_TYPE_UINT32_BE = BON_CTRL_UINT32_BE, 177 | 178 | BON_TYPE_SINT64_LE = BON_CTRL_SINT64_LE, 179 | BON_TYPE_SINT64_BE = BON_CTRL_SINT64_BE, 180 | BON_TYPE_UINT64_LE = BON_CTRL_UINT64_LE, 181 | BON_TYPE_UINT64_BE = BON_CTRL_UINT64_BE, 182 | 183 | BON_TYPE_FLOAT_LE = BON_CTRL_FLOAT_LE, 184 | BON_TYPE_FLOAT_BE = BON_CTRL_FLOAT_BE, 185 | BON_TYPE_DOUBLE_LE = BON_CTRL_DOUBLE_LE, 186 | BON_TYPE_DOUBLE_BE = BON_CTRL_DOUBLE_BE, 187 | 188 | BON_TYPE_SINT16 = BON_CTRL_SINT16, 189 | BON_TYPE_UINT16 = BON_CTRL_UINT16, 190 | BON_TYPE_SINT32 = BON_CTRL_SINT32, 191 | BON_TYPE_UINT32 = BON_CTRL_UINT32, 192 | BON_TYPE_SINT64 = BON_CTRL_SINT64, 193 | BON_TYPE_UINT64 = BON_CTRL_UINT64, 194 | BON_TYPE_FLOAT = BON_CTRL_FLOAT, 195 | BON_TYPE_DOUBLE = BON_CTRL_DOUBLE 196 | } bon_type_id; 197 | 198 | //------------------------------------------------------------------------------ 199 | 200 | /* 201 | The general type system. 202 | This is used for reading and writing. 203 | For writing, the user may spec up a type and write it. 204 | For reading, the user may spec up a type and ask BON to read it. 205 | For reading, the user may inquire BON about the type of the next value. 206 | */ 207 | typedef struct bon_type bon_type; 208 | 209 | 210 | void bon_free_type(bon_type* t); 211 | 212 | // Functions for creating these types 213 | bon_type* bon_new_type_simple(bon_type_id id); 214 | bon_type* bon_new_type_simple_array(bon_size n, bon_type_id id); 215 | bon_type* bon_new_type_array(bon_size n, bon_type* type); 216 | 217 | /* 218 | 'names' and 'types' are arrays of size 'n'. 219 | 'names' points either to string literals, or to some other string whose lifetime EXCEEDS that of the returned type. I repeat: the strings pointed to by the elements of 'names' MUST NOT BEE FREED OR MODIFIED while the returned bon_type is in use. 220 | Ovnership of the bon_type:s pointed to by 'types' are given over to the returned bon_type. 221 | 222 | The arrays 'names' and 'types' may be freed after this call, but not what their elements point to. 223 | 224 | Use case: 225 | 226 | struct Foo { float bar[3]; double baz; } 227 | 228 | bon_type* greateFooType() { 229 | const char* names[2] = { "bar", "baz" }; 230 | bon_type* types[2] = { 231 | bon_new_type_simple_array( 3, BON_TYPE_FLOAT ), 232 | bon_new_type_simple( BON_TYPE_DOUBLE ) 233 | }; 234 | return bon_new_type_struct(2, names, types); 235 | } 236 | */ 237 | bon_type* bon_new_type_struct(bon_size n, const char** names, bon_type** types); 238 | 239 | 240 | /* 241 | struct Vert { float pos[3]; uint8_t color[4]; } 242 | bon_type* bon_new_type_fmt("{$[3f]$[4u8]}", "pos", "color") 243 | 244 | u8 u16 u32 u64 - unsigned int 245 | i8 i16 i32 i64 - signed integer 246 | f - float 247 | d - double 248 | {...} - object. Interleave $ (key placeholder) and types. 249 | [3f] - array of three floats 250 | [#u8] - array of bytes (array size given in vargs as bon_size) 251 | 252 | NULL on fail 253 | */ 254 | bon_type* bon_new_type_fmt(const char* fmt, ...); 255 | 256 | // Returns true if the two types are exactly equal 257 | bon_bool bon_type_eq(const bon_type* a, const bon_type* b); 258 | 259 | 260 | //------------------------------------------------------------------------------ 261 | 262 | 263 | typedef struct { 264 | bon_size size; // Usage 265 | bon_size cap; // Number of bytes allocated in 'data' 266 | uint8_t* data; 267 | } bon_byte_vec; 268 | 269 | /* 270 | Usage: 271 | bon_byte_vec vec = {0,0,0}; 272 | bon_w_doc* B = bon_w_new(bon_vec_writer, &vec, BON_W_FLAGS_DEFAULT); 273 | write_bon( B ); 274 | bon_w_close( B ); 275 | use( vec.data ); 276 | free(vec.data); 277 | */ 278 | bon_bool bon_vec_writer(void* userData, const void* data, uint64_t nbytes); 279 | 280 | 281 | /* 282 | Usage: 283 | FILE* fp = fopen(FILE_NAME, "wb"); 284 | bon_w_doc* B = bon_w_new(&bon_file_writer, fp, BON_W_FLAGS_DEFAULT); 285 | write_bon( B ); 286 | bon_w_close( B ); 287 | fclose( fp ); 288 | */ 289 | bon_bool bon_file_writer(void* user, const void* data, uint64_t nbytes); 290 | 291 | 292 | 293 | //------------------------------------------------------------------------------ 294 | // BON writer API 295 | 296 | /* 297 | Low-level API for outputting BON in a serial fashion. 298 | You must take care to create a correct BON file. In particular: 299 | 300 | Match bon_w_obj_begin with bon_w_obj_end 301 | Match bon_w_list_begin with bon_w_list_end 302 | Match bon_w_block_begin with bon_w_block_end (or use bon_w_block) 303 | 304 | Make sure bon_w_block_ref references a block with a larger block_id. 305 | 306 | Interleave keys and values inside of calls to bon_w_obj_begin bon_w_obj_end 307 | */ 308 | 309 | 310 | /* 311 | A writer will be called upon to write a BON-doc in small increments. 312 | It can be connected to a file-writer, memory-writer, socket etc. 313 | To signal an error, return false. BON_ERR_WRITE_ERROR will be set in the bon_w_doc. 314 | */ 315 | typedef bon_bool (*bon_w_writer_t)(void* userData, const void* data, uint64_t nbytes); 316 | 317 | 318 | typedef struct bon_w_doc bon_w_doc; 319 | 320 | 321 | typedef enum { 322 | BON_W_FLAG_DEFAULT = 0, 323 | 324 | // Compute CRC-32, and add to footer. 325 | BON_W_FLAG_CRC = 1 << 0, 326 | 327 | // Don't write header nor footer. 328 | BON_W_FLAG_SKIP_HEADER_FOOTER = 1 << 1, 329 | 330 | // Save cpu by not checking strings for utf8 correctness 331 | BON_W_FLAG_SKIP_STRING_CHECKS = 1 << 2 332 | } bon_w_flags; 333 | 334 | 335 | // Top level structure 336 | bon_w_doc* bon_w_new (bon_w_writer_t writer, void* userData, bon_w_flags flags); 337 | void bon_w_flush (bon_w_doc* B); // Flush writes to the writer 338 | 339 | // Writes footer and flushes. Returns final error (if any) 340 | bon_error bon_w_close (bon_w_doc* B); 341 | 342 | void bon_w_set_error (bon_w_doc* B, bon_error err); 343 | static bon_error bon_w_error (bon_w_doc* B); 344 | const char* bon_w_err_str (bon_w_doc* B); // Human readable error message 345 | 346 | void bon_w_block_begin (bon_w_doc* B, bon_block_id block_id); // open-ended 347 | void bon_w_block_end (bon_w_doc* B); 348 | void bon_w_block (bon_w_doc* B, bon_block_id block_id, const void* data, bon_size nbytes); 349 | 350 | 351 | // The different types of values: 352 | static void bon_w_obj_begin (bon_w_doc* B); 353 | static void bon_w_obj_end (bon_w_doc* B); 354 | 355 | // you must write a value right after this. 356 | static void bon_w_key (bon_w_doc* B, const char* utf8); 357 | 358 | static void bon_w_list_begin (bon_w_doc* B); 359 | static void bon_w_list_end (bon_w_doc* B); 360 | 361 | static void bon_w_block_ref (bon_w_doc* B, bon_block_id id); 362 | 363 | static void bon_w_nil (bon_w_doc* B); 364 | static void bon_w_bool (bon_w_doc* B, bon_bool val); 365 | static void bon_w_string (bon_w_doc* B, const char* utf8, bon_size nbytes); 366 | static void bon_w_cstring (bon_w_doc* B, const char* utf8); // Zero-ended 367 | 368 | // Numbers: 369 | static void bon_w_uint64 (bon_w_doc* B, uint64_t val); 370 | static void bon_w_sint64 (bon_w_doc* B, int64_t val); 371 | static void bon_w_float (bon_w_doc* B, float val); 372 | static void bon_w_double (bon_w_doc* B, double val); 373 | 374 | // Write a value read from another BON-file: 375 | void bon_w_value (bon_w_doc* B, bon_value* val); 376 | 377 | 378 | // Writing coherent data 379 | // nbytes == number of bytes implied by 'type', but required for extra safety 380 | void bon_w_pack (bon_w_doc* B, const void* data, bon_size nbytes, 381 | bon_type* type); 382 | 383 | void bon_w_pack_fmt(bon_w_doc* B, const void* data, bon_size nbytes, 384 | const char* fmt, ...); 385 | 386 | // Helper: 387 | void bon_w_pack_array(bon_w_doc* B, const void* data, bon_size nbytes, 388 | bon_size n_elem, bon_type_id type); 389 | 390 | 391 | 392 | //------------------------------------------------------------------------------ 393 | // BON reader API 394 | 395 | 396 | // Helper function for reading the entire contents of a file: 397 | uint8_t* bon_read_file(bon_size* out_size, const char* path); 398 | 399 | 400 | typedef struct bon_r_doc bon_r_doc; 401 | 402 | 403 | typedef enum { 404 | BON_R_FLAG_DEFAULT = 0, 405 | 406 | /* 407 | Will trigger BON_ERR_MISSING_CRC If the BON file has no CRC, 408 | or BON_ERR_WRONG_CRC if it is incorrect. 409 | No further parsning of the file will be atempted. 410 | */ 411 | BON_R_FLAG_REQUIRE_CRC = 1 << 0, 412 | 413 | // Save cpu by not checking strings for utf8 correctness 414 | BON_R_FLAG_SKIP_STRING_CHECKS = 1 << 2 415 | } bon_r_flags; 416 | 417 | 418 | // Will parse a BON file. use bon_r_error to query success. 419 | bon_r_doc* bon_r_open (const uint8_t* data, bon_size nbytes, bon_r_flags flags); 420 | void bon_r_close (bon_r_doc* B); 421 | bon_value* bon_r_root (bon_r_doc* B); // Access the root object 422 | bon_error bon_r_error (bon_r_doc* B); 423 | const char* bon_r_err_str(bon_r_doc* B); // Human readable error message 424 | 425 | 426 | //------------------------------------------------------------------------------ 427 | 428 | // The logical type of a bon value. 429 | typedef enum { 430 | BON_LOGICAL_ERROR, // If the given value was NULL 431 | BON_LOGICAL_NIL, 432 | BON_LOGICAL_BOOL, 433 | BON_LOGICAL_UINT, 434 | BON_LOGICAL_SINT, 435 | BON_LOGICAL_DOUBLE, 436 | BON_LOGICAL_STRING, 437 | BON_LOGICAL_LIST, 438 | BON_LOGICAL_OBJECT 439 | } bon_logical_type; 440 | 441 | static bon_logical_type bon_r_value_type(bon_r_doc* B, bon_value* val); 442 | 443 | // Inspeciting values. Use these before attemting to read a value. 444 | // Passing NULL as 'val' will make all these functions return BON_FALSE. 445 | 446 | // val==NULL -> BON_FALSE; 447 | static bon_bool bon_r_is_nil (bon_r_doc* B, bon_value* val); // note: bon_r_is_nil(B,NULL) == BON_FALSE 448 | static bon_bool bon_r_is_bool (bon_r_doc* B, bon_value* val); 449 | static bon_bool bon_r_is_int (bon_r_doc* B, bon_value* val); // signed or unsigned 450 | static bon_bool bon_r_is_number (bon_r_doc* B, bon_value* val); // int or double 451 | static bon_bool bon_r_is_double (bon_r_doc* B, bon_value* val); // Specifically double 452 | static bon_bool bon_r_is_string (bon_r_doc* B, bon_value* val); 453 | static bon_bool bon_r_is_list (bon_r_doc* B, bon_value* val); 454 | static bon_bool bon_r_is_object (bon_r_doc* B, bon_value* val); 455 | 456 | 457 | 458 | //------------------------------------------------------------------------------ 459 | // Reading values. 460 | 461 | // Returns true iff 'val' is a bool and is true. False on fail. No implicit conversion. 462 | static bon_bool bon_r_bool (bon_r_doc* B, bon_value* val); 463 | 464 | // Will return zero if of the wrong type. Double/ints are converted to each other. 465 | static uint64_t bon_r_uint (bon_r_doc* B, bon_value* val); 466 | static int64_t bon_r_int (bon_r_doc* B, bon_value* val); 467 | static float bon_r_float (bon_r_doc* B, bon_value* val); 468 | static double bon_r_double (bon_r_doc* B, bon_value* val); 469 | 470 | // Returns NULL if wrong type 471 | static const char* bon_r_cstr (bon_r_doc* B, bon_value* val); // zero-ended UTF-8 472 | static bon_size bon_r_strlen(bon_r_doc* B, bon_value* val); // in bytes (UTF-8) 473 | 474 | // Returns 0 if it is not a list. 475 | static bon_size bon_r_list_size(bon_r_doc* B, bon_value* list); 476 | static bon_value* bon_r_list_elem(bon_r_doc* B, bon_value* list, bon_size ix); 477 | 478 | // Number of keys-value pairs in an object 479 | static bon_size bon_r_obj_size(bon_r_doc* B, bon_value* val); 480 | 481 | // Return NULL if 'val' is not an object, or ix is out of range. 482 | static const char* bon_r_obj_key (bon_r_doc* B, bon_value* val, bon_size ix); 483 | static bon_value* bon_r_obj_value(bon_r_doc* B, bon_value* val, bon_size ix); 484 | 485 | // Returns NULL if 'val' is not an object or does not have the given key. 486 | static bon_value* bon_r_get_key(bon_r_doc* B, bon_value* val, const char* key); 487 | 488 | 489 | //------------------------------------------------------------------------------ 490 | /* 491 | API for quickly reading aggregates: 492 | Any BON value can be read with the 'normal' functions above. 493 | However, ff the BON encoder wrote data in "aggregate" format, 494 | the following may prove to be faster, and _much_ faster for large amounts of data. 495 | */ 496 | 497 | /* 498 | Can read to: BON_BOOL, const char*, ints, floats, structs 499 | Can NOT read to: lists 500 | 501 | If the type is a perfect match, bon_r_unpack may do a simple memcpy. 502 | If not, it will try to convert the values (double->int etc). 503 | 504 | return BON_FALSE if there is a mismatch in types, 505 | array sizes or any missing object keys. 506 | */ 507 | bon_bool bon_r_unpack(bon_r_doc* B, bon_value* srcVal, 508 | void* dst, bon_size nbytes, 509 | const bon_type* dstType); 510 | 511 | // Convenience: 512 | bon_bool bon_r_unpack_fmt(bon_r_doc* B, bon_value* srcVal, 513 | void* dst, bon_size nbytes, const char* fmt, ...); 514 | 515 | /* 516 | bon_r_unpack_ptr_fmt is by far the fastest way to read data, but it only works if the format is EXACTLY right. If it is, bon_r_unpack_ptr_fmt returns a pointer to the data. Else, NULL. 517 | 518 | Even if bon_r_raw_fmt returns NULL, bon_r_unpack_fmt may work with the same arguments. 519 | This is because bon_r_unpack_fmt can do value conversions, ignore extra keys in objects etc. 520 | 521 | Examples: 522 | 523 | const int n = bon_r_list_size(B, val) 524 | const float* src = bon_r_unpack_ptr_fmt(B, val, n * sizeof(float), "[#f]", n); 525 | 526 | struct Vert { float pos[3]; uint8_t color[4]; } 527 | const int n = bon_r_list_size(B, val) 528 | const Vert* verts = bon_r_unpack_ptr_fmt(B, val, n * sizeof(Vert), 529 | "[#{$[3f]$[4u8]}]", n, "pos", "color"); 530 | 531 | */ 532 | const void* bon_r_unpack_ptr (bon_r_doc* B, bon_value* srcVal, 533 | bon_size nbytes, const bon_type* dstType); 534 | const void* bon_r_unpack_ptr_fmt(bon_r_doc* B, bon_value* srcVal, 535 | bon_size nbytes, const char* fmt, ...); 536 | 537 | // Quick access to a pointer e.g. pointer of bytes or floats 538 | const void* bon_r_unpack_array(bon_r_doc* B, bon_value* srcVal, 539 | bon_size nelem, bon_type_id type); 540 | 541 | 542 | //------------------------------------------------------------------------------ 543 | 544 | 545 | // Quick and dirty printout of a value, in json-like format (but NOT json conforming). 546 | // Useful for debugging. 547 | void bon_print(bon_r_doc* B, bon_value* value, FILE* out, size_t indent); 548 | 549 | //------------------------------------------------------------------------------ 550 | 551 | #include "inline.h" 552 | 553 | #endif 554 | -------------------------------------------------------------------------------- /libbon/bon/crc32.c: -------------------------------------------------------------------------------- 1 | // 2 | // crc.32.c 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #include "crc32.h" 10 | 11 | 12 | /* 13 | // Computed by 14 | void make_crc_table(void) 15 | { 16 | unsigned long c; 17 | int n, k; 18 | 19 | for (n = 0; n < 256; n++) { 20 | c = (unsigned long) n; 21 | for (k = 0; k < 8; k++) { 22 | if (c & 1) 23 | c = 0xedb88320L ^ (c >> 1); 24 | else 25 | c = c >> 1; 26 | } 27 | crc_table[n] = c; 28 | } 29 | } 30 | */ 31 | 32 | uint32_t crc_table[256] = { 33 | 0x0, 0x77073096, 0xEE0E612C, 0x990951BA, 34 | 0x76DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 35 | 0xEDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 36 | 0x9B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 37 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 38 | 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 39 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 40 | 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 41 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 42 | 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 43 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 44 | 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 45 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 46 | 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 47 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 48 | 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 49 | 0x76DC4190, 0x1DB7106, 0x98D220BC, 0xEFD5102A, 50 | 0x71B18589, 0x6B6B51F, 0x9FBFE4A5, 0xE8B8D433, 51 | 0x7807C9A2, 0xF00F934, 0x9609A88E, 0xE10E9818, 52 | 0x7F6A0DBB, 0x86D3D2D, 0x91646C97, 0xE6635C01, 53 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 54 | 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 55 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 56 | 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 57 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 58 | 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 59 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 60 | 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 61 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 62 | 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 63 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 64 | 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 65 | 0xEDB88320, 0x9ABFB3B6, 0x3B6E20C, 0x74B1D29A, 66 | 0xEAD54739, 0x9DD277AF, 0x4DB2615, 0x73DC1683, 67 | 0xE3630B12, 0x94643B84, 0xD6D6A3E, 0x7A6A5AA8, 68 | 0xE40ECF0B, 0x9309FF9D, 0xA00AE27, 0x7D079EB1, 69 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 70 | 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 71 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 72 | 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 73 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 74 | 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 75 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 76 | 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 77 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 78 | 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 79 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 80 | 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 81 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x26D930A, 82 | 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x5005713, 83 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0xCB61B38, 84 | 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21, 85 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 86 | 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 87 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 88 | 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 89 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 90 | 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 91 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 92 | 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 93 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 94 | 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 95 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 96 | 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 97 | }; 98 | 99 | int crc_table_computed = 0; 100 | 101 | void make_crc_table(void) 102 | { 103 | unsigned long c; 104 | int n, k; 105 | 106 | for (n = 0; n < 256; n++) { 107 | c = (unsigned long) n; 108 | for (k = 0; k < 8; k++) { 109 | if (c & 1) 110 | c = 0xedb88320L ^ (c >> 1); 111 | else 112 | c = c >> 1; 113 | } 114 | crc_table[n] = (uint32_t)c; 115 | } 116 | crc_table_computed = 1; 117 | } 118 | 119 | uint32_t crc_update(uint32_t c, const uint8_t* buf, uint64_t len) 120 | { 121 | if (!crc_table_computed) 122 | make_crc_table(); 123 | 124 | for (uint64_t n = 0; n < len; n++) { 125 | c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); 126 | } 127 | return c; 128 | } 129 | 130 | uint32_t crc_calc(const uint8_t* data, uint64_t size) 131 | { 132 | return crc_update(0xffffffff, data, size) ^ 0xffffffff; 133 | } 134 | -------------------------------------------------------------------------------- /libbon/bon/crc32.h: -------------------------------------------------------------------------------- 1 | // 2 | // crc32.h 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #ifndef BON_crc32_h 10 | #define BON_crc32_h 11 | 12 | #include 13 | 14 | /* 15 | The normal CRC32 found in GZIP, PNG etc. 16 | http://www.faqs.org/rfcs/rfc1952.html 17 | http://www.w3.org/TR/PNG/#D-CRCAppendix 18 | */ 19 | 20 | /* 21 | Usage: 22 | uint8_t data[] = { .... }; 23 | 24 | uint32_t crc_inv = 0xffffffff; // initial value 25 | crc_inv = crc_update(crc_inv, buff1, sizeof(buff1)); 26 | crc_inv = crc_update(crc_inv, buff2, sizeof(buff2)); 27 | uint32_t crc = crc_inv ^ 0xffffffff; // Final invert 28 | */ 29 | uint32_t crc_update(uint32_t crc_inv, const uint8_t* data, uint64_t size); 30 | 31 | // Calcualte the crc of the given bytes. 32 | uint32_t crc_calc(const uint8_t* data, uint64_t size); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /libbon/bon/inline.h: -------------------------------------------------------------------------------- 1 | // 2 | // inline.h 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #ifndef BON_inline_h 10 | #define BON_inline_h 11 | 12 | #include // memcpy 13 | #include "private.h" 14 | 15 | /* 16 | Inlining functions gives us speedups of 3x in some cases! 17 | */ 18 | 19 | #include "write_inline.h" 20 | #include "read_inline.h" 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /libbon/bon/private.h: -------------------------------------------------------------------------------- 1 | // 2 | // private.h 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #ifndef BON_private_h 10 | #define BON_private_h 11 | 12 | #include 13 | 14 | /* 15 | This is NOT part of the public API. 16 | Only include this file if you want to poke around the internals of BON! 17 | HERE BE DRAGONS 18 | */ 19 | 20 | 21 | //------------------------------------------------------------------------------ 22 | 23 | typedef enum { 24 | BON_VALUE_NONE = 0, 25 | BON_VALUE_NIL = BON_CTRL_NIL, 26 | BON_VALUE_BOOL = BON_TYPE_BOOL, 27 | BON_VALUE_UINT64 = BON_TYPE_UINT64, 28 | BON_VALUE_SINT64 = BON_TYPE_SINT64, 29 | BON_VALUE_DOUBLE = BON_TYPE_DOUBLE, 30 | BON_VALUE_STRING = BON_TYPE_STRING, 31 | BON_VALUE_BLOCK_REF = BON_CTRL_BLOCK_REF, 32 | BON_VALUE_LIST = BON_CTRL_LIST_BEGIN, 33 | BON_VALUE_OBJ = BON_CTRL_OBJ_BEGIN, 34 | BON_VALUE_AGGREGATE = 255, // Won't conflict with any of the aboe 35 | } bon_value_type; 36 | 37 | 38 | 39 | //------------------------------------------------------------------------------ 40 | 41 | 42 | /* A code byte is divided into several ranges of values. 43 | This enum specifies those ranges. 44 | */ 45 | typedef enum { 46 | BON_SHORT_POS_INT_START = 0, 47 | BON_SHORT_POS_INT_COUNT = 32, 48 | 49 | BON_SHORT_STRING_START = 32, 50 | BON_SHORT_STRING_COUNT = 32, 51 | 52 | BON_SHORT_CODES_START = 64, // Control codes - see bon_ctrl 53 | BON_SHORT_CODES_COUNT = 64, 54 | 55 | BON_SHORT_BLOCK_START = 128, 56 | BON_SHORT_BLOCK_COUNT = 64, 57 | BON_SHORT_BLOCK_END = BON_SHORT_BLOCK_START + BON_SHORT_BLOCK_COUNT, 58 | 59 | BON_SHORT_ARRAY_START = BON_SHORT_BLOCK_END, 60 | BON_SHORT_ARRAY_COUNT = 16, 61 | 62 | BON_SHORT_BYTE_ARRAY_START = BON_SHORT_ARRAY_START + BON_SHORT_ARRAY_COUNT, 63 | BON_SHORT_BYTE_ARRAY_COUNT = 16, 64 | 65 | BON_SHORT_STRUCT_START = BON_SHORT_BYTE_ARRAY_START + BON_SHORT_BYTE_ARRAY_COUNT, 66 | BON_SHORT_STRUCT_COUNT = 16, 67 | 68 | BON_SHORT_NEG_INT_START = BON_SHORT_STRUCT_START + BON_SHORT_STRUCT_COUNT, 69 | BON_SHORT_NEG_INT_COUNT = 16, 70 | 71 | BON_SHORT_AGGREGATES_START = BON_SHORT_ARRAY_START 72 | } bon_compressed_ranges; 73 | 74 | #ifdef DEBUG 75 | # define BON_COMPRESS_(start, count, n) (assert(n < count), (uint8_t)((start) + n)) 76 | #else 77 | # define BON_COMPRESS_(start, count, n) (uint8_t)((start) + n) 78 | #endif 79 | 80 | #define BON_COMPRESS(prefix, n) BON_COMPRESS_(prefix ## START, prefix ## COUNT, n) 81 | 82 | #define BON_SHORT_STRING(n) BON_COMPRESS(BON_SHORT_STRING_, n) 83 | #define BON_SHORT_BLOCK(n) BON_COMPRESS(BON_SHORT_BLOCK_, n) 84 | #define BON_SHORT_ARRAY(n) BON_COMPRESS(BON_SHORT_ARRAY_, n) 85 | #define BON_SHORT_BYTE_ARRAY(n) BON_COMPRESS(BON_SHORT_BYTE_ARRAY_, n) 86 | #define BON_SHORT_STRUCT(n) BON_COMPRESS(BON_SHORT_STRUCT_, n) 87 | 88 | 89 | 90 | //------------------------------------------------------------------------------ 91 | // bon_type etc 92 | 93 | typedef struct bon_type_array bon_type_array; 94 | typedef struct bon_kt bon_kt; 95 | 96 | struct bon_type_array { 97 | bon_size size; 98 | bon_type* type; 99 | }; 100 | 101 | typedef struct bon_type_struct bon_type_struct; 102 | struct bon_type_struct { 103 | bon_size size; // size of 'kts' 104 | bon_kt* kts; // Key-type map 105 | }; 106 | 107 | struct bon_type { 108 | bon_type_id id; 109 | 110 | // Further info (if applicable) 111 | union { 112 | bon_type_array* array; 113 | bon_type_struct* strct; 114 | } u; 115 | }; 116 | 117 | 118 | // key-type pair 119 | struct bon_kt { 120 | /* 121 | Key is in UTF8. 122 | It either points to inside of a read BON document 123 | or to a string literal supplied by the user 124 | */ 125 | const char* key; 126 | bon_type type; 127 | }; 128 | 129 | 130 | //------------------------------------------------------------------------------ 131 | 132 | 133 | struct bon_w_doc { 134 | bon_w_writer_t writer; 135 | void* userData; // Sent to writer 136 | 137 | // Write buffer: 138 | uint8_t* buff; 139 | bon_size buff_size; // size of 'buff 140 | bon_size buff_ix; // usage of 'buff' 141 | 142 | uint32_t crc_inv; // Accumulator of crc value (if BON_W_FLAG_CRC is set) 143 | bon_w_flags flags; 144 | bon_error error; // If any 145 | }; 146 | 147 | //------------------------------------------------------------------------------ 148 | 149 | 150 | 151 | typedef struct bon_kv bon_kv; 152 | 153 | 154 | typedef struct { 155 | bon_size size; // Size in bytes, excluding trailing zero. 156 | const char* ptr; // Points to 'size' bytes of an utf8 encoded string, followed by a zero. 157 | } bon_value_str; 158 | 159 | 160 | typedef struct { 161 | bon_size size; 162 | bon_value* data; 163 | } bon_list; 164 | 165 | 166 | typedef struct { 167 | bon_type type; 168 | const uint8_t* data; 169 | bon_value* exploded; // if non-NULL, this contains the packed data in explicit form. Lazily calculated iff user queires it. 170 | } bon_value_agg; 171 | 172 | 173 | typedef struct { 174 | bon_size size; 175 | bon_kv* data; 176 | } bon_obj; 177 | 178 | 179 | typedef union { 180 | bon_bool boolean; 181 | uint64_t u64; 182 | int64_t s64; 183 | double dbl; 184 | bon_value_str str; 185 | bon_list list; 186 | bon_obj obj; 187 | bon_value_agg* agg; // Pointer to keep down size of bon_value 188 | bon_block_id blockRefId; 189 | } bon_value_union; 190 | 191 | 192 | // 24 bytes on a 64-bit machine. 193 | struct bon_value { 194 | bon_value_type type; 195 | bon_value_union u; 196 | }; 197 | 198 | 199 | /* Low level statistics about a bon-file. */ 200 | typedef struct { 201 | bon_size bytes_file; // Number of bytes in the entire file 202 | 203 | bon_size count_string; 204 | bon_size bytes_string_dry; // Number of bytes taken up by strings (excluding fluff). 205 | //bon_size bytes_string_wet; // Number of bytes taken up by strings (including header and zero byte). 206 | 207 | bon_size count_aggr; 208 | bon_size bytes_aggr_dry; // Number of bytes taken up by strings (excluding header). 209 | //bon_size bytes_aggr_wet; // Number of bytes taken up by strings (including header). 210 | } bon_stats; 211 | 212 | 213 | typedef struct { 214 | bon_block_id id; // Id of block 215 | const uint8_t* payload; // Pointer into document to payload start 216 | bon_size payload_size; // Byte size of payload. 0 means unknown. 217 | bon_value value; // value, if 'parsed' is true. 218 | bon_bool parsed; // if false, 'value' is not yet valid. 219 | } bon_r_block; 220 | 221 | 222 | typedef struct { 223 | bon_size size; 224 | bon_size cap; 225 | bon_r_block* data; 226 | } bon_r_blocks; 227 | 228 | 229 | struct bon_r_doc { 230 | bon_r_blocks blocks; 231 | bon_stats stats; // Info about the read file 232 | bon_r_flags flags; 233 | bon_error error; // If any 234 | char* errstr; // If applicable 235 | }; 236 | 237 | 238 | struct bon_kv { 239 | const char* key; // UTF8 - points to inside of document 240 | bon_value val; 241 | }; 242 | 243 | 244 | //------------------------------------------------------------------------------ 245 | 246 | 247 | // Helper for reading a binary stream without overflowing: 248 | 249 | typedef struct { 250 | bon_r_doc* B; 251 | const uint8_t* data; // Incremented as we read 252 | bon_size nbytes; // Decremented as we read 253 | bon_block_id block_id; // Current block being read, or BON_BAD_BLOCK_ID. 254 | 255 | // For quick access 256 | bon_error error; 257 | const uint8_t* err_offset; // Where was the error triggered? 258 | bon_r_flags flags; 259 | } bon_reader; 260 | 261 | bon_reader make_br(bon_r_doc* B, const uint8_t* data, bon_size nbytes, bon_block_id blockid); 262 | 263 | 264 | /* Read a simple value denoted by 't', and interpret is as a signed int. */ 265 | int64_t br_read_sint64(bon_reader* br, bon_type_id t); 266 | 267 | /* Read a simple value denoted by 't', and interpret is as an unsigned int. */ 268 | uint64_t br_read_uint64(bon_reader* br, bon_type_id t); 269 | 270 | /* Read a simple value denoted by 't', and interpret is as a double. */ 271 | double br_read_double(bon_reader* br, bon_type_id t); 272 | 273 | 274 | //------------------------------------------------------------------------------ 275 | // Things common to bon.c, write.c, read.c: 276 | 277 | void bon_onError(const char* msg); 278 | 279 | bon_type* bon_new_type_fmt_ap(const char** fmt, va_list* ap); 280 | 281 | bon_size bon_aggregate_payload_size(const bon_type* type); 282 | bon_size bon_struct_payload_size(const bon_type_struct* strct); 283 | 284 | void bon_free_type_insides(bon_type* t); 285 | 286 | // Byte size of atomic types 287 | uint64_t bon_type_size(bon_type_id t); 288 | 289 | // Returns NULL on fail 290 | bon_value* bon_r_get_block(bon_r_doc* B, bon_block_id block_id); 291 | 292 | static bon_value* bon_r_follow_refs(bon_r_doc* B, bon_value* val); 293 | 294 | // Endianness conversion (used for crc32) 295 | uint32_t le_to_uint32(uint32_t v); 296 | uint32_t uint32_to_le(uint32_t v); 297 | 298 | //------------------------------------------------------------------------------ 299 | 300 | // TODO: handle failed allocs 301 | #define BON_ALLOC_TYPE(n, type) (type*)malloc(n * sizeof(type)) 302 | #define BON_CALLOC_TYPE(n, type) (type*)calloc(n, sizeof(type)) 303 | 304 | #if 0 305 | // Doubling in size. Only makes about 4% (positive) dfference in speed. 306 | #define BON_VECTOR_EXPAND(vec, Type, amnt) \ 307 | /**/ (vec).size += amnt; \ 308 | /**/ if ((vec).size > (vec).cap) { \ 309 | /**/ size_t newCap = (vec).cap ? 2 * (vec).cap : 32; \ 310 | /**/ if (newCap < amnt) { newCap = 2*amnt; } \ 311 | /**/ (vec).data = (Type*)realloc((vec).data, newCap * sizeof(Type)); \ 312 | /**/ (vec).cap = newCap; \ 313 | /**/ } 314 | 315 | #else 316 | #define BON_VECTOR_EXPAND(vec, Type, amnt) \ 317 | /**/ (vec).size += amnt; \ 318 | /**/ if ((vec).size > (vec).cap) { \ 319 | /**/ size_t newCap = ((vec).size + 2) + (vec).size/2; \ 320 | /**/ (vec).data = (Type*)realloc((vec).data, newCap * sizeof(Type)); \ 321 | /**/ (vec).cap = newCap; \ 322 | /**/ } 323 | #endif 324 | 325 | 326 | 327 | //------------------------------------------------------------------------------ 328 | 329 | 330 | #endif 331 | -------------------------------------------------------------------------------- /libbon/bon/read_inline.h: -------------------------------------------------------------------------------- 1 | // 2 | // read_inline.h 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #ifndef BON_read_inline_h 10 | #define BON_read_inline_h 11 | 12 | 13 | 14 | //------------------------------------------------------------------------------ 15 | 16 | BON_INLINE bon_bool bon_r_is_nil(bon_r_doc* B, bon_value* val) 17 | { 18 | val = bon_r_follow_refs(B, val); 19 | if (!val) { return BON_FALSE; } 20 | return val->type == BON_VALUE_NIL; 21 | } 22 | 23 | BON_INLINE bon_bool bon_r_is_bool(bon_r_doc* B, bon_value* val) 24 | { 25 | val = bon_r_follow_refs(B, val); 26 | if (!val) { return BON_FALSE; } 27 | return val->type == BON_VALUE_BOOL; 28 | } 29 | 30 | BON_INLINE bon_bool bon_r_is_int(bon_r_doc* B, bon_value* val) 31 | { 32 | val = bon_r_follow_refs(B, val); 33 | if (!val) { return BON_FALSE; } 34 | 35 | return val->type == BON_VALUE_SINT64 36 | || val->type == BON_VALUE_UINT64; 37 | } 38 | 39 | BON_INLINE bon_bool bon_r_is_number(bon_r_doc* B, bon_value* val) 40 | { 41 | val = bon_r_follow_refs(B, val); 42 | if (!val) { return BON_FALSE; } 43 | 44 | return val->type == BON_VALUE_SINT64 45 | || val->type == BON_VALUE_UINT64 46 | || val->type == BON_VALUE_DOUBLE; 47 | } 48 | 49 | BON_INLINE bon_bool bon_r_is_double(bon_r_doc* B, bon_value* val) 50 | { 51 | val = bon_r_follow_refs(B, val); 52 | if (!val) { return BON_FALSE; } 53 | return val->type == BON_VALUE_DOUBLE; 54 | } 55 | 56 | BON_INLINE bon_bool bon_r_is_string(bon_r_doc* B, bon_value* val) 57 | { 58 | val = bon_r_follow_refs(B, val); 59 | if (!val) { return BON_FALSE; } 60 | return val->type == BON_VALUE_STRING; 61 | } 62 | 63 | BON_INLINE bon_bool bon_r_is_list(bon_r_doc* B, bon_value* val) 64 | { 65 | val = bon_r_follow_refs(B, val); 66 | if (!val) { return BON_FALSE; } 67 | if (val->type == BON_VALUE_LIST) { return BON_TRUE; } 68 | if (val->type == BON_VALUE_AGGREGATE) { 69 | const bon_value_agg* agg = val->u.agg; 70 | return agg->type.id == BON_TYPE_ARRAY; 71 | } 72 | return BON_FALSE; 73 | } 74 | 75 | BON_INLINE bon_bool bon_r_is_object(bon_r_doc* B, bon_value* val) 76 | { 77 | val = bon_r_follow_refs(B, val); 78 | if (!val) { return BON_FALSE; } 79 | 80 | if (val->type == BON_VALUE_OBJ) { return BON_TRUE; } 81 | if (val->type == BON_VALUE_AGGREGATE) { 82 | const bon_value_agg* agg = val->u.agg; 83 | return agg->type.id == BON_TYPE_STRUCT; 84 | } 85 | return BON_FALSE; 86 | } 87 | 88 | 89 | BON_INLINE bon_logical_type bon_r_value_type(bon_r_doc* B, bon_value* val) 90 | { 91 | val = bon_r_follow_refs(B, val); 92 | if (!val) { return BON_LOGICAL_ERROR; } 93 | 94 | switch (val->type) 95 | { 96 | case BON_VALUE_NIL: { return BON_LOGICAL_NIL; } 97 | case BON_VALUE_BOOL: { return BON_LOGICAL_BOOL; } 98 | case BON_VALUE_UINT64: { return BON_LOGICAL_UINT; } 99 | case BON_VALUE_SINT64: { return BON_LOGICAL_SINT; } 100 | case BON_VALUE_DOUBLE: { return BON_LOGICAL_DOUBLE; } 101 | case BON_VALUE_STRING: { return BON_LOGICAL_STRING; } 102 | case BON_VALUE_LIST: { return BON_LOGICAL_LIST; } 103 | case BON_VALUE_OBJ: { return BON_LOGICAL_OBJECT; } 104 | 105 | case BON_VALUE_AGGREGATE: { 106 | if (bon_r_is_list(B, val)) { 107 | return BON_LOGICAL_LIST; 108 | } else if (bon_r_is_object(B, val)) { 109 | return BON_LOGICAL_OBJECT; 110 | } else { 111 | return BON_LOGICAL_ERROR; 112 | } 113 | } 114 | 115 | default: { 116 | return BON_LOGICAL_ERROR; 117 | } 118 | } 119 | } 120 | 121 | 122 | //------------------------------------------------------------------------------ 123 | 124 | 125 | bon_value* bon_exploded_aggr(bon_r_doc* B, bon_value* val); 126 | 127 | BON_INLINE bon_value* bon_r_follow_refs(bon_r_doc* B, bon_value* val) 128 | { 129 | /* We should be protected from infinite recursion here, 130 | since we check all blockRefId on reading, 131 | ensuring they only point to blocks with higher ID:s. */ 132 | while (val && val->type == BON_VALUE_BLOCK_REF && B->error==0) 133 | { 134 | val = bon_r_get_block(B, val->u.blockRefId); 135 | } 136 | return val; 137 | } 138 | 139 | 140 | BON_INLINE bon_value* follow_and_explode(bon_r_doc* B, bon_value* val) 141 | { 142 | val = bon_r_follow_refs(B, val); 143 | 144 | if (val && val->type == BON_VALUE_AGGREGATE) { 145 | // If the user treats it like an object, convert it into one. 146 | val = bon_exploded_aggr(B, val); 147 | } 148 | 149 | return val; 150 | } 151 | 152 | 153 | //------------------------------------------------------------------------------ 154 | 155 | BON_INLINE bon_bool bon_r_bool(bon_r_doc* B, bon_value* val) 156 | { 157 | val = bon_r_follow_refs(B, val); 158 | 159 | if (val && val->type == BON_VALUE_BOOL) { 160 | return val->u.boolean; 161 | } else { 162 | return BON_FALSE; 163 | } 164 | } 165 | 166 | 167 | BON_INLINE int64_t bon_r_int(bon_r_doc* B, bon_value* val) 168 | { 169 | val = bon_r_follow_refs(B, val); 170 | 171 | if (!val) { 172 | return 0; 173 | } else if (val->type == BON_VALUE_UINT64) { 174 | return (int64_t)val->u.u64; 175 | } else if (val->type == BON_VALUE_SINT64) { 176 | return val->u.s64; 177 | } else if (val->type == BON_VALUE_DOUBLE) { 178 | return (int64_t)val->u.dbl; 179 | } else { 180 | return 0; 181 | } 182 | } 183 | 184 | 185 | BON_INLINE uint64_t bon_r_uint(bon_r_doc* B, bon_value* val) 186 | { 187 | val = bon_r_follow_refs(B, val); 188 | 189 | if (val && val->type == BON_VALUE_UINT64) { 190 | return val->u.u64; 191 | } else { 192 | return (uint64_t)bon_r_int(B, val); 193 | } 194 | } 195 | 196 | 197 | BON_INLINE double bon_r_double(bon_r_doc* B, bon_value* val) 198 | { 199 | val = bon_r_follow_refs(B, val); 200 | 201 | if (!val) { 202 | return 0; 203 | } else if (val->type == BON_VALUE_DOUBLE) { 204 | return val->u.dbl; 205 | } else if (val->type == BON_VALUE_UINT64) { 206 | return val->u.u64; 207 | } else if (val->type == BON_VALUE_SINT64) { 208 | return val->u.s64; 209 | } else { 210 | return 0; 211 | } 212 | } 213 | 214 | 215 | BON_INLINE float bon_r_float(bon_r_doc* B, bon_value* obj) 216 | { 217 | return (float)bon_r_double(B, obj); 218 | } 219 | 220 | 221 | 222 | BON_INLINE bon_size bon_r_strlen(bon_r_doc* B, bon_value* val) 223 | { 224 | val = bon_r_follow_refs(B, val); 225 | 226 | if (!val) { 227 | return 0; 228 | } else if (val->type == BON_VALUE_STRING) { 229 | return val->u.str.size; 230 | } else { 231 | return 0; 232 | } 233 | } 234 | 235 | 236 | BON_INLINE const char* bon_r_cstr(bon_r_doc* B, bon_value* val) 237 | { 238 | val = bon_r_follow_refs(B, val); 239 | 240 | if (!val) { 241 | return NULL; 242 | } else if (val->type == BON_VALUE_STRING) { 243 | return (const char*)val->u.str.ptr; 244 | } else { 245 | return NULL; 246 | } 247 | } 248 | 249 | 250 | BON_INLINE bon_size bon_r_list_size(bon_r_doc* B, bon_value* val) 251 | { 252 | val = bon_r_follow_refs(B, val); 253 | if (!val) { return 0; } 254 | 255 | switch (val->type) { 256 | case BON_VALUE_LIST: 257 | return val->u.list.size; 258 | 259 | case BON_VALUE_AGGREGATE: { 260 | const bon_value_agg* agg = val->u.agg; 261 | if (agg->type.id == BON_TYPE_ARRAY) { 262 | return agg->type.u.array->size; 263 | } else { 264 | return 0; 265 | } 266 | } 267 | 268 | default: 269 | return 0; 270 | } 271 | } 272 | 273 | 274 | BON_INLINE bon_value* bon_r_list_elem(bon_r_doc* B, bon_value* val, bon_size ix) 275 | { 276 | val = follow_and_explode(B, val); 277 | if (!val) { return NULL; } 278 | 279 | if (val->type == BON_VALUE_LIST) 280 | { 281 | const bon_list* vals = &val->u.list; 282 | if (ix < vals->size) { 283 | return &vals->data[ ix ]; 284 | } 285 | } 286 | 287 | return NULL; 288 | } 289 | 290 | 291 | //------------------------------------------------------------------------------ 292 | 293 | 294 | // Number of keys-value pairs in an object 295 | BON_INLINE bon_size bon_r_obj_size(bon_r_doc* B, bon_value* val) 296 | { 297 | val = bon_r_follow_refs(B, val); 298 | if (!val) { return 0; } 299 | 300 | switch (val->type) { 301 | case BON_VALUE_OBJ: 302 | return val->u.obj.size; 303 | 304 | case BON_VALUE_AGGREGATE: { 305 | const bon_value_agg* agg = val->u.agg; 306 | if (agg->type.id == BON_TYPE_STRUCT) { 307 | return agg->type.u.strct->size; 308 | } else { 309 | return 0; 310 | } 311 | } 312 | 313 | default: 314 | return 0; 315 | } 316 | } 317 | 318 | // Return NULL if 'val' is not an object, or ix is out of range. 319 | BON_INLINE const char* bon_r_obj_key(bon_r_doc* B, bon_value* val, bon_size ix) 320 | { 321 | val = follow_and_explode(B, val); 322 | 323 | if (!val || val->type != BON_VALUE_OBJ) { 324 | return NULL; 325 | } 326 | 327 | bon_obj* kvs = &val->u.obj; 328 | if (ix < kvs->size) { 329 | return kvs->data[ix].key; 330 | } else { 331 | return NULL; 332 | } 333 | } 334 | 335 | BON_INLINE bon_value* bon_r_obj_value(bon_r_doc* B, bon_value* val, bon_size ix) 336 | { 337 | val = follow_and_explode(B, val); 338 | 339 | if (!val || val->type != BON_VALUE_OBJ) { 340 | return NULL; 341 | } 342 | 343 | bon_obj* kvs = &val->u.obj; 344 | if (ix < kvs->size) { 345 | return &kvs->data[ix].val; 346 | } else { 347 | return NULL; 348 | } 349 | } 350 | 351 | 352 | BON_INLINE bon_value* bon_r_get_key(bon_r_doc* B, bon_value* val, const char* key) 353 | { 354 | val = follow_and_explode(B, val); 355 | 356 | if (!val || val->type != BON_VALUE_OBJ) { 357 | return NULL; 358 | } 359 | 360 | const bon_obj* kvs = &val->u.obj; 361 | 362 | for (bon_size i=0; isize; ++i) { 363 | bon_kv* kv = &kvs->data[i]; 364 | 365 | // The key should always be a string: 366 | if (strcmp(key, kv->key) == 0) { 367 | return &kv->val; 368 | } 369 | } 370 | 371 | return NULL; 372 | } 373 | 374 | 375 | //------------------------------------------------------------------------------ 376 | 377 | 378 | #endif 379 | -------------------------------------------------------------------------------- /libbon/bon/type.c: -------------------------------------------------------------------------------- 1 | // 2 | // type.c 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #include "bon.h" 10 | #include "private.h" 11 | #include 12 | #include // malloc, free, calloc 13 | #include // va_list, va_start, va_arg, va_end 14 | #include // strcmp 15 | 16 | 17 | 18 | //------------------------------------------------------------------------------ 19 | 20 | void bon_free_type_insides(bon_type* t) 21 | { 22 | switch (t->id) { 23 | case BON_TYPE_ARRAY: 24 | bon_free_type(t->u.array->type); 25 | free(t->u.array); 26 | break; 27 | 28 | case BON_TYPE_STRUCT: { 29 | for (bon_size ti=0; ti < t->u.strct->size; ++ti) { 30 | bon_free_type_insides( &t->u.strct->kts[ti].type ); 31 | } 32 | free(t->u.strct->kts); 33 | free(t->u.strct); 34 | } break; 35 | 36 | default: 37 | break; 38 | } 39 | } 40 | 41 | void bon_free_type(bon_type* t) { 42 | bon_free_type_insides(t); 43 | free(t); 44 | } 45 | 46 | bon_type* bon_new_type_simple(bon_type_id id) { 47 | bon_type* t = BON_ALLOC_TYPE(1, bon_type); 48 | t->id = id; 49 | return t; 50 | } 51 | 52 | bon_type* bon_new_type_simple_array(bon_size n, bon_type_id id) { 53 | return bon_new_type_array(n, bon_new_type_simple(id)); 54 | } 55 | 56 | bon_type* bon_new_type_array(bon_size n, bon_type* type) { 57 | bon_type* t = BON_ALLOC_TYPE(1, bon_type); 58 | t->id = BON_TYPE_ARRAY; 59 | t->u.array = BON_ALLOC_TYPE(1, bon_type_array); 60 | t->u.array->size = n; 61 | t->u.array->type = type; 62 | return t; 63 | } 64 | 65 | bon_type* bon_new_type_struct(bon_size n, const char** names, bon_type** types) { 66 | 67 | bon_type_struct* strct = BON_ALLOC_TYPE(1, bon_type_struct);; 68 | strct->size = n; 69 | strct->kts = BON_ALLOC_TYPE(n, bon_kt); 70 | 71 | for (bon_size i=0; ikts[i].key = names[i]; 73 | strct->kts[i].type = *types[i]; 74 | free( types[i] ); 75 | } 76 | 77 | bon_type* t = BON_ALLOC_TYPE(1, bon_type); 78 | t->id = BON_TYPE_STRUCT; 79 | t->u.strct = strct; 80 | return t; 81 | } 82 | 83 | bon_type* bon_new_type_fmt_ap_obj(const char** fmt, va_list* ap) 84 | { 85 | if (**fmt != '{') { 86 | return NULL; 87 | } 88 | ++*fmt; 89 | 90 | typedef struct { 91 | bon_size size, cap; 92 | bon_kt* data; 93 | } exp_strct; 94 | 95 | exp_strct expStrct = {0,0,0}; 96 | 97 | for (;;) { 98 | switch (**fmt) { 99 | case '}': { 100 | ++*fmt; 101 | 102 | bon_type_struct* strct = BON_ALLOC_TYPE(1, bon_type_struct); 103 | strct->size = expStrct.size; 104 | strct->kts = expStrct.data; 105 | 106 | bon_type* ret = BON_ALLOC_TYPE(1, bon_type); 107 | ret->id = BON_TYPE_STRUCT; 108 | ret->u.strct = strct; 109 | return ret; 110 | } 111 | 112 | case '\0': 113 | fprintf(stderr, "obj with no ending }\n"); 114 | goto on_error; 115 | 116 | case '$': { 117 | ++*fmt; // Skip key placeholder ($) 118 | const char* key = va_arg(*ap, const char*); 119 | bon_type* tmp_type = bon_new_type_fmt_ap(fmt, ap); 120 | if (!tmp_type) { 121 | goto on_error; 122 | } 123 | 124 | BON_VECTOR_EXPAND(expStrct, bon_kt, 1); 125 | 126 | bon_kt* kt = expStrct.data + expStrct.size - 1; 127 | kt->key = key; 128 | kt->type = *tmp_type; 129 | free(tmp_type); 130 | } break; 131 | 132 | default: { 133 | fprintf(stderr, "obj with no key placeholder ($)\n"); 134 | goto on_error; 135 | } 136 | } 137 | } 138 | 139 | 140 | on_error: 141 | for (bon_size ix=0; ixsize; ++i) { 258 | sum += bon_aggregate_payload_size( &strct->kts[i].type ); 259 | } 260 | return sum; 261 | } 262 | 263 | /* the size of the payload. */ 264 | bon_size bon_aggregate_payload_size(const bon_type* type) 265 | { 266 | switch (type->id) { 267 | case BON_TYPE_ARRAY: 268 | return type->u.array->size * 269 | bon_aggregate_payload_size(type->u.array->type); 270 | 271 | case BON_TYPE_STRUCT: { 272 | return bon_struct_payload_size(type->u.strct); 273 | } 274 | 275 | default: 276 | return bon_type_size( type->id ); 277 | } 278 | } 279 | 280 | bon_bool bon_type_eq(const bon_type* a, const bon_type* b) 281 | { 282 | if (a==b) { return BON_TRUE; } 283 | if (!a || !b) { return BON_FALSE; } 284 | if (a->id != b->id) { return BON_FALSE; } 285 | 286 | if (a->id == BON_TYPE_ARRAY) { 287 | if (a->u.array->size != b->u.array->size) { return BON_FALSE; } 288 | return bon_type_eq(a->u.array->type, b->u.array->type); 289 | } else if (a->id == BON_TYPE_STRUCT) { 290 | const bon_type_struct* as = a->u.strct; 291 | const bon_type_struct* bs = b->u.strct; 292 | if (as->size != bs->size) { return BON_FALSE; } 293 | for (bon_size ix=0; ixsize; ++ix) { 294 | if (strcmp(as->kts[ix].key, bs->kts[ix].key) != 0) { return BON_FALSE; } 295 | if (!bon_type_eq(&as->kts[ix].type, &bs->kts[ix].type)) { return BON_FALSE; } 296 | } 297 | return BON_TRUE; 298 | } else { 299 | return a->id == b->id; 300 | } 301 | } 302 | 303 | //------------------------------------------------------------------------------ 304 | 305 | uint64_t bon_type_size(bon_type_id t) 306 | { 307 | switch (t) 308 | { 309 | case BON_TYPE_SINT8: 310 | case BON_TYPE_UINT8: 311 | return 1; 312 | 313 | case BON_TYPE_SINT16_LE: 314 | case BON_TYPE_SINT16_BE: 315 | case BON_TYPE_UINT16_LE: 316 | case BON_TYPE_UINT16_BE: 317 | return 2; 318 | 319 | case BON_TYPE_SINT32_LE: 320 | case BON_TYPE_SINT32_BE: 321 | case BON_TYPE_UINT32_LE: 322 | case BON_TYPE_UINT32_BE: 323 | case BON_TYPE_FLOAT_LE: 324 | case BON_TYPE_FLOAT_BE: 325 | return 4; 326 | 327 | case BON_TYPE_SINT64_LE: 328 | case BON_TYPE_SINT64_BE: 329 | case BON_TYPE_UINT64_LE: 330 | case BON_TYPE_UINT64_BE: 331 | case BON_TYPE_DOUBLE_LE: 332 | case BON_TYPE_DOUBLE_BE: 333 | return 8; 334 | 335 | default: 336 | fprintf(stderr, "BON: bad type in 'bon_type_size'\n"); 337 | return 0; 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /libbon/bon/write.c: -------------------------------------------------------------------------------- 1 | // 2 | // write.c 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | 10 | #include "bon.h" 11 | #include "private.h" 12 | #include "crc32.h" 13 | #include 14 | #include // va_list, va_start, va_arg, va_end 15 | #include // malloc, free, realloc, calloc, ... 16 | 17 | 18 | //------------------------------------------------------------------------------ 19 | 20 | 21 | bon_bool bon_vec_writer(void* userData, const void* data, uint64_t nbytes) { 22 | bon_byte_vec* vec = (bon_byte_vec*)userData; 23 | bon_size oldSize = vec->size; 24 | BON_VECTOR_EXPAND(*vec, uint8_t, nbytes); 25 | if (vec->data) { 26 | memcpy(vec->data + oldSize, data, nbytes); 27 | return BON_TRUE; 28 | } else { 29 | // Alloc fail 30 | return BON_FALSE; 31 | } 32 | } 33 | 34 | 35 | bon_bool bon_file_writer(void* user, const void* data, uint64_t nbytes) 36 | { 37 | FILE* fp = (FILE*)user; 38 | fwrite(data, 1, nbytes, fp); 39 | return !ferror(fp); 40 | } 41 | 42 | 43 | //------------------------------------------------------------------------------ 44 | 45 | 46 | void bon_w_set_error(bon_w_doc* B, bon_error err) 47 | { 48 | bon_onError(bon_err_str(err)); 49 | 50 | if (B->error == BON_SUCCESS) { 51 | B->error = err; 52 | } 53 | } 54 | 55 | const char* bon_w_err_str(bon_w_doc* B) 56 | { 57 | if (B->error) { 58 | return bon_err_str(B->error); 59 | } else { 60 | return "No error"; 61 | } 62 | } 63 | 64 | void bon_w_assert(bon_w_doc* B, bon_bool statement, bon_error onFail) 65 | { 66 | if (!statement) { 67 | bon_w_set_error(B, onFail); 68 | } 69 | } 70 | 71 | 72 | 73 | //------------------------------------------------------------------------------ 74 | // Writing 75 | 76 | 77 | // Bypass buffer 78 | BON_INLINE void bon_write_to_writer(bon_w_doc* B, const void* data, bon_size n) 79 | { 80 | if (!B->writer(B->userData, data, n)) { 81 | B->error = BON_ERR_WRITE_ERROR; 82 | } 83 | 84 | if (B->flags & BON_W_FLAG_CRC) { 85 | B->crc_inv = crc_update(B->crc_inv, (const uint8_t*)data, n); 86 | } 87 | } 88 | 89 | void bon_w_flush(bon_w_doc* B) { 90 | if (B->buff_ix > 0) { 91 | bon_write_to_writer(B, B->buff, B->buff_ix); 92 | B->buff_ix = 0; 93 | } 94 | } 95 | 96 | void bon_w_raw_flush_buff(bon_w_doc* B, const void* data, bon_size bs) { 97 | if (!B->buff) { 98 | // Unbuffered 99 | bon_write_to_writer(B, data, bs); 100 | return; 101 | } 102 | 103 | const bon_size BIG_CHUNK = 1024; 104 | 105 | if (bs >= BIG_CHUNK) { 106 | /* 107 | The power of the buffer is to mitigate many small writes (death by many small cuts). 108 | A big chunk like this can be sent right to the writer. 109 | */ 110 | bon_w_flush(B); 111 | bon_write_to_writer(B, data, bs); 112 | return; 113 | } 114 | 115 | if (B->buff_ix + bs < B->buff_size) { 116 | // Fits in the buffer 117 | memcpy(B->buff + B->buff_ix, data, bs); 118 | B->buff_ix += bs; 119 | } else { 120 | bon_w_flush(B); 121 | 122 | if (bs < B->buff_size) { 123 | // Now it fits 124 | memcpy(B->buff, data, bs); 125 | B->buff_ix = bs; 126 | } else { 127 | // Too large for buffer - write directly: 128 | bon_write_to_writer(B, data, bs); 129 | } 130 | } 131 | } 132 | 133 | 134 | 135 | //------------------------------------------------------------------------------ 136 | 137 | 138 | void bon_w_header(bon_w_doc* B) 139 | { 140 | const uint8_t header[4] = {'B', 'O', 'N', '0'}; 141 | bon_w_raw(B, header, 4); 142 | } 143 | 144 | void bon_w_footer(bon_w_doc* B) 145 | { 146 | if (B->flags & BON_W_FLAG_CRC) 147 | { 148 | // Add contribution of buffered data: 149 | B->crc_inv = crc_update(B->crc_inv, B->buff, B->buff_ix); 150 | 151 | uint32_t crc = B->crc_inv ^ 0xffffffff; 152 | uint32_t crc_le = uint32_to_le(crc); 153 | 154 | bon_w_raw_uint8 (B, BON_CTRL_FOOTER_CRC); 155 | bon_w_raw_uint32(B, crc_le); 156 | bon_w_raw_uint8 (B, BON_CTRL_FOOTER_CRC); 157 | } 158 | else 159 | { 160 | bon_w_raw_uint8 (B, BON_CTRL_FOOTER); 161 | } 162 | } 163 | 164 | bon_w_doc* bon_w_new(bon_w_writer_t writer, void* userData, bon_w_flags flags) 165 | { 166 | bon_w_doc* B = BON_CALLOC_TYPE(1, bon_w_doc); 167 | B->writer = writer; 168 | B->userData = userData; 169 | B->crc_inv = 0xffffffff; 170 | B->error = BON_SUCCESS; 171 | B->flags = flags; 172 | 173 | #if 0 174 | // Slower. 175 | B->buff = NULL; 176 | B->buff_ix = 0; 177 | B->buff_size = 0; 178 | #else 179 | const unsigned BUFF_SIZE = 64*1024; 180 | B->buff = malloc(BUFF_SIZE); 181 | B->buff_ix = 0; 182 | B->buff_size = BUFF_SIZE; 183 | #endif 184 | 185 | if ((B->flags & BON_W_FLAG_SKIP_HEADER_FOOTER) == 0) { 186 | bon_w_header(B); 187 | } 188 | 189 | return B; 190 | } 191 | 192 | bon_error bon_w_close(bon_w_doc* B) 193 | { 194 | if ((B->flags & BON_W_FLAG_SKIP_HEADER_FOOTER) == 0) { 195 | bon_w_footer(B); 196 | } 197 | bon_w_flush(B); 198 | bon_error err = B->error; 199 | free(B->buff); 200 | free(B); 201 | return err; 202 | } 203 | 204 | void bon_w_begin_block_sized(bon_w_doc* B, bon_block_id block_id, bon_size nbytes) 205 | { 206 | bon_w_ctrl_vlq(B, BON_CTRL_BLOCK_BEGIN, block_id); 207 | bon_w_vlq(B, nbytes); 208 | } 209 | 210 | void bon_w_block_begin(bon_w_doc* B, bon_block_id block_id) 211 | { 212 | bon_w_begin_block_sized(B, block_id, 0); 213 | } 214 | 215 | void bon_w_block_end(bon_w_doc* B) 216 | { 217 | bon_w_raw_uint8(B, BON_CTRL_BLOCK_END); 218 | } 219 | 220 | void bon_w_block(bon_w_doc* B, bon_block_id block_id, const void* data, bon_size nbytes) 221 | { 222 | bon_w_begin_block_sized(B, block_id, nbytes); 223 | bon_w_raw(B, data, nbytes); 224 | bon_w_block_end(B); 225 | } 226 | 227 | 228 | // Write a value read from another BON-file: 229 | void bon_w_value(bon_w_doc* B, bon_value* v) 230 | { 231 | assert(v); 232 | 233 | switch (v->type) { 234 | case BON_VALUE_NIL: 235 | bon_w_nil(B); 236 | break; 237 | 238 | case BON_VALUE_BOOL: 239 | bon_w_bool(B, v->u.boolean); 240 | break; 241 | 242 | case BON_VALUE_UINT64: 243 | bon_w_uint64(B, v->u.u64); 244 | break; 245 | 246 | case BON_VALUE_SINT64: 247 | bon_w_sint64(B, v->u.s64); 248 | break; 249 | 250 | case BON_VALUE_DOUBLE: 251 | bon_w_double(B, v->u.dbl); 252 | break; 253 | 254 | case BON_VALUE_STRING: 255 | bon_w_string(B, v->u.str.ptr, v->u.str.size); 256 | break; 257 | 258 | case BON_VALUE_BLOCK_REF: 259 | bon_w_block_ref(B, v->u.blockRefId); 260 | break; 261 | 262 | case BON_VALUE_LIST: { 263 | bon_list* list = &v->u.list; 264 | bon_w_list_begin(B); 265 | for (bon_size ix=0; ixsize; ++ix) { 266 | bon_w_value(B, &list->data[ix]); 267 | } 268 | bon_w_list_end(B); 269 | } break; 270 | 271 | case BON_VALUE_OBJ: { 272 | bon_obj* obj = &v->u.obj; 273 | bon_w_obj_begin(B); 274 | for (bon_size ix=0; ixsize; ++ix) { 275 | bon_w_key(B, obj->data[ix].key); 276 | bon_w_value(B, &obj->data[ix].val); 277 | } 278 | bon_w_obj_end(B); 279 | } break; 280 | 281 | case BON_VALUE_AGGREGATE: { 282 | bon_value_agg* agg = v->u.agg; 283 | bon_type* type = &agg->type; 284 | bon_w_pack(B, agg->data, bon_aggregate_payload_size(type), type); 285 | } break; 286 | 287 | default: 288 | bon_w_set_error(B, BON_ERR_BAD_VALUE); 289 | } 290 | } 291 | 292 | 293 | //------------------------------------------------------------------------------ 294 | // Value writing 295 | 296 | 297 | //------------------------------------------------------------------------------ 298 | 299 | void bon_w_packegate_type(bon_w_doc* B, bon_type* type); 300 | 301 | void bon_w_pack_array_type(bon_w_doc* B, bon_size length, bon_type* element_type) 302 | { 303 | if (element_type->id == BON_TYPE_UINT8 && length < BON_SHORT_BYTE_ARRAY_COUNT) 304 | { 305 | bon_w_raw_uint8(B, BON_SHORT_BYTE_ARRAY(length)); 306 | return; 307 | } 308 | 309 | if (length < BON_SHORT_ARRAY_COUNT) 310 | { 311 | bon_w_raw_uint8(B, BON_SHORT_ARRAY(length)); 312 | bon_w_packegate_type(B, element_type); 313 | return; 314 | } 315 | 316 | bon_w_ctrl_vlq(B, BON_CTRL_ARRAY_VLQ, length); 317 | bon_w_packegate_type(B, element_type); 318 | } 319 | 320 | void bon_w_packegate_type(bon_w_doc* B, bon_type* type) 321 | { 322 | switch (type->id) { 323 | case BON_TYPE_ARRAY: 324 | bon_w_pack_array_type(B, type->u.array->size, type->u.array->type); 325 | break; 326 | 327 | 328 | case BON_TYPE_STRUCT: { 329 | bon_type_struct* strct = type->u.strct; 330 | bon_size n = strct->size; 331 | 332 | if (n < BON_SHORT_STRUCT_COUNT) { 333 | bon_w_raw_uint8(B, BON_SHORT_STRUCT(n)); 334 | } 335 | else { 336 | bon_w_ctrl_vlq(B, BON_CTRL_STRUCT_VLQ, n); 337 | } 338 | 339 | for (bon_size ti=0; tikts + ti; 341 | bon_w_cstring(B, kt->key); 342 | bon_w_packegate_type(B, &kt->type); 343 | } 344 | } break; 345 | 346 | 347 | default: 348 | // Simple type - write ctrl code: 349 | bon_w_raw_uint8(B, type->id); 350 | break; 351 | } 352 | } 353 | 354 | void bon_w_pack(bon_w_doc* B, const void* data, bon_size nbytes, bon_type* type) 355 | { 356 | // Sanity check: 357 | bon_size expected_size = bon_aggregate_payload_size(type); 358 | if (nbytes != expected_size) { 359 | fprintf(stderr, "bon_w_pack: wrong size. Expected %d, got %d\n", (int)expected_size, (int)nbytes); 360 | bon_w_set_error(B, BON_ERR_BAD_AGGREGATE_SIZE); 361 | return; 362 | } 363 | 364 | bon_w_packegate_type(B, type); 365 | bon_w_raw(B, data, nbytes); 366 | } 367 | 368 | void bon_w_pack_fmt(bon_w_doc* B, const void* data, bon_size nbytes, 369 | const char* fmt, ...) 370 | { 371 | va_list ap; 372 | va_start(ap, fmt); 373 | bon_type* type = bon_new_type_fmt_ap(&fmt, &ap); 374 | va_end(ap); 375 | 376 | if (type) { 377 | bon_w_pack(B, data, nbytes, type); 378 | bon_free_type(type); 379 | } 380 | } 381 | 382 | void bon_w_pack_array(bon_w_doc* B, const void* data, bon_size nbytes, 383 | bon_size len, bon_type_id element_t) 384 | { 385 | bon_w_assert(B, len * bon_type_size(element_t) == nbytes, 386 | BON_ERR_BAD_AGGREGATE_SIZE); 387 | if (B->error) { 388 | return; 389 | } 390 | 391 | if (element_t == BON_TYPE_UINT8 && len < BON_SHORT_BYTE_ARRAY_COUNT) 392 | { 393 | bon_w_raw_uint8(B, BON_SHORT_BYTE_ARRAY(len)); 394 | bon_w_raw(B, data, nbytes); 395 | return; 396 | } 397 | 398 | if (len < BON_SHORT_ARRAY_COUNT) 399 | { 400 | bon_w_raw_uint8(B, BON_SHORT_ARRAY(len)); 401 | bon_w_raw_uint8(B, element_t); 402 | bon_w_raw(B, data, nbytes); 403 | return; 404 | } 405 | 406 | bon_w_ctrl_vlq(B, BON_CTRL_ARRAY_VLQ, len); 407 | bon_w_raw_uint8(B, element_t); 408 | 409 | bon_w_raw(B, data, nbytes); 410 | } 411 | -------------------------------------------------------------------------------- /libbon/bon/write_inline.h: -------------------------------------------------------------------------------- 1 | // 2 | // write_inline.h 3 | // BON 4 | // 5 | // Written 2013 by Emil Ernerfeldt. 6 | // Copyright (c) 2013 Emil Ernerfeldt 7 | // This is free software, under the MIT license (see LICENSE.txt for details). 8 | 9 | #ifndef BON_write_inline_h 10 | #define BON_write_inline_h 11 | 12 | #include 13 | #include // isfinite 14 | #include "utf.h" // utf8_check_string 15 | 16 | //#define inline 17 | 18 | #define BON_INLINE static inline 19 | 20 | #ifndef isfinite 21 | BON_INLINE int isfinite(double x) { return x-x == 0.0; } 22 | #endif 23 | 24 | 25 | BON_INLINE bon_error bon_w_error(bon_w_doc* B) { 26 | return B->error; 27 | } 28 | 29 | //------------------------------------------------------------------------------ 30 | // Util functions: 31 | 32 | void bon_w_raw_flush_buff(bon_w_doc* B, const void* data, bon_size bs); 33 | 34 | BON_INLINE void bon_w_raw(bon_w_doc* B, const void* data, bon_size bs) 35 | { 36 | if (B->buff_ix + bs < B->buff_size) { 37 | // Fits in the buffer 38 | memcpy(B->buff + B->buff_ix, data, bs); 39 | B->buff_ix += bs; 40 | } else { 41 | bon_w_raw_flush_buff(B, data, bs); 42 | } 43 | } 44 | 45 | 46 | BON_INLINE void bon_w_raw_uint8(bon_w_doc* B, uint8_t val) { 47 | bon_w_raw(B, &val, sizeof(val)); 48 | } 49 | 50 | BON_INLINE void bon_w_raw_uint16(bon_w_doc* B, uint16_t val) { 51 | bon_w_raw(B, &val, sizeof(val)); 52 | } 53 | 54 | BON_INLINE void bon_w_raw_uint32(bon_w_doc* B, uint32_t val) { 55 | bon_w_raw(B, &val, sizeof(val)); 56 | } 57 | 58 | BON_INLINE void bon_w_raw_uint64(bon_w_doc* B, uint64_t val) { 59 | bon_w_raw(B, &val, sizeof(val)); 60 | } 61 | 62 | 63 | //------------------------------------------------------------------------------ 64 | // VLQ 65 | // See http://en.wikipedia.org/wiki/Variable-length_quantity 66 | // See http://rosettacode.org/wiki/Variable-length_quantity 67 | 68 | 69 | // Maximum number of bytes to encode a very large number 70 | #define BON_VARINT_MAX_LEN 10 71 | 72 | BON_INLINE uint32_t bon_vlq_size(bon_size x) 73 | { 74 | if (x < (1ULL << 7)) return 1; 75 | if (x < (1ULL << 14)) return 2; 76 | if (x < (1ULL << 21)) return 3; 77 | if (x < (1ULL << 28)) return 4; 78 | if (x < (1ULL << 35)) return 5; 79 | if (x < (1ULL << 42)) return 6; 80 | if (x < (1ULL << 49)) return 7; 81 | if (x < (1ULL << 56)) return 8; 82 | if (x < (1ULL << 63)) return 9; 83 | return 10; 84 | } 85 | 86 | // Returns number of bytes written 87 | BON_INLINE uint32_t bon_w_vlq_to(uint8_t* out, bon_size x) 88 | { 89 | uint32_t size = bon_vlq_size(x); 90 | 91 | for (uint32_t i = 0; i < size; ++i) { 92 | out[i] = ((x >> ((size - 1 - i) * 7)) & 0x7f) | 0x80; 93 | } 94 | 95 | out[size-1] &= 0x7f; // Remove last flag 96 | 97 | return size; 98 | } 99 | 100 | #if 1 101 | BON_INLINE void bon_w_vlq(bon_w_doc* B, bon_size x) 102 | { 103 | uint8_t buff[BON_VARINT_MAX_LEN]; 104 | uint32_t size = bon_w_vlq_to(buff, x); 105 | bon_w_raw(B, buff, size); 106 | } 107 | #endif 108 | 109 | // Done often, lets do it fast: 110 | BON_INLINE void bon_w_ctrl_vlq(bon_w_doc* B, bon_ctrl ctrl, bon_size x) 111 | { 112 | if (x < 128) { 113 | // Common case optimization 114 | uint8_t buff[2] = { (uint8_t)ctrl, (uint8_t)x }; 115 | bon_w_raw(B, buff, 2); 116 | } else { 117 | uint8_t buff[1 + BON_VARINT_MAX_LEN]; 118 | buff[0] = ctrl; 119 | uint32_t size = bon_w_vlq_to(1 + buff, x); 120 | bon_w_raw(B, buff, 1 + size); 121 | } 122 | } 123 | 124 | //------------------------------------------------------------------------------ 125 | // Public API functions: 126 | 127 | 128 | /* 129 | A much faster version of 130 | bon_w_raw_uint8(B, type_ctrl); 131 | bon_w_raw(B, &data, sizeof(data)); 132 | */ 133 | #define BON_WRITE_QUICKLY(type_ctrl, data) \ 134 | /**/ unsigned char buf[1 + sizeof(data)]; \ 135 | /**/ buf[0] = type_ctrl; \ 136 | /**/ memcpy(buf+1, &data, sizeof(data)); \ 137 | /**/ bon_w_raw(B, buf, sizeof(buf)); \ 138 | 139 | 140 | BON_INLINE void bon_w_obj_begin(bon_w_doc* B) { 141 | bon_w_raw_uint8(B, BON_CTRL_OBJ_BEGIN); 142 | } 143 | 144 | BON_INLINE void bon_w_obj_end(bon_w_doc* B) { 145 | bon_w_raw_uint8(B, BON_CTRL_OBJ_END); 146 | } 147 | 148 | BON_INLINE void bon_w_obj_sized(bon_w_doc* B, bon_size n) { 149 | bon_w_ctrl_vlq(B, BON_CTRL_OBJ_VLQ, n); 150 | } 151 | 152 | BON_INLINE void bon_w_key(bon_w_doc* B, const char* utf8) { 153 | bon_w_cstring(B, utf8); 154 | } 155 | 156 | BON_INLINE void bon_w_list_begin(bon_w_doc* B) { 157 | bon_w_raw_uint8(B, BON_CTRL_LIST_BEGIN); 158 | } 159 | 160 | BON_INLINE void bon_w_list_end(bon_w_doc* B) { 161 | bon_w_raw_uint8(B, BON_CTRL_LIST_END); 162 | } 163 | 164 | BON_INLINE void bon_w_list_sized(bon_w_doc* B, bon_size n) { 165 | bon_w_ctrl_vlq(B, BON_CTRL_LIST_VLQ, n); 166 | } 167 | 168 | 169 | BON_INLINE void bon_w_block_ref(bon_w_doc* B, bon_block_id block_id) 170 | { 171 | if (block_id < BON_SHORT_BLOCK_COUNT) { 172 | bon_w_raw_uint8(B, BON_SHORT_BLOCK(block_id)); 173 | } else { 174 | bon_w_ctrl_vlq(B, BON_CTRL_BLOCK_REF, block_id); 175 | } 176 | } 177 | 178 | BON_INLINE void bon_w_nil(bon_w_doc* B) { 179 | bon_w_raw_uint8(B, BON_CTRL_NIL); 180 | } 181 | 182 | BON_INLINE void bon_w_bool(bon_w_doc* B, bon_bool val) { 183 | if (val) { 184 | bon_w_raw_uint8(B, BON_CTRL_TRUE); 185 | } else { 186 | bon_w_raw_uint8(B, BON_CTRL_FALSE); 187 | } 188 | } 189 | 190 | BON_INLINE void bon_w_string(bon_w_doc* B, const char* utf8, bon_size nbytes) { 191 | if (nbytes == BON_ZERO_ENDED) { 192 | nbytes = strlen(utf8); 193 | } 194 | 195 | if ((B->flags & BON_W_FLAG_SKIP_STRING_CHECKS) == 0) 196 | { 197 | if (!utf8_check_string(utf8, nbytes)) { 198 | // Invalid UTF-8. 199 | bon_w_set_error(B, BON_ERR_NOT_UTF8); 200 | } 201 | } 202 | 203 | if (nbytes < BON_SHORT_STRING_COUNT) { 204 | bon_w_raw_uint8(B, BON_SHORT_STRING(nbytes)); 205 | } else { 206 | bon_w_ctrl_vlq(B, BON_CTRL_STRING_VLQ, nbytes); 207 | } 208 | 209 | bon_w_raw(B, utf8, nbytes); 210 | bon_w_raw_uint8(B, 0); // Zero-ended 211 | } 212 | 213 | BON_INLINE void bon_w_cstring(bon_w_doc* B, const char* utf8) 214 | { 215 | bon_w_string(B, utf8, BON_ZERO_ENDED); 216 | } 217 | 218 | BON_INLINE void bon_w_uint64(bon_w_doc* B, uint64_t u64) 219 | { 220 | if (u64 < BON_SHORT_POS_INT_COUNT) { 221 | bon_w_raw_uint8(B, (uint8_t)u64); 222 | } else if (u64 == (u64 & 0xff)) { 223 | uint8_t u8 = (uint8_t)u64; 224 | BON_WRITE_QUICKLY(BON_CTRL_UINT8, u8); 225 | } else if (u64 == (u64 & 0xffff)) { 226 | uint16_t u16 = (uint16_t)u64; 227 | BON_WRITE_QUICKLY(BON_CTRL_UINT16, u16); 228 | } else if (u64 == (u64 & 0xffffffff)) { 229 | uint32_t u32 = (uint32_t)u64; 230 | BON_WRITE_QUICKLY(BON_CTRL_UINT32, u32); 231 | } else { 232 | BON_WRITE_QUICKLY(BON_CTRL_UINT64, u64); 233 | } 234 | } 235 | 236 | BON_INLINE void bon_w_sint64(bon_w_doc* B, int64_t s64) { 237 | if (s64 >= 0) { 238 | bon_w_uint64(B, (uint64_t)s64); 239 | } else if (-16 <= s64) { 240 | bon_w_raw_uint8(B, (uint8_t)s64); 241 | } else if (-0x80 <= s64 && s64 < 0x80) { 242 | uint8_t u8 = (uint8_t)s64; 243 | BON_WRITE_QUICKLY(BON_CTRL_SINT8, u8); 244 | } else if (-0x8000 <= s64 && s64 < 0x8000) { 245 | uint16_t u16 = (uint16_t)s64; 246 | BON_WRITE_QUICKLY(BON_CTRL_SINT16, u16); 247 | } else if (-0x80000000LL <= s64 && s64 < 0x80000000LL) { 248 | uint32_t u32 = (uint32_t)s64; 249 | BON_WRITE_QUICKLY(BON_CTRL_SINT32, u32); 250 | } else { 251 | BON_WRITE_QUICKLY(BON_CTRL_SINT64, s64); 252 | } 253 | } 254 | 255 | 256 | BON_INLINE void bon_w_float(bon_w_doc* B, float val) 257 | { 258 | #if 1 259 | // I think this can be optimized to testing just the exponent sign bit. 260 | int64_t ival = (int64_t)val; 261 | if (val == (float)ival) { 262 | bon_w_sint64(B, ival); 263 | return; 264 | } 265 | #endif 266 | 267 | BON_WRITE_QUICKLY(BON_CTRL_FLOAT, val); 268 | } 269 | 270 | BON_INLINE void bon_w_double(bon_w_doc* B, double val) 271 | { 272 | if (!isfinite(val) || (double)(float)val == val) { 273 | bon_w_float(B, (float)val); 274 | } else { 275 | BON_WRITE_QUICKLY(BON_CTRL_DOUBLE, val); 276 | } 277 | } 278 | 279 | 280 | #endif 281 | -------------------------------------------------------------------------------- /test/bench.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // bench.cpp 3 | // BON 4 | // 5 | // Created by emilk on 2013-04-10. 6 | // Copyright (c) 2013 Emil Ernerfeldt. All rights reserved. 7 | // 8 | 9 | #define CATCH_CONFIG_MAIN 10 | #include "catch.hpp" 11 | 12 | 13 | extern "C" { 14 | #include 15 | #include 16 | #include 17 | } 18 | 19 | #include 20 | #include 21 | #include // accumulate 22 | 23 | 24 | 25 | 26 | #ifdef __clang__ 27 | # pragma clang diagnostic push 28 | # pragma clang diagnostic ignored "-Wconversion" 29 | # pragma clang diagnostic ignored "-Wshadow" 30 | # pragma clang diagnostic ignored "-Wunused-parameter" 31 | # pragma clang diagnostic ignored "-Wunused-function" 32 | # include 33 | # pragma clang diagnostic pop 34 | #else 35 | # include 36 | #endif 37 | 38 | 39 | 40 | 41 | 42 | using namespace std; 43 | 44 | 45 | //------------------------------------------------------------------------------ 46 | 47 | 48 | #if DEBUG 49 | const int NUM_VALS = 16 * 1024; 50 | #else 51 | const int NUM_VALS = 16 * 1024*1024; 52 | #endif 53 | 54 | using namespace std::chrono; 55 | typedef high_resolution_clock Clock; 56 | 57 | 58 | template 59 | void time(const Fun& fun) 60 | { 61 | Clock::time_point start = Clock::now(); 62 | 63 | fun(); 64 | 65 | Clock::time_point stop = Clock::now(); 66 | 67 | auto ms = duration_cast(stop - start).count(); 68 | 69 | std::cout << ms << " ms" << std::endl; 70 | } 71 | 72 | template 73 | void printStatistics(It begin, It end) 74 | { 75 | const auto N = std::distance(begin, end); 76 | typedef double Float; 77 | 78 | Float mean = std::accumulate(begin, end, (Float)0) / (Float)N; 79 | 80 | Float dev = std::sqrt( std::accumulate(begin, end, (Float)0, [=](Float acc, Float v){ 81 | return acc + (v-mean)*(v-mean); 82 | } ) / N ); 83 | 84 | auto p = std::minmax_element(begin, end); 85 | auto min = *p.first; 86 | auto max = *p.second; 87 | 88 | const int W = 5; 89 | std::cout << "mean, dev, min, max: " << std::setprecision(5) 90 | << std::setw(W) << mean << ", " 91 | << std::setw(W) << dev << ", " 92 | << std::setw(W) << min << ", " 93 | << std::setw(W) << max << std::endl; 94 | } 95 | 96 | template 97 | void time_n(unsigned n, const Fun& fun, const Cleanup& cleanup) 98 | { 99 | std::vector times; 100 | 101 | const unsigned warmup = n; 102 | 103 | for (unsigned i=0; i(stop - start).count() ); 115 | 116 | cleanup(); 117 | } 118 | 119 | printStatistics(begin(times), end(times)); 120 | } 121 | 122 | template 123 | void time_n_plus_one(unsigned n, const Fun& fun, const Cleanup& cleanup) 124 | { 125 | time_n(n, fun, cleanup); 126 | fun(); 127 | } 128 | 129 | template 130 | void time_n(unsigned n, const Fun& fun) 131 | { 132 | time_n(n, fun, [](){}); 133 | } 134 | 135 | #if 1 136 | 137 | TEST_CASE( "BON/bench/writing/msgpack", "MsgPack speed of writing floats") 138 | { 139 | printf("\nMsgPack write speed:\n"); 140 | const std::vector src(NUM_VALS, 3.14f); 141 | 142 | printf("Writing... "); 143 | time_n(16, [&]() { 144 | msgpack::sbuffer buffer; // simple buffer 145 | 146 | //msgpack::pack(&buffer, src); 147 | for (auto f : src) { 148 | msgpack::pack(&buffer, f); 149 | } 150 | }); 151 | 152 | #if 0 153 | msgpack::unpacked msg; 154 | msgpack_object mobj; 155 | 156 | printf("Parsing... "); 157 | time([&]() { 158 | msgpack::unpack(&msg, buffer.data(), buffer.size()); 159 | mobj = msg.get(); 160 | }); 161 | msgpack::object obj = mobj; 162 | 163 | printf("Copying... "); 164 | time([&]() { 165 | obj.convert(&dst); 166 | REQUIRE( dst[1] == 3.14f ); 167 | }); 168 | #endif 169 | } 170 | 171 | #endif 172 | 173 | #if 1 174 | 175 | TEST_CASE( "BON/bench/writing", "Speed of bon_w_float") 176 | { 177 | printf("\nBON write speed:\n"); 178 | const std::vector src(NUM_VALS, 3.14f); 179 | 180 | bon_byte_vec vec = {0,0,0}; 181 | 182 | printf("Writing... "); 183 | time_n_plus_one(16, [&]() { 184 | bon_w_doc* B = bon_w_new(bon_vec_writer, &vec, BON_W_FLAG_DEFAULT); 185 | bon_w_obj_begin(B); 186 | bon_w_key(B, "list"); 187 | 188 | bon_w_list_sized(B, src.size()); 189 | for (auto f : src) { 190 | bon_w_float(B, f); 191 | } 192 | 193 | bon_w_obj_end(B); 194 | REQUIRE( bon_w_close(B) == BON_SUCCESS ); 195 | }, [&](){ 196 | free(vec.data); 197 | memset(&vec, 0, sizeof(vec)); 198 | }); 199 | 200 | printf("Parsing... "); 201 | bon_r_doc* B; 202 | bon_value* root; 203 | bon_value* list; 204 | 205 | time_n(16, [&]() { 206 | B = bon_r_open(vec.data, vec.size, BON_R_FLAG_DEFAULT); 207 | root = bon_r_root(B); 208 | list = bon_r_get_key(B, root, "list"); 209 | }, [&]() { 210 | bon_r_close(B); 211 | }); 212 | 213 | free(vec.data); 214 | } 215 | 216 | #endif 217 | 218 | 219 | #if 1 220 | 221 | template 222 | void run_float_benchmark(bool packed) 223 | { 224 | const std::vector src(NUM_VALS, 3.14f); 225 | std::vector dst(NUM_VALS, 0.0f); 226 | 227 | bon_byte_vec vec = {0,0,0}; 228 | 229 | printf("Writing... "); 230 | time([&]() { 231 | bon_w_doc* B = bon_w_new(bon_vec_writer, &vec, BON_W_FLAG_DEFAULT); 232 | bon_w_obj_begin(B); 233 | bon_w_key(B, "list"); 234 | if (packed) { 235 | if (std::is_same::value) { 236 | bon_w_pack_array(B, src.data(), sizeof(SrcType)*NUM_VALS, NUM_VALS, BON_TYPE_FLOAT); 237 | } else { 238 | bon_w_pack_array(B, src.data(), sizeof(SrcType)*NUM_VALS, NUM_VALS, BON_TYPE_DOUBLE); 239 | } 240 | } else { 241 | bon_w_list_sized(B, src.size()); 242 | if (std::is_same::value) { 243 | for (auto f : src) { 244 | bon_w_float(B, f); 245 | } 246 | } else { 247 | for (auto f : src) { 248 | bon_w_double(B, f); 249 | } 250 | } 251 | } 252 | bon_w_obj_end(B); 253 | REQUIRE( bon_w_close(B) == BON_SUCCESS ); 254 | }); 255 | 256 | bon_r_doc* B; 257 | bon_value* root; 258 | bon_value* list; 259 | 260 | auto parse = [&]() { 261 | B = bon_r_open(vec.data, vec.size, BON_R_FLAG_DEFAULT); 262 | root = bon_r_root(B); 263 | list = bon_r_get_key(B, root, "list"); 264 | }; 265 | 266 | printf("Parsing... "); 267 | #if 0 268 | time_n(16, parse, [&]() { 269 | bon_r_close(B); 270 | }); 271 | parse(); 272 | #else 273 | time([&]() { parse(); }); 274 | #endif 275 | 276 | printf("Copying... "); 277 | //time_n(10, [&]() { 278 | time([&]() { 279 | bool win = false; 280 | //if (true) { 281 | if (packed) { 282 | if (std::is_same::value) { 283 | win = bon_r_unpack_fmt(B, list, dst.data(), sizeof(DstType)*NUM_VALS, "[#f]", NUM_VALS); 284 | } else if (std::is_same::value) { 285 | win = bon_r_unpack_fmt(B, list, dst.data(), sizeof(DstType)*NUM_VALS, "[#d]", NUM_VALS); 286 | } else { 287 | } 288 | } else { 289 | for (bon_size ix = 0; ix < NUM_VALS; ++ix) { 290 | bon_value* elem = bon_r_list_elem(B, list, ix); 291 | if (!elem) { 292 | win = false; 293 | break; 294 | } 295 | if (std::is_same::value) { 296 | dst[ix] = bon_r_float(B, elem); 297 | } else { 298 | dst[ix] = (DstType)bon_r_double(B, elem); 299 | } 300 | } 301 | win = true; 302 | } 303 | REQUIRE( win ); 304 | REQUIRE( dst[1] == 3.14f ); 305 | }); 306 | 307 | bon_r_close(B); 308 | 309 | printf("Used %d MB\n", (int)std::round((float)vec.size / 1024 / 1024)); 310 | 311 | free(vec.data); 312 | } 313 | 314 | template 315 | void float_bench_msgpack() 316 | { 317 | const std::vector src(NUM_VALS, 3.14f); 318 | std::vector dst; 319 | dst.reserve(NUM_VALS); // To make it fair 320 | 321 | msgpack::sbuffer buffer; // simple buffer 322 | 323 | printf("Writing... "); 324 | time([&]() { 325 | msgpack::pack(&buffer, src); 326 | }); 327 | 328 | msgpack::unpacked msg; 329 | msgpack_object mobj; 330 | 331 | printf("Parsing... "); 332 | time([&]() { 333 | msgpack::unpack(&msg, buffer.data(), buffer.size()); 334 | mobj = msg.get(); 335 | }); 336 | msgpack::object obj = mobj; 337 | 338 | printf("Copying... "); 339 | time([&]() { 340 | obj.convert(&dst); 341 | REQUIRE( dst[1] == 3.14f ); 342 | }); 343 | 344 | printf("Used %d MB\n", (int)std::round((float)buffer.size() / 1024 / 1024)); 345 | } 346 | 347 | 348 | template 349 | void array_write_bench() 350 | { 351 | printf("\n"); 352 | printf("bon (packed):\n"); 353 | run_float_benchmark(true); 354 | 355 | printf("\n"); 356 | printf("bon (exploded):\n"); 357 | run_float_benchmark(false); 358 | 359 | printf("\n"); 360 | printf("msgpack:\n"); 361 | float_bench_msgpack(); 362 | } 363 | 364 | 365 | TEST_CASE( "BON/bench", "Benching writing and reading of packed values vs" ) 366 | { 367 | printf("----------WARMUP---------\n"); 368 | array_write_bench(); 369 | printf("\n----\n"); 370 | 371 | #if 0 372 | run_float_benchmark(false); 373 | #else 374 | printf("Benchmark - packed vs 'exploded', writing and reading of %d values\n", NUM_VALS); 375 | 376 | printf("------------------------------------------------------------------------------\n"); 377 | printf("float -> float\n"); 378 | array_write_bench(); 379 | 380 | 381 | printf("------------------------------------------------------------------------------\n"); 382 | printf("float -> double\n"); 383 | array_write_bench(); 384 | #endif 385 | 386 | } 387 | #endif // DEBUG/bench 388 | -------------------------------------------------------------------------------- /test/test.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 2013-04-05 \" DATE 7 | .Dt test 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm test, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner --------------------------------------------------------------------------------