├── .clang-format ├── .gitignore ├── .travis.yml ├── LICENSE-BSD2 ├── Makefile ├── README ├── VERSION ├── bench ├── .ycm_extra_conf.py ├── Makefile ├── bench.c ├── bench.h ├── buf_bench.c ├── dict_bench.c ├── heap_bench.c ├── log_bench.c ├── map_bench.c ├── skiplist_bench.c └── strings_bench.c ├── example ├── .ycm_extra_conf.py ├── Makefile ├── buf_example.c ├── cfg_example.c ├── cfg_example.cfg ├── datetime_example.c ├── dict_example.c ├── event_example.c ├── event_timer_example.c ├── heap_example.c ├── ketama_example.c ├── list_example.c ├── log_example.c ├── map_example.c ├── md5_example.c ├── queue_example.c ├── signals_example.c ├── skiplist_example.c ├── stack_example.c └── strings_example.c ├── lint.py ├── src ├── buf.c ├── buf.h ├── cfg.c ├── cfg.h ├── datetime.c ├── datetime.h ├── dict.c ├── dict.h ├── event.c ├── event.h ├── event_epoll.c ├── event_kqueue.c ├── event_timer.c ├── heap.c ├── heap.h ├── ketama.c ├── ketama.h ├── list.c ├── list.h ├── log.c ├── log.h ├── map.c ├── map.h ├── md5.c ├── md5.h ├── queue.c ├── queue.h ├── signals.h ├── skiplist.c ├── skiplist.h ├── stack.c ├── stack.h ├── strings.c ├── strings.h └── utils.h └── test ├── .ycm_extra_conf.py ├── Makefile ├── buf_test.c ├── cfg_test.c ├── datetime_test.c ├── dict_test.c ├── event_test.c ├── heap_test.c ├── ketama_test.c ├── list_test.c ├── log_test.c ├── map_test.c ├── queue_test.c ├── skiplist_test.c ├── stack_test.c ├── strings_test.c ├── test.c ├── test.h └── utils_test.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -1 4 | ConstructorInitializerIndentWidth: 4 5 | AlignEscapedNewlinesLeft: true 6 | AlignTrailingComments: true 7 | AllowAllParametersOfDeclarationOnNextLine: true 8 | AllowShortBlocksOnASingleLine: false 9 | AllowShortIfStatementsOnASingleLine: true 10 | AllowShortLoopsOnASingleLine: true 11 | AllowShortFunctionsOnASingleLine: All 12 | AlwaysBreakTemplateDeclarations: true 13 | AlwaysBreakBeforeMultilineStrings: true 14 | BreakBeforeBinaryOperators: false 15 | BreakBeforeTernaryOperators: true 16 | BreakConstructorInitializersBeforeComma: false 17 | BinPackParameters: true 18 | ColumnLimit: 80 19 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 20 | DerivePointerAlignment: true 21 | ExperimentalAutoDetectBinPacking: false 22 | IndentCaseLabels: true 23 | IndentWrappedFunctionNames: false 24 | IndentFunctionDeclarationAfterType: false 25 | MaxEmptyLinesToKeep: 1 26 | KeepEmptyLinesAtTheStartOfBlocks: false 27 | NamespaceIndentation: None 28 | ObjCSpaceAfterProperty: false 29 | ObjCSpaceBeforeProtocolList: false 30 | PenaltyBreakBeforeFirstCallParameter: 1 31 | PenaltyBreakComment: 300 32 | PenaltyBreakString: 1000 33 | PenaltyBreakFirstLessLess: 120 34 | PenaltyExcessCharacter: 1000000 35 | PenaltyReturnTypeOnItsOwnLine: 200 36 | PointerAlignment: Right 37 | SpacesBeforeTrailingComments: 2 38 | Cpp11BracedListStyle: true 39 | Standard: Auto 40 | IndentWidth: 4 41 | TabWidth: 8 42 | UseTab: Never 43 | BreakBeforeBraces: Attach 44 | SpacesInParentheses: false 45 | SpacesInAngles: false 46 | SpaceInEmptyParentheses: false 47 | SpacesInCStyleCastParentheses: false 48 | SpacesInContainerLiterals: true 49 | SpaceBeforeAssignmentOperators: true 50 | ContinuationIndentWidth: 4 51 | CommentPragmas: '^ IWYU pragma:' 52 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 53 | SpaceBeforeParens: ControlStatements 54 | DisableFormat: false 55 | ... 56 | 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.o 3 | *.out 4 | *.so 5 | *.h.gch 6 | .*.sw[opn] 7 | *.pyc 8 | src/test 9 | test.log.* 10 | test/test 11 | example/*_example 12 | bench/bench 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: c 4 | install: 5 | - sudo apt-get install -qq gcc-4.8 6 | - sudo apt-get install -qq clang-format-3.5 7 | script: make lint && make runtests && make runbench 8 | -------------------------------------------------------------------------------- /LICENSE-BSD2: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, hit9 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 18 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # vim:set noet: 2 | 3 | runtests: 4 | make runtests -C test 5 | 6 | examples: 7 | make example -C example 8 | 9 | runbench: 10 | make runbench -C bench 11 | 12 | lint: 13 | python lint.py 14 | 15 | lintfix: 16 | python lint.py fix 17 | 18 | clean: 19 | rm -f src/*.o 20 | make clean -C test 21 | make clean -C example 22 | make clean -C bench 23 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | buf alpha 2 | cfg alpha 3 | datetime alpha 4 | dict alpha 5 | event alpha 6 | heap alpha 7 | ketama alpha 8 | list alpha 9 | log alpha 10 | map alpha 11 | queue alpha 12 | skiplist alpha 13 | stack alpha 14 | strings alpha 15 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.3.1 2 | -------------------------------------------------------------------------------- /bench/.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | # This file is NOT licensed under the GPLv3, which is the license for the rest 2 | # of YouCompleteMe. 3 | # 4 | # Here's the license text for this file: 5 | # 6 | # This is free and unencumbered software released into the public domain. 7 | # 8 | # Anyone is free to copy, modify, publish, use, compile, sell, or 9 | # distribute this software, either in source code form or as a compiled 10 | # binary, for any purpose, commercial or non-commercial, and by any 11 | # means. 12 | # 13 | # In jurisdictions that recognize copyright laws, the author or authors 14 | # of this software dedicate any and all copyright interest in the 15 | # software to the public domain. We make this dedication for the benefit 16 | # of the public at large and to the detriment of our heirs and 17 | # successors. We intend this dedication to be an overt act of 18 | # relinquishment in perpetuity of all present and future rights to this 19 | # software under copyright law. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | # OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # For more information, please refer to 30 | 31 | import os 32 | import ycm_core 33 | 34 | flags = [ 35 | # INSERT FLAGS HERE 36 | '-Wall', 37 | '-I../src', 38 | '-std=c99', 39 | '-D_GNU_SOURCE', 40 | '-pthread', 41 | ] 42 | 43 | 44 | # Set this to the absolute path to the folder (NOT the file!) containing the 45 | # compile_commands.json file to use that instead of 'flags'. See here for 46 | # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html 47 | # 48 | # You can get CMake to generate this file for you by adding: 49 | # set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) 50 | # to your CMakeLists.txt file. 51 | # 52 | # Most projects will NOT need to set this to anything; you can just change the 53 | # 'flags' list of compilation flags. Notice that YCM itself uses that approach. 54 | compilation_database_folder = '' 55 | 56 | if os.path.exists(compilation_database_folder): 57 | database = ycm_core.CompilationDatabase(compilation_database_folder) 58 | else: 59 | database = None 60 | 61 | SOURCE_EXTENSIONS = ['.cpp', '.cxx', '.cc', '.c', '.m', '.mm'] 62 | 63 | 64 | def DirectoryOfThisScript(): 65 | return os.path.dirname(os.path.abspath(__file__)) 66 | 67 | 68 | def MakeRelativePathsInFlagsAbsolute(flags, working_directory): 69 | if not working_directory: 70 | return list(flags) 71 | new_flags = [] 72 | make_next_absolute = False 73 | path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] 74 | for flag in flags: 75 | new_flag = flag 76 | 77 | if make_next_absolute: 78 | make_next_absolute = False 79 | if not flag.startswith('/'): 80 | new_flag = os.path.join(working_directory, flag) 81 | 82 | for path_flag in path_flags: 83 | if flag == path_flag: 84 | make_next_absolute = True 85 | break 86 | 87 | if flag.startswith(path_flag): 88 | path = flag[len(path_flag):] 89 | new_flag = path_flag + os.path.join(working_directory, path) 90 | break 91 | 92 | if new_flag: 93 | new_flags.append(new_flag) 94 | return new_flags 95 | 96 | 97 | def IsHeaderFile(filename): 98 | extension = os.path.splitext(filename)[1] 99 | return extension in ['.h', '.hxx', '.hpp', '.hh'] 100 | 101 | 102 | def GetCompilationInfoForFile(filename): 103 | if IsHeaderFile(filename): 104 | basename = os.path.splitext(filename)[0] 105 | for extension in SOURCE_EXTENSIONS: 106 | replacement_file = basename + extension 107 | if os.path.exists(replacement_file): 108 | compilation_info = database.GetCompilationInfoForFile( 109 | replacement_file) 110 | if compilation_info.compiler_flags_: 111 | return compilation_info 112 | return None 113 | return database.GetCompilationInfoForFile(filename) 114 | 115 | 116 | def FlagsForFile(filename, **kwargs): 117 | if database: 118 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a 119 | # python list, but a "list-like" StringVec object 120 | compilation_info = GetCompilationInfoForFile(filename) 121 | if not compilation_info: 122 | return None 123 | 124 | final_flags = MakeRelativePathsInFlagsAbsolute( 125 | compilation_info.compiler_flags_, 126 | compilation_info.compiler_working_dir_) 127 | 128 | else: 129 | relative_to = DirectoryOfThisScript() 130 | final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) 131 | 132 | return { 133 | 'flags': final_flags, 134 | 'do_cache': True 135 | } 136 | -------------------------------------------------------------------------------- /bench/Makefile: -------------------------------------------------------------------------------- 1 | # vim:set noet: 2 | 3 | default: runbench 4 | 5 | NAME=bench 6 | CC=cc -std=c99 7 | OPTIMIZATION?=-O2 8 | CFLAGS=-Wall $(OPTIMIZATION) -D_GNU_SOURCE -g -I../src 9 | LDFLAGS=-Wall -pthread 10 | BIN=$(NAME) 11 | SRC:=$(wildcard ../src/*.c) $(wildcard *.c) 12 | EV_EPOLL:=$(wildcard ../src/event_epoll.c) 13 | EV_KQUEUE:=$(wildcard ../src/event_kqueue.c) 14 | EV_TIMER:=$(wildcard ../src/event_timer.c) 15 | SRC:=$(filter-out $(EV_EPOLL), $(SRC)) 16 | SRC:=$(filter-out $(EV_KQUEUE), $(SRC)) 17 | SRC:=$(filter-out $(EV_TIMER), $(SRC)) 18 | OBJ:=$(SRC:c=o) 19 | 20 | $(BIN): $(OBJ) 21 | 22 | runbench: $(BIN) 23 | ./$(BIN) 24 | 25 | clean: 26 | rm -f $(BIN) $(OBJ) 27 | -------------------------------------------------------------------------------- /bench/bench.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "bench.h" 9 | #include "datetime.h" 10 | 11 | /** 12 | * buf_bench 13 | */ 14 | void case_buf_puts(struct bench_ctx *ctx); 15 | static struct bench_case buf_bench_cases[] = { 16 | {"buf_puts", &case_buf_puts, 10000}, 17 | {"buf_puts", &case_buf_puts, 1000000}, 18 | {NULL, NULL, 0}, 19 | }; 20 | 21 | /** 22 | * dict_bench 23 | */ 24 | void case_dict_set(struct bench_ctx *ctx); 25 | void case_dict_get(struct bench_ctx *ctx); 26 | void case_dict_pop(struct bench_ctx *ctx); 27 | static struct bench_case dict_bench_cases[] = { 28 | {"dict_set", &case_dict_set, 10000}, 29 | {"dict_set", &case_dict_set, 1000000}, 30 | {"dict_get", &case_dict_get, 10000}, 31 | {"dict_get", &case_dict_get, 1000000}, 32 | {"dict_pop", &case_dict_pop, 10000}, 33 | {"dict_pop", &case_dict_pop, 1000000}, 34 | {NULL, NULL, 0}, 35 | }; 36 | 37 | /** 38 | * heap_bench 39 | */ 40 | void case_heap_push(struct bench_ctx *ctx); 41 | void case_heap_pop(struct bench_ctx *ctx); 42 | void case_heap_top(struct bench_ctx *ctx); 43 | static struct bench_case heap_bench_cases[] = { 44 | {"heap_push", &case_heap_push, 10000}, 45 | {"heap_push", &case_heap_push, 1000000}, 46 | {"heap_pop", &case_heap_pop, 10000}, 47 | {"heap_pop", &case_heap_pop, 1000000}, 48 | {"heap_top", &case_heap_top, 10000}, 49 | {"heap_top", &case_heap_top, 1000000}, 50 | {NULL, NULL, 0}, 51 | }; 52 | 53 | /** 54 | * log_bench 55 | */ 56 | void case_log_devnull(struct bench_ctx *ctx); 57 | static struct bench_case log_bench_cases[] = { 58 | {"log_devnull", &case_log_devnull, 1000000}, {NULL, NULL, 0}, 59 | }; 60 | 61 | /** 62 | * map_bench 63 | */ 64 | void case_map_set(struct bench_ctx *ctx); 65 | void case_map_get(struct bench_ctx *ctx); 66 | void case_map_pop(struct bench_ctx *ctx); 67 | static struct bench_case map_bench_cases[] = { 68 | {"map_set", &case_map_set, 10000}, 69 | {"map_set", &case_map_set, 1000000}, 70 | {"map_get", &case_map_get, 10000}, 71 | {"map_get", &case_map_get, 1000000}, 72 | {"map_pop", &case_map_pop, 10000}, 73 | {"map_pop", &case_map_pop, 1000000}, 74 | {NULL, NULL, 0}, 75 | }; 76 | 77 | /** 78 | * skiplist_bench 79 | */ 80 | void case_skiplist_push(struct bench_ctx *ctx); 81 | void case_skiplist_get(struct bench_ctx *ctx); 82 | void case_skiplist_pop(struct bench_ctx *ctx); 83 | void case_skiplist_popfirst(struct bench_ctx *ctx); 84 | void case_skiplist_first(struct bench_ctx *ctx); 85 | static struct bench_case skiplist_bench_cases[] = { 86 | {"skiplist_push", &case_skiplist_push, 10000}, 87 | {"skiplist_push", &case_skiplist_push, 1000000}, 88 | {"skiplist_get", &case_skiplist_get, 10000}, 89 | {"skiplist_get", &case_skiplist_get, 1000000}, 90 | {"skiplist_pop", &case_skiplist_pop, 10000}, 91 | {"skiplist_pop", &case_skiplist_pop, 1000000}, 92 | {"skiplist_first", &case_skiplist_first, 10000}, 93 | {"skiplist_first", &case_skiplist_first, 1000000}, 94 | {"skiplist_popfirst", &case_skiplist_popfirst, 10000}, 95 | {"skiplist_popfirst", &case_skiplist_popfirst, 1000000}, 96 | {NULL, NULL, 0}, 97 | }; 98 | 99 | /** 100 | * strings_bench 101 | */ 102 | void case_strings_search(struct bench_ctx *ctx); 103 | void case_strings_replace(struct bench_ctx *ctx); 104 | void case_strings_rand(struct bench_ctx *ctx); 105 | static struct bench_case strings_bench_cases[] = { 106 | {"strings_search", &case_strings_search, 1000000}, 107 | {"strings_replace", &case_strings_replace, 1000000}, 108 | {"strings_rand", &case_strings_rand, 1000000}, 109 | {NULL, NULL, 0}, 110 | }; 111 | 112 | /** 113 | * bench 114 | */ 115 | void bench_ctx_reset_start_at(struct bench_ctx *ctx) { 116 | assert(ctx != NULL); 117 | ctx->start_at = datetime_stamp_now(); 118 | } 119 | 120 | void bench_ctx_reset_end_at(struct bench_ctx *ctx) { 121 | assert(ctx != NULL); 122 | ctx->end_at = datetime_stamp_now(); 123 | } 124 | 125 | static void run_cases(const char *name, struct bench_case cases[]) { 126 | int idx = 0; 127 | 128 | while (1) { 129 | struct bench_case c = cases[idx]; 130 | if (c.name == NULL || c.fn == NULL) break; 131 | fprintf(stderr, "%-17s %-20s ", name, c.name); 132 | struct bench_ctx ctx = {datetime_stamp_now(), -1, c.n}; 133 | (c.fn)(&ctx); 134 | double start_at = ctx.start_at; 135 | double end_at = ctx.end_at; 136 | if (end_at < 0) end_at = datetime_stamp_now(); 137 | idx += 1; 138 | fprintf(stderr, "%10ld%10ldns/op\n", c.n, 139 | (long)(1000000.0 * (end_at - start_at) / (double)c.n)); 140 | } 141 | } 142 | 143 | int main(int argc, const char *argv[]) { 144 | run_cases("buf_bench", buf_bench_cases); 145 | run_cases("dict_bench", dict_bench_cases); 146 | run_cases("heap_bench", heap_bench_cases); 147 | run_cases("log_stderr", log_bench_cases); 148 | run_cases("map_bench", map_bench_cases); 149 | run_cases("skiplist_bench", skiplist_bench_cases); 150 | run_cases("strings_bench", strings_bench_cases); 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /bench/bench.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | struct bench_ctx { 6 | double start_at; /* bench start_at */ 7 | double end_at; /* bench end_at */ 8 | long n; /* bench times */ 9 | }; 10 | 11 | struct bench_case { 12 | const char *name; /* bench case name */ 13 | void (*fn)(struct bench_ctx *ctx); /* bench function */ 14 | long n; /* bench times */ 15 | }; 16 | 17 | void bench_ctx_reset_start_at(struct bench_ctx *ctx); 18 | void bench_ctx_reset_end_at(struct bench_ctx *ctx); 19 | -------------------------------------------------------------------------------- /bench/buf_bench.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include "bench.h" 6 | #include "buf.h" 7 | 8 | void case_buf_puts(struct bench_ctx *ctx) { 9 | struct buf *buf = buf(NULL); 10 | int i; 11 | bench_ctx_reset_start_at(ctx); 12 | for (i = 0; i < ctx->n; i++) { 13 | buf_puts(buf, "abcdefg"); 14 | } 15 | bench_ctx_reset_end_at(ctx); 16 | buf_free(buf); 17 | } 18 | -------------------------------------------------------------------------------- /bench/dict_bench.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include "bench.h" 7 | #include "dict.h" 8 | 9 | void case_dict_set(struct bench_ctx *ctx) { 10 | struct dict *dict = dict(); 11 | /* keys suite */ 12 | int i; 13 | char keys[ctx->n][4]; 14 | for (i = 0; i < ctx->n; i++) sprintf(keys[i], "%d", i & 999); 15 | /* bench */ 16 | bench_ctx_reset_start_at(ctx); 17 | for (i = 0; i < ctx->n; i++) { 18 | dict_set(dict, keys[i], "val"); 19 | } 20 | bench_ctx_reset_end_at(ctx); 21 | dict_free(dict); 22 | } 23 | 24 | void case_dict_get(struct bench_ctx *ctx) { 25 | struct dict *dict = dict(); 26 | /* suite */ 27 | int i; 28 | char keys[ctx->n][4]; 29 | for (i = 0; i < ctx->n; i++) { 30 | sprintf(keys[i], "%d", i & 999); 31 | dict_set(dict, keys[i], "val"); 32 | } 33 | /* bench */ 34 | bench_ctx_reset_start_at(ctx); 35 | for (i = 0; i < ctx->n; i++) { 36 | dict_get(dict, keys[i]); 37 | } 38 | bench_ctx_reset_end_at(ctx); 39 | dict_free(dict); 40 | } 41 | 42 | void case_dict_pop(struct bench_ctx *ctx) { 43 | struct dict *dict = dict(); 44 | /* suite */ 45 | int i; 46 | char keys[ctx->n][4]; 47 | for (i = 0; i < ctx->n; i++) { 48 | sprintf(keys[i], "%d", i & 999); 49 | dict_set(dict, keys[i], "val"); 50 | } 51 | /* bench */ 52 | bench_ctx_reset_start_at(ctx); 53 | for (i = 0; i < ctx->n; i++) { 54 | dict_pop(dict, keys[i]); 55 | } 56 | bench_ctx_reset_end_at(ctx); 57 | dict_free(dict); 58 | } 59 | -------------------------------------------------------------------------------- /bench/heap_bench.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include "bench.h" 7 | #include "heap.h" 8 | 9 | int heap_bench_cmp(void *a, void *b) { return *(int *)a - *(int *)b; } 10 | void case_heap_push(struct bench_ctx *ctx) { 11 | struct heap *heap = heap(&heap_bench_cmp); 12 | int i; 13 | bench_ctx_reset_start_at(ctx); 14 | for (i = 0; i < ctx->n; i++) { 15 | heap_push(heap, &i); 16 | } 17 | bench_ctx_reset_end_at(ctx); 18 | heap_free(heap); 19 | } 20 | 21 | void case_heap_pop(struct bench_ctx *ctx) { 22 | struct heap *heap = heap(&heap_bench_cmp); 23 | int i; 24 | for (i = 0; i < ctx->n; i++) { 25 | heap_push(heap, &i); 26 | } 27 | bench_ctx_reset_start_at(ctx); 28 | for (i = 0; i < ctx->n; i++) { 29 | heap_pop(heap); 30 | } 31 | bench_ctx_reset_end_at(ctx); 32 | heap_free(heap); 33 | } 34 | 35 | void case_heap_top(struct bench_ctx *ctx) { 36 | struct heap *heap = heap(&heap_bench_cmp); 37 | int i; 38 | for (i = 0; i < ctx->n; i++) { 39 | heap_push(heap, &i); 40 | } 41 | bench_ctx_reset_start_at(ctx); 42 | for (i = 0; i < ctx->n; i++) { 43 | heap_top(heap); 44 | } 45 | bench_ctx_reset_end_at(ctx); 46 | heap_free(heap); 47 | } 48 | -------------------------------------------------------------------------------- /bench/log_bench.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "bench.h" 8 | #include "log.h" 9 | 10 | void case_log_devnull(struct bench_ctx *ctx) { 11 | log_open("bench", "/dev/null", 0); 12 | bench_ctx_reset_start_at(ctx); 13 | int i; 14 | for (i = 0; i < ctx->n; i++) { 15 | log_info("hello %s", "world"); 16 | } 17 | bench_ctx_reset_end_at(ctx); 18 | } 19 | -------------------------------------------------------------------------------- /bench/map_bench.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "bench.h" 8 | #include "map.h" 9 | 10 | void case_map_set(struct bench_ctx *ctx) { 11 | struct map *m = map(); 12 | /* keys suite */ 13 | int i; 14 | char keys[ctx->n][4]; 15 | for (i = 0; i < ctx->n; i++) sprintf(keys[i], "%d", i & 999); 16 | /* bench */ 17 | bench_ctx_reset_start_at(ctx); 18 | for (i = 0; i < ctx->n; i++) { 19 | map_set(m, keys[i], "val"); 20 | } 21 | bench_ctx_reset_end_at(ctx); 22 | map_free(m); 23 | } 24 | 25 | void case_map_get(struct bench_ctx *ctx) { 26 | struct map *m = map(); 27 | /* suite */ 28 | int i; 29 | char keys[ctx->n][4]; 30 | for (i = 0; i < ctx->n; i++) { 31 | sprintf(keys[i], "%d", i & 999); 32 | map_set(m, keys[i], "val"); 33 | } 34 | /* bench */ 35 | bench_ctx_reset_start_at(ctx); 36 | for (i = 0; i < ctx->n; i++) { 37 | map_get(m, keys[i]); 38 | } 39 | bench_ctx_reset_end_at(ctx); 40 | map_free(m); 41 | } 42 | 43 | void case_map_pop(struct bench_ctx *ctx) { 44 | struct map *m = map(); 45 | /* suite */ 46 | int i; 47 | char keys[ctx->n][4]; 48 | for (i = 0; i < ctx->n; i++) { 49 | sprintf(keys[i], "%d", i & 999); 50 | map_set(m, keys[i], "val"); 51 | } 52 | /* bench */ 53 | bench_ctx_reset_start_at(ctx); 54 | for (i = 0; i < ctx->n; i++) { 55 | map_pop(m, keys[i]); 56 | } 57 | bench_ctx_reset_end_at(ctx); 58 | map_free(m); 59 | } 60 | -------------------------------------------------------------------------------- /bench/skiplist_bench.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "bench.h" 9 | #include "skiplist.h" 10 | 11 | void case_skiplist_push(struct bench_ctx *ctx) { 12 | struct skiplist *sl = skiplist(NULL); 13 | 14 | int i; 15 | bench_ctx_reset_start_at(ctx); 16 | for (i = 0; i < ctx->n; i++) { 17 | skiplist_push(sl, i, "v"); 18 | } 19 | bench_ctx_reset_end_at(ctx); 20 | skiplist_free(sl); 21 | } 22 | 23 | void case_skiplist_get(struct bench_ctx *ctx) { 24 | struct skiplist *sl = skiplist(NULL); 25 | 26 | int i; 27 | for (i = 0; i < ctx->n; i++) { 28 | skiplist_push(sl, i, "v"); 29 | } 30 | bench_ctx_reset_start_at(ctx); 31 | for (i = 0; i < ctx->n; i++) { 32 | skiplist_get(sl, i); 33 | } 34 | bench_ctx_reset_end_at(ctx); 35 | skiplist_free(sl); 36 | } 37 | 38 | void case_skiplist_pop(struct bench_ctx *ctx) { 39 | struct skiplist *sl = skiplist(NULL); 40 | 41 | int i; 42 | for (i = 0; i < ctx->n; i++) { 43 | skiplist_push(sl, i, "v"); 44 | } 45 | bench_ctx_reset_start_at(ctx); 46 | for (i = 0; i < ctx->n; i++) { 47 | skiplist_pop(sl, i); 48 | } 49 | bench_ctx_reset_end_at(ctx); 50 | skiplist_free(sl); 51 | } 52 | 53 | void case_skiplist_popfirst(struct bench_ctx *ctx) { 54 | struct skiplist *sl = skiplist(NULL); 55 | 56 | int i; 57 | for (i = 0; i < ctx->n; i++) { 58 | skiplist_push(sl, i, "v"); 59 | } 60 | bench_ctx_reset_start_at(ctx); 61 | for (i = 0; i < ctx->n; i++) { 62 | skiplist_popfirst(sl); 63 | } 64 | bench_ctx_reset_end_at(ctx); 65 | skiplist_free(sl); 66 | } 67 | 68 | void case_skiplist_first(struct bench_ctx *ctx) { 69 | struct skiplist *sl = skiplist(NULL); 70 | 71 | int i; 72 | for (i = 0; i < ctx->n; i++) { 73 | skiplist_push(sl, i, "v"); 74 | } 75 | bench_ctx_reset_start_at(ctx); 76 | for (i = 0; i < ctx->n; i++) { 77 | skiplist_first(sl); 78 | } 79 | bench_ctx_reset_end_at(ctx); 80 | skiplist_free(sl); 81 | } 82 | -------------------------------------------------------------------------------- /bench/strings_bench.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "bench.h" 8 | #include "strings.h" 9 | 10 | void case_strings_search(struct bench_ctx *ctx) { 11 | /* suite */ 12 | int i; 13 | char strs[ctx->n][32]; 14 | char subs[ctx->n][4]; 15 | for (i = 0; i < ctx->n; i++) { 16 | sprintf(strs[i], "%d", i); 17 | sprintf(subs[i], "%d", i & 999); 18 | } 19 | /* bench */ 20 | bench_ctx_reset_start_at(ctx); 21 | for (i = 0; i < ctx->n; i++) { 22 | strings_search(strs[i], subs[i], 0); 23 | } 24 | bench_ctx_reset_end_at(ctx); 25 | } 26 | 27 | void case_strings_replace(struct bench_ctx *ctx) { 28 | /* suite */ 29 | int i; 30 | char strs[ctx->n][32]; 31 | char subs[ctx->n][4]; 32 | for (i = 0; i < ctx->n; i++) { 33 | sprintf(strs[i], "%d", i); 34 | sprintf(subs[i], "%d", i & 999); 35 | } 36 | /* bench */ 37 | bench_ctx_reset_start_at(ctx); 38 | for (i = 0; i < ctx->n; i++) { 39 | char dst[100]; 40 | strings_replace(dst, strs[i], subs[i], "rep"); 41 | } 42 | bench_ctx_reset_end_at(ctx); 43 | } 44 | 45 | void case_strings_rand(struct bench_ctx *ctx) { 46 | int i; 47 | for (i = 0; i < ctx->n; i++) { 48 | char s[32]; 49 | strings_rand(s, 32); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /example/.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | # This file is NOT licensed under the GPLv3, which is the license for the rest 2 | # of YouCompleteMe. 3 | # 4 | # Here's the license text for this file: 5 | # 6 | # This is free and unencumbered software released into the public domain. 7 | # 8 | # Anyone is free to copy, modify, publish, use, compile, sell, or 9 | # distribute this software, either in source code form or as a compiled 10 | # binary, for any purpose, commercial or non-commercial, and by any 11 | # means. 12 | # 13 | # In jurisdictions that recognize copyright laws, the author or authors 14 | # of this software dedicate any and all copyright interest in the 15 | # software to the public domain. We make this dedication for the benefit 16 | # of the public at large and to the detriment of our heirs and 17 | # successors. We intend this dedication to be an overt act of 18 | # relinquishment in perpetuity of all present and future rights to this 19 | # software under copyright law. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | # OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # For more information, please refer to 30 | 31 | import os 32 | import ycm_core 33 | 34 | flags = [ 35 | # INSERT FLAGS HERE 36 | '-Wall', 37 | '-I../src', 38 | '-std=c99', 39 | '-D_GNU_SOURCE', 40 | '-pthread', 41 | ] 42 | 43 | 44 | # Set this to the absolute path to the folder (NOT the file!) containing the 45 | # compile_commands.json file to use that instead of 'flags'. See here for 46 | # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html 47 | # 48 | # You can get CMake to generate this file for you by adding: 49 | # set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) 50 | # to your CMakeLists.txt file. 51 | # 52 | # Most projects will NOT need to set this to anything; you can just change the 53 | # 'flags' list of compilation flags. Notice that YCM itself uses that approach. 54 | compilation_database_folder = '' 55 | 56 | if os.path.exists(compilation_database_folder): 57 | database = ycm_core.CompilationDatabase(compilation_database_folder) 58 | else: 59 | database = None 60 | 61 | SOURCE_EXTENSIONS = ['.cpp', '.cxx', '.cc', '.c', '.m', '.mm'] 62 | 63 | 64 | def DirectoryOfThisScript(): 65 | return os.path.dirname(os.path.abspath(__file__)) 66 | 67 | 68 | def MakeRelativePathsInFlagsAbsolute(flags, working_directory): 69 | if not working_directory: 70 | return list(flags) 71 | new_flags = [] 72 | make_next_absolute = False 73 | path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] 74 | for flag in flags: 75 | new_flag = flag 76 | 77 | if make_next_absolute: 78 | make_next_absolute = False 79 | if not flag.startswith('/'): 80 | new_flag = os.path.join(working_directory, flag) 81 | 82 | for path_flag in path_flags: 83 | if flag == path_flag: 84 | make_next_absolute = True 85 | break 86 | 87 | if flag.startswith(path_flag): 88 | path = flag[len(path_flag):] 89 | new_flag = path_flag + os.path.join(working_directory, path) 90 | break 91 | 92 | if new_flag: 93 | new_flags.append(new_flag) 94 | return new_flags 95 | 96 | 97 | def IsHeaderFile(filename): 98 | extension = os.path.splitext(filename)[1] 99 | return extension in ['.h', '.hxx', '.hpp', '.hh'] 100 | 101 | 102 | def GetCompilationInfoForFile(filename): 103 | if IsHeaderFile(filename): 104 | basename = os.path.splitext(filename)[0] 105 | for extension in SOURCE_EXTENSIONS: 106 | replacement_file = basename + extension 107 | if os.path.exists(replacement_file): 108 | compilation_info = database.GetCompilationInfoForFile( 109 | replacement_file) 110 | if compilation_info.compiler_flags_: 111 | return compilation_info 112 | return None 113 | return database.GetCompilationInfoForFile(filename) 114 | 115 | 116 | def FlagsForFile(filename, **kwargs): 117 | if database: 118 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a 119 | # python list, but a "list-like" StringVec object 120 | compilation_info = GetCompilationInfoForFile(filename) 121 | if not compilation_info: 122 | return None 123 | 124 | final_flags = MakeRelativePathsInFlagsAbsolute( 125 | compilation_info.compiler_flags_, 126 | compilation_info.compiler_working_dir_) 127 | 128 | else: 129 | relative_to = DirectoryOfThisScript() 130 | final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) 131 | 132 | return { 133 | 'flags': final_flags, 134 | 'do_cache': True 135 | } 136 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | # vim:set noet: 2 | 3 | default: example 4 | 5 | CC?=cc -std=c99 6 | CFLAGS?=-Wall -I../src -D_GNU_SOURCE 7 | LDFLAGS?=-Wall 8 | 9 | buf_example: buf_example.c ../src/buf.c 10 | cfg_example: cfg_example.c ../src/buf.c ../src/cfg.c 11 | datetime_example: datetime_example.c ../src/datetime.c 12 | dict_example: dict_example.c ../src/dict.c 13 | event_example: event_example.c ../src/event.c 14 | event_timer_example: event_timer_example.c ../src/event.c 15 | heap_example: heap_example.c ../src/heap.c 16 | ketama_example: ketama_example.c ../src/md5.c ../src/ketama.c 17 | list_example: list_example.c ../src/list.c 18 | log_example: log_example.c ../src/log.c 19 | map_example: map_example.c ../src/map.c 20 | md5_example: md5_example.c ../src/md5.c 21 | queue_example: queue_example.c ../src/queue.c 22 | signals_example: signals_example.c ../src/event.c 23 | skiplist_example: skiplist_example.c ../src/skiplist.c 24 | stack_example: stack_example.c ../src/stack.c 25 | strings_example: strings_example.c ../src/strings.c 26 | 27 | example: buf_example\ 28 | cfg_example\ 29 | datetime_example\ 30 | dict_example\ 31 | event_example\ 32 | event_timer_example\ 33 | heap_example\ 34 | ketama_example\ 35 | list_example\ 36 | log_example\ 37 | map_example\ 38 | md5_example\ 39 | queue_example\ 40 | signals_example\ 41 | skiplist_example\ 42 | stack_example\ 43 | strings_example 44 | 45 | clean: 46 | rm -f *_example 47 | -------------------------------------------------------------------------------- /example/buf_example.c: -------------------------------------------------------------------------------- 1 | // cc buf_example.c buf.c 2 | 3 | #include 4 | #include 5 | 6 | #include "buf.h" 7 | 8 | int main(int argc, const char *argv[]) { 9 | struct buf *buf = buf("example"); 10 | /* test if buffer is empty or NULL */ 11 | assert(buf != NULL && !buf_isempty(buf)); 12 | /* put string to buffer */ 13 | assert(buf_puts(buf, "abc") == BUF_OK); 14 | /* put char to buffer */ 15 | assert(buf_putc(buf, 'd') == BUF_OK); 16 | /* format data into buffer */ 17 | assert(buf_sprintf(buf, "%s%d", "efg", 123) == BUF_OK); 18 | /* print buffer, should be "exampleabcdefg123" */ 19 | printf("%s\n", str(buf)); 20 | /* get buffer length */ 21 | printf("current buffer length is %zu, capacity is %zu\n", buf_len(buf), 22 | buf_cap(buf)); 23 | /* free buffer */ 24 | buf_free(buf); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /example/cfg_example.c: -------------------------------------------------------------------------------- 1 | // cc cfg_example.c cfg.c buf.c 2 | 3 | #include 4 | #include 5 | 6 | #include "buf.h" 7 | #include "cfg.h" 8 | 9 | #define READ_UNIT 1024 10 | 11 | int read_file_into_buffer(const char *filename, struct buf *buf); 12 | 13 | int main(int argc, const char *argv[]) { 14 | /* Read file into buffer */ 15 | struct buf *buf = buf_new(NULL); 16 | if (read_file_into_buffer("cfg_example.cfg", buf) != BUF_OK) return -1; 17 | 18 | /* Parse config */ 19 | struct cfg cfg = {buf->data, buf->len, 1}; 20 | int err = 0; 21 | cfg_each(cfg, err) { 22 | printf("%.*s => %.*s\n", (int)(cfg.key_len), cfg.key, 23 | (int)(cfg.val_len), cfg.val); 24 | } 25 | 26 | if (err == CFG_EBADFMT) { 27 | printf("bad format on line %zd\n", cfg.lineno); 28 | return -2; 29 | } 30 | 31 | buf_free(buf); 32 | return 0; 33 | } 34 | 35 | int read_file_into_buffer(const char *filename, struct buf *buf) { 36 | int nread; 37 | FILE *fp = fopen(filename, "r"); 38 | 39 | if (fp == NULL) return -1; 40 | 41 | while (1) { 42 | if (buf_grow(buf, buf->len + READ_UNIT) != BUF_OK) return BUF_ENOMEM; 43 | 44 | if ((nread = fread(buf->data + buf->len, sizeof(char), 45 | buf->cap - buf->len, fp)) <= 0) 46 | break; 47 | 48 | buf->len += nread; 49 | } 50 | return BUF_OK; 51 | } 52 | -------------------------------------------------------------------------------- /example/cfg_example.cfg: -------------------------------------------------------------------------------- 1 | port 8125 # port to bind 2 | 3 | # all backend nodes 4 | node 127.0.0.1:8126 5 | node 127.0.0.1:8127 6 | node 127.0.0.1:8128 7 | -------------------------------------------------------------------------------- /example/datetime_example.c: -------------------------------------------------------------------------------- 1 | // cc datetime_example.c datetime.c 2 | 3 | #include 4 | 5 | #include "datetime.h" 6 | 7 | int main(int argc, const char *argv[]) { 8 | /* get current timstamp (in milliseconds) */ 9 | printf("timstamp (ms) for now is: %.3f ms\n", datetime_stamp_now()); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /example/dict_example.c: -------------------------------------------------------------------------------- 1 | // cc dict_example.c dict.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "dict.h" 8 | 9 | int main(int argc, const char *argv[]) { 10 | /* allocate a new dict */ 11 | struct dict *dict = dict(); 12 | /* set key and values to dict */ 13 | char *key1 = "key1"; 14 | char *key2 = "key2"; 15 | char *val1 = "val1"; 16 | char *val2 = "val2"; 17 | assert(dict_set(dict, key1, val1) == DICT_OK); 18 | assert(dict_set(dict, key2, val2) == DICT_OK); 19 | /* get dict length */ 20 | assert(dict_len(dict) == 2); 21 | /* get data by key */ 22 | assert(dict_get(dict, key1) == val1); 23 | assert(dict_get(dict, key2) == val2); 24 | /* iterate dict */ 25 | struct dict_iter iter = {dict}; 26 | struct dict_node *node = NULL; 27 | dict_each(&iter, node) { 28 | printf("%s => %s\n", node->key, (char *)node->val); 29 | } 30 | /* free the dict */ 31 | dict_free(dict); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /example/event_example.c: -------------------------------------------------------------------------------- 1 | // cc event_example.c event.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "event.h" 8 | 9 | void echo(struct event_loop *loop, int fd, int mask, void *data) { 10 | char s[256]; 11 | 12 | if (fd == STDIN_FILENO) { 13 | scanf("%s", s); 14 | 15 | if (strcmp(s, "exit") == 0) { 16 | /* stop the event loop on message "exit" */ 17 | printf("bye!\n"); 18 | event_loop_stop(loop); 19 | } else { 20 | printf("your input is: %s\n", s); 21 | } 22 | } 23 | } 24 | 25 | void heartbeat(struct event_loop *loop, int id, void *data) { 26 | printf("heartbeat every 1000ms\n"); 27 | } 28 | 29 | int main(int argc, const char *argv[]) { 30 | /* allocate a new event loop with events number limitation 1024 */ 31 | struct event_loop *loop = event_loop(1024); 32 | /* call `echo()` when stdin is readable (on data coming in) */ 33 | event_add_in(loop, STDIN_FILENO, &echo, NULL); 34 | /* call `heartbeat` every 1 second */ 35 | event_add_timer(loop, 1000, &heartbeat, NULL); 36 | /* start event loop */ 37 | printf("start event loop, type 'exit' to exit.. \n"); 38 | event_loop_start(loop); 39 | /* free event loop */ 40 | event_loop_free(loop); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /example/event_timer_example.c: -------------------------------------------------------------------------------- 1 | // cc event_timer_example.c event.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "event.h" 8 | 9 | void beat1000(struct event_loop *loop, int id, void *data) { 10 | printf("heartbeat every 1s\n"); 11 | } 12 | 13 | void beat2000(struct event_loop *loop, int id, void *data) { 14 | printf("heartbeat every 2s\n"); 15 | } 16 | 17 | void beat3000(struct event_loop *loop, int id, void *data) { 18 | printf("heartbeat every 3s\n"); 19 | } 20 | 21 | void beatonce(struct event_loop *loop, int id, void *data) { 22 | printf("heartbeat only once after 5s\n"); 23 | /* stop this timer */ 24 | event_del_timer(loop, id); 25 | } 26 | 27 | int main(int argc, const char *argv[]) { 28 | /* allocate a new event loop with no fire events */ 29 | struct event_loop *loop = event_loop(0); 30 | /* add some timer events */ 31 | event_add_timer(loop, 1000, &beat1000, NULL); 32 | event_add_timer(loop, 2000, &beat2000, NULL); 33 | event_add_timer(loop, 3000, &beat3000, NULL); 34 | event_add_timer(loop, 5000, &beatonce, NULL); 35 | /* start event loop */ 36 | event_loop_start(loop); 37 | /* stop event loop */ 38 | event_loop_stop(loop); 39 | /* free event loop */ 40 | event_loop_free(loop); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /example/heap_example.c: -------------------------------------------------------------------------------- 1 | // cc heap_example.c heap.c 2 | 3 | #include 4 | #include 5 | 6 | #include "heap.h" 7 | 8 | /* heap node comparator type, return negative if arg#0 < arg#1 */ 9 | int cmp(void *a, void *b) { return *(int *)a - *(int *)b; } 10 | int main(int argc, const char *argv[]) { 11 | /* allocate empty heap with comparator */ 12 | struct heap *heap = heap(cmp); 13 | /* push data into heap */ 14 | int a = 4, b = 1, c = 3, d = 2, e = 5; 15 | assert(heap_push(heap, &a) == HEAP_OK); 16 | assert(heap_push(heap, &b) == HEAP_OK); 17 | assert(heap_push(heap, &c) == HEAP_OK); 18 | assert(heap_push(heap, &d) == HEAP_OK); 19 | assert(heap_push(heap, &e) == HEAP_OK); 20 | /* get current heap memory capacity (or memory allocated) */ 21 | printf("current heap allocated memory: %zu\n", heap_cap(heap)); 22 | printf("current heap length: %zu\n", heap_len(heap)); 23 | /* get current smallest data */ 24 | printf("smallest: %d\n", *(int *)heap_top(heap)); 25 | /* pop and print all data (should be in order) */ 26 | while (heap_len(heap) != 0) printf("%d\n", *(int *)heap_pop(heap)); 27 | /* free heap */ 28 | heap_free(heap); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /example/ketama_example.c: -------------------------------------------------------------------------------- 1 | // cc ketama_example.c ketama.c md5.c 2 | 3 | #include 4 | #include 5 | 6 | #include "ketama.h" 7 | 8 | int main(int argc, const char *argv[]) { 9 | /* ketama node array */ 10 | struct ketama_node nodes[4] = { 11 | {"127.0.0.1:8000", 1, "server a"}, 12 | {"127.0.0.1:8001", 1, "server b"}, 13 | {"127.0.0.1:8002", 1, "server c"}, 14 | {"127.0.0.1:8003", 1, "server d"}, 15 | }; 16 | /* create a ketama ring of 4 nodes */ 17 | struct ketama_ring *ring = ketama_ring(nodes, 4); 18 | /* get node by null-terminated key */ 19 | struct ketama_node *node = ketama_node_get(ring, "key"); 20 | printf("get a node by 'key': %s %s\n", node->key, (char *)node->data); 21 | /* get again, consistence hashing */ 22 | node = ketama_node_get(ring, "key"); 23 | printf("get again by 'key': %s %s\n", node->key, (char *)node->data); 24 | /* free the ring */ 25 | ketama_ring_free(ring); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /example/list_example.c: -------------------------------------------------------------------------------- 1 | // cc list_example.c list.c 2 | 3 | #include 4 | #include 5 | 6 | #include "list.h" 7 | 8 | int main(int argc, const char *argv[]) { 9 | /* allocate a empty list */ 10 | struct list *list = list(); 11 | /* push data to the list (on the right)*/ 12 | assert(list_push(list, "a") == LIST_OK); 13 | assert(list_push(list, "b") == LIST_OK); 14 | assert(list_push(list, "c") == LIST_OK); 15 | assert(list_push(list, "d") == LIST_OK); 16 | /* get list current length */ 17 | assert(list_len(list) == 4); 18 | /* pop a node from list (on the left) */ 19 | assert(list_pop(list) != NULL); 20 | /* iterate the list and print the nodes */ 21 | struct list_node *node; 22 | list_each(list, node) { printf("%s ", (char *)node->data); } 23 | /* free the list */ 24 | list_free(list); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /example/log_example.c: -------------------------------------------------------------------------------- 1 | // cc log_example.c log.c -rdynamic 2 | 3 | #include "log.h" 4 | 5 | char make_segmentfault() { 6 | char *s = NULL; 7 | char ch = s[1]; // segment fault 8 | return ch; 9 | } 10 | 11 | int main(int argc, const char *argv[]) { 12 | /* open global logger to stderr (by setting filename to NULL) */ 13 | log_open("example", NULL, 0); 14 | /* set log level to info, also the default level */ 15 | log_setlevel(LOG_INFO); 16 | /* debug mesage won't be seen */ 17 | log_debug("debug message"); 18 | /* but info and warn, error message can be seen */ 19 | log_info("info message"); 20 | log_warn("warn message"); 21 | log_error("error message: %s", "someting wrong"); 22 | /* will log trace back on segmentfault automatically */ 23 | make_segmentfault(); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /example/map_example.c: -------------------------------------------------------------------------------- 1 | // cc map_example.c map.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "map.h" 8 | 9 | int main(int argc, const char *argv[]) { 10 | /* allocate a new map */ 11 | struct map *m = map(); 12 | /* set key and values to map */ 13 | char *key1 = "key1"; 14 | char *key2 = "key2"; 15 | char *val1 = "val1"; 16 | char *val2 = "val2"; 17 | assert(map_set(m, key1, val1) == MAP_OK); 18 | assert(map_set(m, key2, val2) == MAP_OK); 19 | /* get map length */ 20 | assert(map_len(m) == 2); 21 | /* get data by key */ 22 | assert(map_get(m, key1) == val1); 23 | assert(map_get(m, key2) == val2); 24 | /* set some key values more */ 25 | assert(map_set(m, "key3", "val3") == MAP_OK); 26 | assert(map_set(m, "key4", "val4") == MAP_OK); 27 | assert(map_set(m, "key5", "val5") == MAP_OK); 28 | assert(map_set(m, "key6", "val6") == MAP_OK); 29 | assert(map_set(m, "key7", "val7") == MAP_OK); 30 | /* iterate the map */ 31 | struct map_iter iter = {m}; 32 | struct map_node *node = NULL; 33 | map_each(&iter, node) { 34 | printf("%s => %s\n", node->key, (char *)node->val); 35 | } 36 | /* free the map */ 37 | map_free(m); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /example/md5_example.c: -------------------------------------------------------------------------------- 1 | // cc md5_example.c md5.c 2 | 3 | #include 4 | #include 5 | 6 | #include "md5.h" 7 | 8 | int main(int argc, const char *argv[]) { 9 | char *key = "key"; 10 | unsigned long len = strlen(key); 11 | /* get hashing number by key */ 12 | printf("md5 hash for 'key': %u\n", hash_md5(key, len)); 13 | /* get md5 signatures (16) */ 14 | unsigned char results[16]; 15 | md5_signature((unsigned char *)key, len, results); 16 | int i; 17 | for (i = 0; i < 16; i++) printf("%x", results[i]); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /example/queue_example.c: -------------------------------------------------------------------------------- 1 | // cc queue_example.c queue.c 2 | 3 | #include 4 | #include 5 | 6 | #include "queue.h" 7 | 8 | int main(int argc, const char *argv[]) { 9 | /* allocate a new empty queue */ 10 | struct queue *queue = queue(); 11 | /* push data to queue */ 12 | char *val1 = "val1"; 13 | char *val2 = "val2"; 14 | char *val3 = "val3"; 15 | assert(queue_push(queue, val1) == QUEUE_OK); 16 | assert(queue_push(queue, val2) == QUEUE_OK); 17 | assert(queue_push(queue, val3) == QUEUE_OK); 18 | /* get queue len */ 19 | printf("current queue length: %zu\n", queue_len(queue)); 20 | /* pop data from queue, the order is the same with push */ 21 | void *data; 22 | while ((data = queue_pop(queue)) != NULL) 23 | printf("pop data: %s\n", (char *)data); 24 | /* free queue */ 25 | queue_free(queue); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /example/signals_example.c: -------------------------------------------------------------------------------- 1 | // cc signals_example.c event.c 2 | #include 3 | 4 | #include "event.h" 5 | #include "signals.h" 6 | 7 | void on_keyboardinterrupt(int signal) { printf("keyboardinterrupt!\n"); } 8 | void on_signalterm(int signal) { printf("signal term received!\n"); } 9 | void beat1000(struct event_loop *loop, int id, void *data) { 10 | printf("heartbeat every 1s\n"); 11 | } 12 | 13 | int main(int argc, const char *argv[]) { 14 | signals_register(SIGINT, &on_keyboardinterrupt); 15 | signals_register(SIGTERM, &on_signalterm); 16 | /* forever event loop to heart-beat every 10s */ 17 | struct event_loop *loop = event_loop(0); 18 | event_add_timer(loop, 1000, &beat1000, NULL); 19 | event_loop_start(loop); 20 | event_loop_free(loop); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /example/skiplist_example.c: -------------------------------------------------------------------------------- 1 | // cc skiplist_example.c skiplist.c 2 | 3 | #include 4 | #include 5 | 6 | #include "skiplist.h" 7 | 8 | int main(int argc, const char *argv[]) { 9 | /* allocate a new empty skiplist (using default score comparator) */ 10 | struct skiplist *skiplist = skiplist(NULL); 11 | /* push data into skiplist */ 12 | unsigned long a = 4, b = 2, c = 3, d = 7, e = 5, f = 6, g = 9, h = 8, i = 1; 13 | assert(skiplist_push(skiplist, a, "a") == SKIPLIST_OK); 14 | assert(skiplist_push(skiplist, b, "b") == SKIPLIST_OK); 15 | assert(skiplist_push(skiplist, c, "c") == SKIPLIST_OK); 16 | assert(skiplist_push(skiplist, d, "d") == SKIPLIST_OK); 17 | assert(skiplist_push(skiplist, e, "e") == SKIPLIST_OK); 18 | assert(skiplist_push(skiplist, f, "f") == SKIPLIST_OK); 19 | assert(skiplist_push(skiplist, g, "g") == SKIPLIST_OK); 20 | assert(skiplist_push(skiplist, i, "i") == SKIPLIST_OK); 21 | assert(skiplist_push(skiplist, h, "h") == SKIPLIST_OK); 22 | /* get data by node */ 23 | printf("score: %lu => data: %s\n", a, (char *)skiplist_get(skiplist, a)); 24 | printf("score: %lu => data: %s\n", b, (char *)skiplist_get(skiplist, b)); 25 | printf("score: %lu => data: %s\n", c, (char *)skiplist_get(skiplist, c)); 26 | printf("score: %lu => data: %s\n", d, (char *)skiplist_get(skiplist, d)); 27 | printf("score: %lu => data: %s\n", e, (char *)skiplist_get(skiplist, e)); 28 | printf("score: %lu => data: %s\n", f, (char *)skiplist_get(skiplist, f)); 29 | printf("score: %lu => data: %s\n", g, (char *)skiplist_get(skiplist, g)); 30 | printf("score: %lu => data: %s\n", h, (char *)skiplist_get(skiplist, h)); 31 | printf("score: %lu => data: %s\n", i, (char *)skiplist_get(skiplist, i)); 32 | /* print current skiplist */ 33 | skiplist_print(skiplist); 34 | /* iterate the skiplist */ 35 | struct skiplist_node *node; 36 | skiplist_each(skiplist, node) { printf("%lu ", node->score); } 37 | printf("\n"); 38 | /* get the first and last node */ 39 | struct skiplist_node *first = skiplist_first(skiplist); 40 | struct skiplist_node *last = skiplist_last(skiplist); 41 | printf("first node => score: %ld, data: '%s'\n", first->score, 42 | (char *)first->data); 43 | printf("last node => score: %ld, data: '%s'\n", last->score, 44 | (char *)last->data); 45 | /* pop the first and last node */ 46 | printf("pop first node, data: '%s'\n", (char *)skiplist_popfirst(skiplist)); 47 | printf("pop last node, data: '%s'\n", (char *)skiplist_poplast(skiplist)); 48 | /* print current skiplist length and height */ 49 | printf("skiplist height: %d\n", skiplist_height(skiplist)); 50 | printf("skiplist len: %zu\n", skiplist_len(skiplist)); 51 | /* clear the skiplist */ 52 | skiplist_clear(skiplist); 53 | printf("skiplist height after clear: %d\n", skiplist_height(skiplist)); 54 | printf("skiplist len after clear: %zu\n", skiplist_len(skiplist)); 55 | skiplist_print(skiplist); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /example/stack_example.c: -------------------------------------------------------------------------------- 1 | // cc stack_example.c stack.c 2 | 3 | #include 4 | #include 5 | 6 | #include "stack.h" 7 | 8 | int main(int argc, const char *argv[]) { 9 | /* allocate a new empty stack with memory capacity 1 */ 10 | struct stack *stack = stack(1); 11 | /* push data to stack */ 12 | char *val1 = "val1"; 13 | char *val2 = "val2"; 14 | char *val3 = "val3"; 15 | assert(stack_push(stack, val1) == STACK_OK); 16 | assert(stack_push(stack, val2) == STACK_OK); 17 | assert(stack_push(stack, val3) == STACK_OK); 18 | /* current stack capacity (or allocated memory) */ 19 | printf("current stack allocated memory: %zu\n", stack_cap(stack)); 20 | /* get stack len */ 21 | printf("current stack length: %zu\n", stack_len(stack)); 22 | /* pop data from stack, the order is invse with push */ 23 | void *data; 24 | while ((data = stack_pop(stack)) != NULL) 25 | printf("pop data: %s\n", (char *)data); 26 | /* free stack */ 27 | stack_free(stack); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /example/strings_example.c: -------------------------------------------------------------------------------- 1 | // cc strings_example.c strings.c 2 | #include 3 | 4 | #include "strings.h" 5 | 6 | void example_search() { 7 | /* search string via Boyer Moore algorithm */ 8 | char *s = "this is a simple example"; 9 | char *sub = "mp"; 10 | size_t first_idx = strings_search(s, sub, 0); 11 | size_t second_idx = strings_search(s, sub, first_idx + 1); 12 | printf("'%s' is seen in '%s' at index %zu\n", sub, s, first_idx); 13 | printf("'%s' is seen in '%s' again at index %zu\n", sub, s, second_idx); 14 | } 15 | 16 | void example_rand() { 17 | /* get some random strings (length 20) */ 18 | char s[20]; 19 | printf("random string with length 20: %s\n", strings_rand(s, 20)); 20 | printf("random string with length 20: %s\n", strings_rand(s, 20)); 21 | printf("random string with length 20: %s\n", strings_rand(s, 20)); 22 | } 23 | 24 | void example_replace() { 25 | /* replace string example */ 26 | char *src = "this is a simple example"; 27 | char *sub = "mp"; 28 | char *rep = "**"; 29 | char dst[100] = {0}; 30 | strings_replace(dst, src, sub, rep); 31 | printf("replace '%s' in '%s' with '%s': '%s'\n", sub, src, rep, dst); 32 | } 33 | 34 | int main(int argc, const char *argv[]) { 35 | example_search(); 36 | example_rand(); 37 | example_replace(); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /lint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Lint script via clang-format. 5 | 6 | Usage 7 | 8 | $ ./lint.py [fix] 9 | """ 10 | 11 | import sys 12 | import os 13 | import subprocess 14 | import xml.etree.ElementTree as ET 15 | 16 | FILE_EXTENSIONS = ('.h', '.c') 17 | CLANGFOMART_NAMES = ['clang-format', 'clang-format-3.5', 'clang-format-3.6'] 18 | CLANGFORMAT_LINT_OPTIONS = ['-output-replacements-xml', '-style=file'] 19 | CLANGFORMAT_FIX_OPTIONS = ['-i', '-style=file'] 20 | 21 | 22 | def print_usage(): 23 | print "Usage: ./lint.py [fix]" 24 | 25 | 26 | def traverse_files(): 27 | """Get file path list recursively. 28 | """ 29 | paths = [] 30 | for root, dirs, files in os.walk("./"): 31 | for filename in files: 32 | if filename.endswith(FILE_EXTENSIONS): 33 | paths.append(os.path.join(root, filename)) 34 | return paths 35 | 36 | 37 | def get_clang_format_bin(): 38 | for name in CLANGFOMART_NAMES: 39 | try: 40 | subprocess.check_output([name, "-version"]) 41 | except OSError: 42 | continue 43 | else: 44 | return name 45 | raise Exception("No clang-format command available.") 46 | 47 | 48 | def lint(fix=False): 49 | num_errors = 0 50 | num_error_files = 0 51 | num_fixed_files = 0 52 | clangformat_bin = get_clang_format_bin() 53 | for path in traverse_files(): 54 | cmd = [clangformat_bin, path] 55 | cmd.extend(CLANGFORMAT_LINT_OPTIONS) 56 | out = subprocess.check_output(cmd) 57 | root = ET.fromstring(out) 58 | has_error = False 59 | for tag in root.findall('replacement'): 60 | offset = tag.get('offset', None) 61 | length = tag.get("length", None) 62 | if offset is not None: 63 | has_error = True 64 | num_errors += 1 65 | print "{0}:{1},{2}".format(path, offset, length) 66 | if has_error: 67 | num_error_files += 1 68 | if fix: 69 | cmd = [clangformat_bin, path] 70 | cmd.extend(CLANGFORMAT_FIX_OPTIONS) 71 | if subprocess.call(cmd) == 0: 72 | num_fixed_files += 1 73 | if has_error: 74 | print "{} fixed".format(path) 75 | if num_errors > 0 and num_error_files != num_fixed_files: 76 | sys.exit(1) 77 | 78 | 79 | if __name__ == '__main__': 80 | if len(sys.argv) <= 1: # Case ./program 81 | lint(fix=False) 82 | elif sys.argv[1] == 'fix': # Case ./program fix 83 | lint(fix=True) 84 | else: 85 | print_usage() 86 | -------------------------------------------------------------------------------- /src/buf.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "buf.h" 13 | 14 | /* Create new buffer and init it with a C null-terminated 15 | * string if `s` is not NULL. 16 | */ 17 | struct buf *buf_new(const char *s) { 18 | struct buf *buf = malloc(sizeof(struct buf)); 19 | 20 | if (buf != NULL) { 21 | buf->len = 0; 22 | buf->cap = 0; 23 | buf->data = NULL; 24 | 25 | if (s != NULL) { 26 | if (buf_puts(buf, s) != BUF_OK) return NULL; 27 | } 28 | } 29 | return buf; 30 | } 31 | 32 | /* Create empty buffer */ 33 | struct buf *buf_empty(void) { 34 | return buf_new(NULL); 35 | } 36 | /* Free a buffer and its data, no operation is performed 37 | * if the buffer is NULL. */ 38 | void buf_free(struct buf *buf) { 39 | if (buf != NULL) { 40 | if (buf->data != NULL) free(buf->data); 41 | free(buf); 42 | } 43 | } 44 | 45 | /* Clear a buffer and the data memory will also be freed. */ 46 | void buf_clear(struct buf *buf) { 47 | assert(buf != NULL); 48 | 49 | if (buf->data != NULL) free(buf->data); 50 | buf->data = NULL; 51 | buf->len = 0; 52 | buf->cap = 0; 53 | } 54 | 55 | /* Grow a buffer's capacity to given size, the new capacity is 56 | * calculated like k*unit>=cap, by default, the unit is current cap, 57 | * if the unit is large enough, use BUF_UNIT_MAX instead. */ 58 | int buf_grow(struct buf *buf, size_t cap) { 59 | assert(buf != NULL); 60 | 61 | if (cap > BUF_CAP_MAX) return BUF_ENOMEM; 62 | 63 | if (cap <= buf->cap) return BUF_OK; 64 | 65 | size_t unit = buf->cap; 66 | 67 | if (unit > BUF_UNIT_MAX) unit = BUF_UNIT_MAX; 68 | 69 | if (unit < BUF_UNIT_MIN) unit = BUF_UNIT_MIN; 70 | 71 | size_t new_cap = buf->cap + unit; 72 | while (new_cap < cap) new_cap += unit; 73 | 74 | char *data = realloc(buf->data, new_cap * sizeof(char)); 75 | 76 | if (data == NULL) return BUF_ENOMEM; 77 | 78 | buf->data = data; 79 | buf->cap = new_cap; 80 | return BUF_OK; 81 | } 82 | 83 | /* Put chars on the end of a buffer */ 84 | int buf_put(struct buf *buf, char *data, size_t len) { 85 | int error = buf_grow(buf, buf->len + len); 86 | 87 | if (error == BUF_OK) { 88 | memcpy(buf->data + buf->len, data, len); 89 | buf->len += len; 90 | } 91 | return error; 92 | } 93 | 94 | /* Put null-terminated chars to the end of a buffer. */ 95 | int buf_puts(struct buf *buf, const char *s) { 96 | return buf_put(buf, (char *)s, strlen(s)); 97 | } 98 | 99 | /* Put a single char to the end of a buffer. */ 100 | int buf_putc(struct buf *buf, char ch) { 101 | int error = buf_grow(buf, buf->len + 1); 102 | 103 | if (error == BUF_OK) { 104 | buf->data[buf->len] = ch; 105 | buf->len += 1; 106 | } 107 | return error; 108 | } 109 | 110 | /* Get buffer data as null-terminated chars */ 111 | char *buf_str(struct buf *buf) { 112 | assert(buf != NULL); 113 | 114 | if (buf->len < buf->cap && buf->data[buf->len] == '\0') return buf->data; 115 | 116 | if (buf->len + 1 <= buf->cap || buf_grow(buf, buf->len + 1) == BUF_OK) { 117 | buf->data[buf->len] = '\0'; 118 | return buf->data; 119 | } 120 | 121 | return NULL; 122 | } 123 | 124 | /* Return 1 if a buffer is empty, else 0 */ 125 | int buf_isempty(struct buf *buf) { 126 | assert(buf != NULL); 127 | if (buf->len == 0) return 1; 128 | return 0; 129 | } 130 | 131 | /* Formatted printing to a buffer. */ 132 | int buf_sprintf(struct buf *buf, const char *fmt, ...) { 133 | assert(buf != NULL); 134 | 135 | if (buf->len >= buf->cap && buf_grow(buf, buf->len + 1) != BUF_OK) 136 | return BUF_ENOMEM; 137 | 138 | va_list ap; 139 | int num; 140 | 141 | va_start(ap, fmt); 142 | num = vsnprintf(buf->data + buf->len, buf->cap - buf->len, fmt, ap); 143 | va_end(ap); 144 | 145 | if (num < 0) return BUF_EFAILED; 146 | 147 | size_t size = (size_t)num; 148 | 149 | if (size >= buf->cap - buf->len) { 150 | if (buf_grow(buf, buf->len + size + 1) != BUF_OK) return BUF_ENOMEM; 151 | va_start(ap, fmt); 152 | num = vsnprintf(buf->data + buf->len, buf->cap - buf->len, fmt, ap); 153 | va_end(ap); 154 | } 155 | 156 | if (num < 0) return BUF_EFAILED; 157 | 158 | buf->len += num; 159 | return BUF_OK; 160 | } 161 | 162 | /* Romve part of buf on the left. */ 163 | void buf_lrm(struct buf *buf, size_t len) { 164 | assert(buf != NULL); 165 | 166 | if (len > buf->len) { 167 | buf->len = 0; 168 | return; 169 | } 170 | 171 | buf->len -= len; 172 | memmove(buf->data, buf->data + len, buf->len); 173 | } 174 | 175 | /* Get buf length. */ 176 | size_t buf_len(struct buf *buf) { 177 | assert(buf != NULL); 178 | return buf->len; 179 | } 180 | 181 | /* Get buf capacity. */ 182 | size_t buf_cap(struct buf *buf) { 183 | assert(buf != NULL); 184 | return buf->cap; 185 | } 186 | -------------------------------------------------------------------------------- /src/buf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Dynamic buffer implementation. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __BUF_H__ 9 | #define __BUF_H__ 10 | 11 | #include 12 | #include 13 | 14 | #if defined(__cplusplus) 15 | extern "C" { 16 | #endif 17 | 18 | #define BUF_CAP_MAX 64 * 1024 * 1024 /* max buffer capacity: 64mb */ 19 | #define BUF_UNIT_MIN 1 /* min buffer realloc unit: 1 */ 20 | #define BUF_UNIT_MAX 1024 * 1024 /* max buffer realloc unit: 1mb */ 21 | 22 | #define buf(s) buf_new(s) 23 | #define str(b) buf_str(b) 24 | 25 | enum { 26 | BUF_OK = 0, /* operation is ok */ 27 | BUF_ENOMEM = 1, /* no memory error */ 28 | BUF_EFAILED = 2, /* operation is failed */ 29 | }; 30 | 31 | struct buf { 32 | size_t len; /* buffer length */ 33 | size_t cap; /* buffer capacity */ 34 | char *data; /* real buffer pointer */ 35 | }; 36 | 37 | struct buf *buf_new(const char *s); 38 | struct buf *buf_empty(void); 39 | void buf_free(struct buf *buf); 40 | void buf_clear(struct buf *buf); 41 | int buf_grow(struct buf *buf, size_t cap); 42 | int buf_put(struct buf *buf, char *data, size_t len); 43 | int buf_puts(struct buf *buf, const char *s); 44 | int buf_putc(struct buf *buf, char ch); 45 | char *buf_str(struct buf *buf); 46 | int buf_isempty(struct buf *buf); 47 | int buf_sprintf(struct buf *buf, const char *fmt, ...); 48 | void buf_lrm(struct buf *buf, size_t len); 49 | size_t buf_len(struct buf *buf); 50 | size_t buf_cap(struct buf *buf); 51 | 52 | #if defined(__cplusplus) 53 | } 54 | #endif 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/cfg.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "cfg.h" 9 | 10 | /* Get key and val from cfg. */ 11 | int cfg_get(struct cfg *cfg) { 12 | assert(cfg != NULL); 13 | assert(cfg->lineno > 0); 14 | 15 | if (cfg->data == NULL || cfg->len == 0) return CFG_EOF; 16 | 17 | char *data = cfg->data; 18 | size_t len = cfg->len; 19 | size_t idx = 0; 20 | 21 | cfg->key = NULL; 22 | cfg->val = NULL; 23 | cfg->key_len = 0; 24 | cfg->val_len = 0; 25 | 26 | while (idx < len) { 27 | switch (data[idx]) { 28 | case '\t': 29 | case ' ': 30 | if (cfg->key != NULL && cfg->key_len == 0) /* key end */ 31 | cfg->key_len = data + idx - cfg->key; 32 | if (cfg->val != NULL && cfg->val_len == 0) /* val end */ 33 | cfg->val_len = data + idx - cfg->val; 34 | break; 35 | case '\n': 36 | if (cfg->val != NULL && cfg->val_len == 0) /* val end */ 37 | cfg->val_len = data + idx - cfg->val; 38 | if (cfg->key_len != 0 && cfg->val_len != 0) 39 | /* line end ok with key & val */ 40 | return CFG_OK; 41 | if (cfg->key != NULL && cfg->val == NULL) 42 | /* line contains only one word */ 43 | return CFG_EBADFMT; 44 | cfg->lineno++; 45 | break; 46 | case '#': 47 | if (cfg->val != NULL && cfg->val_len == 0) /* val end */ 48 | cfg->val_len = data + idx - cfg->val; 49 | 50 | while (idx + 1 < len && data[idx + 1] != '\n') { 51 | idx++; 52 | cfg->data++; 53 | cfg->len--; 54 | } 55 | break; 56 | default: 57 | if (cfg->key == NULL) /* key start */ 58 | cfg->key = data + idx; 59 | if (cfg->val == NULL && cfg->key_len != 0) /* val start */ 60 | cfg->val = data + idx; 61 | if (cfg->key_len != 0 && cfg->val_len != 0) 62 | /* bad char after key and val */ 63 | return CFG_EBADFMT; 64 | break; 65 | } 66 | 67 | idx++; 68 | cfg->data++; 69 | cfg->len--; 70 | }; 71 | 72 | return CFG_EOF; 73 | } 74 | -------------------------------------------------------------------------------- /src/cfg.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Simple configuration reader. 5 | * deps: None. 6 | * 7 | * example cfg string: 8 | * 9 | * # proxy port 10 | * port 8125 11 | * # proxy backend nodes 12 | * node 127.0.0.1:8126 13 | * node 127.0.0.1:8127 14 | * node 127.0.0.1:8128 15 | * 16 | * example usage: 17 | * 18 | * struct cfg cfg = {buf, buf_len, 1}; 19 | * int err = 0; 20 | * cfg_each(cfg, err) { 21 | * cfg.key.. 22 | * cfg.key_len.. 23 | * cfg.val.. 24 | * cfg.val_len.. 25 | * } 26 | */ 27 | 28 | #ifndef __CFG_H__ 29 | #define __CFG_H__ 30 | 31 | #include 32 | 33 | #if defined(__cplusplus) 34 | extern "C" { 35 | #endif 36 | 37 | #define cfg_each(cfg, err) while (((err) = cfg_get(&(cfg))) == CFG_OK) 38 | 39 | enum { 40 | CFG_OK = 0, /* operation is ok */ 41 | CFG_EOF = 1, /* EOF reached */ 42 | CFG_EBADFMT = 2, /* invalid format */ 43 | }; 44 | 45 | struct cfg { 46 | char *data; /* currnt cfg data */ 47 | size_t len; /* currnt cfg data length */ 48 | size_t lineno; /* cfg lineno */ 49 | char *key; /* current key */ 50 | size_t key_len; /* current key length */ 51 | char *val; /* current val */ 52 | size_t val_len; /* current val length */ 53 | }; 54 | 55 | int cfg_get(struct cfg *cfg); 56 | 57 | #if defined(__cplusplus) 58 | } 59 | #endif 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/datetime.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | /* Get timestamp (in milliseconds) for now. */ 10 | double datetime_stamp_now(void) { 11 | #if defined CLOCK_REALTIME 12 | struct timespec ts; 13 | int rc = clock_gettime(CLOCK_REALTIME, &ts); 14 | assert(rc == 0); 15 | return ts.tv_sec * 1000 + ts.tv_nsec / 1000000.0; 16 | #else 17 | struct timeval tv; 18 | gettimeofday(&tv, NULL); 19 | return (1000000 * tv.tv_sec + tv.tv_usec) / 1000.0; 20 | #endif 21 | } 22 | -------------------------------------------------------------------------------- /src/datetime.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Datetime utils. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __DATETIME_H__ 9 | #define __DATETIME_H__ 10 | 11 | #if defined(__cplusplus) 12 | extern "C" { 13 | #endif 14 | 15 | double datetime_stamp_now(void); 16 | 17 | #if defined(__cplusplus) 18 | } 19 | #endif 20 | #endif 21 | -------------------------------------------------------------------------------- /src/dict.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Dynamic sized list-based hashtable implementation. 5 | * deps: None 6 | */ 7 | 8 | #ifndef __DICT_H__ 9 | #define __DICT_H__ 10 | 11 | #include 12 | #include 13 | 14 | #if defined(__cplusplus) 15 | extern "C" { 16 | #endif 17 | 18 | #define DICT_LOAD_LIMIT 0.72 /* load factor */ 19 | #define dict() dict_new() 20 | #define dict_iter(dict) dict_iter_new(dict) 21 | #define dict_each(iter, node) while (((node) = dict_iter_next((iter))) != NULL) 22 | 23 | enum { 24 | DICT_OK = 0, /* operation is ok */ 25 | DICT_ENOMEM = 1, /* no memory error */ 26 | }; 27 | 28 | struct dict_node { 29 | char *key; /* key string */ 30 | size_t len; /* key length will be set on `node_new` */ 31 | void *val; /* value data */ 32 | struct dict_node *next; /* next node */ 33 | }; 34 | 35 | struct dict { 36 | size_t idx; /* index in table sizes */ 37 | size_t len; /* dict length */ 38 | struct dict_node **table; /* node table */ 39 | }; 40 | 41 | struct dict_iter { 42 | struct dict *dict; /* dict to iterate */ 43 | size_t index; /* current table index */ 44 | struct dict_node *node; /* current dict node */ 45 | }; 46 | 47 | struct dict *dict_new(void); 48 | void dict_clear(struct dict *dict); /* O(N) */ 49 | void dict_free(struct dict *dict); 50 | size_t dict_len(struct dict *dict); /* O(1) */ 51 | size_t dict_cap(struct dict *dict); /* O(1) */ 52 | int dict_set(struct dict *dict, char *key, void *val); /* O(1) */ 53 | void *dict_get(struct dict *dict, char *key); /* O(1) */ 54 | void *dict_pop(struct dict *dict, char *key); /* O(1) */ 55 | int dict_has(struct dict *dict, char *key); /* O(1) */ 56 | struct dict_iter *dict_iter_new(struct dict *dict); 57 | void dict_iter_free(struct dict_iter *iter); 58 | struct dict_node *dict_iter_next(struct dict_iter *iter); 59 | void dict_iter_rewind(struct dict_iter *iter); 60 | int dict_iset(struct dict *dict, char *key, size_t len, void *val); /* O(1) */ 61 | void *dict_iget(struct dict *dict, char *key, size_t len); /* O(1) */ 62 | void *dict_ipop(struct dict *dict, char *key, size_t len); /* O(1) */ 63 | int dict_ihas(struct dict *dict, char *key, size_t len); /* O(1) */ 64 | 65 | #if defined(__cplusplus) 66 | } 67 | #endif 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/event.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "event.h" 9 | 10 | #include "event_timer.c" 11 | #ifdef HAVE_KQUEUE 12 | #include "event_kqueue.c" 13 | #else 14 | #ifdef HAVE_EPOLL 15 | #include "event_epoll.c" 16 | #else 17 | #error "no event lib avaliable" 18 | #endif 19 | #endif 20 | 21 | /* Create an event loop. */ 22 | struct event_loop *event_loop_new(int size) { 23 | assert(size >= 0); 24 | 25 | /* event numbers must be greater than RESERVED_FDS + FDSET_INCR */ 26 | size += EVENT_FDSET_INCR + EVENT_MIN_RESERVED_FDS; 27 | 28 | struct event_loop *loop = malloc(sizeof(struct event_loop)); 29 | 30 | if (loop == NULL) return NULL; 31 | 32 | loop->size = size; 33 | loop->events = NULL; 34 | loop->api = NULL; 35 | loop->num_timers = 0; 36 | 37 | /* events */ 38 | loop->events = malloc(sizeof(struct event) * size); 39 | if (loop->events == NULL) { 40 | free(loop); 41 | return NULL; 42 | } 43 | 44 | /* event api */ 45 | if (event_api_loop_new(loop) != EVENT_OK) { 46 | free(loop->events); 47 | free(loop); 48 | return NULL; 49 | } 50 | 51 | /* timer heap */ 52 | loop->timer_heap = event_timer_heap_new(); 53 | if (loop->timer_heap == NULL) { 54 | event_api_loop_free(loop); 55 | free(loop->events); 56 | free(loop); 57 | return NULL; 58 | } 59 | 60 | /* init all timers id to -1 */ 61 | int i; 62 | for (i = 0; i < EVENT_TIMER_ID_MAX; i++) loop->timers[i].id = -1; 63 | 64 | /* init all events mask to NONE */ 65 | for (i = 0; i < size; i++) loop->events[i].mask = EVENT_NONE; 66 | return loop; 67 | } 68 | 69 | /* Free an event loop. */ 70 | void event_loop_free(struct event_loop *loop) { 71 | if (loop != NULL) { 72 | event_timer_heap_free(loop->timer_heap); 73 | event_api_loop_free(loop); 74 | if (loop->events != NULL) free(loop->events); 75 | free(loop); 76 | } 77 | } 78 | 79 | /* Wait for events. */ 80 | int event_wait(struct event_loop *loop) { 81 | assert(loop != NULL); 82 | 83 | long time_now = event_time_now(); 84 | long timeout = -1; /* block forever */ 85 | struct event_timer *nearest_timer = event_nearest_timer(loop); 86 | 87 | if (nearest_timer != NULL) timeout = nearest_timer->fire_at - time_now; 88 | 89 | int result = event_api_wait(loop, timeout); 90 | event_process_timers(loop); 91 | return result; 92 | } 93 | 94 | /* Start event loop */ 95 | int event_loop_start(struct event_loop *loop) { 96 | assert(loop != NULL); 97 | 98 | loop->state = EVENT_LOOP_RUNNING; 99 | 100 | int err; 101 | 102 | while (loop->state != EVENT_LOOP_STOPPED) 103 | if ((err = event_wait(loop)) != EVENT_OK) return err; 104 | 105 | return EVENT_OK; 106 | } 107 | 108 | /* Stop event loop */ 109 | void event_loop_stop(struct event_loop *loop) { 110 | assert(loop != NULL); 111 | loop->state = EVENT_LOOP_STOPPED; 112 | } 113 | 114 | /* Add an event to event loop (mod if the fd already in set). */ 115 | int event_add(struct event_loop *loop, int fd, int mask, event_cb_t cb, 116 | void *data) { 117 | assert(loop != NULL); 118 | assert(loop->api != NULL); 119 | assert(cb != NULL); 120 | 121 | if (fd > loop->size) return EVENT_ERANGE; 122 | 123 | int err = event_api_add(loop, fd, mask); 124 | 125 | if (err != EVENT_OK) return err; 126 | 127 | struct event *ev = &loop->events[fd]; 128 | ev->mask |= mask; 129 | 130 | if (mask & EVENT_ERROR) ev->ecb = cb; 131 | if (mask & EVENT_READABLE) ev->rcb = cb; 132 | if (mask & EVENT_WRITABLE) ev->wcb = cb; 133 | 134 | ev->data = data; 135 | return EVENT_OK; 136 | } 137 | 138 | /* Delete an event from loop. */ 139 | int event_del(struct event_loop *loop, int fd, int mask) { 140 | assert(loop != NULL); 141 | 142 | if (fd > loop->size) return EVENT_ERANGE; 143 | 144 | struct event *ev = &loop->events[fd]; 145 | 146 | if (ev->mask == EVENT_NONE) return EVENT_OK; 147 | 148 | int err = event_api_del(loop, fd, mask); 149 | 150 | if (err != EVENT_OK) return err; 151 | 152 | ev->mask = ev->mask & (~mask); 153 | return EVENT_OK; 154 | } 155 | 156 | /* Add timer to event loop. (interval#ms) */ 157 | int event_add_timer(struct event_loop *loop, long interval, event_timer_cb_t cb, 158 | void *data) { 159 | assert(loop != NULL && loop->timers != NULL && loop->timer_heap != NULL); 160 | assert(interval > 0); 161 | 162 | int id; 163 | struct event_timer *timer; 164 | 165 | /* find available id */ 166 | for (id = 0; id < EVENT_TIMER_ID_MAX; id++) { 167 | timer = &loop->timers[id]; 168 | if (timer->id < 0) break; 169 | } 170 | 171 | if (id >= EVENT_TIMER_ID_MAX) return EVENT_ERANGE; 172 | 173 | timer->id = id; 174 | timer->cb = cb; 175 | timer->interval = interval; 176 | timer->fire_at = event_time_now() + interval; 177 | timer->data = data; 178 | loop->num_timers += 1; 179 | /* push to heap */ 180 | event_timer_heap_push(loop->timer_heap, timer); 181 | return EVENT_OK; 182 | } 183 | 184 | /* Delete timer from event loop. */ 185 | int event_del_timer(struct event_loop *loop, int id) { 186 | assert(loop != NULL && loop->timers != NULL && loop->timer_heap != NULL); 187 | 188 | if (id < 0 || id >= EVENT_TIMER_ID_MAX) return EVENT_ERANGE; 189 | 190 | struct event_timer *timer = &loop->timers[id]; 191 | 192 | if (timer->id < 0) return EVENT_ENOTFOUND; 193 | 194 | int error; 195 | if ((error = event_timer_heap_del(loop->timer_heap, id)) != EVENT_OK) 196 | return error; 197 | 198 | timer->id = -1; 199 | loop->num_timers -= 1; 200 | return EVENT_OK; 201 | } 202 | -------------------------------------------------------------------------------- /src/event.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Event loop wrapper. 5 | * deps: event_epoll.c event_kqueue.c. 6 | */ 7 | 8 | #ifndef __EVENT_H__ 9 | #define __EVENT_H__ 10 | 11 | #if defined(__cplusplus) 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | 17 | #define EVENT_MIN_RESERVED_FDS 32 18 | #define EVENT_FDSET_INCR 96 19 | #define EVENT_TIMER_ID_MAX 1024 * 10 20 | 21 | #define EVENT_NONE 0b000 22 | #define EVENT_READABLE 0b001 23 | #define EVENT_WRITABLE 0b010 24 | #define EVENT_ERROR 0b100 25 | 26 | #define EVENT_LOOP_RUNNING 0 27 | #define EVENT_LOOP_STOPPED 1 28 | 29 | #ifdef __linux__ 30 | #define HAVE_EPOLL 1 31 | #endif 32 | 33 | #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ 34 | defined(__OpenBSD__) 35 | #define HAVE_KQUEUE 1 36 | #endif 37 | 38 | #ifdef __sun 39 | #include 40 | #ifdef _dtrace_version 41 | #define HAVE_EVPORT 1 42 | #endif 43 | #endif 44 | 45 | #define event_loop(size) event_loop_new(size) 46 | #define event_add_in(loop, fd, cb, data) \ 47 | event_add(loop, fd, EVENT_READABLE, cb, data); 48 | #define event_add_out(loop, fd, cb, data) \ 49 | event_add(loop, fd, EVENT_WRITABLE, cb, data); 50 | #define event_del_in(loop, fd) event_del(loop, fd, EVENT_READABLE); 51 | #define event_del_out(loop, fd) event_del(loop, fd, EVENT_WRITABLE); 52 | 53 | enum { 54 | EVENT_OK = 0, /* operation is ok */ 55 | EVENT_ENOMEM = 1, /* no memory error */ 56 | EVENT_EFAILED = 2, /* operation is failed */ 57 | EVENT_ERANGE = 3, /* range is invalid */ 58 | EVENT_ENOTFOUND = 4, /* not found error */ 59 | }; 60 | 61 | struct event_loop; 62 | struct event_api; 63 | 64 | typedef void (*event_cb_t)(struct event_loop *loop, int fd, int mask, 65 | void *data); 66 | typedef void (*event_timer_cb_t)(struct event_loop *loop, int id, void *data); 67 | 68 | struct event { 69 | int mask; /* EVENT_(NONE|READABLE|WRITABLE..) */ 70 | event_cb_t rcb; /* callback function on EVENT_READABLE */ 71 | event_cb_t wcb; /* callback function on EVENT_WRITABLE */ 72 | event_cb_t ecb; /* callback function on EVENT_ERROR */ 73 | void *data; /* user defined data */ 74 | }; 75 | 76 | struct event_timer { 77 | int id; /* timer identifier [0, EVENT_TIMER_ID_MAX) */ 78 | event_timer_cb_t cb; /* callback function on timer fired */ 79 | long interval; /* periodicity interval to fire (ms) */ 80 | long fire_at; /* the time for the next fire (ms) */ 81 | void *data; /* user defined data */ 82 | }; 83 | 84 | struct event_timer_heap { 85 | struct event_timer *timers[EVENT_TIMER_ID_MAX]; 86 | size_t len; 87 | }; 88 | 89 | struct event_loop { 90 | int size; /* the max number of fds to track */ 91 | int state; /* one of EVENT_LOOP_(STOPPED|RUNNING) */ 92 | int num_timers; /* the number of timers */ 93 | struct event *events; /* struct event[] */ 94 | struct event_api *api; /* to be implemented */ 95 | struct event_timer 96 | timers[EVENT_TIMER_ID_MAX]; /* struct event_timers[MAX] */ 97 | struct event_timer_heap *timer_heap; 98 | }; 99 | 100 | struct event_loop *event_loop_new(int size); 101 | void event_loop_free(struct event_loop *loop); 102 | int event_loop_start(struct event_loop *loop); 103 | void event_loop_stop(struct event_loop *loop); 104 | int event_add(struct event_loop *loop, int fd, int mask, event_cb_t cb, 105 | void *data); /* O(1) */ 106 | int event_del(struct event_loop *loop, int fd, int mask); 107 | int event_wait(struct event_loop *loop); 108 | int event_add_timer(struct event_loop *loop, long interval, event_timer_cb_t cb, 109 | void *data); /* O(EVENT_TIMER_ID_MAX) */ 110 | int event_del_timer(struct event_loop *loop, int id); /* O(1) */ 111 | 112 | #if defined(__cplusplus) 113 | } 114 | #endif 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /src/event_epoll.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "event.h" 11 | 12 | #define EVENT_EPOLL_ALWAYS_ET 1 13 | 14 | struct event_api { 15 | int ep; /* epoll descriptor */ 16 | struct epoll_event * 17 | events; /* struct epoll_events[], with size `loop->size` */ 18 | }; 19 | 20 | static int event_api_loop_new(struct event_loop *loop) { 21 | assert(loop != NULL); 22 | assert(loop->size > 0); 23 | assert(loop->api == NULL); 24 | 25 | struct event_api *api = malloc(sizeof(struct event_api)); 26 | 27 | if (api == NULL) return EVENT_ENOMEM; 28 | 29 | api->ep = epoll_create(loop->size); 30 | 31 | if (api->ep < 0) { 32 | free(api); 33 | return EVENT_EFAILED; 34 | } 35 | 36 | api->events = malloc(sizeof(struct epoll_event) * loop->size); 37 | 38 | if (api->events == NULL) { 39 | close(api->ep); 40 | free(api); 41 | return EVENT_ENOMEM; 42 | } 43 | 44 | loop->api = api; 45 | return EVENT_OK; 46 | } 47 | 48 | void event_api_loop_free(struct event_loop *loop) { 49 | assert(loop != NULL); 50 | 51 | if (loop->api != NULL) { 52 | if (loop->api->ep > 0) close(loop->api->ep); 53 | if (loop->api->events != NULL) free(loop->api->events); 54 | free(loop->api); 55 | } 56 | } 57 | 58 | int event_api_add(struct event_loop *loop, int fd, int mask) { 59 | assert(loop != NULL); 60 | assert(loop->events != NULL); 61 | assert(loop->api != NULL); 62 | 63 | struct epoll_event ev; 64 | 65 | int op = 66 | loop->events[fd].mask == EVENT_NONE ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; 67 | 68 | ev.events = 0; 69 | ev.data.fd = fd; 70 | 71 | mask |= loop->events[fd].mask; /* merge old events */ 72 | 73 | if (EVENT_EPOLL_ALWAYS_ET) ev.events |= EPOLLET; 74 | if (mask & EVENT_READABLE) ev.events |= EPOLLIN; 75 | if (mask & EVENT_WRITABLE) ev.events |= EPOLLOUT; 76 | if (mask & EVENT_ERROR) ev.events |= EPOLLERR; 77 | 78 | if (epoll_ctl(loop->api->ep, op, fd, &ev) < 0) return EVENT_EFAILED; 79 | 80 | return EVENT_OK; 81 | } 82 | 83 | int event_api_del(struct event_loop *loop, int fd, int delmask) { 84 | assert(loop != NULL); 85 | assert(loop->events != NULL); 86 | assert(loop->api != NULL); 87 | 88 | struct epoll_event ev; 89 | int mask = loop->events[fd].mask & (~delmask); 90 | 91 | ev.events = 0; 92 | 93 | if (EVENT_EPOLL_ALWAYS_ET) ev.events |= EPOLLET; 94 | if (mask & EVENT_READABLE) ev.events |= EPOLLIN; 95 | if (mask & EVENT_WRITABLE) ev.events |= EPOLLOUT; 96 | if (mask & EVENT_ERROR) ev.events |= EPOLLERR; 97 | 98 | ev.data.fd = fd; 99 | 100 | if (mask != EVENT_NONE) { 101 | epoll_ctl(loop->api->ep, EPOLL_CTL_MOD, fd, &ev); 102 | } else { 103 | /* Note: kernel < 2.6.9 requires a non null event pointer even for 104 | * EPOLL_CTL_DEL. */ 105 | epoll_ctl(loop->api->ep, EPOLL_CTL_DEL, fd, &ev); 106 | } 107 | return EVENT_OK; 108 | } 109 | 110 | int event_api_wait(struct event_loop *loop, int timeout) { 111 | assert(loop != NULL); 112 | assert(loop->events != NULL); 113 | 114 | struct event_api *api = loop->api; 115 | 116 | assert(api != NULL); 117 | assert(api->ep >= 0); 118 | assert(api->events != NULL); 119 | 120 | int i; 121 | int nfds = epoll_wait(api->ep, api->events, loop->size, timeout); 122 | 123 | if (nfds > 0) { 124 | for (i = 0; i < nfds; i++) { 125 | struct epoll_event ee = api->events[i]; 126 | int fd = ee.data.fd; 127 | struct event ev = loop->events[fd]; 128 | 129 | int mask = 0; 130 | 131 | if (ee.events & EPOLLERR) mask |= EVENT_ERROR; 132 | if (ee.events & EPOLLIN) mask |= EVENT_READABLE; 133 | if (ee.events & EPOLLOUT) mask |= EVENT_WRITABLE; 134 | 135 | if (mask & EVENT_ERROR && ev.ecb != NULL) 136 | (ev.ecb)(loop, fd, mask, ev.data); 137 | if (mask & EVENT_READABLE && ev.rcb != NULL) 138 | (ev.rcb)(loop, fd, mask, ev.data); 139 | if (mask & EVENT_WRITABLE && ev.wcb != NULL) 140 | (ev.wcb)(loop, fd, mask, ev.data); 141 | } 142 | 143 | return EVENT_OK; 144 | } 145 | 146 | if (nfds == 0) { 147 | if (timeout >= 0) return EVENT_OK; 148 | } 149 | 150 | return EVENT_EFAILED; 151 | } 152 | -------------------------------------------------------------------------------- /src/event_kqueue.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "event.h" 13 | 14 | #define EVENT_KQUEUE_ALWAYS_CLEAR 1 15 | 16 | struct event_api { 17 | int kp; /* kqueue descriptor */ 18 | struct kevent *events; /* struct kevent[], with size `loop->size` */ 19 | }; 20 | 21 | static int event_api_loop_new(struct event_loop *loop) { 22 | assert(loop != NULL); 23 | assert(loop->size > 0); 24 | assert(loop->api == NULL); 25 | 26 | struct event_api *api = malloc(sizeof(struct event_api)); 27 | 28 | if (api == NULL) return EVENT_ENOMEM; 29 | 30 | api->kp = kqueue(); 31 | 32 | if (api->kp < 0) { 33 | free(api); 34 | return EVENT_EFAILED; 35 | } 36 | 37 | api->events = malloc(sizeof(struct kevent) * loop->size); 38 | 39 | if (api->events == NULL) { 40 | close(api->kp); 41 | free(api); 42 | return EVENT_ENOMEM; 43 | } 44 | 45 | loop->api = api; 46 | return EVENT_OK; 47 | } 48 | 49 | void event_api_loop_free(struct event_loop *loop) { 50 | assert(loop != NULL); 51 | 52 | if (loop->api != NULL) { 53 | if (loop->api->kp > 0) close(loop->api->kp); 54 | if (loop->api->events != NULL) free(loop->api->events); 55 | free(loop->api); 56 | } 57 | } 58 | 59 | int event_api_add(struct event_loop *loop, int fd, int mask) { 60 | assert(loop != NULL); 61 | assert(loop->api != NULL); 62 | 63 | struct kevent ev; 64 | struct event_api *api = loop->api; 65 | 66 | int op = EV_ADD; 67 | 68 | if (EVENT_KQUEUE_ALWAYS_CLEAR) op |= EV_CLEAR; 69 | 70 | if (mask & EVENT_READABLE) { 71 | EV_SET(&ev, fd, EVFILT_READ, op, 0, 0, NULL); 72 | if (kevent(api->kp, &ev, 1, NULL, 0, NULL) < 0) return EVENT_EFAILED; 73 | } 74 | 75 | if (mask & EVENT_WRITABLE) { 76 | EV_SET(&ev, fd, EVFILT_WRITE, op, 0, 0, NULL); 77 | if (kevent(api->kp, &ev, 1, NULL, 0, NULL) < 0) return EVENT_EFAILED; 78 | } 79 | return EVENT_OK; 80 | } 81 | 82 | int event_api_del(struct event_loop *loop, int fd, int mask) { 83 | assert(loop != NULL); 84 | assert(loop->api != NULL); 85 | 86 | struct kevent ev; 87 | struct event_api *api = loop->api; 88 | 89 | int op = EV_DELETE; 90 | 91 | if (EVENT_KQUEUE_ALWAYS_CLEAR) op |= EV_CLEAR; 92 | 93 | if (mask & EVENT_READABLE) { 94 | EV_SET(&ev, fd, EVFILT_READ, op, 0, 0, NULL); 95 | if (kevent(api->kp, &ev, 1, NULL, 0, NULL) < 0) return EVENT_EFAILED; 96 | } 97 | 98 | if (mask & EVENT_WRITABLE) { 99 | EV_SET(&ev, fd, EVFILT_WRITE, op, 0, 0, NULL); 100 | if (kevent(api->kp, &ev, 1, NULL, 0, NULL) < 0) return EVENT_EFAILED; 101 | } 102 | 103 | return EVENT_OK; 104 | } 105 | 106 | int event_api_wait(struct event_loop *loop, int timeout) { 107 | assert(loop != NULL); 108 | 109 | struct event_api *api = loop->api; 110 | 111 | assert(api != NULL); 112 | assert(api->kp >= 0); 113 | 114 | int nfds; 115 | 116 | if (timeout >= 0) { 117 | struct timespec tv; 118 | tv.tv_sec = timeout / 1000; 119 | tv.tv_nsec = (timeout - tv.tv_sec * 1000) * 1000000; 120 | nfds = kevent(api->kp, NULL, 0, api->events, loop->size, &tv); 121 | } else { 122 | nfds = kevent(api->kp, NULL, 0, api->events, loop->size, NULL); 123 | } 124 | 125 | int i; 126 | 127 | if (nfds > 0) { 128 | for (i = 0; i < nfds; i++) { 129 | struct kevent ke = api->events[i]; 130 | int fd = ke.ident; 131 | struct event ev = loop->events[fd]; 132 | 133 | int mask = 0; 134 | 135 | if (ke.flags & EV_ERROR) mask |= EVENT_ERROR; 136 | if (ke.filter == EVFILT_READ) mask |= EVENT_READABLE; 137 | if (ke.filter == EVFILT_WRITE) mask |= EVENT_WRITABLE; 138 | 139 | if (mask & EVENT_ERROR && ev.ecb != NULL) 140 | (ev.ecb)(loop, fd, mask, ev.data); 141 | if (mask & EVENT_READABLE && ev.rcb != NULL) 142 | (ev.rcb)(loop, fd, mask, ev.data); 143 | if (mask & EVENT_WRITABLE && ev.wcb != NULL) 144 | (ev.wcb)(loop, fd, mask, ev.data); 145 | } 146 | 147 | return EVENT_OK; 148 | } 149 | 150 | if (nfds == 0) { 151 | if (timeout >= 0) return EVENT_OK; 152 | } 153 | 154 | return EVENT_EFAILED; 155 | } 156 | -------------------------------------------------------------------------------- /src/event_timer.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "event.h" 11 | 12 | /* Get time now in milliseconds. */ 13 | static long event_time_now(void) { 14 | struct timeval tv; 15 | gettimeofday(&tv, NULL); 16 | return (1000000 * tv.tv_sec + tv.tv_usec) / 1000; 17 | } 18 | 19 | /** 20 | * Event timer heap 21 | */ 22 | 23 | /* Create a timer heap. */ 24 | struct event_timer_heap *event_timer_heap_new(void) { 25 | struct event_timer_heap *heap = malloc(sizeof(struct event_timer_heap)); 26 | if (heap != NULL) heap->len = 0; 27 | return heap; 28 | } 29 | 30 | /* Free a timer heap. */ 31 | void event_timer_heap_free(struct event_timer_heap *heap) { 32 | if (heap != NULL) free(heap); 33 | } 34 | 35 | /* Sift down the timer heap. */ 36 | void event_timer_heap_siftdown(struct event_timer_heap *heap, size_t start_idx, 37 | size_t idx) { 38 | assert(heap != NULL && heap->timers != NULL); 39 | 40 | size_t parent_idx; 41 | struct event_timer *parent; 42 | struct event_timer *timer = heap->timers[idx]; 43 | 44 | while (idx > start_idx) { 45 | parent_idx = (idx - 1) >> 1; 46 | parent = heap->timers[parent_idx]; 47 | if (timer->fire_at < parent->fire_at) { 48 | heap->timers[idx] = parent; 49 | idx = parent_idx; 50 | continue; 51 | } 52 | break; 53 | } 54 | heap->timers[idx] = timer; 55 | } 56 | 57 | /* Sift up the timer heap. */ 58 | void event_timer_heap_siftup(struct event_timer_heap *heap, size_t idx) { 59 | assert(heap != NULL && heap->timers != NULL); 60 | 61 | size_t len = heap->len; 62 | size_t start_idx = idx; 63 | size_t child_idx = idx * 2 + 1; 64 | size_t right_idx; 65 | struct event_timer *timer = heap->timers[idx]; 66 | 67 | while (child_idx < len) { 68 | right_idx = child_idx + 1; 69 | if (right_idx < len && 70 | heap->timers[child_idx]->fire_at >= 71 | heap->timers[right_idx]->fire_at) 72 | child_idx = right_idx; 73 | heap->timers[idx] = heap->timers[child_idx]; 74 | idx = child_idx; 75 | child_idx = idx * 2 + 1; 76 | } 77 | 78 | heap->timers[idx] = timer; 79 | event_timer_heap_siftdown(heap, start_idx, idx); 80 | } 81 | 82 | /* Push a timer into timer heap. */ 83 | int event_timer_heap_push(struct event_timer_heap *heap, 84 | struct event_timer *timer) { 85 | assert(heap != NULL && heap->timers != NULL); 86 | 87 | if (heap->len >= EVENT_TIMER_ID_MAX) return EVENT_ERANGE; 88 | 89 | heap->timers[heap->len++] = timer; 90 | event_timer_heap_siftdown(heap, 0, heap->len - 1); 91 | return EVENT_OK; 92 | } 93 | 94 | /* Pop a timer from heap, NULL on empty. */ 95 | struct event_timer *event_timer_heap_pop(struct event_timer_heap *heap) { 96 | assert(heap != NULL && heap->timers != NULL); 97 | 98 | if (heap->len == 0) return NULL; 99 | 100 | struct event_timer *tail = heap->timers[--heap->len]; 101 | if (heap->len == 0) return tail; 102 | struct event_timer *head = heap->timers[0]; 103 | heap->timers[0] = tail; 104 | event_timer_heap_siftup(heap, 0); 105 | return head; 106 | } 107 | 108 | /* Get the smallest timer from heap, NULL on empty. */ 109 | struct event_timer *event_timer_heap_top(struct event_timer_heap *heap) { 110 | assert(heap != NULL && heap->timers != NULL); 111 | 112 | if (heap->len == 0) return NULL; 113 | return heap->timers[0]; 114 | } 115 | 116 | /* Delete timer by id from heap. */ 117 | int event_timer_heap_del(struct event_timer_heap *heap, int id) { 118 | assert(heap != NULL && heap->timers != NULL); 119 | 120 | if (id < 0 || id >= EVENT_TIMER_ID_MAX) return EVENT_ERANGE; 121 | 122 | if (heap->len == 0) return EVENT_ENOTFOUND; 123 | 124 | int i; 125 | for (i = 0; i < heap->len; i++) { 126 | if (heap->timers[i]->id == id) { 127 | heap->len -= 1; 128 | if (heap->len > 0) { 129 | heap->timers[i] = heap->timers[heap->len]; 130 | event_timer_heap_siftup(heap, i); 131 | } 132 | return EVENT_OK; 133 | } 134 | } 135 | return EVENT_ENOTFOUND; 136 | } 137 | 138 | /* Replace the top item with another. */ 139 | int event_timer_heap_replace(struct event_timer_heap *heap, 140 | struct event_timer *timer) { 141 | assert(heap != NULL && heap->timers != NULL); 142 | 143 | if (heap->len == 0) return EVENT_ERANGE; 144 | heap->timers[0] = timer; 145 | event_timer_heap_siftup(heap, 0); 146 | return EVENT_OK; 147 | } 148 | 149 | /** 150 | * Event loop. 151 | */ 152 | 153 | /** 154 | * Get the nearest timer to fire from timer heap. O(1) 155 | */ 156 | struct event_timer *event_nearest_timer(struct event_loop *loop) { 157 | assert(loop != NULL && loop->timers != NULL && loop->timer_heap != NULL); 158 | return event_timer_heap_top(loop->timer_heap); 159 | } 160 | 161 | /** 162 | * Fire timed out timers from heap and update their fire_at. O(log(N)). 163 | */ 164 | void event_process_timers(struct event_loop *loop) { 165 | assert(loop != NULL && loop->timers != NULL && loop->timer_heap); 166 | 167 | struct event_timer *timer; 168 | 169 | while (1) { 170 | timer = event_timer_heap_top(loop->timer_heap); 171 | if (timer == NULL) /* no waiting timers to be processed */ 172 | break; 173 | if (timer->fire_at <= event_time_now()) { 174 | /* fire this timeouted timer */ 175 | if (timer->cb != NULL) (timer->cb)(loop, timer->id, timer->data); 176 | if (timer->id < 0) { 177 | continue; // won't push back if the timer is invalid now. 178 | } 179 | /* push back the timer */ 180 | timer->fire_at += timer->interval; 181 | event_timer_heap_replace(loop->timer_heap, timer); 182 | continue; 183 | } 184 | /* the other timers are not ready now */ 185 | break; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/heap.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "heap.h" 9 | 10 | /* Create an empty heap. */ 11 | struct heap *heap_new(heap_cmp_t cmp) { 12 | struct heap *heap = malloc(sizeof(struct heap)); 13 | 14 | if (heap != NULL) { 15 | heap->cap = 0; 16 | heap->len = 0; 17 | heap->cmp = cmp; 18 | heap->data = NULL; 19 | } 20 | return heap; 21 | } 22 | 23 | /* Free heap. */ 24 | void heap_free(struct heap *heap) { 25 | if (heap != NULL) { 26 | if (heap->data != NULL) free(heap->data); 27 | free(heap); 28 | } 29 | } 30 | 31 | /* Clear a heap. */ 32 | void heap_clear(struct heap *heap) { 33 | assert(heap != NULL); 34 | heap->len = 0; 35 | } 36 | 37 | /* Get heap length. */ 38 | size_t heap_len(struct heap *heap) { 39 | assert(heap != NULL); 40 | return heap->len; 41 | } 42 | 43 | /* Get heap cap. */ 44 | size_t heap_cap(struct heap *heap) { 45 | assert(heap != NULL); 46 | return heap->cap; 47 | } 48 | 49 | /* Grow a heap's memory capacity to given cap. */ 50 | int heap_grow(struct heap *heap, size_t cap) { 51 | assert(heap != NULL); 52 | 53 | if (cap > HEAP_CAP_MAX) return HEAP_ENOMEM; 54 | 55 | if (cap <= heap->cap) return HEAP_OK; 56 | 57 | size_t unit = heap->cap; 58 | 59 | if (unit > HEAP_UNIT_MAX) unit = HEAP_UNIT_MAX; 60 | 61 | if (unit < HEAP_UNIT_MIN) unit = HEAP_UNIT_MIN; 62 | 63 | size_t new_cap = heap->cap + unit; 64 | while (new_cap < cap) new_cap += unit; 65 | 66 | void **data = realloc(heap->data, new_cap * sizeof(void *)); 67 | if (data == NULL) return HEAP_ENOMEM; 68 | 69 | heap->data = data; 70 | heap->cap = new_cap; 71 | 72 | if (heap->len > new_cap) heap->len = new_cap; 73 | return HEAP_OK; 74 | } 75 | 76 | /* Push data to heap. */ 77 | int heap_push(struct heap *heap, void *data) { 78 | assert(heap != NULL); 79 | 80 | if (heap->len <= heap->cap && heap_grow(heap, heap->len + 1) != HEAP_OK) 81 | return HEAP_ENOMEM; 82 | 83 | assert(heap->data != NULL); 84 | heap->data[heap->len++] = data; 85 | heap_siftdown(heap, 0, heap->len - 1); 86 | return HEAP_OK; 87 | } 88 | 89 | /* Pop data from heap, NULL on empty. */ 90 | void *heap_pop(struct heap *heap) { 91 | assert(heap != NULL); 92 | 93 | if (heap->len == 0) return NULL; 94 | 95 | assert(heap->data != NULL && heap->len >= 1); 96 | 97 | void *tail = heap->data[--heap->len]; 98 | 99 | if (heap->len == 0) return tail; 100 | void *head = heap->data[0]; 101 | heap->data[0] = tail; 102 | heap_siftup(heap, 0); 103 | return head; 104 | } 105 | 106 | /* Push data to heap and pop the top, this runs more efficiently 107 | * than `heap_push` followed by a separate call to `heap_pop`. */ 108 | void *heap_pushpop(struct heap *heap, void *data) { 109 | assert(heap != NULL); 110 | 111 | if (heap->len == 0) return data; 112 | 113 | assert(heap->data != NULL); 114 | 115 | void *head = heap->data[0]; 116 | 117 | if ((heap->cmp)(head, data) < 0) { 118 | heap->data[0] = data; 119 | data = head; 120 | heap_siftup(heap, 0); 121 | } 122 | return data; 123 | } 124 | 125 | /* Get the smallest data from heap, NULL on empty. */ 126 | void *heap_top(struct heap *heap) { 127 | assert(heap != NULL); 128 | 129 | if (heap->len == 0) return NULL; 130 | 131 | assert(heap->data != NULL && heap->len >= 1); 132 | return heap->data[0]; 133 | } 134 | 135 | /* Delete node by index and return the data., NULL on not found. */ 136 | void *heap_del(struct heap *heap, size_t idx) { 137 | assert(heap != NULL); 138 | 139 | if (heap->len == 0) return NULL; 140 | 141 | if (idx >= heap->len) return NULL; 142 | 143 | assert(heap->data != NULL && heap->len >= 1); 144 | 145 | void *tail = heap->data[--heap->len]; 146 | 147 | if (heap->len == 0) return tail; 148 | 149 | void *data = heap->data[idx]; 150 | heap->data[idx] = tail; 151 | heap_siftup(heap, idx); 152 | return data; 153 | } 154 | 155 | /* Replace the top item and returns the original data. */ 156 | void *heap_replace(struct heap *heap, void *data) { 157 | assert(heap != NULL && heap->data != NULL); 158 | if (heap->len == 0) return NULL; 159 | void *orig = heap->data[0]; 160 | heap->data[0] = data; 161 | heap_siftup(heap, 0); 162 | return orig; 163 | } 164 | 165 | /* Sift down the heap. */ 166 | void heap_siftdown(struct heap *heap, size_t start_idx, size_t idx) { 167 | assert(heap != NULL && heap->data != NULL); 168 | 169 | size_t parent_idx; 170 | void *parent_data; 171 | void *data = heap->data[idx]; 172 | 173 | while (idx > start_idx) { 174 | parent_idx = (idx - 1) >> 1; 175 | parent_data = heap->data[parent_idx]; 176 | 177 | if ((heap->cmp)(data, parent_data) < 0) { 178 | heap->data[idx] = parent_data; 179 | idx = parent_idx; 180 | continue; 181 | } 182 | break; 183 | } 184 | 185 | heap->data[idx] = data; 186 | } 187 | 188 | /* Sift up the heap. */ 189 | void heap_siftup(struct heap *heap, size_t idx) { 190 | assert(heap != NULL && heap->data != NULL); 191 | 192 | void *data = heap->data[idx]; 193 | 194 | size_t len = heap->len; 195 | size_t start_idx = idx; 196 | size_t child_idx = idx * 2 + 1; 197 | size_t right_idx; 198 | 199 | while (child_idx < len) { 200 | right_idx = child_idx + 1; 201 | 202 | if (right_idx < len && 203 | (heap->cmp)(heap->data[child_idx], heap->data[right_idx]) >= 0) 204 | child_idx = right_idx; 205 | heap->data[idx] = heap->data[child_idx]; 206 | idx = child_idx; 207 | child_idx = idx * 2 + 1; 208 | } 209 | 210 | heap->data[idx] = data; 211 | heap_siftdown(heap, start_idx, idx); 212 | } 213 | -------------------------------------------------------------------------------- /src/heap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Array based binary heap (or priority queue) implementation. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __HEAP_H__ 9 | #define __HEAP_H__ 10 | 11 | #include 12 | 13 | #if defined(__cplusplus) 14 | extern "C" { 15 | #endif 16 | 17 | #define HEAP_CAP_MAX 16 * 1024 * 1024 /* max memory capacity: 16mb */ 18 | #define HEAP_UNIT_MIN 1 /* min heap realloc unit: 1 */ 19 | #define HEAP_UNIT_MAX 1024 /* max heap realloc unit: 1kb */ 20 | 21 | #define heap(cmp) heap_new(cmp) 22 | 23 | /* heap node comparator type, return negative if arg#0 < arg#1 */ 24 | typedef int (*heap_cmp_t)(void *, void *); 25 | 26 | enum { 27 | HEAP_OK = 0, /* operation is ok */ 28 | HEAP_ENOMEM = 1, /* no memory error */ 29 | }; 30 | 31 | struct heap { 32 | void **data; /* heap array data */ 33 | size_t cap; /* heap capacity */ 34 | size_t len; /* heap length */ 35 | heap_cmp_t cmp; /* node data comparator */ 36 | }; 37 | 38 | struct heap *heap_new(heap_cmp_t cmp); 39 | void heap_free(struct heap *heap); 40 | void heap_clear(struct heap *heap); /* O(1). */ 41 | size_t heap_len(struct heap *heap); /* O(1) */ 42 | size_t heap_cap(struct heap *heap); /* O(1) */ 43 | int heap_grow(struct heap *heap, size_t cap); 44 | void *heap_pop(struct heap *heap); /* O(logN) */ 45 | int heap_push(struct heap *heap, void *data); /* O(logN) */ 46 | void *heap_pushpop(struct heap *heap, void *data); /* O(logN) */ 47 | void *heap_top(struct heap *heap); /* O(1) */ 48 | void *heap_del(struct heap *heap, size_t idx); /* O(log(N)) */ 49 | void *heap_replace(struct heap *heap, void *data); /* O(log(N)) */ 50 | void heap_siftdown(struct heap *heap, size_t start_idx, 51 | size_t idx); /* O(logN) */ 52 | void heap_siftup(struct heap *heap, size_t idx); /* O(logN) */ 53 | 54 | #if defined(__cplusplus) 55 | } 56 | #endif 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/ketama.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ketama.h" 11 | #include "md5.h" 12 | 13 | static uint32_t ketama_hash(char *key, size_t len, size_t align) { 14 | assert(align < 4); 15 | unsigned char results[16]; 16 | md5_signature((unsigned char *)key, (unsigned long)len, results); 17 | return ((uint32_t)(results[3 + align * 4] & 0xff) << 24) | 18 | ((uint32_t)(results[2 + align * 4] & 0xff) << 16) | 19 | ((uint32_t)(results[1 + align * 4] & 0xff) << 8) | 20 | (results[0 + align * 4] & 0xff); 21 | } 22 | 23 | static int ketama_node_cmp(const void *node_a, const void *node_b) { 24 | uint32_t hash_a = ((struct ketama_node *)node_a)->hash; 25 | uint32_t hash_b = ((struct ketama_node *)node_b)->hash; 26 | 27 | if (hash_a > hash_b) 28 | return 1; 29 | else if (hash_a < hash_b) 30 | return -1; 31 | else 32 | return 0; 33 | } 34 | 35 | /* Create ketama hash ring from nodes array. */ 36 | struct ketama_ring *ketama_ring_new(struct ketama_node *nodes, size_t len) { 37 | if (len > 0) assert(nodes != NULL); 38 | 39 | struct ketama_ring *ring = malloc(sizeof(struct ketama_ring)); 40 | 41 | if (ring == NULL) return NULL; 42 | 43 | int i; 44 | 45 | for (i = 0, ring->len = 0; i < len; i++) ring->len += nodes[i].weight * 160; 46 | 47 | ring->nodes = malloc(sizeof(struct ketama_node) * ring->len); 48 | 49 | if (ring->nodes == NULL) { 50 | free(ring); 51 | return NULL; 52 | } 53 | 54 | int j, k, n, digits; 55 | struct ketama_node *node; 56 | unsigned int num; 57 | size_t key_len_max; 58 | 59 | for (i = 0, k = 0; i < len; i++) { 60 | node = &nodes[i]; 61 | 62 | for (digits = 0, num = node->weight; num > 0; num /= 10, ++digits) 63 | ; 64 | 65 | assert(node->key != NULL); 66 | assert(node->hash == 0); 67 | 68 | key_len_max = strlen(node->key) + digits + 1; 69 | char key[key_len_max]; 70 | 71 | for (j = 0; j < node->weight * 40; j++) { 72 | memset(key, 0, key_len_max); 73 | sprintf(key, "%s-%d", node->key, j); 74 | for (n = 0; n < 4; n++, k++) { 75 | ring->nodes[k].key = node->key; 76 | ring->nodes[k].weight = node->weight; 77 | ring->nodes[k].data = node->data; 78 | ring->nodes[k].idata = node->idata; 79 | ring->nodes[k].idx = i; 80 | ring->nodes[k].hash = ketama_hash(key, strlen(key), n); 81 | } 82 | } 83 | } 84 | 85 | qsort(ring->nodes, ring->len, sizeof(struct ketama_node), ketama_node_cmp); 86 | return ring; 87 | } 88 | 89 | /* Free ketama ring. */ 90 | void ketama_ring_free(struct ketama_ring *ring) { 91 | if (ring != NULL) { 92 | if (ring->nodes != NULL) free(ring->nodes); 93 | free(ring); 94 | } 95 | } 96 | 97 | /* Get node by key from ring. */ 98 | struct ketama_node *ketama_node_iget(struct ketama_ring *ring, char *key, 99 | size_t key_len) { 100 | assert(ring != NULL); 101 | assert(key != NULL); 102 | assert(ring->nodes != NULL); 103 | 104 | struct ketama_node *nodes = ring->nodes; 105 | size_t len = ring->len; 106 | 107 | if (len == 0) return NULL; 108 | 109 | if (len == 1) return &nodes[0]; 110 | 111 | int left = 0, right = len, mid; 112 | uint32_t hash = ketama_hash(key, key_len, 0); 113 | uint32_t mval, pval; 114 | 115 | while (1) { 116 | mid = (left + right) / 2; 117 | 118 | if (mid == len) return &nodes[0]; 119 | 120 | mval = nodes[mid].hash; 121 | pval = mid == 0 ? 0 : nodes[mid - 1].hash; 122 | 123 | if (hash <= mval && hash > pval) return &nodes[mid]; 124 | 125 | if (mval < hash) { 126 | left = mid + 1; 127 | } else { 128 | right = mid - 1; 129 | } 130 | 131 | if (left > right) return &nodes[0]; 132 | } 133 | } 134 | 135 | struct ketama_node *ketama_node_get(struct ketama_ring *ring, char *key) { 136 | return ketama_node_iget(ring, key, strlen(key)); 137 | } 138 | -------------------------------------------------------------------------------- /src/ketama.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Ketama consistent hashing implementation. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __KETAMA_H__ 9 | #define __KETAMA_H__ 10 | 11 | #include 12 | #include 13 | 14 | #if defined(__cplusplus) 15 | extern "C" { 16 | #endif 17 | 18 | #define ketama_ring(nodes, size) ketama_ring_new(nodes, size) 19 | 20 | /* Note ketama ring's `nodes` and its `length` is not the orignal parameter 21 | * passed in. */ 22 | struct ketama_ring { 23 | size_t len; /* hash ring nodes array length */ 24 | struct ketama_node *nodes; /* hash ring nodes array */ 25 | }; 26 | 27 | struct ketama_node { 28 | char *key; /* node key */ 29 | unsigned int weight; /* node weight */ 30 | void *data; /* user data */ 31 | long idata; /* user long typed data */ 32 | size_t idx; /* node idx in origin array */ 33 | uint32_t hash; /* hash value in the ring */ 34 | }; 35 | 36 | struct ketama_ring *ketama_ring_new(struct ketama_node *nodes, size_t len); 37 | void ketama_ring_free(struct ketama_ring *ring); 38 | struct ketama_node *ketama_node_iget(struct ketama_ring *ring, char *key, 39 | size_t key_len); /* O(logN) */ 40 | struct ketama_node *ketama_node_get(struct ketama_ring *ring, 41 | char *key); /* O(logN) */ 42 | 43 | #if defined(__cplusplus) 44 | } 45 | #endif 46 | #endif 47 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "list.h" 9 | 10 | /* Create list node with data. */ 11 | struct list_node *list_node_new(void *data) { 12 | struct list_node *node = malloc(sizeof(struct list_node)); 13 | 14 | if (node != NULL) { 15 | node->data = data; 16 | node->prev = NULL; 17 | node->next = NULL; 18 | } 19 | return node; 20 | } 21 | 22 | /* Free list node. */ 23 | void list_node_free(struct list_node *node) { 24 | if (node != NULL) free(node); 25 | } 26 | 27 | /* Create an empty list. */ 28 | struct list *list_new(void) { 29 | struct list *list = malloc(sizeof(struct list)); 30 | 31 | if (list != NULL) { 32 | list->head = NULL; 33 | list->tail = NULL; 34 | list->len = 0; 35 | } 36 | return list; 37 | } 38 | 39 | /* Free list. */ 40 | void list_free(struct list *list) { 41 | if (list != NULL) { 42 | list_clear(list); 43 | free(list); 44 | } 45 | } 46 | 47 | /* Clear list. */ 48 | void list_clear(struct list *list) { 49 | assert(list != NULL); 50 | 51 | while (list_len(list) != 0) list_lpop(list); 52 | } 53 | 54 | /* Get list length. */ 55 | size_t list_len(struct list *list) { 56 | assert(list != NULL); 57 | return list->len; 58 | } 59 | 60 | /* Push an item to list on the left. */ 61 | int list_lpush(struct list *list, void *data) { 62 | assert(list != NULL); 63 | 64 | struct list_node *node = list_node_new(data); 65 | 66 | if (node == NULL) return LIST_ENOMEM; 67 | 68 | if (list->len == 0) { 69 | assert(list->head == NULL && list->tail == NULL); 70 | list->head = node; 71 | list->tail = node; 72 | } else { 73 | assert(list->head != NULL && list->tail != NULL); 74 | struct list_node *head = list->head; 75 | assert(head->prev == NULL && node->prev == NULL); 76 | head->prev = node; 77 | node->next = head; 78 | list->head = node; 79 | } 80 | 81 | list->len += 1; 82 | return LIST_OK; 83 | } 84 | 85 | /* Push an item to list on the right. */ 86 | int list_rpush(struct list *list, void *data) { 87 | assert(list != NULL); 88 | 89 | struct list_node *node = list_node_new(data); 90 | 91 | if (node == NULL) return LIST_ENOMEM; 92 | 93 | if (list->len == 0) { 94 | assert(list->head == NULL && list->tail == NULL); 95 | list->head = node; 96 | list->tail = node; 97 | } else { 98 | assert(list->head != NULL && list->tail != NULL); 99 | struct list_node *tail = list->tail; 100 | assert(tail->next == NULL && node->next == NULL); 101 | tail->next = node; 102 | node->prev = tail; 103 | list->tail = node; 104 | } 105 | 106 | list->len += 1; 107 | return LIST_OK; 108 | } 109 | 110 | /* Pop an item from list on the left. */ 111 | void *list_lpop(struct list *list) { 112 | assert(list != NULL); 113 | 114 | if (list->len == 0) { 115 | assert(list->head == NULL && list->tail == NULL); 116 | return NULL; 117 | } 118 | 119 | assert(list->head != NULL && list->tail != NULL); 120 | 121 | struct list_node *head = list->head; 122 | struct list_node *node = head->next; 123 | 124 | if (node == NULL) { 125 | assert(list->len == 1); 126 | list->tail = NULL; 127 | } else { 128 | assert(list->len >= 2); 129 | node->prev = NULL; 130 | } 131 | 132 | list->head = node; 133 | list->len -= 1; 134 | 135 | void *data = head->data; 136 | list_node_free(head); 137 | return data; 138 | } 139 | 140 | /* Pop an item from list on the right. */ 141 | void *list_rpop(struct list *list) { 142 | assert(list != NULL); 143 | 144 | if (list->len == 0) { 145 | assert(list->head == NULL && list->tail == NULL); 146 | return NULL; 147 | } 148 | 149 | assert(list->head != NULL && list->tail != NULL); 150 | 151 | struct list_node *tail = list->tail; 152 | struct list_node *node = tail->prev; 153 | 154 | if (node == NULL) { 155 | assert(list->len == 1); 156 | list->head = NULL; 157 | } else { 158 | assert(list->len >= 2); 159 | node->next = NULL; 160 | } 161 | 162 | list->tail = node; 163 | list->len -= 1; 164 | 165 | void *data = tail->data; 166 | list_node_free(tail); 167 | return data; 168 | } 169 | 170 | /* Get the head node data, NULL on empty list. */ 171 | void *list_head(struct list *list) { 172 | assert(list != NULL); 173 | 174 | if (list->len == 0) { 175 | assert(list->head == NULL && list->tail == NULL); 176 | return NULL; 177 | } 178 | 179 | assert(list->head != NULL && list->tail != NULL); 180 | return list->head->data; 181 | } 182 | 183 | /* Get the tail node data, NULL on empty list. */ 184 | void *list_tail(struct list *list) { 185 | assert(list != NULL); 186 | 187 | if (list->len == 0) { 188 | assert(list->head == NULL && list->tail == NULL); 189 | return NULL; 190 | } 191 | 192 | assert(list->head != NULL && list->tail != NULL); 193 | return list->tail->data; 194 | } 195 | 196 | /* Create list iter, example: 197 | * 198 | * struct list_iter *iter = list_iter_new(list) 199 | * void *data; 200 | * 201 | * while ((data = list_iter_next(iter)) != NULL) { 202 | * ... 203 | * } 204 | * list_iter_free(iter); 205 | * 206 | * Or use it like this: 207 | * 208 | * struct list_iter iter = {list, NULL}; 209 | * void *data; 210 | * 211 | * while ((data = list_iter_next(&iter)) != NULL) { 212 | * ... 213 | * } 214 | * 215 | * Or use macro `list_each`: 216 | * 217 | * struct list_node *node; 218 | * list_each(list, node) { 219 | * node.data.. 220 | * } 221 | */ 222 | struct list_iter *list_iter_new(struct list *list) { 223 | assert(list != NULL); 224 | 225 | struct list_iter *iter = malloc(sizeof(struct list_iter)); 226 | 227 | if (iter != NULL) { 228 | iter->list = list; 229 | iter->node = list->head; 230 | } 231 | return iter; 232 | } 233 | 234 | /* Free list iter. */ 235 | void list_iter_free(struct list_iter *iter) { 236 | if (iter != NULL) free(iter); 237 | } 238 | 239 | /* Get current node data and seek next, NULL on tail. */ 240 | void *list_iter_next(struct list_iter *iter) { 241 | assert(iter != NULL); 242 | 243 | struct list_node *node = iter->node; 244 | 245 | if (node == NULL) return NULL; 246 | 247 | iter->node = node->next; 248 | return node->data; 249 | } 250 | 251 | /* Get current node data and seek prev, NULL on head. */ 252 | void *list_iter_prev(struct list_iter *iter) { 253 | assert(iter != NULL); 254 | 255 | struct list_node *node = iter->node; 256 | 257 | if (node == NULL) return NULL; 258 | 259 | iter->node = node->prev; 260 | return node->data; 261 | } 262 | 263 | /* Seek iter to list's head. */ 264 | void list_iter_seek_head(struct list_iter *iter) { 265 | assert(iter != NULL && iter->list != NULL); 266 | iter->node = iter->list->head; 267 | } 268 | 269 | /* Seek iter to list's tail. */ 270 | void list_iter_seek_tail(struct list_iter *iter) { 271 | assert(iter != NULL && iter->list != NULL); 272 | iter->node = iter->list->tail; 273 | } 274 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Double-linked list implementation. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __LIST_H__ 9 | #define __LIST_H__ 10 | 11 | #include 12 | 13 | #if defined(__cplusplus) 14 | extern "C" { 15 | #endif 16 | 17 | #define list() list_new() 18 | #define list_iter(list) list_iter_new(list) 19 | #define list_push(list, data) list_rpush(list, data) 20 | #define list_pop(list) list_lpop(list) 21 | #define list_each(list, node) \ 22 | for ((node) = (list)->head; (node) != NULL; (node) = (node)->next) 23 | 24 | enum { 25 | LIST_OK = 0, /* operation is ok */ 26 | LIST_ENOMEM = 1, /* no memory error */ 27 | }; 28 | 29 | struct list_node { 30 | struct list_node *prev; /* prev node */ 31 | struct list_node *next; /* next node */ 32 | void *data; /* node data */ 33 | }; 34 | 35 | struct list { 36 | struct list_node *head; /* head node */ 37 | struct list_node *tail; /* last node */ 38 | size_t len; /* list length */ 39 | }; 40 | 41 | struct list_iter { 42 | struct list *list; /* list to iterate */ 43 | struct list_node *node; /* current list node */ 44 | }; 45 | 46 | struct list_node *list_node_new(void *data); 47 | void list_node_free(struct list_node *node); 48 | struct list *list_new(void); 49 | void list_free(struct list *list); 50 | void list_clear(struct list *list); 51 | size_t list_len(struct list *list); /* O(1) */ 52 | int list_lpush(struct list *list, void *data); /* O(1) */ 53 | int list_rpush(struct list *list, void *data); /* O(1) */ 54 | void *list_lpop(struct list *list); /* O(1) */ 55 | void *list_rpop(struct list *list); /* O(1) */ 56 | void *list_head(struct list *list); /* O(1) */ 57 | void *list_tail(struct list *list); /* O(1) */ 58 | struct list_iter *list_iter_new(struct list *list); 59 | void list_iter_free(struct list_iter *iter); 60 | void *list_iter_next(struct list_iter *iter); 61 | void *list_iter_prev(struct list_iter *iter); 62 | void list_iter_seek_head(struct list_iter *iter); 63 | void list_iter_seek_tail(struct list_iter *iter); 64 | 65 | #if defined(__cplusplus) 66 | } 67 | #endif 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "log.h" 21 | 22 | static struct logger logger; 23 | static long log_pid; 24 | 25 | static void on_sigsegv(int signal) { 26 | if (signal == SIGSEGV) { 27 | log_critical("Segmentfault received!"); 28 | log_trace(); 29 | exit(1); 30 | } 31 | } 32 | 33 | /* Open global logger, if `filename` is NULL, use stderr. 34 | * The `rotate_size` only works when logging to a file, 35 | * and `rotate_size==0` means no rotation. */ 36 | int log_open(char *name, char *filename, size_t rotate_size) { 37 | assert(name != NULL); 38 | 39 | struct logger *l = &logger; 40 | 41 | l->name = name; 42 | l->level = LOG_INFO; 43 | l->filename = NULL; 44 | 45 | if (filename == NULL) { 46 | l->fd = STDERR_FILENO; 47 | l->rotate_size = 0; 48 | l->fsize = 0; 49 | } else { 50 | assert(strlen(filename) <= LOG_FILENAME_LEN_MAX); 51 | l->filename = filename; 52 | l->rotate_size = rotate_size; 53 | l->fd = open(filename, LOG_FILE_PERM, LOG_FILE_MODE); 54 | 55 | if (l->fd < 0) { 56 | return LOG_EOPEN; 57 | } 58 | 59 | struct stat st; 60 | if (fstat(l->fd, &st) != 0) return LOG_ESTAT; 61 | l->fsize = st.st_size; 62 | } 63 | /* Initialize global log_pid */ 64 | #ifdef __linux__ 65 | /* using syacall to get tid, or `pid` on system view */ 66 | log_pid = (long)syscall(SYS_gettid); 67 | #else 68 | /* I can't find a way to get tid from system view, only print the pid */ 69 | log_pid = getpid(); 70 | #endif 71 | /* register traceback handler on segmentation fault. */ 72 | struct sigaction signal_action; 73 | sigemptyset(&signal_action.sa_mask); 74 | signal_action.sa_flags = 0; 75 | signal_action.sa_handler = &on_sigsegv; 76 | sigaction(SIGSEGV, &signal_action, NULL); 77 | 78 | if (LOG_THREAD_SAFE) pthread_mutex_init(&(l->lock), NULL); 79 | return LOG_OK; 80 | } 81 | 82 | /* Close global logger. */ 83 | void log_close(void) { 84 | struct logger *l = &logger; 85 | 86 | if (LOG_THREAD_SAFE) pthread_mutex_destroy(&(l->lock)); 87 | 88 | if (l->fd < 0 || l->fd == STDERR_FILENO) return; 89 | close(l->fd); 90 | } 91 | 92 | /* Reopen logging file. */ 93 | int log_reopen(void) { 94 | struct logger *l = &logger; 95 | 96 | if (l->fd < 0 || l->fd == STDERR_FILENO) return LOG_OK; 97 | 98 | close(l->fd); 99 | 100 | assert(l->filename != NULL); 101 | 102 | l->fd = open(l->filename, LOG_FILE_PERM, LOG_FILE_MODE); 103 | 104 | if (l->fd < 0) return LOG_EOPEN; 105 | return LOG_OK; 106 | } 107 | 108 | /* Set logger's level. */ 109 | void log_setlevel(int level) { 110 | struct logger *l = &logger; 111 | 112 | if (level > LOG_CRITICAL) { 113 | l->level = LOG_CRITICAL; 114 | } else if (level < LOG_DEBUG) { 115 | l->level = LOG_DEBUG; 116 | } else { 117 | l->level = level; 118 | } 119 | } 120 | 121 | /* Rotate log file. */ 122 | int log_rotate(void) { 123 | struct logger *l = &logger; 124 | 125 | assert(l->name != NULL); 126 | assert(l->fd > 0 && l->fd != STDERR_FILENO); 127 | assert(l->filename != NULL); 128 | 129 | char buf[LOG_FILENAME_LEN_MAX]; 130 | time_t sec; 131 | struct timeval tv; 132 | struct tm *tm; 133 | gettimeofday(&tv, NULL); 134 | sec = tv.tv_sec; 135 | tm = localtime(&sec); 136 | sprintf(buf, "%s.%04d%02d%02d-%02d%02d%03d", l->filename, 137 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, 138 | tm->tm_min, tm->tm_sec); 139 | 140 | if (rename(l->filename, buf)) return LOG_ERENAME; 141 | l->fsize = 0; 142 | return log_reopen(); 143 | } 144 | 145 | /* Format logging message to file/stderr. */ 146 | int log_log(int level, char *levelname, const char *fmt, ...) { 147 | struct logger *l = &logger; 148 | 149 | assert(levelname != NULL); 150 | assert(l->name != NULL); 151 | assert(l->fd == STDERR_FILENO || l->fd > 0); 152 | 153 | if (level < l->level) return LOG_OK; 154 | 155 | int len = 0, size = LOG_LINE_LEN_MAX; 156 | 157 | char buf[size + 1]; 158 | 159 | /* Format time and name, level, pid */ 160 | struct timeval tv; 161 | gettimeofday(&tv, NULL); 162 | len += strftime(buf + len, size - len, "%Y-%m-%d %H:%M:%S.", 163 | localtime(&tv.tv_sec)); 164 | len += snprintf(buf + len, size - len, "%03ld %-5s %s[%ld] ", 165 | (long)tv.tv_usec / 1000, levelname, l->name, log_pid); 166 | /* Format message with args */ 167 | va_list args; 168 | va_start(args, fmt); 169 | len += vsnprintf(buf + len, size - len, fmt, args); 170 | va_end(args); 171 | 172 | /* log line long */ 173 | if (len > LOG_LINE_LEN_MAX) { 174 | log_error("the log is too large"); 175 | return LOG_ELINESIZE; 176 | } 177 | 178 | buf[len++] = '\n'; 179 | return log_write(buf, len); 180 | } 181 | 182 | int log_trace(void) { 183 | void *stack[32]; 184 | size_t size = backtrace(stack, 32); 185 | char **symbols = backtrace_symbols(stack, size); 186 | 187 | if (symbols == NULL || size <= 0) return LOG_OK; 188 | 189 | size_t len_max = 1024 * size; 190 | char buf[len_max]; 191 | size_t len = 0, i; 192 | 193 | for (i = 0; i < size; i++) 194 | len += snprintf(buf + len, 1024, " [%zu] %s\n", i, symbols[i]); 195 | 196 | free(symbols); 197 | return log_write(buf, len); 198 | } 199 | 200 | int log_write(char *buf, size_t len) { 201 | assert(buf != NULL); 202 | if (len == 0) return LOG_OK; 203 | 204 | struct logger *l = &logger; 205 | 206 | if (LOG_THREAD_SAFE) pthread_mutex_lock(&(l->lock)); 207 | 208 | if (write(l->fd, buf, len) < 0) { 209 | return LOG_EWRITE; 210 | } else if (l->filename != NULL) { 211 | l->fsize += len; 212 | if (l->rotate_size != 0) { 213 | if (l->fsize > l->rotate_size) log_rotate(); 214 | } 215 | } 216 | 217 | if (LOG_THREAD_SAFE) pthread_mutex_unlock(&(l->lock)); 218 | return LOG_OK; 219 | } 220 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Logging implementation. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __LOG_H__ 9 | #define __LOG_H__ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #if defined(__cplusplus) 16 | extern "C" { 17 | #endif 18 | 19 | #define LOG_LINE_LEN_MAX 1024 * 1024 // 1mb 20 | #define LOG_FILENAME_LEN_MAX 1024 // 1kb 21 | #define LOG_FILE_MODE 0644 22 | #define LOG_FILE_PERM O_WRONLY | O_APPEND | O_CREAT 23 | #define LOG_THREAD_SAFE 1 24 | 25 | #define LOG_DEBUG_S "DEBUG" 26 | #define LOG_INFO_S "INFO" 27 | #define LOG_WARN_S "WARN" 28 | #define LOG_ERROR_S "ERROR" 29 | #define LOG_CRITICAL_S "CRIT" 30 | 31 | enum { 32 | LOG_DEBUG = 10, 33 | LOG_INFO = 20, 34 | LOG_WARN = 30, 35 | LOG_ERROR = 40, 36 | LOG_CRITICAL = 50, 37 | }; 38 | 39 | enum { 40 | LOG_OK = 0, /* operation is ok */ 41 | LOG_EOPEN = 1, /* failed to open file */ 42 | LOG_EWRITE = 2, /* failed to write to file */ 43 | LOG_ESTAT = 3, /* failed to stat file */ 44 | LOG_ERENAME = 4, /* failed to rename file */ 45 | LOG_ELINESIZE = 5, /* log line is too large */ 46 | }; 47 | 48 | struct logger { 49 | char *name; /* logger name */ 50 | char *filename; /* filename to log */ 51 | size_t rotate_size; /* rotate size, in bytes (0 for no rotation) */ 52 | int level; /* logging level */ 53 | int fd; /* fd to write */ 54 | size_t fsize; /* original file size + number of bytes written) */ 55 | pthread_mutex_t lock; /* lock on logging */ 56 | }; 57 | 58 | #define log_debug(...) log_log(LOG_DEBUG, LOG_DEBUG_S, __VA_ARGS__) 59 | #define log_info(...) log_log(LOG_INFO, LOG_INFO_S, __VA_ARGS__) 60 | #define log_warn(...) log_log(LOG_WARN, LOG_WARN_S, __VA_ARGS__) 61 | #define log_error(...) log_log(LOG_ERROR, LOG_ERROR_S, __VA_ARGS__) 62 | #define log_critical(...) log_log(LOG_CRITICAL, LOG_CRITICAL_S, __VA_ARGS__) 63 | 64 | int log_open(char *name, char *filename, size_t rotate_size); 65 | void log_close(void); 66 | int log_reopen(void); 67 | void log_setlevel(int level); 68 | int log_rotate(void); 69 | int log_log(int level, char *levelname, const char *fmt, ...); 70 | int log_trace(void); 71 | int log_write(char *buf, size_t len); 72 | 73 | #if defined(__cplusplus) 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/map.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "map.h" 11 | 12 | /* Hash function. */ 13 | static uint32_t map_hash(char *key, size_t len) { 14 | /* DJBX33A hash function from PHP */ 15 | register int hash = 5381; 16 | for (; len >= 8; len -= 8) { 17 | hash = ((hash << 5) + hash) + *key++; 18 | hash = ((hash << 5) + hash) + *key++; 19 | hash = ((hash << 5) + hash) + *key++; 20 | hash = ((hash << 5) + hash) + *key++; 21 | hash = ((hash << 5) + hash) + *key++; 22 | hash = ((hash << 5) + hash) + *key++; 23 | hash = ((hash << 5) + hash) + *key++; 24 | hash = ((hash << 5) + hash) + *key++; 25 | } 26 | switch (len) { 27 | case 7: 28 | hash = ((hash << 5) + hash) + *key++; 29 | case 6: 30 | hash = ((hash << 5) + hash) + *key++; 31 | case 5: 32 | hash = ((hash << 5) + hash) + *key++; 33 | case 4: 34 | hash = ((hash << 5) + hash) + *key++; 35 | case 3: 36 | hash = ((hash << 5) + hash) + *key++; 37 | case 2: 38 | hash = ((hash << 5) + hash) + *key++; 39 | case 1: 40 | hash = ((hash << 5) + hash) + *key++; 41 | break; 42 | case 0: 43 | break; 44 | } 45 | return hash; 46 | } 47 | 48 | /* If two key equals. */ 49 | int map_keycmp(char *key1, size_t len1, char *key2, size_t len2) { 50 | if (len1 == len2 && (memcmp(key1, key2, len1) == 0)) return 1; 51 | return 0; 52 | } 53 | 54 | /* Create a map. */ 55 | struct map *map_new(void) { 56 | struct map *m = malloc(sizeof(struct map)); 57 | 58 | if (m != NULL) { 59 | m->cap = 0; 60 | m->len = 0; 61 | m->table = NULL; 62 | } 63 | return m; 64 | } 65 | 66 | /* Free map. */ 67 | void map_free(struct map *m) { 68 | if (m != NULL) { 69 | if (m->table != NULL) free(m->table); 70 | free(m); 71 | } 72 | } 73 | 74 | /* Clear map. */ 75 | void map_clear(struct map *m) { 76 | assert(m != NULL); 77 | if (m->table != NULL) free(m->table); 78 | m->cap = 0; 79 | m->len = 0; 80 | m->table = NULL; 81 | } 82 | 83 | /* Resize and rehash map. */ 84 | int map_resize(struct map *m) { 85 | assert(m != NULL); 86 | 87 | /* double sized cap */ 88 | size_t cap = m->cap * 2; 89 | 90 | /* validate new cap */ 91 | if (cap < 1) cap = MAP_CAP_INIT; 92 | 93 | if (cap > MAP_CAP_MAX) return MAP_ENOMEM; 94 | 95 | /* create new table */ 96 | struct map_node *table = malloc(cap * sizeof(struct map_node)); 97 | 98 | if (table == NULL) return MAP_ENOMEM; 99 | 100 | /* init all keys to NULL */ 101 | int i; 102 | for (i = 0; i < cap; i++) table[i].key = NULL; 103 | 104 | /* rehash old into new table */ 105 | int mask = cap - 1; 106 | for (i = 0; i < m->cap; i++) { 107 | struct map_node *node = &m->table[i]; 108 | 109 | if (node->key == NULL) continue; 110 | 111 | int j = map_hash(node->key, node->len) & mask; 112 | for (;; j = (j + 1) & mask) { 113 | if (table[j].key != NULL) continue; 114 | table[j].key = node->key; 115 | table[j].len = node->len; 116 | table[j].val = node->val; 117 | break; 118 | } 119 | } 120 | free(m->table); 121 | m->table = table; 122 | m->cap = cap; 123 | return MAP_OK; 124 | } 125 | 126 | /* Get map length. */ 127 | size_t map_len(struct map *m) { 128 | assert(m != NULL); 129 | return m->len; 130 | } 131 | 132 | /* Get map capacity. */ 133 | size_t map_cap(struct map *m) { 134 | assert(m != NULL); 135 | return m->cap; 136 | } 137 | 138 | /* Set a key into map. */ 139 | int map_iset(struct map *m, char *key, size_t len, void *val) { 140 | assert(m != NULL); 141 | assert(key != NULL); 142 | 143 | /* if require resize */ 144 | if ((m->cap * MAP_LOAD_LIMIT < m->len || m->cap < MAP_CAP_INIT) && 145 | map_resize(m) != MAP_OK) 146 | return MAP_ENOMEM; 147 | 148 | /* try to find this key */ 149 | int mask = m->cap - 1; 150 | size_t i = map_hash(key, len) & mask; 151 | 152 | for (;; i = (i + 1) & mask) { 153 | struct map_node *node = &m->table[i]; 154 | if (node->key == NULL) { 155 | node->key = key; 156 | node->len = len; 157 | node->val = val; 158 | m->len++; 159 | return MAP_OK; 160 | } 161 | if (map_keycmp(node->key, node->len, key, len)) { 162 | node->val = val; 163 | return MAP_OK; 164 | } 165 | } 166 | } 167 | 168 | /* Set a NULL-terminated key into map. */ 169 | int map_set(struct map *m, char *key, void *val) { 170 | return map_iset(m, key, strlen(key), val); 171 | } 172 | 173 | /* Get map node by key. */ 174 | struct map_node *map_get_node(struct map *m, char *key, size_t len) { 175 | int mask = m->cap - 1; 176 | size_t i = map_hash(key, len) & mask; 177 | struct map_node *node = &m->table[i]; 178 | 179 | for (; node->key != NULL; i = (i + 1) & mask, node = &m->table[i]) 180 | if (map_keycmp(node->key, node->len, key, len)) return node; 181 | return NULL; 182 | } 183 | 184 | /* Get val by key from map, NULL on not found. */ 185 | void *map_iget(struct map *m, char *key, size_t len) { 186 | assert(m != NULL); 187 | assert(key != NULL); 188 | 189 | struct map_node *node = map_get_node(m, key, len); 190 | if (node != NULL) return node->val; 191 | return NULL; 192 | } 193 | 194 | /* Get val by NULL-terminated key from map, NULL on not found. */ 195 | void *map_get(struct map *m, char *key) { 196 | return map_iget(m, key, strlen(key)); 197 | } 198 | 199 | /* Test if a key is in map. */ 200 | int map_ihas(struct map *m, char *key, size_t len) { 201 | assert(m != NULL); 202 | assert(key != NULL); 203 | 204 | struct map_node *node = map_get_node(m, key, len); 205 | if (node != NULL) return 1; 206 | return 0; 207 | } 208 | 209 | /* Test if a key is in map by a NULL-terminated key. */ 210 | int map_has(struct map *m, char *key) { return map_ihas(m, key, strlen(key)); } 211 | /* Pop a key from map, NULL on not found. */ 212 | void *map_ipop(struct map *m, char *key, size_t len) { 213 | assert(m != NULL); 214 | assert(key != NULL); 215 | 216 | struct map_node *node = map_get_node(m, key, len); 217 | if (node != NULL) { 218 | node->key = NULL; 219 | m->len--; 220 | return node->val; 221 | } 222 | return NULL; 223 | } 224 | 225 | /* Pop a key from map by NULL-terminated key, NULL on not found. */ 226 | void *map_pop(struct map *m, char *key) { 227 | return map_ipop(m, key, strlen(key)); 228 | } 229 | 230 | /* Create map iter. */ 231 | struct map_iter *map_iter_new(struct map *m) { 232 | assert(m != NULL); 233 | struct map_iter *iter = malloc(sizeof(struct map_iter)); 234 | 235 | if (iter != NULL) { 236 | iter->m = m; 237 | iter->i = 0; 238 | } 239 | return iter; 240 | } 241 | 242 | /* Free map iter. */ 243 | void map_iter_free(struct map_iter *iter) { 244 | if (iter != NULL) free(iter); 245 | } 246 | 247 | /* Get next. */ 248 | struct map_node *map_iter_next(struct map_iter *iter) { 249 | assert(iter != NULL && iter->m != NULL); 250 | 251 | struct map *m = iter->m; 252 | 253 | if (m->table == NULL) return NULL; 254 | 255 | for (; iter->i < m->cap; iter->i++) { 256 | struct map_node *node = &m->table[iter->i]; 257 | if (node->key != NULL) { 258 | iter->i++; 259 | return node; 260 | } 261 | } 262 | return NULL; 263 | } 264 | 265 | /* Rewind map iter. */ 266 | void map_iter_rewind(struct map_iter *iter) { 267 | assert(iter != NULL); 268 | iter->i = 0; 269 | } 270 | -------------------------------------------------------------------------------- /src/map.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Dynamic sized simple open-addressing hashtable implementation. 5 | * deps: None 6 | */ 7 | 8 | #ifndef __MAP_H__ 9 | #define __MAP_H__ 10 | 11 | #include 12 | #include 13 | 14 | #if defined(__cplusplus) 15 | extern "C" { 16 | #endif 17 | 18 | #define MAP_LOAD_LIMIT 0.72 /* load factor */ 19 | #define MAP_CAP_MAX 1024 * 1024 * 1024 /* 1GB */ 20 | #define MAP_CAP_INIT 16 /* init table size: must be 2** */ 21 | 22 | #define map() map_new() 23 | #define map_iter(m) map_iter_new(m) 24 | #define map_each(iter, node) while (((node) = map_iter_next((iter))) != NULL) 25 | 26 | enum { 27 | MAP_OK = 0, /* operation is ok */ 28 | MAP_ENOMEM = 1, /* no memory error */ 29 | }; 30 | 31 | struct map_node { 32 | char *key; /* key string */ 33 | size_t len; /* key length */ 34 | void *val; /* value data*/ 35 | }; 36 | 37 | struct map { 38 | size_t cap; /* map capacity */ 39 | size_t len; /* map length */ 40 | struct map_node *table; /* node table */ 41 | }; 42 | 43 | struct map_iter { 44 | struct map *m; /* map to iterate */ 45 | size_t i; /* current table index */ 46 | }; 47 | 48 | struct map *map_new(void); 49 | void map_free(struct map *m); 50 | void map_clear(struct map *m); /* O(1) */ 51 | size_t map_len(struct map *m); /* O(1) */ 52 | size_t map_cap(struct map *m); /* O(1) */ 53 | int map_set(struct map *m, char *key, void *val); /* O(1) */ 54 | void *map_get(struct map *m, char *key); /* O(1) */ 55 | int map_has(struct map *m, char *key); /* O(1) */ 56 | void *map_pop(struct map *m, char *key); /* O(1) */ 57 | int map_iset(struct map *m, char *key, size_t len, void *val); /* O(1) */ 58 | void *map_iget(struct map *m, char *key, size_t len); /* O(1) */ 59 | int map_ihas(struct map *m, char *key, size_t len); /* O(1) */ 60 | void *map_ipop(struct map *m, char *key, size_t len); /* O(1) */ 61 | struct map_iter *map_iter_new(struct map *m); 62 | void map_iter_free(struct map_iter *iter); 63 | struct map_node *map_iter_next(struct map_iter *iter); 64 | void map_iter_rewind(struct map_iter *iter); 65 | 66 | #if defined(__cplusplus) 67 | } 68 | #endif 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /src/md5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 3 | * MD5 Message-Digest Algorithm (RFC 1321). 4 | * 5 | * Homepage: 6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 7 | * 8 | * Author: Alexander Peslyak, better known as Solar Designer 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "md5.h" 17 | 18 | typedef unsigned int MD5_u32plus; 19 | 20 | typedef struct { 21 | MD5_u32plus lo, hi; 22 | MD5_u32plus a, b, c, d; 23 | unsigned char buffer[64]; 24 | MD5_u32plus block[16]; 25 | } MD5_CTX; 26 | 27 | /* 28 | * The basic MD5 functions. 29 | * 30 | * F and G are optimized compared to their RFC 1321 definitions for 31 | * architectures that lack an AND-NOT instruction, just like in Colin Plumb's 32 | * implementation. 33 | */ 34 | #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) 35 | #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) 36 | #define H(x, y, z) ((x) ^ (y) ^ (z)) 37 | #define I(x, y, z) ((y) ^ ((x) | ~(z))) 38 | 39 | /* 40 | * The MD5 transformation for all four rounds. 41 | */ 42 | #define STEP(f, a, b, c, d, x, t, s) \ 43 | (a) += f((b), (c), (d)) + (x) + (t); \ 44 | (a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); \ 45 | (a) += (b); 46 | 47 | /* 48 | * SET reads 4 input bytes in little-endian byte order and stores them 49 | * in a properly aligned word in host byte order. 50 | * 51 | * The check for little-endian architectures that tolerate unaligned 52 | * memory accesses is just an optimization. Nothing will break if it 53 | * doesn't work. 54 | */ 55 | #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) 56 | #define SET(n) (*(MD5_u32plus *)&ptr[(n)*4]) 57 | #define GET(n) SET(n) 58 | #else 59 | #define SET(n) \ 60 | (ctx->block[(n)] = (MD5_u32plus)ptr[(n)*4] | \ 61 | ((MD5_u32plus)ptr[(n)*4 + 1] << 8) | \ 62 | ((MD5_u32plus)ptr[(n)*4 + 2] << 16) | \ 63 | ((MD5_u32plus)ptr[(n)*4 + 3] << 24)) 64 | #define GET(n) (ctx->block[(n)]) 65 | #endif 66 | 67 | /* 68 | * This processes one or more 64-byte data blocks, but does NOT update 69 | * the bit counters. There are no alignment requirements. 70 | */ 71 | static void *body(MD5_CTX *ctx, void *data, unsigned long size) { 72 | unsigned char *ptr; 73 | MD5_u32plus a, b, c, d; 74 | MD5_u32plus saved_a, saved_b, saved_c, saved_d; 75 | 76 | ptr = data; 77 | 78 | a = ctx->a; 79 | b = ctx->b; 80 | c = ctx->c; 81 | d = ctx->d; 82 | 83 | do { 84 | saved_a = a; 85 | saved_b = b; 86 | saved_c = c; 87 | saved_d = d; 88 | 89 | /* Round 1 */ 90 | STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) 91 | STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) 92 | STEP(F, c, d, a, b, SET(2), 0x242070db, 17) 93 | STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) 94 | STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) 95 | STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) 96 | STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) 97 | STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) 98 | STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) 99 | STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) 100 | STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) 101 | STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) 102 | STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) 103 | STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) 104 | STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) 105 | STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) 106 | 107 | /* Round 2 */ 108 | STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) 109 | STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) 110 | STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) 111 | STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) 112 | STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) 113 | STEP(G, d, a, b, c, GET(10), 0x02441453, 9) 114 | STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) 115 | STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) 116 | STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) 117 | STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) 118 | STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) 119 | STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) 120 | STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) 121 | STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) 122 | STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) 123 | STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) 124 | 125 | /* Round 3 */ 126 | STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) 127 | STEP(H, d, a, b, c, GET(8), 0x8771f681, 11) 128 | STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) 129 | STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23) 130 | STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) 131 | STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11) 132 | STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) 133 | STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23) 134 | STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) 135 | STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11) 136 | STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) 137 | STEP(H, b, c, d, a, GET(6), 0x04881d05, 23) 138 | STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) 139 | STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11) 140 | STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) 141 | STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23) 142 | 143 | /* Round 4 */ 144 | STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) 145 | STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) 146 | STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) 147 | STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) 148 | STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) 149 | STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) 150 | STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) 151 | STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) 152 | STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) 153 | STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) 154 | STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) 155 | STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) 156 | STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) 157 | STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) 158 | STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) 159 | STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) 160 | 161 | a += saved_a; 162 | b += saved_b; 163 | c += saved_c; 164 | d += saved_d; 165 | 166 | ptr += 64; 167 | } while (size -= 64); 168 | 169 | ctx->a = a; 170 | ctx->b = b; 171 | ctx->c = c; 172 | ctx->d = d; 173 | 174 | return ptr; 175 | } 176 | 177 | void MD5_Init(MD5_CTX *ctx) { 178 | ctx->a = 0x67452301; 179 | ctx->b = 0xefcdab89; 180 | ctx->c = 0x98badcfe; 181 | ctx->d = 0x10325476; 182 | 183 | ctx->lo = 0; 184 | ctx->hi = 0; 185 | } 186 | 187 | void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size) { 188 | MD5_u32plus saved_lo; 189 | unsigned long used, free; 190 | 191 | saved_lo = ctx->lo; 192 | if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) { 193 | ctx->hi++; 194 | } 195 | ctx->hi += size >> 29; 196 | 197 | used = saved_lo & 0x3f; 198 | 199 | if (used) { 200 | free = 64 - used; 201 | 202 | if (size < free) { 203 | memcpy(&ctx->buffer[used], data, size); 204 | return; 205 | } 206 | 207 | memcpy(&ctx->buffer[used], data, free); 208 | data = (unsigned char *)data + free; 209 | size -= free; 210 | body(ctx, ctx->buffer, 64); 211 | } 212 | 213 | if (size >= 64) { 214 | data = body(ctx, data, size & ~(unsigned long)0x3f); 215 | size &= 0x3f; 216 | } 217 | 218 | memcpy(ctx->buffer, data, size); 219 | } 220 | 221 | void MD5_Final(unsigned char *result, MD5_CTX *ctx) { 222 | unsigned long used, free; 223 | 224 | used = ctx->lo & 0x3f; 225 | 226 | ctx->buffer[used++] = 0x80; 227 | 228 | free = 64 - used; 229 | 230 | if (free < 8) { 231 | memset(&ctx->buffer[used], 0, free); 232 | body(ctx, ctx->buffer, 64); 233 | used = 0; 234 | free = 64; 235 | } 236 | 237 | memset(&ctx->buffer[used], 0, free - 8); 238 | 239 | ctx->lo <<= 3; 240 | ctx->buffer[56] = ctx->lo; 241 | ctx->buffer[57] = ctx->lo >> 8; 242 | ctx->buffer[58] = ctx->lo >> 16; 243 | ctx->buffer[59] = ctx->lo >> 24; 244 | ctx->buffer[60] = ctx->hi; 245 | ctx->buffer[61] = ctx->hi >> 8; 246 | ctx->buffer[62] = ctx->hi >> 16; 247 | ctx->buffer[63] = ctx->hi >> 24; 248 | 249 | body(ctx, ctx->buffer, 64); 250 | 251 | result[0] = ctx->a; 252 | result[1] = ctx->a >> 8; 253 | result[2] = ctx->a >> 16; 254 | result[3] = ctx->a >> 24; 255 | result[4] = ctx->b; 256 | result[5] = ctx->b >> 8; 257 | result[6] = ctx->b >> 16; 258 | result[7] = ctx->b >> 24; 259 | result[8] = ctx->c; 260 | result[9] = ctx->c >> 8; 261 | result[10] = ctx->c >> 16; 262 | result[11] = ctx->c >> 24; 263 | result[12] = ctx->d; 264 | result[13] = ctx->d >> 8; 265 | result[14] = ctx->d >> 16; 266 | result[15] = ctx->d >> 24; 267 | 268 | memset(ctx, 0, sizeof(*ctx)); 269 | } 270 | 271 | /* 272 | * Just a simple method for getting the signature 273 | * result must be == 16 274 | */ 275 | void md5_signature(unsigned char *key, unsigned long length, 276 | unsigned char *result) { 277 | MD5_CTX my_md5; 278 | 279 | MD5_Init(&my_md5); 280 | (void)MD5_Update(&my_md5, key, length); 281 | MD5_Final(result, &my_md5); 282 | } 283 | 284 | uint32_t hash_md5(const char *key, size_t key_length) { 285 | unsigned char results[16]; 286 | 287 | md5_signature((unsigned char *)key, (unsigned long)key_length, results); 288 | 289 | return ((uint32_t)(results[3] & 0xFF) << 24) | 290 | ((uint32_t)(results[2] & 0xFF) << 16) | 291 | ((uint32_t)(results[1] & 0xFF) << 8) | (results[0] & 0xFF); 292 | } 293 | -------------------------------------------------------------------------------- /src/md5.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * md5 hash function. 5 | * deps: None. 6 | */ 7 | 8 | /* 9 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 10 | * MD5 Message-Digest Algorithm (RFC 1321). 11 | * 12 | * Homepage: 13 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 14 | * 15 | * Author: Alexander Peslyak, better known as Solar Designer 17 | */ 18 | 19 | #ifndef __MD5_H__ 20 | #define __MD5_H__ 21 | 22 | #include 23 | #include 24 | 25 | #if defined(__cplusplus) 26 | extern "C" { 27 | #endif 28 | 29 | void md5_signature(unsigned char *key, unsigned long length, 30 | unsigned char *result); 31 | uint32_t hash_md5(const char *key, size_t key_length); 32 | 33 | #if defined(__cplusplus) 34 | } 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/queue.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "queue.h" 9 | 10 | /* Create new queue node. */ 11 | struct queue_node *queue_node_new(void *data) { 12 | struct queue_node *node = malloc(sizeof(struct queue_node)); 13 | 14 | if (node != NULL) { 15 | node->data = data; 16 | node->next = NULL; 17 | } 18 | return node; 19 | } 20 | 21 | /* Free a queue node. */ 22 | void queue_node_free(struct queue_node *node) { 23 | if (node != NULL) free(node); 24 | } 25 | 26 | /* Create a empty queue */ 27 | struct queue *queue_new(void) { 28 | struct queue *queue = malloc(sizeof(struct queue)); 29 | if (queue != NULL) { 30 | queue->head = NULL; 31 | queue->tail = NULL; 32 | queue->len = 0; 33 | } 34 | return queue; 35 | } 36 | 37 | /* Free queue. */ 38 | void queue_free(struct queue *queue) { 39 | if (queue != NULL) { 40 | queue_clear(queue); 41 | free(queue); 42 | } 43 | } 44 | 45 | /* Clear a queue. */ 46 | void queue_clear(struct queue *queue) { 47 | assert(queue != NULL); 48 | 49 | while (queue_len(queue) != 0) queue_pop(queue); 50 | } 51 | 52 | /* Get queue length. */ 53 | size_t queue_len(struct queue *queue) { 54 | assert(queue != NULL); 55 | return queue->len; 56 | } 57 | 58 | /* Push an item into the queue. */ 59 | int queue_push(struct queue *queue, void *data) { 60 | assert(queue != NULL); 61 | 62 | struct queue_node *node = queue_node_new(data); 63 | if (node == NULL) return QUEUE_ENOMEM; 64 | 65 | if (queue->len == 0) { 66 | assert(queue->head == NULL && queue->tail == NULL); 67 | queue->head = node; 68 | queue->tail = node; 69 | } else { 70 | assert(queue->head != NULL && queue->tail != NULL); 71 | queue->tail->next = node; 72 | queue->tail = node; 73 | } 74 | 75 | queue->len += 1; 76 | return QUEUE_OK; 77 | } 78 | 79 | /* Pop an item from the queue, NULL on empty */ 80 | void *queue_pop(struct queue *queue) { 81 | assert(queue != NULL); 82 | 83 | if (queue->len == 0) { 84 | assert(queue->head == NULL && queue->tail == NULL); 85 | return NULL; 86 | } 87 | assert(queue->head != NULL && queue->tail != NULL); 88 | struct queue_node *head = queue->head; 89 | 90 | queue->head = head->next; 91 | queue->len -= 1; 92 | 93 | if (queue->head == NULL) { 94 | assert(queue->len == 0); 95 | queue->tail = NULL; 96 | } 97 | 98 | void *data = head->data; 99 | queue_node_free(head); 100 | return data; 101 | } 102 | 103 | /* Get an item from the top if the queue, NULL on empty. */ 104 | void *queue_top(struct queue *queue) { 105 | assert(queue != NULL); 106 | 107 | if (queue->len == 0) { 108 | assert(queue->head == NULL && queue->tail == NULL); 109 | return NULL; 110 | } 111 | assert(queue->head != NULL && queue->tail != NULL); 112 | return queue->head->data; 113 | } 114 | -------------------------------------------------------------------------------- /src/queue.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * List based queue implementation. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __QUEUE_H__ 9 | #define __QUEUE_H__ 10 | 11 | #include 12 | 13 | #if defined(__cplusplus) 14 | extern "C" { 15 | #endif 16 | 17 | #define queue() queue_new() 18 | 19 | enum { 20 | QUEUE_OK = 0, /* operation is ok */ 21 | QUEUE_ENOMEM = 1, /* no memory error */ 22 | }; 23 | 24 | struct queue_node { 25 | void *data; /* node data */ 26 | struct queue_node *next; /* next node */ 27 | }; 28 | 29 | struct queue { 30 | struct queue_node *head; /* head node */ 31 | struct queue_node *tail; /* last node */ 32 | size_t len; /* queue length */ 33 | }; 34 | 35 | struct queue_node *queue_node_new(void *data); 36 | void queue_node_free(struct queue_node *node); 37 | struct queue *queue_new(void); 38 | void queue_free(struct queue *queue); 39 | void queue_clear(struct queue *queue); 40 | size_t queue_len(struct queue *queue); /* O(1) */ 41 | int queue_push(struct queue *queue, void *data); /* O(1) */ 42 | void *queue_pop(struct queue *queue); /* O(1) */ 43 | void *queue_top(struct queue *queue); /* O(1) */ 44 | 45 | #if defined(__cplusplus) 46 | } 47 | #endif 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/signals.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Signals hanlder util. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __SIGNALS_H__ 9 | #define __SIGNALS_H__ 10 | 11 | #include 12 | #include 13 | 14 | #if defined(__cplusplus) 15 | extern "C" { 16 | #endif 17 | 18 | void signals_register(int signal, void (*handler)(int)) { 19 | struct sigaction signal_action; 20 | sigemptyset(&signal_action.sa_mask); 21 | signal_action.sa_flags = 0; 22 | signal_action.sa_handler = handler; 23 | sigaction(signal, &signal_action, NULL); 24 | } 25 | 26 | #if defined(__cplusplus) 27 | } 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/skiplist.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Skiplist implementation. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __SKIPLIST_H__ 9 | #define __SKIPLIST_H__ 10 | 11 | #include 12 | 13 | #if defined(__cplusplus) 14 | extern "C" { 15 | #endif 16 | 17 | #define SKIPLIST_LEVEL_MAX 32 /* max skiplist level (0~11) */ 18 | #define SKIPLIST_FACTOR_P 0.5 19 | 20 | #define skiplist(cmp) skiplist_new(cmp) 21 | #define skiplist_height(sl) skiplist_level(sl) 22 | #define skiplist_iter(sl) skiplist_iter_new(sl) 23 | #define skiplist_each(sl, node) \ 24 | for ((node) = (sl)->head->forwards[0]; (node) != NULL; \ 25 | (node) = (node)->forwards[0]) 26 | 27 | /* socre comparator type, return < 0 if arg#0 < arg#1 */ 28 | typedef int (*skiplist_cmp_t)(unsigned long score1, unsigned long score2); 29 | 30 | enum { 31 | SKIPLIST_OK = 0, /* operation is ok */ 32 | SKIPLIST_ENOMEM = 1, /* no memory error */ 33 | }; 34 | 35 | struct skiplist_node { 36 | unsigned long score; /* node score */ 37 | void *data; /* node data */ 38 | struct skiplist_node **forwards; /* node forward links */ 39 | struct skiplist_node *backward; /* node backward link */ 40 | }; 41 | 42 | struct skiplist { 43 | size_t len; /* skiplist length */ 44 | int level; /* skiplist level */ 45 | struct skiplist_node *head; /* skiplist head */ 46 | struct skiplist_node *tail; /* skiplist tail */ 47 | skiplist_cmp_t cmp; /* score comparator */ 48 | }; 49 | 50 | struct skiplist_iter { 51 | struct skiplist *skiplist; /* skiplist to iterate */ 52 | struct skiplist_node *node; /* current skiplist node */ 53 | }; 54 | 55 | struct skiplist_node *skiplist_node_new(int level, unsigned long score, 56 | void *data); 57 | void skiplist_node_free(struct skiplist_node *node); 58 | struct skiplist *skiplist_new(skiplist_cmp_t cmp); 59 | void skiplist_free(struct skiplist *skiplist); 60 | void skiplist_clear(struct skiplist *skiplist); 61 | size_t skiplist_len(struct skiplist *skiplist); /* O(1) */ 62 | int skiplist_level(struct skiplist *skiplist); /* O(1) */ 63 | int skiplist_push(struct skiplist *skiplist, unsigned long score, 64 | void *data); /* O(logN) */ 65 | void *skiplist_get(struct skiplist *skiplist, 66 | unsigned long score); /* O(logN) */ 67 | void *skiplist_pop(struct skiplist *skiplist, 68 | unsigned long score); /* O(logN) */ 69 | void *skiplist_popfirst(struct skiplist *skiplist); /* O(1) */ 70 | void *skiplist_poplast(struct skiplist *skiplist); /* O(logN) */ 71 | struct skiplist_node *skiplist_first(struct skiplist *skiplist); /* O(1) */ 72 | struct skiplist_node *skiplist_last(struct skiplist *skiplist); /* O(1) */ 73 | struct skiplist_iter *skiplist_iter_new(struct skiplist *skiplist); 74 | void skiplist_iter_free(struct skiplist_iter *iter); 75 | struct skiplist_node *skiplist_iter_next(struct skiplist_iter *iter); /* O(1) */ 76 | struct skiplist_node *skiplist_iter_prev(struct skiplist_iter *iter); /* O(1) */ 77 | void skiplist_iter_rewind(struct skiplist_iter *iter); /* O(1) */ 78 | void skiplist_print(struct skiplist *skiplist); 79 | 80 | #if defined(__cplusplus) 81 | } 82 | #endif 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/stack.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "stack.h" 9 | 10 | /* Create new stack with an initialized capacity. */ 11 | struct stack *stack_new(size_t cap) { 12 | struct stack *stack = malloc(sizeof(struct stack)); 13 | 14 | if (stack != NULL) { 15 | stack->data = NULL; 16 | stack->len = 0; 17 | stack->cap = 0; 18 | 19 | if (cap > 0 && stack_grow(stack, cap) != STACK_OK) return NULL; 20 | } 21 | 22 | return stack; 23 | } 24 | 25 | /* Free stack and its data. */ 26 | void stack_free(struct stack *stack) { 27 | if (stack != NULL) { 28 | if (stack->data != NULL) free(stack->data); 29 | free(stack); 30 | } 31 | } 32 | 33 | /* Clear stack data to NULL and clean its len and cap to zero. */ 34 | void stack_clear(struct stack *stack) { 35 | assert(stack != NULL); 36 | 37 | if (stack->data != NULL) { 38 | free(stack->data); 39 | stack->data = NULL; 40 | } 41 | 42 | stack->cap = 0; 43 | stack->len = 0; 44 | } 45 | 46 | /* Get stack length */ 47 | size_t stack_len(struct stack *stack) { 48 | assert(stack != NULL); 49 | return stack->len; 50 | } 51 | 52 | /* Get stack capacity */ 53 | size_t stack_cap(struct stack *stack) { 54 | assert(stack != NULL); 55 | return stack->cap; 56 | } 57 | 58 | /* Grow a stack's memory capacity to given cap. */ 59 | int stack_grow(struct stack *stack, size_t cap) { 60 | assert(stack != NULL); 61 | 62 | if (cap > STACK_CAP_MAX) return STACK_ENOMEM; 63 | 64 | if (cap <= stack->cap) return STACK_OK; 65 | 66 | size_t unit = stack->cap; 67 | 68 | if (unit > STACK_UNIT_MAX) unit = STACK_UNIT_MAX; 69 | 70 | if (unit < STACK_UNIT_MIN) unit = STACK_UNIT_MIN; 71 | 72 | size_t new_cap = stack->cap + unit; 73 | while (new_cap < cap) new_cap += unit; 74 | 75 | void **data = realloc(stack->data, new_cap * sizeof(void *)); 76 | if (data == NULL) return STACK_ENOMEM; 77 | 78 | stack->data = data; 79 | stack->cap = new_cap; 80 | 81 | if (stack->len > new_cap) stack->len = new_cap; 82 | return STACK_OK; 83 | } 84 | 85 | /* Push an item on the top of the stack. */ 86 | int stack_push(struct stack *stack, void *data) { 87 | assert(stack != NULL); 88 | 89 | if (stack->len <= stack->cap && 90 | stack_grow(stack, stack->len + 1) != STACK_OK) 91 | return STACK_ENOMEM; 92 | 93 | stack->data[stack->len++] = data; 94 | return STACK_OK; 95 | } 96 | 97 | /* Pop an item from the top of the stack, NULL on empty. */ 98 | void *stack_pop(struct stack *stack) { 99 | assert(stack != NULL); 100 | 101 | if (stack->len == 0 || stack->data == NULL) return NULL; 102 | 103 | return stack->data[--stack->len]; 104 | } 105 | 106 | /* Get an item from the top of the stack, NULL on empty. */ 107 | void *stack_top(struct stack *stack) { 108 | assert(stack != NULL); 109 | 110 | if (stack->len == 0 || stack->data == NULL) return NULL; 111 | 112 | return stack->data[stack->len - 1]; 113 | } 114 | -------------------------------------------------------------------------------- /src/stack.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Array based stack implementation. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __STACK_H__ 9 | #define __STACK_H__ 10 | 11 | #include 12 | 13 | #if defined(__cplusplus) 14 | extern "C" { 15 | #endif 16 | 17 | /* note that real allocated size is cap * sizeof(void *). */ 18 | #define STACK_CAP_MAX 16 * 1024 * 1024 /* max stack capacity: 16mb */ 19 | #define STACK_UNIT_MIN 1 /* min stack realloc unit: 1 */ 20 | #define STACK_UNIT_MAX 1024 /* max stack realloc unit: 1k */ 21 | 22 | #define stack(cap) stack_new(cap) 23 | 24 | enum { 25 | STACK_OK = 0, /* operation is ok */ 26 | STACK_ENOMEM = 1, /* no memory error */ 27 | }; 28 | 29 | struct stack { 30 | size_t len; /* stack length */ 31 | size_t cap; /* stack capacity */ 32 | void **data; /* stack data */ 33 | }; 34 | 35 | struct stack *stack_new(size_t cap); 36 | void stack_free(struct stack *stack); 37 | void stack_clear(struct stack *stack); 38 | size_t stack_len(struct stack *stack); /* O(1) */ 39 | size_t stack_cap(struct stack *stack); /* O(1) */ 40 | void *stack_pop(struct stack *stack); /* O(1) */ 41 | void *stack_top(struct stack *stack); /* O(1) */ 42 | int stack_push(struct stack *stack, void *data); /* O(1) */ 43 | int stack_grow(struct stack *stack, size_t cap); 44 | 45 | #if defined(__cplusplus) 46 | } 47 | #endif 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/strings.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "strings.h" 10 | 11 | /* Search null-terminated string `sub` in string `s`, simple version via 12 | * Boyer Moore string search algorithm. Return the first position of the 13 | * `sub` in the `s`, return the `s`'s length on failure. */ 14 | size_t strings_search(char *s, char *sub, size_t start) { 15 | assert(s != NULL && sub != NULL); 16 | 17 | size_t s_len = strlen(s); 18 | size_t len = strlen(sub); 19 | size_t last = len - 1; 20 | size_t idx; 21 | 22 | size_t table[256] = {0}; 23 | 24 | /* build bad char table */ 25 | for (idx = 0; idx < 256; idx++) table[idx] = len; 26 | 27 | for (idx = 0; idx < len; idx++) table[(int)sub[idx]] = last - idx; 28 | 29 | /* do search */ 30 | size_t i, j, k, t, skip; 31 | 32 | for (i = start; i < s_len; i += skip) { 33 | skip = 0; 34 | for (j = 0; j < len; j++) { 35 | k = last - j; 36 | if (sub[k] != s[i + k]) { 37 | t = table[(int)s[i + k]]; 38 | skip = t > j ? t - j : 1; 39 | break; 40 | } 41 | } 42 | if (skip == 0) 43 | // hit 44 | return i; 45 | } 46 | // failed 47 | return s_len; 48 | } 49 | 50 | /* Create random string with length `len`, you may want 51 | * to set rand seed before all your `rand` calls. */ 52 | char *strings_rand(char *s, size_t len) { 53 | static const char chs[] = 54 | "0123456789" 55 | "abcdefghijklmnopqrstuvwxyz" 56 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 57 | static const int mask = 10 + 26 * 2 - 1; 58 | 59 | int i; 60 | 61 | for (i = 0; i < len; i++) s[i] = chs[rand() & mask]; 62 | 63 | s[len] = 0; 64 | return s; 65 | } 66 | 67 | /* Replace all `sub` in string `src` with `rep` and concat the 68 | * result into `dst`. All the arguments are NULL-terminated c 69 | * string, return */ 70 | char *strings_replace(char *dst, char *src, char *sub, char *rep) { 71 | size_t src_len = strlen(src); 72 | size_t sub_len = strlen(sub); 73 | size_t rep_len = strlen(rep); 74 | size_t dst_len = 0; 75 | size_t idx, start = 0; 76 | 77 | while ((idx = strings_search(src, sub, start)) < src_len) { 78 | strncat(dst + dst_len, src + start, idx - start); 79 | dst_len += idx - start; 80 | strcat(dst + dst_len, rep); 81 | dst_len += rep_len; 82 | start = idx + sub_len; 83 | } 84 | strcat(dst + dst_len, src + start); 85 | dst_len += src_len - start; 86 | dst[dst_len] = '\0'; 87 | return dst; 88 | } 89 | -------------------------------------------------------------------------------- /src/strings.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * 4 | * Util functions for C native strings. 5 | * deps: None. 6 | */ 7 | 8 | #ifndef __STRINGS_H__ 9 | #define __STRINGS_H__ 10 | 11 | #include 12 | 13 | #if defined(__cplusplus) 14 | extern "C" { 15 | #endif 16 | 17 | size_t strings_search(char *s, char *sub, 18 | size_t start); /* best O(N/M), worst O(M*N) */ 19 | char *strings_rand(char *s, size_t len); /* O(N) */ 20 | char *strings_replace(char *dst, char *src, char *sub, 21 | char *rep); /* best O(N), worst O(N*N) */ 22 | 23 | #if defined(__cplusplus) 24 | } 25 | #endif 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | * deps: None. 4 | */ 5 | 6 | #ifndef __UTILS_H__ 7 | #define __UTILS_H__ 8 | 9 | #include 10 | #include 11 | 12 | #if defined(__cplusplus) 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | * MIN and MAX 18 | */ 19 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 20 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 21 | 22 | /** 23 | * OS Platforms 24 | */ 25 | #ifdef __linux__ 26 | #define OSPLAT_LINUX 1 27 | #endif 28 | 29 | #if defined(__APPLE__) 30 | #define OSPLAT_OSX 1 31 | #endif 32 | 33 | #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 34 | #define OSPLAT_BSD 1 35 | #endif 36 | 37 | #ifdef __sun 38 | #define OSPLAT_SUN 1 39 | #endif 40 | 41 | #if defined(__cplusplus) 42 | } 43 | #endif 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /test/.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | # This file is NOT licensed under the GPLv3, which is the license for the rest 2 | # of YouCompleteMe. 3 | # 4 | # Here's the license text for this file: 5 | # 6 | # This is free and unencumbered software released into the public domain. 7 | # 8 | # Anyone is free to copy, modify, publish, use, compile, sell, or 9 | # distribute this software, either in source code form or as a compiled 10 | # binary, for any purpose, commercial or non-commercial, and by any 11 | # means. 12 | # 13 | # In jurisdictions that recognize copyright laws, the author or authors 14 | # of this software dedicate any and all copyright interest in the 15 | # software to the public domain. We make this dedication for the benefit 16 | # of the public at large and to the detriment of our heirs and 17 | # successors. We intend this dedication to be an overt act of 18 | # relinquishment in perpetuity of all present and future rights to this 19 | # software under copyright law. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | # OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # For more information, please refer to 30 | 31 | import os 32 | import ycm_core 33 | 34 | flags = [ 35 | # INSERT FLAGS HERE 36 | '-Wall', 37 | '-I../src', 38 | '-std=c99', 39 | '-D_GNU_SOURCE', 40 | '-pthread', 41 | ] 42 | 43 | 44 | # Set this to the absolute path to the folder (NOT the file!) containing the 45 | # compile_commands.json file to use that instead of 'flags'. See here for 46 | # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html 47 | # 48 | # You can get CMake to generate this file for you by adding: 49 | # set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) 50 | # to your CMakeLists.txt file. 51 | # 52 | # Most projects will NOT need to set this to anything; you can just change the 53 | # 'flags' list of compilation flags. Notice that YCM itself uses that approach. 54 | compilation_database_folder = '' 55 | 56 | if os.path.exists(compilation_database_folder): 57 | database = ycm_core.CompilationDatabase(compilation_database_folder) 58 | else: 59 | database = None 60 | 61 | SOURCE_EXTENSIONS = ['.cpp', '.cxx', '.cc', '.c', '.m', '.mm'] 62 | 63 | 64 | def DirectoryOfThisScript(): 65 | return os.path.dirname(os.path.abspath(__file__)) 66 | 67 | 68 | def MakeRelativePathsInFlagsAbsolute(flags, working_directory): 69 | if not working_directory: 70 | return list(flags) 71 | new_flags = [] 72 | make_next_absolute = False 73 | path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] 74 | for flag in flags: 75 | new_flag = flag 76 | 77 | if make_next_absolute: 78 | make_next_absolute = False 79 | if not flag.startswith('/'): 80 | new_flag = os.path.join(working_directory, flag) 81 | 82 | for path_flag in path_flags: 83 | if flag == path_flag: 84 | make_next_absolute = True 85 | break 86 | 87 | if flag.startswith(path_flag): 88 | path = flag[len(path_flag):] 89 | new_flag = path_flag + os.path.join(working_directory, path) 90 | break 91 | 92 | if new_flag: 93 | new_flags.append(new_flag) 94 | return new_flags 95 | 96 | 97 | def IsHeaderFile(filename): 98 | extension = os.path.splitext(filename)[1] 99 | return extension in ['.h', '.hxx', '.hpp', '.hh'] 100 | 101 | 102 | def GetCompilationInfoForFile(filename): 103 | if IsHeaderFile(filename): 104 | basename = os.path.splitext(filename)[0] 105 | for extension in SOURCE_EXTENSIONS: 106 | replacement_file = basename + extension 107 | if os.path.exists(replacement_file): 108 | compilation_info = database.GetCompilationInfoForFile( 109 | replacement_file) 110 | if compilation_info.compiler_flags_: 111 | return compilation_info 112 | return None 113 | return database.GetCompilationInfoForFile(filename) 114 | 115 | 116 | def FlagsForFile(filename, **kwargs): 117 | if database: 118 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a 119 | # python list, but a "list-like" StringVec object 120 | compilation_info = GetCompilationInfoForFile(filename) 121 | if not compilation_info: 122 | return None 123 | 124 | final_flags = MakeRelativePathsInFlagsAbsolute( 125 | compilation_info.compiler_flags_, 126 | compilation_info.compiler_working_dir_) 127 | 128 | else: 129 | relative_to = DirectoryOfThisScript() 130 | final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) 131 | 132 | return { 133 | 'flags': final_flags, 134 | 'do_cache': True 135 | } 136 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # vim:set noet: 2 | 3 | default: runtests 4 | 5 | NAME=test 6 | CC=cc -std=c99 7 | OPTIMIZATION?= 8 | CFLAGS=-Wall $(OPTIMIZATION) -D_GNU_SOURCE -g -I../src 9 | LDFLAGS=-Wall -pthread 10 | BIN=$(NAME) 11 | SRC:=$(wildcard ../src/*.c) $(wildcard *.c) 12 | EV_EPOLL:=$(wildcard ../src/event_epoll.c) 13 | EV_KQUEUE:=$(wildcard ../src/event_kqueue.c) 14 | EV_TIMER:=$(wildcard ../src/event_timer.c) 15 | SRC:=$(filter-out $(EV_EPOLL), $(SRC)) 16 | SRC:=$(filter-out $(EV_KQUEUE), $(SRC)) 17 | SRC:=$(filter-out $(EV_TIMER), $(SRC)) 18 | OBJ:=$(SRC:c=o) 19 | LOG:=$(NAME)-mtrace.log 20 | UNAME=$(shell uname) 21 | 22 | $(BIN): $(OBJ) 23 | 24 | runtests: $(BIN) 25 | ifeq ($(UNAME), Linux) 26 | @env MALLOC_TRACE=$(LOG) ./$(BIN) 27 | @mtrace $(BIN) $(LOG) 28 | else 29 | ./$(BIN) 30 | endif 31 | 32 | clean: 33 | rm -f $(BIN) $(LOG) $(OBJ) test.log* 34 | -------------------------------------------------------------------------------- /test/buf_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "buf.h" 9 | 10 | void case_buf_clear() { 11 | struct buf *buf1 = buf("test"); 12 | struct buf *buf2 = buf(NULL); 13 | assert(!buf_isempty(buf1)); 14 | assert(buf_isempty(buf2)); 15 | buf_clear(buf1); 16 | buf_clear(buf2); 17 | assert(buf_isempty(buf1)); 18 | assert(buf_isempty(buf2)); 19 | buf_free(buf1); 20 | buf_free(buf2); 21 | } 22 | 23 | void case_buf_put() { 24 | struct buf *buf = buf(NULL); 25 | assert(buf_put(buf, "abc", 3) == BUF_OK); 26 | assert(strcmp(str(buf), "abc") == 0); 27 | assert(buf_put(buf, "efg", 3) == BUF_OK); 28 | assert(strcmp(str(buf), "abcefg") == 0); 29 | buf_free(buf); 30 | } 31 | 32 | void case_buf_puts() { 33 | struct buf *buf = buf(NULL); 34 | assert(buf_puts(buf, "abc") == BUF_OK); 35 | assert(strcmp(str(buf), "abc") == 0); 36 | assert(buf_puts(buf, "efg") == BUF_OK); 37 | assert(strcmp(str(buf), "abcefg") == 0); 38 | buf_free(buf); 39 | } 40 | 41 | void case_buf_putc() { 42 | struct buf *buf = buf(NULL); 43 | assert(buf_putc(buf, 'a') == BUF_OK); 44 | assert(buf_putc(buf, 'b') == BUF_OK); 45 | assert(buf_putc(buf, 'c') == BUF_OK); 46 | assert(buf_putc(buf, 'd') == BUF_OK); 47 | assert(strcmp(str(buf), "abcd") == 0); 48 | buf_free(buf); 49 | } 50 | 51 | void case_buf_str() { 52 | struct buf *buf = buf(NULL); 53 | assert(strcmp(str(buf), "") == 0); 54 | buf_puts(buf, "abcdef"); 55 | assert(strcmp(str(buf), "abcdef") == 0); 56 | buf_free(buf); 57 | } 58 | 59 | void case_buf_sprintf() { 60 | struct buf *buf = buf(NULL); 61 | assert(buf_sprintf(buf, "%s %s!", "hello", "world") == BUF_OK); 62 | assert(strcmp(buf_str(buf), "hello world!") == 0); 63 | buf_free(buf); 64 | } 65 | 66 | void case_buf_isempty() { 67 | struct buf *buf1 = buf("test"); 68 | struct buf *buf2 = buf(NULL); 69 | assert(!buf_isempty(buf1)); 70 | assert(buf_isempty(buf2)); 71 | buf_free(buf1); 72 | buf_free(buf2); 73 | } 74 | 75 | void case_buf_lrm() { 76 | struct buf *buf = buf("testabcdef"); 77 | buf_lrm(buf, 4); 78 | assert(strcmp(buf_str(buf), "abcdef") == 0); 79 | buf_lrm(buf, 100); 80 | assert(strcmp(buf_str(buf), "") == 0); 81 | buf_free(buf); 82 | } 83 | 84 | void case_buf_len() { 85 | struct buf *buf = buf("abcdef"); 86 | assert(buf_len(buf) == 6); 87 | buf_free(buf); 88 | } 89 | 90 | void case_buf_cap() { 91 | struct buf *buf = buf("abcdef"); 92 | assert(buf_cap(buf) == 6); 93 | buf_puts(buf, "abc"); 94 | assert(buf_cap(buf) == 12); 95 | buf_free(buf); 96 | } 97 | -------------------------------------------------------------------------------- /test/cfg_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "cfg.h" 10 | 11 | void case_cfg_get() { 12 | char s[1024] = 13 | "# this is an proxy example cfg.\n" 14 | "port 8125 # port to bind\n" 15 | "# backend server nodes\n" 16 | "node 127.0.0.1:8001\n"; 17 | struct cfg cfg; 18 | cfg.data = s; 19 | cfg.len = strlen(s); 20 | cfg.lineno = 1; 21 | 22 | assert(cfg_get(&cfg) == CFG_OK); 23 | assert(strncmp("port", cfg.key, cfg.key_len) == 0); 24 | assert(strncmp("8125", cfg.val, cfg.val_len) == 0); 25 | 26 | assert(cfg_get(&cfg) == CFG_OK); 27 | assert(strncmp("node", cfg.key, cfg.key_len) == 0); 28 | assert(strncmp("127.0.0.1:8001", cfg.val, cfg.val_len) == 0); 29 | } 30 | -------------------------------------------------------------------------------- /test/datetime_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "datetime.h" 8 | 9 | void case_datetime_stamp_now() { 10 | /* assert current timstamp must be greater than 1447502650543 11 | * (the stamp when I'm writing this test case) */ 12 | assert(datetime_stamp_now() > 1447502650543); 13 | /* assert that the code will run so fast the two getting results 14 | * diff should smaller than 1ms */ 15 | assert((long)datetime_stamp_now() == (long)datetime_stamp_now()); 16 | } 17 | -------------------------------------------------------------------------------- /test/dict_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "dict.h" 9 | 10 | void case_dict_set() { 11 | struct dict *dict = dict(); 12 | char *key = "key", *val = "val"; 13 | assert(dict_set(dict, key, val) == DICT_OK); 14 | assert(dict_len(dict) == 1); 15 | char *val_ = "val_"; 16 | assert(dict_get(dict, key) == val); 17 | assert(dict_set(dict, key, val_) == DICT_OK); 18 | assert(dict_get(dict, key) == val_); 19 | dict_free(dict); 20 | } 21 | 22 | void case_dict_get() { 23 | struct dict *dict = dict(); 24 | char *key = "key", *val = "val"; 25 | assert(dict_set(dict, key, val) == DICT_OK); 26 | assert(dict_len(dict) == 1); 27 | assert(dict_get(dict, key) == val); 28 | assert(dict_get(dict, "not exist") == NULL); 29 | dict_free(dict); 30 | } 31 | 32 | void case_dict_pop() { 33 | struct dict *dict = dict(); 34 | char *key = "key", *val = "val"; 35 | assert(dict_set(dict, key, val) == DICT_OK); 36 | assert(dict_len(dict) == 1); 37 | assert(dict_pop(dict, key) == val); 38 | assert(dict_len(dict) == 0); 39 | assert(dict_pop(dict, key) == NULL); 40 | assert(dict_len(dict) == 0); 41 | dict_free(dict); 42 | } 43 | 44 | void case_dict_has() { 45 | struct dict *dict = dict(); 46 | char *key = "key", *val = "val"; 47 | assert(dict_set(dict, key, val) == DICT_OK); 48 | assert(!dict_has(dict, "not exist")); 49 | assert(dict_has(dict, key)); 50 | dict_free(dict); 51 | } 52 | 53 | void case_dict_clear() { 54 | struct dict *dict = dict(); 55 | assert(dict_set(dict, "key1", "val1") == DICT_OK); 56 | assert(dict_set(dict, "key2", "val2") == DICT_OK); 57 | assert(dict_set(dict, "key3", "val3") == DICT_OK); 58 | assert(dict_set(dict, "key4", "val4") == DICT_OK); 59 | assert(dict_len(dict) == 4); 60 | dict_clear(dict); 61 | assert(dict_len(dict) == 0); 62 | dict_free(dict); 63 | } 64 | 65 | void case_dict_resize() { 66 | struct dict *dict = dict(); 67 | assert(dict_set(dict, "key1", "val1") == DICT_OK); 68 | assert(dict_set(dict, "key2", "val2") == DICT_OK); 69 | assert(dict_set(dict, "key3", "val3") == DICT_OK); 70 | assert(dict_set(dict, "key4", "val4") == DICT_OK); 71 | assert(dict_len(dict) == 4); 72 | assert(dict->idx == 0 && dict_cap(dict) == 7); 73 | assert(dict_set(dict, "key5", "val5") == DICT_OK); 74 | assert(dict_set(dict, "key6", "val6") == DICT_OK); 75 | assert(dict_len(dict) == 6); 76 | assert(dict->idx == 1 && dict_cap(dict) == 17); 77 | dict_free(dict); 78 | } 79 | 80 | void case_dict_iter() { 81 | struct dict *dict = dict(); 82 | char *key1 = "key1"; 83 | char *key2 = "key2"; 84 | char *key3 = "key3"; 85 | char *key4 = "key4"; 86 | char *key5 = "key5"; 87 | char *key6 = "key6"; 88 | assert(dict_set(dict, key1, "val1") == DICT_OK); 89 | assert(dict_set(dict, key2, "val2") == DICT_OK); 90 | assert(dict_set(dict, key3, "val3") == DICT_OK); 91 | assert(dict_set(dict, key4, "val4") == DICT_OK); 92 | assert(dict_set(dict, key5, "val5") == DICT_OK); 93 | assert(dict_set(dict, key6, "val6") == DICT_OK); 94 | 95 | struct dict_iter *iter = dict_iter(dict); 96 | 97 | assert(dict_iter_next(iter) != NULL); 98 | assert(dict_iter_next(iter) != NULL); 99 | assert(dict_iter_next(iter) != NULL); 100 | assert(dict_iter_next(iter) != NULL); 101 | assert(dict_iter_next(iter) != NULL); 102 | assert(dict_iter_next(iter) != NULL); 103 | assert(dict_iter_next(iter) == NULL); 104 | dict_iter_free(iter); 105 | 106 | struct dict_iter iter2 = {dict}; 107 | struct dict_node *node = NULL; 108 | dict_each(&iter2, node) { 109 | assert(node != NULL); 110 | assert(strlen((char *)node->val) == 4); 111 | }; 112 | dict_free(dict); 113 | } 114 | -------------------------------------------------------------------------------- /test/event_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "event.h" 14 | 15 | int fds[2]; 16 | 17 | void *write_data(void *data) { 18 | usleep(1000); 19 | char buf[256] = "abcdef"; 20 | assert(write(fds[1], buf, 6) == 6); 21 | return NULL; 22 | } 23 | 24 | void read_data(struct event_loop *loop, int fd, int mask, void *data) { 25 | char buf[256]; 26 | 27 | if (fd == fds[0]) { 28 | assert(read(fd, buf, 6) == 6); 29 | assert(strcmp(buf, "abcdef") == 0); 30 | event_loop_stop(loop); 31 | } 32 | } 33 | 34 | void case_event_simple() { 35 | assert(pipe(fds) == 0); 36 | 37 | struct event_loop *loop = event_loop_new(100); 38 | event_add_in(loop, fds[0], &read_data, NULL); 39 | 40 | pthread_t t; 41 | pthread_create(&t, NULL, &write_data, NULL); 42 | 43 | event_loop_start(loop); 44 | event_loop_free(loop); 45 | 46 | pthread_join(t, NULL); 47 | } 48 | -------------------------------------------------------------------------------- /test/heap_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "heap.h" 8 | 9 | int heap_cmp(void *a, void *b) { return *(int *)a - *(int *)b; } 10 | void case_heap_clear() { 11 | struct heap *heap = heap(heap_cmp); 12 | int a = 1; 13 | assert(heap_push(heap, (void *)&a) == HEAP_OK); 14 | assert(heap_len(heap) == 1); 15 | assert(heap_cap(heap) == 1); 16 | heap_clear(heap); 17 | assert(heap_len(heap) == 0); 18 | heap_free(heap); 19 | } 20 | 21 | void case_heap_len() { 22 | struct heap *heap = heap(heap_cmp); 23 | assert(heap_len(heap) == 0); 24 | int a = 1, b = 2, c = 3; 25 | assert(heap_push(heap, (void *)&a) == HEAP_OK); 26 | assert(heap_push(heap, (void *)&b) == HEAP_OK); 27 | assert(heap_push(heap, (void *)&c) == HEAP_OK); 28 | assert(heap_len(heap) == 3); 29 | heap_free(heap); 30 | } 31 | 32 | void case_heap_cap() { 33 | struct heap *heap = heap(heap_cmp); 34 | assert(heap_cap(heap) == 0); 35 | int a = 1, b = 2, c = 3; 36 | assert(heap_push(heap, (void *)&a) == HEAP_OK); 37 | assert(heap_cap(heap) == 1); 38 | assert(heap_push(heap, (void *)&b) == HEAP_OK); 39 | assert(heap_cap(heap) == 2); 40 | assert(heap_push(heap, (void *)&c) == HEAP_OK); 41 | assert(heap_cap(heap) == 4); 42 | heap_free(heap); 43 | } 44 | 45 | void case_heap_push() { 46 | struct heap *heap = heap(heap_cmp); 47 | int a = 3, b = 1, c = 2; 48 | assert(heap_push(heap, (void *)&a) == HEAP_OK); 49 | assert(heap_push(heap, (void *)&b) == HEAP_OK); 50 | assert(heap_push(heap, (void *)&c) == HEAP_OK); 51 | assert(heap_len(heap) == 3); 52 | assert(*(int *)heap_pop(heap) == 1); 53 | assert(*(int *)heap_pop(heap) == 2); 54 | assert(*(int *)heap_pop(heap) == 3); 55 | heap_free(heap); 56 | } 57 | 58 | void case_heap_pop() { 59 | struct heap *heap = heap(heap_cmp); 60 | int a = 3, b = 1, c = 2, d = 4; 61 | assert(heap_push(heap, (void *)&a) == HEAP_OK); 62 | assert(heap_push(heap, (void *)&b) == HEAP_OK); 63 | assert(heap_push(heap, (void *)&c) == HEAP_OK); 64 | assert(heap_push(heap, (void *)&d) == HEAP_OK); 65 | assert(heap_len(heap) == 4); 66 | assert(*(int *)heap_pop(heap) == 1); 67 | assert(*(int *)heap_pop(heap) == 2); 68 | assert(*(int *)heap_pop(heap) == 3); 69 | assert(*(int *)heap_pop(heap) == 4); 70 | assert(heap_len(heap) == 0); 71 | heap_free(heap); 72 | } 73 | 74 | void case_heap_top() { 75 | struct heap *heap = heap(heap_cmp); 76 | int a = 3, b = 2, c = 4, d = 1; 77 | assert(heap_push(heap, (void *)&a) == HEAP_OK); 78 | assert(*(int *)(heap_top(heap)) == 3); 79 | assert(heap_push(heap, (void *)&b) == HEAP_OK); 80 | assert(*(int *)(heap_top(heap)) == 2); 81 | assert(heap_push(heap, (void *)&c) == HEAP_OK); 82 | assert(*(int *)(heap_top(heap)) == 2); 83 | assert(heap_push(heap, (void *)&d) == HEAP_OK); 84 | assert(*(int *)(heap_top(heap)) == 1); 85 | heap_free(heap); 86 | } 87 | 88 | void case_heap_pushpop() { 89 | struct heap *heap = heap(heap_cmp); 90 | int a = 3, b = 2, c = 4; 91 | assert(*(int *)heap_pushpop(heap, (void *)&a) == 3); 92 | assert(heap_push(heap, (void *)&b) == HEAP_OK); 93 | assert(heap_push(heap, (void *)&c) == HEAP_OK); 94 | assert(*(int *)heap_pushpop(heap, (void *)&a) == 2); 95 | heap_free(heap); 96 | } 97 | 98 | void case_heap_del() { 99 | struct heap *heap = heap(heap_cmp); 100 | int a = 3, b = 2, c = 5, d = 7, e = 4, f = 6, g = 1, h = 4; 101 | assert(heap_push(heap, (void *)&a) == HEAP_OK); 102 | assert(heap_push(heap, (void *)&b) == HEAP_OK); 103 | assert(heap_push(heap, (void *)&c) == HEAP_OK); 104 | assert(heap_push(heap, (void *)&d) == HEAP_OK); 105 | assert(heap_push(heap, (void *)&e) == HEAP_OK); 106 | assert(heap_push(heap, (void *)&f) == HEAP_OK); 107 | assert(heap_push(heap, (void *)&g) == HEAP_OK); 108 | assert(4 == *(int *)heap_del(heap, 4)); /* this assert means nothing */ 109 | assert(heap_len(heap) == 6); 110 | assert(1 == *(int *)heap_pop(heap)); 111 | assert(2 == *(int *)heap_pop(heap)); 112 | assert(3 == *(int *)heap_pop(heap)); 113 | assert(heap_push(heap, (void *)&h) == HEAP_OK); 114 | assert(4 == *(int *)heap_pop(heap)); 115 | assert(5 == *(int *)heap_pop(heap)); 116 | assert(6 == *(int *)heap_pop(heap)); 117 | assert(7 == *(int *)heap_pop(heap)); 118 | heap_free(heap); 119 | } 120 | 121 | void case_heap_repalce() { 122 | struct heap *heap = heap(heap_cmp); 123 | int a = 3, b = 2, c = 5, d = 7, e = 4, f = 6, g = 1, h = 4; 124 | assert(heap_push(heap, (void *)&a) == HEAP_OK); 125 | assert(heap_push(heap, (void *)&b) == HEAP_OK); 126 | assert(heap_push(heap, (void *)&c) == HEAP_OK); 127 | assert(heap_push(heap, (void *)&d) == HEAP_OK); 128 | assert(heap_push(heap, (void *)&e) == HEAP_OK); 129 | assert(heap_push(heap, (void *)&f) == HEAP_OK); 130 | assert(heap_push(heap, (void *)&g) == HEAP_OK); 131 | assert(1 == *(int *)heap_replace(heap, (void *)&h)); 132 | assert(2 == *(int *)heap_pop(heap)); 133 | assert(3 == *(int *)heap_pop(heap)); 134 | assert(4 == *(int *)heap_pop(heap)); 135 | heap_free(heap); 136 | } 137 | -------------------------------------------------------------------------------- /test/ketama_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ketama.h" 11 | #include "strings.h" 12 | 13 | void case_ketama_balance() { 14 | struct ketama_node nodes[13] = { 15 | {"127.0.0.1:8000", 1}, 16 | {"127.0.0.1:8001", 1}, 17 | {"127.0.0.1:8002", 1}, 18 | {"127.0.0.1:8003", 1}, 19 | {"127.0.0.1:8004", 1}, 20 | {"127.0.0.1:8005", 1}, 21 | {"127.0.0.1:8006", 1}, 22 | {"127.0.0.1:8007", 1}, 23 | {"127.0.0.1:8008", 1}, 24 | {"127.0.0.1:8009", 1}, 25 | {"127.0.0.1:8010", 1}, 26 | {"127.0.0.1:8011", 1}, 27 | {"127.0.0.1:8012", 1}, 28 | }; 29 | struct ketama_ring *ring = ketama_ring_new(nodes, 13); 30 | assert(ring->nodes != NULL); 31 | assert(ring->len == 13 * 160); 32 | 33 | int i; 34 | for (i = 0; i < 2600; i++) { 35 | char key[11]; 36 | strings_rand(key, 10); 37 | struct ketama_node *node = ketama_node_get(ring, key); 38 | nodes[node->idx].idata += 1; 39 | } 40 | 41 | for (i = 0; i < 13; i++) 42 | assert(nodes[i].idata > 150 && nodes[i].idata < 300); 43 | 44 | ketama_ring_free(ring); 45 | } 46 | 47 | void case_ketama_consistence() { 48 | /* consistence testing */ 49 | struct ketama_node nodes[5] = { 50 | {"192.168.0.1:9527", 1}, 51 | {"192.168.0.2:9527", 1}, 52 | {"192.168.0.3:9527", 2}, 53 | {"192.168.0.4:9527", 2}, 54 | {"192.168.0.5:9527", 4}, 55 | }; 56 | struct ketama_ring *ring = ketama_ring_new(nodes, 5); 57 | assert(ring->len == (1 + 1 + 2 + 2 + 4) * 160); 58 | assert(ring->nodes != NULL); 59 | 60 | char key[11]; 61 | srand(time(NULL)); 62 | strings_rand(key, 10); 63 | struct ketama_node *node = ketama_node_get(ring, key); 64 | int i; 65 | for (i = 0; i < 1000; i++) { 66 | assert(strcmp(node->key, ketama_node_get(ring, key)->key) == 0); 67 | } 68 | 69 | ketama_ring_free(ring); 70 | } 71 | -------------------------------------------------------------------------------- /test/list_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "list.h" 8 | 9 | void case_list_clear() { 10 | struct list *list = list(); 11 | char *s1 = "s1", *s2 = "s2", *s3 = "s3"; 12 | assert(list_lpush(list, s1) == LIST_OK); 13 | assert(list_lpush(list, s2) == LIST_OK); 14 | assert(list_lpush(list, s3) == LIST_OK); 15 | assert(list_len(list) == 3); 16 | list_clear(list); 17 | assert(list_len(list) == 0); 18 | assert(list->head == NULL); 19 | assert(list->tail == NULL); 20 | list_free(list); 21 | } 22 | 23 | void case_list_lpush() { 24 | struct list *list = list(); 25 | char *s1 = "s1", *s2 = "s2", *s3 = "s3"; 26 | assert(list_lpush(list, s1) == LIST_OK); 27 | assert(list->head->data == s1); 28 | assert(list->tail->data == s1); 29 | assert(list_len(list) == 1); 30 | assert(list_lpush(list, s2) == LIST_OK); 31 | assert(list->head->data == s2); 32 | assert(list->tail->data == s1); 33 | assert(list_len(list) == 2); 34 | assert(list_lpush(list, s3) == LIST_OK); 35 | assert(list->head->data == s3); 36 | assert(list->tail->data == s1); 37 | assert(list_len(list) == 3); 38 | list_free(list); 39 | } 40 | 41 | void case_list_rpush() { 42 | struct list *list = list(); 43 | char *s1 = "s1", *s2 = "s2", *s3 = "s3"; 44 | assert(list_rpush(list, s1) == LIST_OK); 45 | assert(list->head->data == s1); 46 | assert(list->tail->data == s1); 47 | assert(list_len(list) == 1); 48 | assert(list_rpush(list, s2) == LIST_OK); 49 | assert(list->head->data == s1); 50 | assert(list->tail->data == s2); 51 | assert(list_len(list) == 2); 52 | assert(list_rpush(list, s3) == LIST_OK); 53 | assert(list->head->data == s1); 54 | assert(list->tail->data == s3); 55 | assert(list_len(list) == 3); 56 | list_free(list); 57 | } 58 | 59 | void case_list_lpop() { 60 | struct list *list = list(); 61 | char *s1 = "s1", *s2 = "s2", *s3 = "s3"; 62 | assert(list_rpush(list, s1) == LIST_OK); 63 | assert(list_rpush(list, s2) == LIST_OK); 64 | assert(list_rpush(list, s3) == LIST_OK); 65 | assert(list_len(list) == 3); 66 | assert(list->head->data == s1); 67 | assert(list_lpop(list) == s1); 68 | assert(list_len(list) == 2); 69 | assert(list->head->data == s2); 70 | assert(list_lpop(list) == s2); 71 | assert(list_len(list) == 1); 72 | assert(list->head->data == s3); 73 | assert(list_lpop(list) == s3); 74 | assert(list_len(list) == 0); 75 | assert(list->head == NULL); 76 | list_free(list); 77 | } 78 | 79 | void case_list_rpop() { 80 | struct list *list = list(); 81 | char *s1 = "s1", *s2 = "s2", *s3 = "s3"; 82 | assert(list_lpush(list, s1) == LIST_OK); 83 | assert(list_lpush(list, s2) == LIST_OK); 84 | assert(list_lpush(list, s3) == LIST_OK); 85 | assert(list_len(list) == 3); 86 | assert(list->tail->data == s1); 87 | assert(list_rpop(list) == s1); 88 | assert(list_len(list) == 2); 89 | assert(list->tail->data == s2); 90 | assert(list_rpop(list) == s2); 91 | assert(list_len(list) == 1); 92 | assert(list->tail->data == s3); 93 | assert(list_rpop(list) == s3); 94 | assert(list_len(list) == 0); 95 | assert(list->tail == NULL); 96 | list_free(list); 97 | } 98 | 99 | void case_list_iter() { 100 | struct list *list = list(); 101 | char *s1 = "s1", *s2 = "s2", *s3 = "s3"; 102 | list_push(list, s1); 103 | list_push(list, s2); 104 | list_push(list, s3); 105 | struct list_iter *iter = list_iter(list); 106 | assert(iter->list == list); 107 | assert(iter->node == list->head); 108 | assert(list_iter_next(iter) == s1); 109 | assert(list_iter_next(iter) == s2); 110 | assert(list_iter_next(iter) == s3); 111 | list_iter_seek_tail(iter); 112 | assert(list_iter_prev(iter) == s3); 113 | assert(list_iter_prev(iter) == s2); 114 | assert(list_iter_prev(iter) == s1); 115 | list_iter_free(iter); 116 | int i = 0; 117 | struct list_node *node; 118 | list_each(list, node) { 119 | char *s = node->data; 120 | assert(s[1] - 49 == i); 121 | i += 1; 122 | } 123 | assert(list_len(list) == i); 124 | list_free(list); 125 | } 126 | -------------------------------------------------------------------------------- /test/log_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "log.h" 8 | 9 | void case_log_open_close() { 10 | assert(log_open("test", NULL, 0) == LOG_OK); 11 | assert(log_open("test", "test.log", 0) == LOG_OK); 12 | assert(log_open("test", "test.log", 0) == LOG_OK); 13 | assert(log_open("test", "test.log", 1024) == LOG_OK); 14 | log_close(); 15 | } 16 | 17 | void case_log_log() { 18 | assert(log_open("test", "test.log", 0) == LOG_OK); 19 | log_debug("test message debug"); 20 | log_info("test message info"); 21 | log_warn("test message warn"); 22 | log_error("test message error"); 23 | log_critical("test message critical"); 24 | log_close(); 25 | } 26 | 27 | void case_log_rotate() { 28 | assert(log_open("test", "test.log", 1024) == LOG_OK); 29 | int i; 30 | 31 | for (i = 0; i < 10; i++) log_info("test message"); 32 | } 33 | -------------------------------------------------------------------------------- /test/map_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "map.h" 9 | #include "strings.h" 10 | 11 | void case_map_set() { 12 | struct map *m = map(); 13 | char *key = "key", *val = "val"; 14 | assert(map_set(m, key, val) == MAP_OK); 15 | assert(map_len(m) == 1); 16 | assert(map_get(m, key) == val); 17 | /* set multiple times */ 18 | int i; 19 | for (i = 0; i < 10000; i++) { 20 | char key[11]; 21 | char val[21]; 22 | strings_rand(key, 10); 23 | strings_rand(val, 20); 24 | assert(map_set(m, key, val) == MAP_OK); 25 | assert(map_get(m, key) == val); 26 | } 27 | map_free(m); 28 | } 29 | 30 | void case_map_get() { 31 | struct map *m = map(); 32 | char *key = "key", *val = "val"; 33 | assert(map_set(m, key, val) == MAP_OK); 34 | assert(map_len(m) == 1); 35 | assert(map_get(m, key) == val); 36 | assert(map_get(m, "not exist") == NULL); 37 | map_free(m); 38 | } 39 | 40 | void case_map_pop() { 41 | struct map *m = map(); 42 | char *key = "key", *val = "val"; 43 | assert(map_set(m, key, val) == MAP_OK); 44 | assert(map_len(m) == 1); 45 | assert(map_pop(m, key) == val); 46 | assert(map_len(m) == 0); 47 | assert(map_pop(m, "not-exist") == NULL); 48 | map_free(m); 49 | } 50 | 51 | void case_map_has() { 52 | struct map *m = map(); 53 | char *key = "key", *val = "val"; 54 | assert(map_set(m, key, val) == MAP_OK); 55 | assert(!map_has(m, "not exist")); 56 | assert(map_has(m, key)); 57 | map_free(m); 58 | } 59 | 60 | void case_map_clear() { 61 | struct map *m = map(); 62 | assert(map_set(m, "key1", "val1") == MAP_OK); 63 | assert(map_set(m, "key2", "val2") == MAP_OK); 64 | assert(map_set(m, "key3", "val3") == MAP_OK); 65 | assert(map_set(m, "key4", "val4") == MAP_OK); 66 | assert(map_set(m, "key5", "val5") == MAP_OK); 67 | assert(map_len(m) == 5); 68 | map_clear(m); 69 | assert(map_len(m) == 0); 70 | map_free(m); 71 | } 72 | 73 | void case_map_iter() { 74 | struct map *m = map(); 75 | char *key1 = "key1"; 76 | char *key2 = "key2"; 77 | char *key3 = "key3"; 78 | char *key4 = "key4"; 79 | char *key5 = "key5"; 80 | char *key6 = "key6"; 81 | assert(map_set(m, key1, "val1") == MAP_OK); 82 | assert(map_set(m, key2, "val2") == MAP_OK); 83 | assert(map_set(m, key3, "val3") == MAP_OK); 84 | assert(map_set(m, key4, "val4") == MAP_OK); 85 | assert(map_set(m, key5, "val5") == MAP_OK); 86 | assert(map_set(m, key6, "val6") == MAP_OK); 87 | struct map_iter *iter = map_iter(m); 88 | assert(map_iter_next(iter) != NULL); 89 | assert(map_iter_next(iter) != NULL); 90 | assert(map_iter_next(iter) != NULL); 91 | assert(map_iter_next(iter) != NULL); 92 | assert(map_iter_next(iter) != NULL); 93 | assert(map_iter_next(iter) != NULL); 94 | assert(map_iter_next(iter) == NULL); 95 | map_iter_free(iter); 96 | map_free(m); 97 | } 98 | -------------------------------------------------------------------------------- /test/queue_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "queue.h" 8 | 9 | void case_queue_clear() { 10 | struct queue *queue = queue(); 11 | queue_push(queue, "a"); 12 | queue_push(queue, "a"); 13 | queue_clear(queue); 14 | assert(queue->head == NULL); 15 | assert(queue->tail == NULL); 16 | assert(queue_len(queue) == 0); 17 | queue_free(queue); 18 | } 19 | 20 | void case_queue_push_pop_top() { 21 | struct queue *queue = queue(); 22 | char *s1 = "a", *s2 = "b", *s3 = "c"; 23 | assert(queue_push(queue, s1) == QUEUE_OK); 24 | assert(queue_push(queue, s2) == QUEUE_OK); 25 | assert(queue_push(queue, s3) == QUEUE_OK); 26 | assert(queue_len(queue) == 3); 27 | assert(queue_top(queue) == s1); 28 | assert(queue_pop(queue) == s1); 29 | assert(queue_top(queue) == s2); 30 | assert(queue_pop(queue) == s2); 31 | assert(queue_top(queue) == s3); 32 | assert(queue_pop(queue) == s3); 33 | assert(queue_len(queue) == 0); 34 | queue_free(queue); 35 | } 36 | -------------------------------------------------------------------------------- /test/skiplist_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "skiplist.h" 8 | 9 | void case_skiplist_base() { 10 | struct skiplist *skiplist = skiplist(NULL); 11 | assert(skiplist != NULL); 12 | assert(skiplist->len == 0); 13 | assert(skiplist->head != NULL); 14 | assert(skiplist->tail == skiplist->head); 15 | assert(skiplist->level == 1); 16 | assert(skiplist_push(skiplist, 2, NULL) == SKIPLIST_OK); 17 | assert(skiplist_push(skiplist, 1, NULL) == SKIPLIST_OK); 18 | assert(skiplist_push(skiplist, 3, NULL) == SKIPLIST_OK); 19 | assert(skiplist_len(skiplist) == 3); 20 | assert(skiplist_level(skiplist) <= SKIPLIST_LEVEL_MAX); 21 | assert(skiplist->tail->score == 3); 22 | skiplist_clear(skiplist); 23 | assert(skiplist_level(skiplist) == 1); 24 | assert(skiplist_len(skiplist) == 0); 25 | assert(skiplist->head != NULL); 26 | assert(skiplist->tail == skiplist->head); 27 | skiplist_free(skiplist); 28 | } 29 | 30 | void case_skiplist_push() { 31 | struct skiplist *skiplist = skiplist(NULL); 32 | struct skiplist_iter *iter = skiplist_iter(skiplist); 33 | assert(skiplist != NULL && iter != NULL); 34 | assert(skiplist_push(skiplist, 3, NULL) == SKIPLIST_OK); 35 | assert(skiplist_push(skiplist, 9, NULL) == SKIPLIST_OK); 36 | assert(skiplist_push(skiplist, 7, NULL) == SKIPLIST_OK); 37 | assert(skiplist_push(skiplist, 2, NULL) == SKIPLIST_OK); 38 | assert(skiplist_push(skiplist, 6, NULL) == SKIPLIST_OK); 39 | assert(skiplist_push(skiplist, 5, NULL) == SKIPLIST_OK); 40 | assert(skiplist_push(skiplist, 8, NULL) == SKIPLIST_OK); 41 | assert(skiplist_push(skiplist, 1, NULL) == SKIPLIST_OK); 42 | assert(skiplist_push(skiplist, 4, NULL) == SKIPLIST_OK); 43 | assert(skiplist_len(skiplist) == 9); 44 | assert(skiplist_level(skiplist) > 0); 45 | assert(skiplist_iter_next(iter)->score == 1); 46 | assert(skiplist_iter_next(iter)->score == 2); 47 | assert(skiplist_iter_next(iter)->score == 3); 48 | assert(skiplist_iter_next(iter)->score == 4); 49 | assert(skiplist_iter_next(iter)->score == 5); 50 | assert(skiplist_iter_next(iter)->score == 6); 51 | assert(skiplist_iter_next(iter)->score == 7); 52 | assert(skiplist_iter_next(iter)->score == 8); 53 | assert(skiplist_iter_next(iter)->score == 9); 54 | assert(skiplist_first(skiplist)->score == 1); 55 | assert(skiplist_last(skiplist)->score == 9); 56 | skiplist_iter_rewind(iter); 57 | struct skiplist_node *first = skiplist_iter_next(iter); 58 | struct skiplist_node *second = skiplist_iter_next(iter); 59 | struct skiplist_node *last = skiplist_last(skiplist); 60 | assert(skiplist->head->forwards[0] == first); 61 | assert(first != skiplist->head); 62 | assert(first->forwards[0] == second); 63 | assert(second->backward == first); 64 | assert(first->backward == skiplist->head); 65 | assert(last->backward != NULL); 66 | assert(last->forwards[0] == NULL); 67 | skiplist_iter_free(iter); 68 | skiplist_free(skiplist); 69 | } 70 | 71 | void case_skiplist_pop() { 72 | struct skiplist *skiplist = skiplist(NULL); 73 | struct skiplist_iter *iter = skiplist_iter(skiplist); 74 | assert(skiplist != NULL && iter != NULL); 75 | int v1 = 1, v2 = 2, v3 = 3, v4 = 4, v5 = 5, v6 = 6, v7 = 7, v8 = 8, v9 = 9; 76 | assert(skiplist_push(skiplist, 3, (void *)(&v3)) == SKIPLIST_OK); 77 | assert(skiplist_push(skiplist, 9, (void *)(&v9)) == SKIPLIST_OK); 78 | assert(skiplist_push(skiplist, 7, (void *)(&v7)) == SKIPLIST_OK); 79 | assert(skiplist_push(skiplist, 2, (void *)(&v2)) == SKIPLIST_OK); 80 | assert(skiplist_push(skiplist, 6, (void *)(&v6)) == SKIPLIST_OK); 81 | assert(skiplist_push(skiplist, 5, (void *)(&v5)) == SKIPLIST_OK); 82 | assert(skiplist_push(skiplist, 8, (void *)(&v8)) == SKIPLIST_OK); 83 | assert(skiplist_push(skiplist, 1, (void *)(&v1)) == SKIPLIST_OK); 84 | assert(skiplist_push(skiplist, 4, (void *)(&v4)) == SKIPLIST_OK); 85 | assert(skiplist_len(skiplist) == 9); 86 | assert(skiplist_level(skiplist) > 0); 87 | assert(skiplist_last(skiplist)->score == 9); 88 | assert(skiplist_pop(skiplist, 3) == &v3); 89 | assert(skiplist_pop(skiplist, 4) == &v4); 90 | assert(skiplist_pop(skiplist, 9) == &v9); 91 | assert(skiplist_pop(skiplist, 1) == &v1); 92 | assert(skiplist_len(skiplist) == 5); 93 | assert(skiplist_first(skiplist)->score == 2); 94 | assert(skiplist->head->forwards[skiplist->level - 1] != NULL); 95 | skiplist_iter_free(iter); 96 | skiplist_free(skiplist); 97 | } 98 | 99 | void case_skiplist_popfirst() { 100 | struct skiplist *skiplist = skiplist(NULL); 101 | struct skiplist_iter *iter = skiplist_iter(skiplist); 102 | assert(skiplist != NULL && iter != NULL); 103 | int v1 = 1, v2 = 2, v3 = 3, v4 = 4, v5 = 5, v6 = 6, v7 = 7, v8 = 8, v9 = 9; 104 | assert(skiplist_push(skiplist, 3, (void *)(&v3)) == SKIPLIST_OK); 105 | assert(skiplist_push(skiplist, 9, (void *)(&v9)) == SKIPLIST_OK); 106 | assert(skiplist_push(skiplist, 7, (void *)(&v7)) == SKIPLIST_OK); 107 | assert(skiplist_push(skiplist, 2, (void *)(&v2)) == SKIPLIST_OK); 108 | assert(skiplist_push(skiplist, 6, (void *)(&v6)) == SKIPLIST_OK); 109 | assert(skiplist_push(skiplist, 5, (void *)(&v5)) == SKIPLIST_OK); 110 | assert(skiplist_push(skiplist, 8, (void *)(&v8)) == SKIPLIST_OK); 111 | assert(skiplist_push(skiplist, 1, (void *)(&v1)) == SKIPLIST_OK); 112 | assert(skiplist_push(skiplist, 4, (void *)(&v4)) == SKIPLIST_OK); 113 | assert(skiplist_len(skiplist) == 9); 114 | assert(skiplist_level(skiplist) > 0); 115 | assert(skiplist_popfirst(skiplist) == &v1); 116 | assert(skiplist_popfirst(skiplist) == &v2); 117 | assert(skiplist_popfirst(skiplist) == &v3); 118 | assert(skiplist_popfirst(skiplist) == &v4); 119 | assert(skiplist_len(skiplist) == 5); 120 | assert(skiplist_level(skiplist) > 0); 121 | assert(skiplist->head->forwards[skiplist->level - 1] != NULL); 122 | struct skiplist_node *first = skiplist_iter_next(iter); 123 | struct skiplist_node *second = skiplist_iter_next(iter); 124 | assert(first->score == 5); 125 | assert(second->score == 6); 126 | assert(first->backward == skiplist->head); 127 | assert(first->forwards[0] == second); 128 | assert(second->backward == first); 129 | assert(skiplist->tail != first && skiplist->tail != second); 130 | assert(skiplist_popfirst(skiplist) == &v5); 131 | assert(skiplist_popfirst(skiplist) == &v6); 132 | assert(skiplist_popfirst(skiplist) == &v7); 133 | assert(skiplist_popfirst(skiplist) == &v8); 134 | assert(skiplist_last(skiplist)->score == 9); 135 | assert(skiplist_popfirst(skiplist) == &v9); 136 | assert(skiplist_last(skiplist) == NULL); 137 | assert(skiplist_level(skiplist) == 1); 138 | assert(skiplist_len(skiplist) == 0); 139 | skiplist_iter_free(iter); 140 | skiplist_free(skiplist); 141 | } 142 | 143 | void case_skiplist_poplast() { 144 | struct skiplist *skiplist = skiplist(NULL); 145 | struct skiplist_iter *iter = skiplist_iter(skiplist); 146 | assert(skiplist != NULL && iter != NULL); 147 | int v1 = 1, v2 = 2, v3 = 3, v4 = 4, v5 = 5, v6 = 6, v7 = 7, v8 = 8, v9 = 9; 148 | assert(skiplist_push(skiplist, 3, (void *)(&v3)) == SKIPLIST_OK); 149 | assert(skiplist_push(skiplist, 9, (void *)(&v9)) == SKIPLIST_OK); 150 | assert(skiplist_push(skiplist, 7, (void *)(&v7)) == SKIPLIST_OK); 151 | assert(skiplist_push(skiplist, 2, (void *)(&v2)) == SKIPLIST_OK); 152 | assert(skiplist_push(skiplist, 6, (void *)(&v6)) == SKIPLIST_OK); 153 | assert(skiplist_push(skiplist, 5, (void *)(&v5)) == SKIPLIST_OK); 154 | assert(skiplist_push(skiplist, 8, (void *)(&v8)) == SKIPLIST_OK); 155 | assert(skiplist_push(skiplist, 1, (void *)(&v1)) == SKIPLIST_OK); 156 | assert(skiplist_push(skiplist, 4, (void *)(&v4)) == SKIPLIST_OK); 157 | assert(skiplist_len(skiplist) == 9); 158 | assert(skiplist_level(skiplist) > 0); 159 | assert(skiplist_poplast(skiplist) == &v9); 160 | assert(skiplist_poplast(skiplist) == &v8); 161 | assert(skiplist_poplast(skiplist) == &v7); 162 | assert(skiplist_poplast(skiplist) == &v6); 163 | assert(skiplist_poplast(skiplist) == &v5); 164 | assert(skiplist_last(skiplist)->score == 4); 165 | assert(skiplist_first(skiplist)->score == 1); 166 | assert(skiplist->tail->score == 4); 167 | assert(skiplist->tail->backward->score == 3); 168 | assert(skiplist->tail->backward->backward->score == 2); 169 | assert(skiplist->head->forwards[skiplist->level - 1] != NULL); 170 | skiplist_iter_free(iter); 171 | skiplist_free(skiplist); 172 | } 173 | 174 | void case_skiplist_get() { 175 | struct skiplist *skiplist = skiplist(NULL); 176 | int v1 = 1, v2 = 2, v3 = 3, v4 = 4, v5 = 5, v6 = 6, v7 = 7, v8 = 8, v9 = 9; 177 | assert(skiplist_push(skiplist, 3, (void *)(&v3)) == SKIPLIST_OK); 178 | assert(skiplist_push(skiplist, 9, (void *)(&v9)) == SKIPLIST_OK); 179 | assert(skiplist_push(skiplist, 7, (void *)(&v7)) == SKIPLIST_OK); 180 | assert(skiplist_push(skiplist, 2, (void *)(&v2)) == SKIPLIST_OK); 181 | assert(skiplist_push(skiplist, 6, (void *)(&v6)) == SKIPLIST_OK); 182 | assert(skiplist_push(skiplist, 5, (void *)(&v5)) == SKIPLIST_OK); 183 | assert(skiplist_push(skiplist, 8, (void *)(&v8)) == SKIPLIST_OK); 184 | assert(skiplist_push(skiplist, 1, (void *)(&v1)) == SKIPLIST_OK); 185 | assert(skiplist_push(skiplist, 4, (void *)(&v4)) == SKIPLIST_OK); 186 | assert(skiplist_len(skiplist) == 9); 187 | assert(skiplist_get(skiplist, 8) == &v8); 188 | assert(skiplist_get(skiplist, 9) == &v9); 189 | assert(skiplist_get(skiplist, 1) == &v1); 190 | assert(skiplist_get(skiplist, 2) == &v2); 191 | assert(skiplist_get(skiplist, 10) == NULL); 192 | assert(skiplist_get(skiplist, 9999) == NULL); 193 | skiplist_free(skiplist); 194 | } 195 | -------------------------------------------------------------------------------- /test/stack_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | 7 | #include "stack.h" 8 | 9 | void case_stack_clear() { 10 | struct stack *stack = stack(5); 11 | stack_push(stack, "hello"); 12 | stack_clear(stack); 13 | assert(stack->data == NULL); 14 | assert(stack_len(stack) == 0); 15 | assert(stack_cap(stack) == 0); 16 | stack_free(stack); 17 | } 18 | 19 | void case_stack_push() { 20 | struct stack *stack = stack(1); 21 | char *s1 = "a", *s2 = "b"; 22 | assert(stack_push(stack, s1) == STACK_OK); 23 | assert(stack_push(stack, s2) == STACK_OK); 24 | assert(stack_len(stack) == 2); 25 | assert(stack_cap(stack) == 2); 26 | assert(stack_pop(stack) == s2); 27 | assert(stack_len(stack) == 1); 28 | assert(stack_cap(stack) == 2); 29 | assert(stack_pop(stack) == s1); 30 | stack_free(stack); 31 | } 32 | 33 | void case_stack_pop() { 34 | struct stack *stack = stack(3); 35 | char *s1 = "a", *s2 = "b"; 36 | assert(stack_push(stack, s1) == STACK_OK); 37 | assert(stack_push(stack, s2) == STACK_OK); 38 | assert(stack_len(stack) == 2); 39 | assert(stack_cap(stack) == 3); 40 | assert(stack_pop(stack) == s2); 41 | assert(stack_pop(stack) == s1); 42 | assert(stack_len(stack) == 0); 43 | assert(stack_cap(stack) == 3); 44 | stack_free(stack); 45 | } 46 | 47 | void case_stack_top() { 48 | struct stack *stack = stack(3); 49 | char *s1 = "a", *s2 = "b", *s3 = "c"; 50 | assert(stack_push(stack, s1) == STACK_OK); 51 | assert(stack_top(stack) == s1); 52 | assert(stack_push(stack, s2) == STACK_OK); 53 | assert(stack_top(stack) == s2); 54 | assert(stack_push(stack, s3) == STACK_OK); 55 | assert(stack_top(stack) == s3); 56 | assert(stack_len(stack) == 3); 57 | assert(stack_cap(stack) == 3); 58 | stack_free(stack); 59 | } 60 | -------------------------------------------------------------------------------- /test/strings_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "strings.h" 9 | 10 | void case_strings_search() { 11 | char s1[] = "this is a simple example"; 12 | assert(strings_search(s1, "this", 0) == 0); 13 | assert(strings_search(s1, "is", 0) == 2); 14 | assert(strings_search(s1, "is", 3) == 5); 15 | assert(strings_search(s1, "mp", 0) == 12); 16 | assert(strings_search(s1, "mp", 13) == 20); 17 | assert(strings_search(s1, "not exist", 0) == strlen(s1)); 18 | 19 | char s2[] = "这是中文是的"; 20 | assert(strings_search(s2, "这是", 0) == 0); 21 | assert(strings_search(s2, "中文", 0) == 6); 22 | assert(strings_search(s2, "是", 0) == 3); 23 | assert(strings_search(s2, "是", 6) == 12); 24 | } 25 | 26 | void case_strings_rand() { 27 | /* we are going to assert n times random strings (with length 16) 28 | * are not the same with each other */ 29 | int n = 100; 30 | char strings[n][17]; /* because 2d array are continuously on 31 | memory, so we use 16+1 */ 32 | int i, j; 33 | 34 | for (i = 0; i < n; i++) { 35 | strings_rand(strings[i], 16); 36 | for (j = 0; j < i; j++) assert(strcmp(strings[i], strings[j]) != 0); 37 | } 38 | } 39 | 40 | void case_strings_replace() { 41 | char *src = "abcdbcefghbcio"; 42 | char *sub = "bc"; 43 | char *rep = "xyz"; 44 | char dst[30] = {0}; 45 | assert(strings_replace(dst, src, sub, rep) == dst); 46 | assert(strcmp(dst, "axyzdxyzefghxyzio") == 0); 47 | } 48 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #ifdef __linux 6 | #include 7 | #endif 8 | 9 | #include "test.h" 10 | 11 | /** 12 | * buf_test 13 | */ 14 | void case_buf_clear(); 15 | void case_buf_put(); 16 | void case_buf_puts(); 17 | void case_buf_putc(); 18 | void case_buf_str(); 19 | void case_buf_isempty(); 20 | void case_buf_sprintf(); 21 | void case_buf_lrm(); 22 | void case_buf_len(); 23 | void case_buf_cap(); 24 | static struct test_case buf_test_cases[] = { 25 | {"buf_clear", &case_buf_clear}, 26 | {"buf_put", &case_buf_put}, 27 | {"buf_puts", &case_buf_puts}, 28 | {"buf_putc", &case_buf_putc}, 29 | {"buf_str", &case_buf_str}, 30 | {"buf_isempty", &case_buf_isempty}, 31 | {"buf_sprintf", &case_buf_sprintf}, 32 | {"buf_lrm", &case_buf_lrm}, 33 | {"buf_len", &case_buf_len}, 34 | {"buf_cap", &case_buf_cap}, 35 | {NULL, NULL}, 36 | }; 37 | 38 | /** 39 | * cfg_test 40 | */ 41 | void case_cfg_get(); 42 | static struct test_case cfg_test_cases[] = { 43 | {"cfg_get", &case_cfg_get}, {NULL, NULL}, 44 | }; 45 | 46 | /** 47 | * datetime_test 48 | */ 49 | void case_datetime_stamp_now(); 50 | static struct test_case datetime_test_cases[] = { 51 | {"datetime_stamp_now", &case_datetime_stamp_now}, {NULL, NULL}, 52 | }; 53 | 54 | /** 55 | * dict_test 56 | */ 57 | void case_dict_set(); 58 | void case_dict_get(); 59 | void case_dict_pop(); 60 | void case_dict_has(); 61 | void case_dict_clear(); 62 | void case_dict_resize(); 63 | void case_dict_iter(); 64 | static struct test_case dict_test_cases[] = { 65 | {"dict_set", &case_dict_set}, 66 | {"dict_get", &case_dict_get}, 67 | {"dict_pop", &case_dict_pop}, 68 | {"dict_has", &case_dict_has}, 69 | {"dict_clear", &case_dict_clear}, 70 | {"dict_resize", &case_dict_resize}, 71 | {"dict_iter", &case_dict_iter}, 72 | {NULL, NULL}, 73 | }; 74 | 75 | /** 76 | * event_test 77 | */ 78 | void case_event_simple(); 79 | static struct test_case event_test_cases[] = { 80 | {"event_simple", &case_event_simple}, {NULL, NULL}, 81 | }; 82 | 83 | /** 84 | * heap_test 85 | */ 86 | void case_heap_clear(); 87 | void case_heap_len(); 88 | void case_heap_cap(); 89 | void case_heap_push(); 90 | void case_heap_pop(); 91 | void case_heap_top(); 92 | void case_heap_pushpop(); 93 | void case_heap_del(); 94 | void case_heap_repalce(); 95 | static struct test_case heap_test_cases[] = { 96 | {"heap_clear", &case_heap_clear}, 97 | {"heap_len", &case_heap_len}, 98 | {"heap_cap", &case_heap_cap}, 99 | {"heap_push", &case_heap_push}, 100 | {"heap_pop", &case_heap_pop}, 101 | {"heap_top", &case_heap_top}, 102 | {"heap_pushpop", &case_heap_pushpop}, 103 | {"heap_del", &case_heap_del}, 104 | {"heap_replace", &case_heap_repalce}, 105 | {NULL, NULL}, 106 | }; 107 | 108 | /** 109 | * ketama_test 110 | */ 111 | void case_ketama_balance(); 112 | void case_ketama_consistence(); 113 | static struct test_case ketama_test_cases[] = { 114 | {"ketama_balance", &case_ketama_balance}, 115 | {"ketama_consistence", &case_ketama_consistence}, 116 | {NULL, NULL}, 117 | }; 118 | 119 | /** 120 | * list_test 121 | */ 122 | void case_list_clear(); 123 | void case_list_lpush(); 124 | void case_list_rpush(); 125 | void case_list_lpop(); 126 | void case_list_rpop(); 127 | void case_list_iter(); 128 | static struct test_case list_test_cases[] = { 129 | {"list_clear", &case_list_clear}, 130 | {"list_lpush", &case_list_lpush}, 131 | {"list_rpush", &case_list_rpush}, 132 | {"list_lpop", &case_list_lpop}, 133 | {"list_rpop", &case_list_rpop}, 134 | {"list_iter", &case_list_iter}, 135 | {NULL, NULL}, 136 | }; 137 | 138 | /** 139 | * log_test 140 | */ 141 | void case_log_open_close(); 142 | void case_log_log(); 143 | void case_log_rotate(); 144 | static struct test_case log_test_cases[] = { 145 | {"log_open_close", &case_log_open_close}, 146 | {"log_log", &case_log_log}, 147 | {"log_rotate", &case_log_rotate}, 148 | {NULL, NULL}, 149 | }; 150 | 151 | /** 152 | * map_test 153 | */ 154 | void case_map_set(); 155 | void case_map_get(); 156 | void case_map_pop(); 157 | void case_map_has(); 158 | void case_map_clear(); 159 | void case_map_iter(); 160 | static struct test_case map_test_cases[] = { 161 | {"map_set", &case_map_set}, 162 | {"map_get", &case_map_get}, 163 | {"map_pop", &case_map_pop}, 164 | {"map_has", &case_map_has}, 165 | {"map_clear", &case_map_clear}, 166 | {"map_iter", &case_map_iter}, 167 | {NULL, NULL}, 168 | }; 169 | 170 | /** 171 | * queue_test 172 | */ 173 | void case_queue_clear(); 174 | void case_queue_push_pop_top(); 175 | static struct test_case queue_test_cases[] = { 176 | {"queue_clear", &case_queue_clear}, 177 | {"queue_push_pop_top", &case_queue_push_pop_top}, 178 | {NULL, NULL}, 179 | }; 180 | 181 | /** 182 | * skiplist_test 183 | */ 184 | void case_skiplist_base(); 185 | void case_skiplist_push(); 186 | void case_skiplist_pop(); 187 | void case_skiplist_popfirst(); 188 | void case_skiplist_poplast(); 189 | void case_skiplist_get(); 190 | static struct test_case skiplist_test_cases[] = { 191 | {"skiplist_base", &case_skiplist_base}, 192 | {"skiplist_push", &case_skiplist_push}, 193 | {"skiplist_pop", &case_skiplist_pop}, 194 | {"skiplist_popfirst", &case_skiplist_popfirst}, 195 | {"skiplist_poplast", &case_skiplist_poplast}, 196 | {"skiplist_get", &case_skiplist_get}, 197 | {NULL, NULL}, 198 | }; 199 | 200 | /** 201 | * stack_test 202 | */ 203 | void case_stack_clear(); 204 | void case_stack_push(); 205 | void case_stack_pop(); 206 | void case_stack_top(); 207 | static struct test_case stack_test_cases[] = { 208 | {"stack_clear", &case_stack_clear}, 209 | {"stack_push", &case_stack_push}, 210 | {"stack_pop", &case_stack_pop}, 211 | {"stack_top", &case_stack_top}, 212 | {NULL, NULL}, 213 | }; 214 | 215 | /** 216 | * strings_test 217 | */ 218 | void case_strings_search(); 219 | void case_strings_rand(); 220 | void case_strings_replace(); 221 | static struct test_case strings_test_cases[] = { 222 | {"strings_search", &case_strings_search}, 223 | {"strings_rand", &case_strings_rand}, 224 | {"strings_replace", &case_strings_replace}, 225 | {NULL, NULL}, 226 | }; 227 | 228 | /** 229 | * utils_test 230 | */ 231 | void case_utils_min(); 232 | void case_utils_max(); 233 | static struct test_case utils_test_cases[] = { 234 | {"utils_min", &case_utils_min}, 235 | {"utils_max", &case_utils_max}, 236 | {NULL, NULL}, 237 | }; 238 | 239 | static void run_cases(const char *name, struct test_case cases[]) { 240 | int idx = 0; 241 | 242 | while (1) { 243 | struct test_case c = cases[idx]; 244 | if (c.name == NULL || c.fn == NULL) break; 245 | double start_at = datetime_stamp_now(); 246 | (c.fn)(); 247 | double end_at = datetime_stamp_now(); 248 | idx += 1; 249 | fprintf(stderr, "ok\t%-27s %-27s %17.3fµs\n", name, c.name, 250 | 1000.0 * (end_at - start_at)); 251 | } 252 | } 253 | 254 | int main(int argc, const char *argv[]) { 255 | #ifdef __linux 256 | mtrace(); 257 | #endif 258 | run_cases("buf_test", buf_test_cases); 259 | run_cases("cfg_test", cfg_test_cases); 260 | run_cases("datetime_test", datetime_test_cases); 261 | run_cases("dict_test", dict_test_cases); 262 | run_cases("event_test", event_test_cases); 263 | run_cases("heap_test", heap_test_cases); 264 | run_cases("ketama_test", ketama_test_cases); 265 | run_cases("list_test", list_test_cases); 266 | run_cases("log_test", log_test_cases); 267 | run_cases("map_test", map_test_cases); 268 | run_cases("queue_test", queue_test_cases); 269 | run_cases("skiplist_test", skiplist_test_cases); 270 | run_cases("stack_test", stack_test_cases); 271 | run_cases("strings_test", strings_test_cases); 272 | run_cases("utils_test", utils_test_cases); 273 | return 0; 274 | } 275 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "datetime.h" 9 | 10 | struct test_case { 11 | const char *name; /* test case name */ 12 | void (*fn)(); /* test case function */ 13 | }; 14 | 15 | static void run_cases(const char *name, struct test_case cases[]); 16 | -------------------------------------------------------------------------------- /test/utils_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Chao Wang 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | 10 | void case_utils_min() { 11 | assert(MIN(1, 3) == 1); 12 | assert(MIN(1, -3) == -3); 13 | } 14 | 15 | void case_utils_max() { 16 | assert(MAX(1, 3) == 3); 17 | assert(MAX(1, -3) == 1); 18 | } 19 | --------------------------------------------------------------------------------