├── .gitignore ├── LICENSE ├── README.md ├── bftree_example.c ├── bftree_map.h ├── bftree_set.h ├── buffered_tree.c ├── buffered_tree.h └── performance ├── Makefile ├── performance ├── performance.c ├── redis-dict.c ├── redis-dict.h ├── sds.c └── sds.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | bftree.dSYM/Contents/Resources/DWARF/bftree 3 | 4 | *.plist 5 | 6 | bftree 7 | 8 | performance/performance 9 | 10 | performance/performance 11 | 12 | performance/performance.dSYM/Contents/Resources/DWARF/performance 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013-2014 Haomai Wang(haomaiwang at gmail dot com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | C-Buffered-tree 2 | =========== 3 | 4 | [Buffered-tree](http://www.cs.cmu.edu/~guyb/realworld/slidesF10/buffertree.pdf) 5 | is a write-optimized data structure. It can full utilize modern large memory. 6 | This buffered-tree is used to as a dictionary type and supports Put, Delete, Get 7 | these methods. 8 | 9 | As we know, developers love implementing dictionary type using hash. Hash can 10 | get excellent performance and simple to implement and use. But the conflict is 11 | the pain of hash implementation. Using buffered-tree, we can implement a much 12 | better space utilization. 13 | 14 | Google implements a [B-tree version](https://code.google.com/p/cpp-btree/w/list) 15 | in order to replace the c++ std library version We can find that B-tree can get 16 | amazing performance compared with c++ std. 17 | 18 | I will deliver my buffered-tree version compared to Google version and other 19 | hash implementation. 20 | 21 | More data structure supporting based on buffered-tree is on going. 22 | 23 | Usage 24 | =========== 25 | 26 | see buftree_example.c 27 | 28 | Customizer 29 | =========== 30 | 31 | C-Buffered-tree is aimed to provide a general dictionary structure to users. 32 | So users can easily expand more types. You can find example on the bottom of 33 | buffer_tree.c of "Map Area" section. 34 | 35 | API 36 | =========== 37 | 38 | ```c 39 | struct bftree *bftree_create(struct bftree_opts *opts); 40 | void bftree_free(struct bftree *tree); 41 | int bftree_put(struct bftree *tree, void *key, void *val); 42 | void *bftree_get(struct bftree *tree, void *key); 43 | int bftree_del(struct bftree *tree, void *key); 44 | // Ineffective count implementation!!!! Don't call it if big tree 45 | int bftree_count(struct bftree *tree); 46 | 47 | // Iterator is in ordering! 48 | struct bftree_iterator *bftree_get_iterator(struct bftree *tree); 49 | struct payload *bftree_next(struct bftree_iterator *iter); 50 | void bftree_free_iterator(struct bftree_iterator *iter); 51 | ``` 52 | -------------------------------------------------------------------------------- /bftree_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "bftree_map.h" 6 | #include "bftree_set.h" 7 | 8 | void bftmap_example() 9 | { 10 | struct bftree *tree; 11 | int i, len; 12 | char buf[100]; 13 | char *val; 14 | struct bftree_iterator *iter; 15 | int count; 16 | 17 | tree = bftmap_create(); 18 | for (i = 0; i < 1000; i++) { 19 | len = snprintf(buf, 100, "key%d", i); 20 | val = malloc(100); 21 | snprintf(val, 100, "val%d", i); 22 | bftmap_put(tree, buf, len, val); 23 | } 24 | 25 | assert(bftree_count(tree) == 1000); 26 | 27 | count = 0; 28 | iter = bftree_get_iterator(tree); 29 | while (bftree_next(iter) != NULL) 30 | count++; 31 | bftree_free_iterator(iter); 32 | assert(count == 1000); 33 | 34 | for (i = 0; i < 10000; i++) { 35 | len = snprintf(buf, 100, "key%d", i); 36 | bftmap_del(tree, buf, len); 37 | } 38 | 39 | for (i = 0; i < 1000; i++) { 40 | len = snprintf(buf, 100, "key%d", i); 41 | bftmap_get(tree, buf, len); 42 | } 43 | 44 | 45 | bftmap_free(tree); 46 | } 47 | 48 | void bftset_example() 49 | { 50 | struct bftree *tree; 51 | int i, len; 52 | char buf[100]; 53 | char *val; 54 | 55 | tree = bftset_create(); 56 | for (i = 0; i < 10000; i++) { 57 | len = snprintf(buf, 100, "key%d", i); 58 | val = malloc(100); 59 | snprintf(val, 100, "val%d", i); 60 | bftset_put(tree, buf, len); 61 | } 62 | 63 | for (i = 0; i < 10000; i++) { 64 | len = snprintf(buf, 100, "key%d", i); 65 | bftset_del(tree, buf, len); 66 | } 67 | 68 | for (i = 0; i < 10000; i++) { 69 | len = snprintf(buf, 100, "key%d", i); 70 | bftset_get(tree, buf, len); 71 | } 72 | 73 | bftmap_free(tree); 74 | } 75 | 76 | int main() 77 | { 78 | bftmap_example(); 79 | bftset_example(); 80 | } 81 | -------------------------------------------------------------------------------- /bftree_map.h: -------------------------------------------------------------------------------- 1 | #ifndef BUFTREE_MAP_H 2 | #define BUFTREE_MAP_H 3 | 4 | #include 5 | 6 | #include "buffered_tree.h" 7 | 8 | // bftmap ownership: 9 | // `key` will be copied and store. User should free `key` buffer passed in 10 | // `val` is ignored whatever it is 11 | 12 | // key is char* and val is char* too. 13 | struct bftree *bftmap_create(); 14 | void bftmap_free(struct bftree *tree); 15 | int bftmap_put(struct bftree *tree, char *key, size_t key_len, char *val); 16 | void *bftmap_get(struct bftree *tree, char *key, size_t key_len); 17 | int bftmap_del(struct bftree *tree, char *key, size_t key_len); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /bftree_set.h: -------------------------------------------------------------------------------- 1 | #ifndef BFTREE_SET_H 2 | #define BFTREE_SET_H 3 | 4 | #include "buffered_tree.h" 5 | 6 | // bftmap ownership: 7 | // `key` will be copied and store. User should free `key` buffer passed in 8 | 9 | // key is char* 10 | struct bftree *bftset_create(); 11 | void bftset_free(struct bftree *tree); 12 | int bftset_put(struct bftree *tree, char *key, size_t key_len); 13 | void *bftset_get(struct bftree *tree, char *key, size_t key_len); 14 | int bftset_del(struct bftree *tree, char *key, size_t key_len); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /buffered_tree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "buffered_tree.h" 8 | 9 | struct container { 10 | struct payload *payload_first; 11 | uint32_t payload_size; 12 | struct node *child; 13 | }; 14 | 15 | struct node { 16 | struct node *parent; 17 | struct container **containers; 18 | uint32_t container_count; 19 | uint32_t container_size; 20 | }; 21 | 22 | struct bftree { 23 | struct node *root; 24 | struct bftree_opts *opts; 25 | uint32_t height; 26 | int is_migrated; 27 | uint32_t del_payload_count; 28 | uint32_t put_payload_count; 29 | }; 30 | 31 | struct bftree_iterator { 32 | struct bftree *tree; 33 | struct payload *next; 34 | int closed; 35 | }; 36 | 37 | static struct container *container_insert(struct bftree *tree, struct node *node, 38 | uint32_t container_idx, struct payload *new_payload); 39 | void bftree_node_print(struct node *node); 40 | static void validate_containers(struct node *node, key_compare_func compare); 41 | 42 | struct payload *payload_create(void *key, void *val, enum payload_type type) 43 | { 44 | struct payload *payload; 45 | 46 | payload = malloc(sizeof(*payload)); 47 | payload->key = key; 48 | payload->val = val; 49 | payload->next = NULL; 50 | payload->type = type; 51 | 52 | return payload; 53 | } 54 | 55 | void payload_free(struct bftree *tree, struct payload *payload, int nofree) 56 | { 57 | if (payload->key && tree->opts->key_destructor) 58 | tree->opts->key_destructor(payload->key); 59 | if (payload->val && tree->opts->val_destructor && !nofree) 60 | tree->opts->val_destructor(payload->val); 61 | if (payload->type == Put) 62 | tree->put_payload_count--; 63 | else 64 | tree->del_payload_count--; 65 | 66 | free(payload); 67 | } 68 | 69 | void payload_replace(struct bftree *tree, struct payload *older, struct payload *newer) 70 | { 71 | void *temp; 72 | temp = older->val; 73 | older->val = newer->val; 74 | newer->val = temp; 75 | older->type = newer->type; 76 | payload_free(tree, newer, 0); 77 | } 78 | 79 | struct container *container_create() 80 | { 81 | struct container *container; 82 | 83 | container = malloc(sizeof(*container)); 84 | container->payload_first = NULL; 85 | container->payload_size = 0; 86 | container->child = NULL; 87 | 88 | return container; 89 | } 90 | 91 | void container_free(struct bftree *tree, struct container *container) 92 | { 93 | struct payload *curr, *next; 94 | 95 | curr = container->payload_first; 96 | while (curr) { 97 | next = curr->next; 98 | payload_free(tree, curr, 0); 99 | curr = next; 100 | } 101 | 102 | free(container); 103 | } 104 | 105 | void insert_after_container(struct node *node, struct container *container, 106 | uint32_t container_idx) 107 | { 108 | if (node->container_size == node->container_count) { 109 | node->containers = realloc(node->containers, sizeof(void*)*node->container_count*2); 110 | node->container_count *= 2; 111 | } 112 | 113 | if (node->container_size == 0) { 114 | node->containers[node->container_size++] = container; 115 | } else { 116 | memmove(&node->containers[container_idx+2], &node->containers[container_idx+1], 117 | (node->container_size-container_idx-1)*sizeof(void*)); 118 | node->containers[container_idx+1] = container; 119 | node->container_size++; 120 | } 121 | } 122 | 123 | struct node *node_create(struct node *parent_node) 124 | { 125 | struct node *node; 126 | 127 | node = malloc(sizeof(*node)); 128 | node->parent = parent_node; 129 | node->containers = malloc(sizeof(struct container*)*BFTREE_DEFAULT_CONTAINER); 130 | node->container_count = BFTREE_DEFAULT_CONTAINER; 131 | node->container_size = 0; 132 | 133 | return node; 134 | } 135 | 136 | void node_free(struct bftree *tree, struct node *node) 137 | { 138 | int i; 139 | struct container *container; 140 | 141 | for (i = 0; i < node->container_size; i++) { 142 | container = node->containers[i]; 143 | container_free(tree, container); 144 | } 145 | free(node->containers); 146 | free(node); 147 | } 148 | 149 | void bftree_free_node(struct bftree *tree, struct node *node) 150 | { 151 | int i; 152 | struct container *container; 153 | 154 | for (i = 0; i < node->container_size; i++) { 155 | container = node->containers[i]; 156 | if (container->child) 157 | bftree_free_node(tree, container->child); 158 | } 159 | node_free(tree, node); 160 | } 161 | 162 | static uint32_t find_container(key_compare_func compare, struct node *node, 163 | void *key, uint32_t start_container) 164 | { 165 | int left, right, middle, result, compared; 166 | struct container **containers; 167 | 168 | left = start_container; 169 | right = node->container_size - 1; 170 | containers = node->containers; 171 | compared = 0; 172 | while (left <= right) { 173 | middle = (left + right) / 2; 174 | compared = compare(key, containers[middle]->payload_first->key); 175 | if (compared < 0) { 176 | right = middle - 1; 177 | } else if (compared > 0) { 178 | left = middle + 1; 179 | } else { 180 | right = middle; 181 | break ; 182 | } 183 | } 184 | if (compared > 0) 185 | result = left - 1; 186 | else if (compared < 0) 187 | result = right; 188 | else 189 | result = right; 190 | 191 | if (result == 0) 192 | return 0; 193 | return right - 1; 194 | } 195 | 196 | static struct container *remove_container(struct node *node, uint32_t idx) 197 | { 198 | struct container *removed; 199 | 200 | removed = node->containers[idx]; 201 | memmove(&node->containers[idx], &node->containers[idx+1], 202 | (node->container_size-idx-1)*sizeof(void*)); 203 | node->container_size--; 204 | return removed; 205 | } 206 | 207 | static struct payload *get_payload(key_compare_func compare, struct payload *payload_start, 208 | void *key, int *is_equal) 209 | { 210 | struct payload *curr_payload, *prev_payload; 211 | int compared; 212 | 213 | prev_payload = NULL; 214 | curr_payload = payload_start; 215 | *is_equal = 0; 216 | while (curr_payload) { 217 | compared = compare(key, curr_payload->key); 218 | if (compared <= 0) { 219 | if (compared == 0) { 220 | *is_equal = 1; 221 | return curr_payload; 222 | } 223 | return prev_payload; 224 | } 225 | prev_payload = curr_payload; 226 | curr_payload = curr_payload->next; 227 | } 228 | 229 | return prev_payload; 230 | } 231 | 232 | static void push_to_child(struct bftree *tree, struct node *node, 233 | struct container *container) 234 | { 235 | struct payload *curr_payload, *next_payload; 236 | uint32_t child_container, push_count; 237 | key_compare_func compare; 238 | 239 | compare = tree->opts->key_compare; 240 | curr_payload = container->payload_first->next; 241 | child_container = 0; 242 | 243 | push_count = container->payload_size / 2; 244 | container->payload_size -= push_count; 245 | while (push_count--) { 246 | next_payload = curr_payload->next; 247 | container->payload_first->next = next_payload; 248 | child_container = find_container(compare, container->child, 249 | curr_payload->key, child_container); 250 | container_insert(tree, container->child, child_container, curr_payload); 251 | curr_payload = next_payload; 252 | } 253 | } 254 | 255 | static void order_container_payload(struct bftree *tree, struct node *node, 256 | uint32_t migrated_idx, uint32_t import_idx) 257 | { 258 | struct payload *separator, *curr; 259 | key_compare_func compare; 260 | int is_equal; 261 | struct container *left, *right; 262 | 263 | left = node->containers[migrated_idx]; 264 | right = node->containers[import_idx]; 265 | compare = tree->opts->key_compare; 266 | separator = get_payload(compare, left->payload_first, 267 | right->payload_first->key, &is_equal); 268 | 269 | if (is_equal) { 270 | // TODO need optimize 271 | struct payload *prev; 272 | prev = left->payload_first; 273 | while (prev->next != separator) { 274 | prev = prev->next; 275 | } 276 | payload_replace(tree, right->payload_first, separator); 277 | separator = prev; 278 | left->payload_size--; 279 | } 280 | 281 | if (separator) { 282 | curr = separator->next; 283 | separator->next = NULL; 284 | tree->is_migrated = 1; 285 | while (curr) { 286 | left->payload_size--; 287 | container_insert(tree, node, import_idx, curr); 288 | curr = curr->next; 289 | } 290 | tree->is_migrated = 0; 291 | } 292 | } 293 | 294 | static void try_split_node(struct bftree *tree, struct node *node) 295 | { 296 | uint32_t middle_container_idx, parent_container_idx; 297 | int i; 298 | struct node *new_node, *new_root; 299 | struct container *container, *new_node_first_container; 300 | 301 | if (node->container_size < BFTREE_CONTAINER_THRESHOLD) { 302 | // the number of container in this node is full 303 | return ; 304 | } 305 | 306 | middle_container_idx = node->container_size / 2; 307 | new_node = node_create(node->parent); 308 | new_node_first_container = node->containers[middle_container_idx]; 309 | new_node_first_container->child = new_node; 310 | for (i = middle_container_idx+1; i < node->container_size; ++i) { 311 | container = node->containers[i]; 312 | insert_after_container(new_node, container, i-middle_container_idx-2); 313 | } 314 | node->container_size -= (i-middle_container_idx); 315 | 316 | if (node == tree->root) { 317 | // produce new root 318 | new_root = node_create(NULL); 319 | tree->root = new_root; 320 | tree->height++; 321 | node->parent = new_root; 322 | new_node->parent = new_root; 323 | container = remove_container(node, 0); 324 | container->child = node; 325 | insert_after_container(new_root, container, 0); 326 | insert_after_container(new_root, new_node_first_container, 0); 327 | } else { 328 | parent_container_idx = find_container(tree->opts->key_compare, 329 | node->parent, new_node_first_container->payload_first->key, 0); 330 | insert_after_container(node->parent, new_node_first_container, parent_container_idx); 331 | order_container_payload(tree, node->parent, parent_container_idx, parent_container_idx+1); 332 | try_split_node(tree, node->parent); 333 | } 334 | } 335 | 336 | static void split_container(struct bftree *tree, struct node *node, 337 | uint32_t container_idx) 338 | { 339 | uint32_t half_count, i; 340 | struct container *new_container, *target; 341 | struct payload *payload; 342 | 343 | new_container = container_create(); 344 | insert_after_container(node, new_container, container_idx); 345 | 346 | target = node->containers[container_idx]; 347 | half_count = target->payload_size / 2; 348 | payload = target->payload_first; 349 | for (i = 0; i < half_count - 1; ++i) 350 | payload = payload->next; 351 | new_container->payload_first = payload->next; 352 | payload->next = NULL; 353 | new_container->payload_size = target->payload_size - half_count; 354 | target->payload_size = half_count; 355 | 356 | try_split_node(tree, node); 357 | } 358 | 359 | static struct container *container_insert(struct bftree *tree, struct node *node, 360 | uint32_t container_idx, struct payload *new_payload) 361 | { 362 | struct payload *curr_payload; 363 | struct container *target; 364 | int is_equal; 365 | key_compare_func compare; 366 | 367 | compare = tree->opts->key_compare; 368 | if (container_idx >= node->container_size) { 369 | target = container_create(); 370 | insert_after_container(node, target, 0); 371 | } else { 372 | target = node->containers[container_idx]; 373 | } 374 | 375 | curr_payload = get_payload(tree->opts->key_compare, target->payload_first, 376 | new_payload->key, &is_equal); 377 | 378 | if (is_equal) { 379 | // exist same key, swap value of payload 380 | payload_replace(tree, curr_payload, new_payload); 381 | } else { 382 | if (curr_payload) { 383 | // not at the header of payload list 384 | new_payload->next = curr_payload->next; 385 | curr_payload->next = new_payload; 386 | } else { 387 | // at the header of payload list 388 | new_payload->next = target->payload_first; 389 | target->payload_first = new_payload; 390 | } 391 | target->payload_size++; 392 | } 393 | 394 | if (target->payload_size > BFTREE_PAYLOAD_THRESHOLD && tree->is_migrated) { 395 | if (target->child) 396 | push_to_child(tree, node, target); 397 | else 398 | split_container(tree, node, container_idx); 399 | } 400 | 401 | return target; 402 | } 403 | 404 | static struct payload *container_get(struct bftree *tree, struct node *node, 405 | uint32_t container_idx, void *key) 406 | { 407 | struct payload *curr_payload; 408 | struct container *container; 409 | int (*compare)(const void *, const void *); 410 | int is_equal; 411 | 412 | if (container_idx >= node->container_size) 413 | return NULL; 414 | 415 | compare = tree->opts->key_compare; 416 | container = node->containers[container_idx]; 417 | curr_payload = get_payload(compare, container->payload_first, key, &is_equal); 418 | 419 | if (is_equal) { 420 | if (curr_payload->type == Put) 421 | return curr_payload; 422 | return NULL; 423 | } 424 | if (container->child) { 425 | container_idx = find_container(compare, container->child, key, 0); 426 | return container_get(tree, container->child, container_idx, key); 427 | } 428 | return NULL; 429 | } 430 | 431 | 432 | // ================================================================ 433 | // ========================== Public API ========================== 434 | // ================================================================ 435 | 436 | struct bftree *bftree_create(struct bftree_opts *opts) 437 | { 438 | struct node *root; 439 | struct bftree *tree; 440 | 441 | tree = malloc(sizeof(*tree)); 442 | root = node_create(NULL); 443 | tree->root = root; 444 | tree->height = 1; 445 | tree->opts = opts; 446 | tree->is_migrated = 0; 447 | tree->del_payload_count = tree->put_payload_count = 0; 448 | assert(opts->key_destructor && opts->val_destructor); 449 | 450 | return tree; 451 | } 452 | 453 | void bftree_free(struct bftree *tree) 454 | { 455 | bftree_free_node(tree, tree->root); 456 | free(tree); 457 | } 458 | 459 | int bftree_put(struct bftree *tree, void *key, void *val) 460 | { 461 | struct payload *new_payload; 462 | uint32_t idx; 463 | 464 | if (!tree || !key) 465 | return BF_WRONG; 466 | 467 | new_payload = payload_create(key, val, Put); 468 | idx = find_container(tree->opts->key_compare, tree->root, new_payload->key, 0); 469 | container_insert(tree, tree->root, idx, new_payload); 470 | 471 | return BF_OK; 472 | } 473 | 474 | void *bftree_get(struct bftree *tree, void *key) 475 | { 476 | uint32_t idx; 477 | struct payload *r; 478 | 479 | if (!tree || !key) 480 | return NULL; 481 | 482 | idx = find_container(tree->opts->key_compare, tree->root, key, 0); 483 | r = container_get(tree, tree->root, idx, key); 484 | 485 | if (r) { 486 | return r->val; 487 | } 488 | return NULL; 489 | } 490 | 491 | int bftree_del(struct bftree *tree, void *key) 492 | { 493 | uint32_t idx; 494 | struct payload *new_payload; 495 | 496 | if (!tree || !key) 497 | return BF_WRONG; 498 | 499 | new_payload = payload_create(key, NULL, Del); 500 | idx = find_container(tree->opts->key_compare, tree->root, new_payload->key, 0); 501 | container_insert(tree, tree->root, idx, new_payload); 502 | 503 | return BF_OK; 504 | } 505 | 506 | struct bftree_iterator *bftree_get_iterator(struct bftree *tree) 507 | { 508 | struct bftree_iterator *iter; 509 | 510 | iter = malloc(sizeof(*iter)); 511 | iter->tree = tree; 512 | iter->next = NULL; 513 | iter->closed = 0; 514 | 515 | return iter; 516 | } 517 | 518 | struct payload *bftree_next(struct bftree_iterator *iter) 519 | { 520 | struct bftree *tree; 521 | struct container *container; 522 | struct node *node; 523 | struct payload *curr, *next, *min; 524 | uint32_t idx; 525 | key_compare_func key_compare; 526 | int is_equal; 527 | 528 | if (iter->closed) 529 | return NULL; 530 | 531 | tree = iter->tree; 532 | key_compare = tree->opts->key_compare; 533 | if (!iter->next) { 534 | if (tree->root->container_size == 0) 535 | return NULL; 536 | iter->next = tree->root->containers[0]->payload_first; 537 | } 538 | 539 | curr = iter->next; 540 | min = NULL; 541 | node = tree->root; 542 | do { 543 | idx = find_container(key_compare, node, curr->key, 0); 544 | container = node->containers[idx]; 545 | next = get_payload(key_compare, container->payload_first, 546 | curr->key, &is_equal); 547 | if (!next) 548 | next = container->payload_first; 549 | else 550 | next = next->next; 551 | if (next) { 552 | if (!min) { 553 | min = next; 554 | } else if (key_compare(next->key, min->key) < 0) { 555 | min = next; 556 | } 557 | } 558 | node = container->child; 559 | } while(node); 560 | 561 | iter->next = min; 562 | if (!min) 563 | iter->closed = 1; 564 | 565 | return curr; 566 | } 567 | 568 | void bftree_free_iterator(struct bftree_iterator *iter) 569 | { 570 | free(iter); 571 | } 572 | 573 | int bftree_count(struct bftree *tree) 574 | { 575 | struct bftree_iterator *iter; 576 | int count; 577 | 578 | count = 0; 579 | 580 | iter = bftree_get_iterator(tree); 581 | while (bftree_next(iter) != NULL) 582 | count++; 583 | bftree_free_iterator(iter); 584 | 585 | return count; 586 | } 587 | 588 | // ================================================================ 589 | // ========================== Debug Area ========================== 590 | // ================================================================ 591 | 592 | static void validate_containers(struct node *node, key_compare_func compare) 593 | { 594 | int i; 595 | struct payload *curr, *prev; 596 | 597 | for (i = 0; i < node->container_size; i++) { 598 | prev = node->containers[i]->payload_first; 599 | curr = prev->next; 600 | while (curr) { 601 | assert(compare(prev->key, curr->key) < 0); 602 | prev = curr; 603 | curr = curr->next; 604 | } 605 | if (i == 0) 606 | continue; 607 | assert(compare(node->containers[i-1]->payload_first->key, 608 | node->containers[i]->payload_first->key) < 0); 609 | } 610 | } 611 | 612 | void bftree_node_print(struct node *node) 613 | { 614 | int i; 615 | struct payload *payload; 616 | 617 | for (i = 0; i < node->container_size; ++i) { 618 | printf("container%d %d %s\t", i, node->containers[i]->payload_size, 619 | node->containers[i]->payload_first->key); 620 | payload = node->containers[i]->payload_first; 621 | while (payload) { 622 | printf("%s => %s ", payload->key, payload->val); 623 | payload = payload->next; 624 | } 625 | printf("\n"); 626 | } 627 | printf("\n"); 628 | } 629 | 630 | // ================================================================ 631 | // ========================== Map Type Area ======================= 632 | // ================================================================ 633 | 634 | typedef char *wstr; 635 | 636 | struct wstrhd { 637 | size_t len; 638 | char buf[]; 639 | }; 640 | 641 | static inline wstr wstr_newlen(const void *init, size_t init_len) 642 | { 643 | struct wstrhd *sh; 644 | sh = malloc(sizeof(struct wstrhd)+init_len+1); 645 | if (sh == NULL) { 646 | return NULL; 647 | } 648 | if (init) { 649 | memcpy(sh->buf, init, init_len); 650 | sh->len = init_len; 651 | } else { 652 | sh->len = 0; 653 | } 654 | sh->buf[sh->len] = '\0'; 655 | return (wstr)(sh->buf); 656 | } 657 | 658 | static inline inline void wstr_free(wstr s) 659 | { 660 | if (s == NULL) { 661 | return ; 662 | } 663 | free(s - sizeof(struct wstrhd)); 664 | } 665 | 666 | static inline size_t wstrlen(const wstr s) 667 | { 668 | struct wstrhd *hd = (struct wstrhd *)(s - sizeof(struct wstrhd)); 669 | return hd->len; 670 | } 671 | 672 | static inline int wstr_keycompare(const void *key1, const void *key2) 673 | { 674 | size_t l1,l2; 675 | 676 | l1 = wstrlen((wstr)key1); 677 | l2 = wstrlen((wstr)key2); 678 | if (l1 != l2) return l1 < l2 ? -1 : 1; 679 | return memcmp(key1, key2, l1); 680 | } 681 | 682 | static struct bftree_opts map_opt = { 683 | NULL, 684 | NULL, 685 | wstr_keycompare, 686 | (void (*)(void*))wstr_free, 687 | free, 688 | }; 689 | 690 | struct bftree *bftmap_create() 691 | { 692 | return bftree_create(&map_opt); 693 | } 694 | 695 | void bftmap_free(struct bftree *tree) 696 | { 697 | bftree_free(tree); 698 | } 699 | 700 | int bftmap_put(struct bftree *tree, char *key, size_t key_len, void *val) 701 | { 702 | if (!key || !key_len) 703 | return BF_WRONG; 704 | 705 | wstr s = wstr_newlen(key, key_len); 706 | return bftree_put(tree, s, val); 707 | } 708 | 709 | void *bftmap_get(struct bftree *tree, char *key, size_t key_len) 710 | { 711 | if (!key || !key_len) 712 | return NULL; 713 | 714 | void *r; 715 | wstr s = wstr_newlen(key, key_len); 716 | r = bftree_get(tree, s); 717 | wstr_free(s); 718 | return r; 719 | } 720 | 721 | int bftmap_del(struct bftree *tree, char *key, size_t key_len) 722 | { 723 | if (!key || !key_len) 724 | return BF_WRONG; 725 | 726 | wstr s = wstr_newlen(key, key_len); 727 | return bftree_del(tree, s); 728 | } 729 | 730 | // ================================================================ 731 | // ========================== Set Type Area ======================= 732 | // ================================================================ 733 | 734 | struct bftree *bftset_create() 735 | { 736 | return bftree_create(&map_opt); 737 | } 738 | 739 | void bftset_free(struct bftree *tree) 740 | { 741 | bftree_free(tree); 742 | } 743 | 744 | int bftset_put(struct bftree *tree, char *key, size_t key_len) 745 | { 746 | if (!key || !key_len) 747 | return BF_WRONG; 748 | 749 | wstr s = wstr_newlen(key, key_len); 750 | return bftree_put(tree, s, NULL); 751 | } 752 | 753 | void *bftset_get(struct bftree *tree, char *key, size_t key_len) 754 | { 755 | if (!key || !key_len) 756 | return NULL; 757 | 758 | void *r; 759 | wstr s = wstr_newlen(key, key_len); 760 | r = bftree_get(tree, s); 761 | wstr_free(s); 762 | return r; 763 | } 764 | 765 | int bftset_del(struct bftree *tree, char *key, size_t key_len) 766 | { 767 | if (!key || !key_len) 768 | return BF_WRONG; 769 | 770 | wstr s = wstr_newlen(key, key_len); 771 | return bftree_del(tree, s); 772 | } 773 | -------------------------------------------------------------------------------- /buffered_tree.h: -------------------------------------------------------------------------------- 1 | #ifndef WHEAT_BUFFERED_TREE 2 | #define WHEAT_BUFFERED_TREE 3 | 4 | #include 5 | 6 | // Normally, L2 Cache line size is 128/256 bytes. 16 containers can 7 | // occupy 128 bytes 8 | #define BFTREE_DEFAULT_CONTAINER 16 9 | #define BFTREE_CONTAINER_THRESHOLD 16 10 | #define BFTREE_PAYLOAD_THRESHOLD 100 11 | 12 | #define BF_OK 0 13 | #define BF_WRONG (-1) 14 | 15 | struct bftree; 16 | typedef int (*key_compare_func)(const void *key1, const void *key2); 17 | 18 | enum payload_type { 19 | Put, 20 | Del, 21 | }; 22 | 23 | struct payload { 24 | uint8_t *key; 25 | uint8_t *val; 26 | struct payload *next; 27 | enum payload_type type; 28 | }; 29 | 30 | struct bftree_opts { 31 | void *(*key_dup)(const void *key); 32 | void *(*val_dup)(const void *obj); 33 | key_compare_func key_compare; 34 | void (*key_destructor)(void *key); 35 | void (*val_destructor)(void *obj); 36 | }; 37 | 38 | 39 | struct bftree *bftree_create(struct bftree_opts *opts); 40 | void bftree_free(struct bftree *tree); 41 | int bftree_put(struct bftree *tree, void *key, void *val); 42 | void *bftree_get(struct bftree *tree, void *key); 43 | int bftree_del(struct bftree *tree, void *key); 44 | // Ineffective count implementation!!!! Don't call it if big tree 45 | int bftree_count(struct bftree *tree); 46 | 47 | // ================== Iterator Area ======================== 48 | // Iterator isn't effective as insert or delete operator, it may 49 | // slower than normal dictionary implementation. 50 | // If modify payload when iterate buffer tree, you should call 51 | // bftree_iter_set_val and bftree_iter_set_del to avoid unexpected 52 | // things 53 | struct bftree_iterator *bftree_get_iterator(struct bftree *tree); 54 | struct payload *bftree_next(struct bftree_iterator *iter); 55 | void bftree_free_iterator(struct bftree_iterator *iter); 56 | 57 | #define payload_getkey(payload) ((payload)->key) 58 | #define payload_getval(payload) ((payload)->val) 59 | 60 | static inline void bftree_iter_set_del(struct bftree_iterator *iter, 61 | struct payload *payload) 62 | { 63 | payload->type = Del; 64 | } 65 | 66 | static inline void bftree_iter_set_val(struct bftree_iterator *iter, 67 | struct payload *payload, 68 | void *val) 69 | { 70 | payload->val = val; 71 | } 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /performance/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE = sds.c redis-dict.c performance.c ../buffered_tree.c 2 | all: performance 3 | performance: $(SOURCE) 4 | cc -g $(SOURCE) -o performance 5 | -------------------------------------------------------------------------------- /performance/performance: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuyuyu101/C-Buffered-tree/dbccc95c6b0be007ac94c9be72b9fb0a6dfddd31/performance/performance -------------------------------------------------------------------------------- /performance/performance.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "redis-dict.h" 8 | #include "../buffered_tree.h" 9 | #include "sds.h" 10 | 11 | #define get_seconds(start, end) ((end.tv_sec-start.tv_sec)+((double)(end.tv_usec-start.tv_usec))/1000000) 12 | 13 | unsigned int dictSdsHash(const void *key) { 14 | return dictGenHashFunction((unsigned char*)key, sdslen((char*)key)); 15 | } 16 | 17 | void dictSdsDestructor(void *privdata, void *val) 18 | { 19 | sdsfree(val); 20 | } 21 | 22 | int dictSdsKeyCompare(void *privdata, const void *key1, 23 | const void *key2) 24 | { 25 | int l1,l2; 26 | DICT_NOTUSED(privdata); 27 | 28 | l1 = sdslen((sds)key1); 29 | l2 = sdslen((sds)key2); 30 | if (l1 != l2) return 0; 31 | return memcmp(key1, key2, l1) == 0; 32 | } 33 | 34 | 35 | dictType sdsdict_type = { 36 | dictSdsHash, /* hash function */ 37 | NULL, /* key dup */ 38 | NULL, /* val dup */ 39 | dictSdsKeyCompare, /* key compare */ 40 | dictSdsDestructor, /* key destructor */ 41 | dictSdsDestructor /* val destructor */ 42 | }; 43 | 44 | void redis_add(dict *d, int times) 45 | { 46 | int i; 47 | sds key, val; 48 | char buf[20]; 49 | 50 | for (i = 0; i < times; ++i) { 51 | snprintf(buf, 20, "key%d", i); 52 | key = sdsnew(buf); 53 | snprintf(buf, 20, "val%d", i); 54 | val = sdsnew(buf); 55 | assert(dictAdd(d, key, val) == DICT_OK); 56 | } 57 | } 58 | 59 | int bftreeSdsKeyCompare(const void *key1, const void *key2) 60 | { 61 | int l1,l2; 62 | 63 | l1 = sdslen((sds)key1); 64 | l2 = sdslen((sds)key2); 65 | if (l1 != l2) return l1 < l2 ? -1 : 1; 66 | return memcmp(key1, key2, l1); 67 | } 68 | 69 | struct bftree *bftree_add(struct bftree *tree, int times) 70 | { 71 | int i; 72 | char buf[20]; 73 | sds key, val; 74 | 75 | for (i = 0; i < times; ++i) { 76 | snprintf(buf, 20, "key%d", i); 77 | key = sdsnew(buf); 78 | snprintf(buf, 20, "val%d", i); 79 | val = sdsnew(buf); 80 | 81 | assert(bftree_put(tree, key, val) == BF_OK); 82 | } 83 | return tree; 84 | } 85 | 86 | void bftree_fetch(struct bftree *tree, int times) 87 | { 88 | int i, j; 89 | char buf[20]; 90 | sds key, val; 91 | 92 | srand(1992); 93 | for (i = 0; i < times; ++i) { 94 | j = rand() % times; 95 | snprintf(buf, 20, "key%d", j); 96 | key = sdsnew(buf); 97 | 98 | assert(bftree_get(tree, key)); 99 | sdsfree(key); 100 | } 101 | } 102 | 103 | void redis_fetch(dict *d, int times) 104 | { 105 | int i, j; 106 | char buf[20]; 107 | sds key, val; 108 | 109 | srand(1992); 110 | for (i = 0; i < times; ++i) { 111 | j = rand() % times; 112 | snprintf(buf, 20, "key%d", j); 113 | key = sdsnew(buf); 114 | 115 | assert(dictFetchValue(d, key)); 116 | sdsfree(key); 117 | } 118 | } 119 | 120 | void redis_del(dict *d, int times) 121 | { 122 | int i, j; 123 | char buf[20]; 124 | sds key, val; 125 | 126 | srand(1992); 127 | for (i = 0; i < times; ++i) { 128 | j = rand() % times; 129 | snprintf(buf, 20, "key%d", j); 130 | key = sdsnew(buf); 131 | 132 | dictDelete(d, key); 133 | sdsfree(key); 134 | } 135 | } 136 | 137 | void bftree_delele(struct bftree *tree, int times) 138 | { 139 | int i, j; 140 | char buf[20]; 141 | sds key, val; 142 | 143 | srand(1992); 144 | for (i = 0; i < times; ++i) { 145 | j = rand() % times; 146 | snprintf(buf, 20, "key%d", j); 147 | key = sdsnew(buf); 148 | 149 | bftree_del(tree, key); 150 | } 151 | } 152 | 153 | int main(int argc, const char *argv[]) 154 | { 155 | struct timeval start, end; 156 | double elapsed; 157 | dict *d; 158 | struct bftree *tree; 159 | const int times = 500; 160 | 161 | struct bftree_opts opt = { 162 | NULL, NULL, bftreeSdsKeyCompare, (void (*)(void*))sdsfree, (void (*)(void*))sdsfree 163 | }; 164 | tree = bftree_create(&opt); 165 | 166 | d = dictCreate(&sdsdict_type, NULL); 167 | 168 | gettimeofday(&start, NULL); 169 | redis_add(d, times); 170 | gettimeofday(&end, NULL); 171 | elapsed = get_seconds(start, end); 172 | printf("redis add %d elements seconds: %f\n", times, elapsed); 173 | 174 | gettimeofday(&start, NULL); 175 | tree = bftree_add(tree, times); 176 | gettimeofday(&end, NULL); 177 | elapsed = get_seconds(start, end); 178 | printf("bftree add %d elements seconds: %f\n", times, elapsed); 179 | 180 | gettimeofday(&start, NULL); 181 | redis_fetch(d, times); 182 | gettimeofday(&end, NULL); 183 | elapsed = get_seconds(start, end); 184 | printf("redis get %d elements seconds: %f\n", times, elapsed); 185 | 186 | gettimeofday(&start, NULL); 187 | bftree_fetch(tree, times); 188 | gettimeofday(&end, NULL); 189 | elapsed = get_seconds(start, end); 190 | printf("bftree get %d elements seconds: %f\n", times, elapsed); 191 | 192 | gettimeofday(&start, NULL); 193 | redis_del(d, times); 194 | gettimeofday(&end, NULL); 195 | elapsed = get_seconds(start, end); 196 | printf("redis delete %d elements seconds: %f\n", times, elapsed); 197 | 198 | gettimeofday(&start, NULL); 199 | bftree_delele(tree, times); 200 | gettimeofday(&end, NULL); 201 | elapsed = get_seconds(start, end); 202 | printf("bftree delete %d elements seconds: %f\n", times, elapsed); 203 | 204 | bftree_free(tree); 205 | dictRelease(d); 206 | 207 | return 0; 208 | } 209 | -------------------------------------------------------------------------------- /performance/redis-dict.c: -------------------------------------------------------------------------------- 1 | /* Hash Tables Implementation. 2 | * 3 | * This file implements in memory hash tables with insert/del/replace/find/ 4 | * get-random-element operations. Hash tables will auto resize if needed 5 | * tables of power of two in size are used, collisions are handled by 6 | * chaining. See the source code for more information... :) 7 | * 8 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * * Neither the name of Redis nor the names of its contributors may be used 20 | * to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "redis-dict.h" 46 | 47 | /* Using dictEnableResize() / dictDisableResize() we make possible to 48 | * enable/disable resizing of the hash table as needed. This is very important 49 | * for Redis, as we use copy-on-write and don't want to move too much memory 50 | * around when there is a child performing saving operations. 51 | * 52 | * Note that even when dict_can_resize is set to 0, not all resizes are 53 | * prevented: an hash table is still allowed to grow if the ratio between 54 | * the number of elements and the buckets > dict_force_resize_ratio. */ 55 | static int dict_can_resize = 1; 56 | static unsigned int dict_force_resize_ratio = 5; 57 | 58 | /* -------------------------- private prototypes ---------------------------- */ 59 | 60 | static int _dictExpandIfNeeded(dict *ht); 61 | static unsigned long _dictNextPower(unsigned long size); 62 | static int _dictKeyIndex(dict *ht, const void *key); 63 | static int _dictInit(dict *ht, dictType *type, void *privDataPtr); 64 | 65 | /* -------------------------- hash functions -------------------------------- */ 66 | 67 | /* Thomas Wang's 32 bit Mix Function */ 68 | unsigned int dictIntHashFunction(unsigned int key) 69 | { 70 | key += ~(key << 15); 71 | key ^= (key >> 10); 72 | key += (key << 3); 73 | key ^= (key >> 6); 74 | key += ~(key << 11); 75 | key ^= (key >> 16); 76 | return key; 77 | } 78 | 79 | /* Identity hash function for integer keys */ 80 | unsigned int dictIdentityHashFunction(unsigned int key) 81 | { 82 | return key; 83 | } 84 | 85 | static uint32_t dict_hash_function_seed = 5381; 86 | 87 | void dictSetHashFunctionSeed(uint32_t seed) { 88 | dict_hash_function_seed = seed; 89 | } 90 | 91 | uint32_t dictGetHashFunctionSeed(void) { 92 | return dict_hash_function_seed; 93 | } 94 | 95 | /* MurmurHash2, by Austin Appleby 96 | * Note - This code makes a few assumptions about how your machine behaves - 97 | * 1. We can read a 4-byte value from any address without crashing 98 | * 2. sizeof(int) == 4 99 | * 100 | * And it has a few limitations - 101 | * 102 | * 1. It will not work incrementally. 103 | * 2. It will not produce the same results on little-endian and big-endian 104 | * machines. 105 | */ 106 | unsigned int dictGenHashFunction(const void *key, int len) { 107 | /* 'm' and 'r' are mixing constants generated offline. 108 | They're not really 'magic', they just happen to work well. */ 109 | uint32_t seed = dict_hash_function_seed; 110 | const uint32_t m = 0x5bd1e995; 111 | const int r = 24; 112 | 113 | /* Initialize the hash to a 'random' value */ 114 | uint32_t h = seed ^ len; 115 | 116 | /* Mix 4 bytes at a time into the hash */ 117 | const unsigned char *data = (const unsigned char *)key; 118 | 119 | while(len >= 4) { 120 | uint32_t k = *(uint32_t*)data; 121 | 122 | k *= m; 123 | k ^= k >> r; 124 | k *= m; 125 | 126 | h *= m; 127 | h ^= k; 128 | 129 | data += 4; 130 | len -= 4; 131 | } 132 | 133 | /* Handle the last few bytes of the input array */ 134 | switch(len) { 135 | case 3: h ^= data[2] << 16; 136 | case 2: h ^= data[1] << 8; 137 | case 1: h ^= data[0]; h *= m; 138 | }; 139 | 140 | /* Do a few final mixes of the hash to ensure the last few 141 | * bytes are well-incorporated. */ 142 | h ^= h >> 13; 143 | h *= m; 144 | h ^= h >> 15; 145 | 146 | return (unsigned int)h; 147 | } 148 | 149 | /* And a case insensitive hash function (based on djb hash) */ 150 | unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) { 151 | unsigned int hash = (unsigned int)dict_hash_function_seed; 152 | 153 | while (len--) 154 | hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */ 155 | return hash; 156 | } 157 | 158 | /* ----------------------------- API implementation ------------------------- */ 159 | 160 | /* Reset a hash table already initialized with ht_init(). 161 | * NOTE: This function should only be called by ht_destroy(). */ 162 | static void _dictReset(dictht *ht) 163 | { 164 | ht->table = NULL; 165 | ht->size = 0; 166 | ht->sizemask = 0; 167 | ht->used = 0; 168 | } 169 | 170 | /* Create a new hash table */ 171 | dict *dictCreate(dictType *type, 172 | void *privDataPtr) 173 | { 174 | dict *d = malloc(sizeof(*d)); 175 | 176 | _dictInit(d,type,privDataPtr); 177 | return d; 178 | } 179 | 180 | /* Initialize the hash table */ 181 | int _dictInit(dict *d, dictType *type, 182 | void *privDataPtr) 183 | { 184 | _dictReset(&d->ht[0]); 185 | _dictReset(&d->ht[1]); 186 | d->type = type; 187 | d->privdata = privDataPtr; 188 | d->rehashidx = -1; 189 | d->iterators = 0; 190 | return DICT_OK; 191 | } 192 | 193 | /* Resize the table to the minimal size that contains all the elements, 194 | * but with the invariant of a USED/BUCKETS ratio near to <= 1 */ 195 | int dictResize(dict *d) 196 | { 197 | int minimal; 198 | 199 | if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR; 200 | minimal = d->ht[0].used; 201 | if (minimal < DICT_HT_INITIAL_SIZE) 202 | minimal = DICT_HT_INITIAL_SIZE; 203 | return dictExpand(d, minimal); 204 | } 205 | 206 | /* Expand or create the hash table */ 207 | int dictExpand(dict *d, unsigned long size) 208 | { 209 | dictht n; /* the new hash table */ 210 | unsigned long realsize = _dictNextPower(size); 211 | 212 | /* the size is invalid if it is smaller than the number of 213 | * elements already inside the hash table */ 214 | if (dictIsRehashing(d) || d->ht[0].used > size) 215 | return DICT_ERR; 216 | 217 | /* Allocate the new hash table and initialize all pointers to NULL */ 218 | n.size = realsize; 219 | n.sizemask = realsize-1; 220 | n.table = malloc(realsize*sizeof(dictEntry*)); 221 | n.used = 0; 222 | 223 | /* Is this the first initialization? If so it's not really a rehashing 224 | * we just set the first hash table so that it can accept keys. */ 225 | if (d->ht[0].table == NULL) { 226 | d->ht[0] = n; 227 | return DICT_OK; 228 | } 229 | 230 | /* Prepare a second hash table for incremental rehashing */ 231 | d->ht[1] = n; 232 | d->rehashidx = 0; 233 | return DICT_OK; 234 | } 235 | 236 | /* Performs N steps of incremental rehashing. Returns 1 if there are still 237 | * keys to move from the old to the new hash table, otherwise 0 is returned. 238 | * Note that a rehashing step consists in moving a bucket (that may have more 239 | * thank one key as we use chaining) from the old to the new hash table. */ 240 | int dictRehash(dict *d, int n) { 241 | if (!dictIsRehashing(d)) return 0; 242 | 243 | while(n--) { 244 | dictEntry *de, *nextde; 245 | 246 | /* Check if we already rehashed the whole table... */ 247 | if (d->ht[0].used == 0) { 248 | free(d->ht[0].table); 249 | d->ht[0] = d->ht[1]; 250 | _dictReset(&d->ht[1]); 251 | d->rehashidx = -1; 252 | return 0; 253 | } 254 | 255 | /* Note that rehashidx can't overflow as we are sure there are more 256 | * elements because ht[0].used != 0 */ 257 | assert(d->ht[0].size > (unsigned)d->rehashidx); 258 | while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++; 259 | de = d->ht[0].table[d->rehashidx]; 260 | /* Move all the keys in this bucket from the old to the new hash HT */ 261 | while(de) { 262 | unsigned int h; 263 | 264 | nextde = de->next; 265 | /* Get the index in the new hash table */ 266 | h = dictHashKey(d, de->key) & d->ht[1].sizemask; 267 | de->next = d->ht[1].table[h]; 268 | d->ht[1].table[h] = de; 269 | d->ht[0].used--; 270 | d->ht[1].used++; 271 | de = nextde; 272 | } 273 | d->ht[0].table[d->rehashidx] = NULL; 274 | d->rehashidx++; 275 | } 276 | return 1; 277 | } 278 | 279 | long long timeInMilliseconds(void) { 280 | struct timeval tv; 281 | 282 | gettimeofday(&tv,NULL); 283 | return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); 284 | } 285 | 286 | /* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */ 287 | int dictRehashMilliseconds(dict *d, int ms) { 288 | long long start = timeInMilliseconds(); 289 | int rehashes = 0; 290 | 291 | while(dictRehash(d,100)) { 292 | rehashes += 100; 293 | if (timeInMilliseconds()-start > ms) break; 294 | } 295 | return rehashes; 296 | } 297 | 298 | /* This function performs just a step of rehashing, and only if there are 299 | * no safe iterators bound to our hash table. When we have iterators in the 300 | * middle of a rehashing we can't mess with the two hash tables otherwise 301 | * some element can be missed or duplicated. 302 | * 303 | * This function is called by common lookup or update operations in the 304 | * dictionary so that the hash table automatically migrates from H1 to H2 305 | * while it is actively used. */ 306 | static void _dictRehashStep(dict *d) { 307 | if (d->iterators == 0) dictRehash(d,1); 308 | } 309 | 310 | /* Add an element to the target hash table */ 311 | int dictAdd(dict *d, void *key, void *val) 312 | { 313 | dictEntry *entry = dictAddRaw(d,key); 314 | 315 | if (!entry) return DICT_ERR; 316 | dictSetVal(d, entry, val); 317 | return DICT_OK; 318 | } 319 | 320 | /* Low level add. This function adds the entry but instead of setting 321 | * a value returns the dictEntry structure to the user, that will make 322 | * sure to fill the value field as he wishes. 323 | * 324 | * This function is also directly exposed to the user API to be called 325 | * mainly in order to store non-pointers inside the hash value, example: 326 | * 327 | * entry = dictAddRaw(dict,mykey); 328 | * if (entry != NULL) dictSetSignedIntegerVal(entry,1000); 329 | * 330 | * Return values: 331 | * 332 | * If key already exists NULL is returned. 333 | * If key was added, the hash entry is returned to be manipulated by the caller. 334 | */ 335 | dictEntry *dictAddRaw(dict *d, void *key) 336 | { 337 | int index; 338 | dictEntry *entry; 339 | dictht *ht; 340 | 341 | if (dictIsRehashing(d)) _dictRehashStep(d); 342 | 343 | /* Get the index of the new element, or -1 if 344 | * the element already exists. */ 345 | if ((index = _dictKeyIndex(d, key)) == -1) 346 | return NULL; 347 | 348 | /* Allocate the memory and store the new entry */ 349 | ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; 350 | entry = malloc(sizeof(*entry)); 351 | entry->next = ht->table[index]; 352 | ht->table[index] = entry; 353 | ht->used++; 354 | 355 | /* Set the hash entry fields. */ 356 | dictSetKey(d, entry, key); 357 | return entry; 358 | } 359 | 360 | /* Add an element, discarding the old if the key already exists. 361 | * Return 1 if the key was added from scratch, 0 if there was already an 362 | * element with such key and dictReplace() just performed a value update 363 | * operation. */ 364 | int dictReplace(dict *d, void *key, void *val) 365 | { 366 | dictEntry *entry, auxentry; 367 | 368 | /* Try to add the element. If the key 369 | * does not exists dictAdd will suceed. */ 370 | if (dictAdd(d, key, val) == DICT_OK) 371 | return 1; 372 | /* It already exists, get the entry */ 373 | entry = dictFind(d, key); 374 | /* Set the new value and free the old one. Note that it is important 375 | * to do that in this order, as the value may just be exactly the same 376 | * as the previous one. In this context, think to reference counting, 377 | * you want to increment (set), and then decrement (free), and not the 378 | * reverse. */ 379 | auxentry = *entry; 380 | dictSetVal(d, entry, val); 381 | dictFreeVal(d, &auxentry); 382 | return 0; 383 | } 384 | 385 | /* dictReplaceRaw() is simply a version of dictAddRaw() that always 386 | * returns the hash entry of the specified key, even if the key already 387 | * exists and can't be added (in that case the entry of the already 388 | * existing key is returned.) 389 | * 390 | * See dictAddRaw() for more information. */ 391 | dictEntry *dictReplaceRaw(dict *d, void *key) { 392 | dictEntry *entry = dictFind(d,key); 393 | 394 | return entry ? entry : dictAddRaw(d,key); 395 | } 396 | 397 | /* Search and remove an element */ 398 | static int dictGenericDelete(dict *d, const void *key, int nofree) 399 | { 400 | unsigned int h, idx; 401 | dictEntry *he, *prevHe; 402 | int table; 403 | 404 | if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */ 405 | if (dictIsRehashing(d)) _dictRehashStep(d); 406 | h = dictHashKey(d, key); 407 | 408 | for (table = 0; table <= 1; table++) { 409 | idx = h & d->ht[table].sizemask; 410 | he = d->ht[table].table[idx]; 411 | prevHe = NULL; 412 | while(he) { 413 | if (dictCompareKeys(d, key, he->key)) { 414 | /* Unlink the element from the list */ 415 | if (prevHe) 416 | prevHe->next = he->next; 417 | else 418 | d->ht[table].table[idx] = he->next; 419 | if (!nofree) { 420 | dictFreeKey(d, he); 421 | dictFreeVal(d, he); 422 | } 423 | free(he); 424 | d->ht[table].used--; 425 | return DICT_OK; 426 | } 427 | prevHe = he; 428 | he = he->next; 429 | } 430 | if (!dictIsRehashing(d)) break; 431 | } 432 | return DICT_ERR; /* not found */ 433 | } 434 | 435 | int dictDelete(dict *ht, const void *key) { 436 | return dictGenericDelete(ht,key,0); 437 | } 438 | 439 | int dictDeleteNoFree(dict *ht, const void *key) { 440 | return dictGenericDelete(ht,key,1); 441 | } 442 | 443 | /* Destroy an entire dictionary */ 444 | int _dictClear(dict *d, dictht *ht) 445 | { 446 | unsigned long i; 447 | 448 | /* Free all the elements */ 449 | for (i = 0; i < ht->size && ht->used > 0; i++) { 450 | dictEntry *he, *nextHe; 451 | 452 | if ((he = ht->table[i]) == NULL) continue; 453 | while(he) { 454 | nextHe = he->next; 455 | dictFreeKey(d, he); 456 | dictFreeVal(d, he); 457 | free(he); 458 | ht->used--; 459 | he = nextHe; 460 | } 461 | } 462 | /* Free the table and the allocated cache structure */ 463 | free(ht->table); 464 | /* Re-initialize the table */ 465 | _dictReset(ht); 466 | return DICT_OK; /* never fails */ 467 | } 468 | 469 | /* Clear & Release the hash table */ 470 | void dictRelease(dict *d) 471 | { 472 | _dictClear(d,&d->ht[0]); 473 | _dictClear(d,&d->ht[1]); 474 | free(d); 475 | } 476 | 477 | dictEntry *dictFind(dict *d, const void *key) 478 | { 479 | dictEntry *he; 480 | unsigned int h, idx, table; 481 | 482 | if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */ 483 | if (dictIsRehashing(d)) _dictRehashStep(d); 484 | h = dictHashKey(d, key); 485 | for (table = 0; table <= 1; table++) { 486 | idx = h & d->ht[table].sizemask; 487 | he = d->ht[table].table[idx]; 488 | while(he) { 489 | if (dictCompareKeys(d, key, he->key)) 490 | return he; 491 | he = he->next; 492 | } 493 | if (!dictIsRehashing(d)) return NULL; 494 | } 495 | return NULL; 496 | } 497 | 498 | void *dictFetchValue(dict *d, const void *key) { 499 | dictEntry *he; 500 | 501 | he = dictFind(d,key); 502 | return he ? dictGetVal(he) : NULL; 503 | } 504 | 505 | dictIterator *dictGetIterator(dict *d) 506 | { 507 | dictIterator *iter = malloc(sizeof(*iter)); 508 | 509 | iter->d = d; 510 | iter->table = 0; 511 | iter->index = -1; 512 | iter->safe = 0; 513 | iter->entry = NULL; 514 | iter->nextEntry = NULL; 515 | return iter; 516 | } 517 | 518 | dictIterator *dictGetSafeIterator(dict *d) { 519 | dictIterator *i = dictGetIterator(d); 520 | 521 | i->safe = 1; 522 | return i; 523 | } 524 | 525 | dictEntry *dictNext(dictIterator *iter) 526 | { 527 | while (1) { 528 | if (iter->entry == NULL) { 529 | dictht *ht = &iter->d->ht[iter->table]; 530 | if (iter->safe && iter->index == -1 && iter->table == 0) 531 | iter->d->iterators++; 532 | iter->index++; 533 | if (iter->index >= (signed) ht->size) { 534 | if (dictIsRehashing(iter->d) && iter->table == 0) { 535 | iter->table++; 536 | iter->index = 0; 537 | ht = &iter->d->ht[1]; 538 | } else { 539 | break; 540 | } 541 | } 542 | iter->entry = ht->table[iter->index]; 543 | } else { 544 | iter->entry = iter->nextEntry; 545 | } 546 | if (iter->entry) { 547 | /* We need to save the 'next' here, the iterator user 548 | * may delete the entry we are returning. */ 549 | iter->nextEntry = iter->entry->next; 550 | return iter->entry; 551 | } 552 | } 553 | return NULL; 554 | } 555 | 556 | void dictReleaseIterator(dictIterator *iter) 557 | { 558 | if (iter->safe && !(iter->index == -1 && iter->table == 0)) 559 | iter->d->iterators--; 560 | free(iter); 561 | } 562 | 563 | /* Return a random entry from the hash table. Useful to 564 | * implement randomized algorithms */ 565 | dictEntry *dictGetRandomKey(dict *d) 566 | { 567 | dictEntry *he, *orighe; 568 | unsigned int h; 569 | int listlen, listele; 570 | 571 | if (dictSize(d) == 0) return NULL; 572 | if (dictIsRehashing(d)) _dictRehashStep(d); 573 | if (dictIsRehashing(d)) { 574 | do { 575 | h = random() % (d->ht[0].size+d->ht[1].size); 576 | he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] : 577 | d->ht[0].table[h]; 578 | } while(he == NULL); 579 | } else { 580 | do { 581 | h = random() & d->ht[0].sizemask; 582 | he = d->ht[0].table[h]; 583 | } while(he == NULL); 584 | } 585 | 586 | /* Now we found a non empty bucket, but it is a linked 587 | * list and we need to get a random element from the list. 588 | * The only sane way to do so is counting the elements and 589 | * select a random index. */ 590 | listlen = 0; 591 | orighe = he; 592 | while(he) { 593 | he = he->next; 594 | listlen++; 595 | } 596 | listele = random() % listlen; 597 | he = orighe; 598 | while(listele--) he = he->next; 599 | return he; 600 | } 601 | 602 | /* ------------------------- private functions ------------------------------ */ 603 | 604 | /* Expand the hash table if needed */ 605 | static int _dictExpandIfNeeded(dict *d) 606 | { 607 | /* Incremental rehashing already in progress. Return. */ 608 | if (dictIsRehashing(d)) return DICT_OK; 609 | 610 | /* If the hash table is empty expand it to the initial size. */ 611 | if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); 612 | 613 | /* If we reached the 1:1 ratio, and we are allowed to resize the hash 614 | * table (global setting) or we should avoid it but the ratio between 615 | * elements/buckets is over the "safe" threshold, we resize doubling 616 | * the number of buckets. */ 617 | if (d->ht[0].used >= d->ht[0].size && 618 | (dict_can_resize || 619 | d->ht[0].used/d->ht[0].size > dict_force_resize_ratio)) 620 | { 621 | return dictExpand(d, d->ht[0].used*2); 622 | } 623 | return DICT_OK; 624 | } 625 | 626 | /* Our hash table capability is a power of two */ 627 | static unsigned long _dictNextPower(unsigned long size) 628 | { 629 | unsigned long i = DICT_HT_INITIAL_SIZE; 630 | 631 | if (size >= LONG_MAX) return LONG_MAX; 632 | while(1) { 633 | if (i >= size) 634 | return i; 635 | i *= 2; 636 | } 637 | } 638 | 639 | /* Returns the index of a free slot that can be populated with 640 | * an hash entry for the given 'key'. 641 | * If the key already exists, -1 is returned. 642 | * 643 | * Note that if we are in the process of rehashing the hash table, the 644 | * index is always returned in the context of the second (new) hash table. */ 645 | static int _dictKeyIndex(dict *d, const void *key) 646 | { 647 | unsigned int h, idx, table; 648 | dictEntry *he; 649 | 650 | /* Expand the hash table if needed */ 651 | if (_dictExpandIfNeeded(d) == DICT_ERR) 652 | return -1; 653 | /* Compute the key hash value */ 654 | h = dictHashKey(d, key); 655 | for (table = 0; table <= 1; table++) { 656 | idx = h & d->ht[table].sizemask; 657 | /* Search if this slot does not already contain the given key */ 658 | he = d->ht[table].table[idx]; 659 | while(he) { 660 | if (dictCompareKeys(d, key, he->key)) 661 | return -1; 662 | he = he->next; 663 | } 664 | if (!dictIsRehashing(d)) break; 665 | } 666 | return idx; 667 | } 668 | 669 | void dictEmpty(dict *d) { 670 | _dictClear(d,&d->ht[0]); 671 | _dictClear(d,&d->ht[1]); 672 | d->rehashidx = -1; 673 | d->iterators = 0; 674 | } 675 | 676 | void dictEnableResize(void) { 677 | dict_can_resize = 1; 678 | } 679 | 680 | void dictDisableResize(void) { 681 | dict_can_resize = 0; 682 | } 683 | 684 | #if 0 685 | 686 | /* The following is code that we don't use for Redis currently, but that is part 687 | of the library. */ 688 | 689 | /* ----------------------- Debugging ------------------------*/ 690 | 691 | #define DICT_STATS_VECTLEN 50 692 | static void _dictPrintStatsHt(dictht *ht) { 693 | unsigned long i, slots = 0, chainlen, maxchainlen = 0; 694 | unsigned long totchainlen = 0; 695 | unsigned long clvector[DICT_STATS_VECTLEN]; 696 | 697 | if (ht->used == 0) { 698 | printf("No stats available for empty dictionaries\n"); 699 | return; 700 | } 701 | 702 | for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0; 703 | for (i = 0; i < ht->size; i++) { 704 | dictEntry *he; 705 | 706 | if (ht->table[i] == NULL) { 707 | clvector[0]++; 708 | continue; 709 | } 710 | slots++; 711 | /* For each hash entry on this slot... */ 712 | chainlen = 0; 713 | he = ht->table[i]; 714 | while(he) { 715 | chainlen++; 716 | he = he->next; 717 | } 718 | clvector[(chainlen < DICT_STATS_VECTLEN) ? chainlen : (DICT_STATS_VECTLEN-1)]++; 719 | if (chainlen > maxchainlen) maxchainlen = chainlen; 720 | totchainlen += chainlen; 721 | } 722 | printf("Hash table stats:\n"); 723 | printf(" table size: %ld\n", ht->size); 724 | printf(" number of elements: %ld\n", ht->used); 725 | printf(" different slots: %ld\n", slots); 726 | printf(" max chain length: %ld\n", maxchainlen); 727 | printf(" avg chain length (counted): %.02f\n", (float)totchainlen/slots); 728 | printf(" avg chain length (computed): %.02f\n", (float)ht->used/slots); 729 | printf(" Chain length distribution:\n"); 730 | for (i = 0; i < DICT_STATS_VECTLEN-1; i++) { 731 | if (clvector[i] == 0) continue; 732 | printf(" %s%ld: %ld (%.02f%%)\n",(i == DICT_STATS_VECTLEN-1)?">= ":"", i, clvector[i], ((float)clvector[i]/ht->size)*100); 733 | } 734 | } 735 | 736 | void dictPrintStats(dict *d) { 737 | _dictPrintStatsHt(&d->ht[0]); 738 | if (dictIsRehashing(d)) { 739 | printf("-- Rehashing into ht[1]:\n"); 740 | _dictPrintStatsHt(&d->ht[1]); 741 | } 742 | } 743 | 744 | /* ----------------------- StringCopy Hash Table Type ------------------------*/ 745 | 746 | static unsigned int _dictStringCopyHTHashFunction(const void *key) 747 | { 748 | return dictGenHashFunction(key, strlen(key)); 749 | } 750 | 751 | static void *_dictStringDup(void *privdata, const void *key) 752 | { 753 | int len = strlen(key); 754 | char *copy = zmalloc(len+1); 755 | DICT_NOTUSED(privdata); 756 | 757 | memcpy(copy, key, len); 758 | copy[len] = '\0'; 759 | return copy; 760 | } 761 | 762 | static int _dictStringCopyHTKeyCompare(void *privdata, const void *key1, 763 | const void *key2) 764 | { 765 | DICT_NOTUSED(privdata); 766 | 767 | return strcmp(key1, key2) == 0; 768 | } 769 | 770 | static void _dictStringDestructor(void *privdata, void *key) 771 | { 772 | DICT_NOTUSED(privdata); 773 | 774 | zfree(key); 775 | } 776 | 777 | dictType dictTypeHeapStringCopyKey = { 778 | _dictStringCopyHTHashFunction, /* hash function */ 779 | _dictStringDup, /* key dup */ 780 | NULL, /* val dup */ 781 | _dictStringCopyHTKeyCompare, /* key compare */ 782 | _dictStringDestructor, /* key destructor */ 783 | NULL /* val destructor */ 784 | }; 785 | 786 | /* This is like StringCopy but does not auto-duplicate the key. 787 | * It's used for intepreter's shared strings. */ 788 | dictType dictTypeHeapStrings = { 789 | _dictStringCopyHTHashFunction, /* hash function */ 790 | NULL, /* key dup */ 791 | NULL, /* val dup */ 792 | _dictStringCopyHTKeyCompare, /* key compare */ 793 | _dictStringDestructor, /* key destructor */ 794 | NULL /* val destructor */ 795 | }; 796 | 797 | /* This is like StringCopy but also automatically handle dynamic 798 | * allocated C strings as values. */ 799 | dictType dictTypeHeapStringCopyKeyValue = { 800 | _dictStringCopyHTHashFunction, /* hash function */ 801 | _dictStringDup, /* key dup */ 802 | _dictStringDup, /* val dup */ 803 | _dictStringCopyHTKeyCompare, /* key compare */ 804 | _dictStringDestructor, /* key destructor */ 805 | _dictStringDestructor, /* val destructor */ 806 | }; 807 | #endif 808 | 809 | -------------------------------------------------------------------------------- /performance/redis-dict.h: -------------------------------------------------------------------------------- 1 | /* Hash Tables Implementation. 2 | * 3 | * This file implements in-memory hash tables with insert/del/replace/find/ 4 | * get-random-element operations. Hash tables will auto-resize if needed 5 | * tables of power of two in size are used, collisions are handled by 6 | * chaining. See the source code for more information... :) 7 | * 8 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * * Neither the name of Redis nor the names of its contributors may be used 20 | * to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #include 37 | 38 | #ifndef __DICT_H 39 | #define __DICT_H 40 | 41 | #define DICT_OK 0 42 | #define DICT_ERR 1 43 | 44 | /* Unused arguments generate annoying warnings... */ 45 | #define DICT_NOTUSED(V) ((void) V) 46 | 47 | typedef struct dictEntry { 48 | void *key; 49 | union { 50 | void *val; 51 | uint64_t u64; 52 | int64_t s64; 53 | } v; 54 | struct dictEntry *next; 55 | } dictEntry; 56 | 57 | typedef struct dictType { 58 | unsigned int (*hashFunction)(const void *key); 59 | void *(*keyDup)(void *privdata, const void *key); 60 | void *(*valDup)(void *privdata, const void *obj); 61 | int (*keyCompare)(void *privdata, const void *key1, const void *key2); 62 | void (*keyDestructor)(void *privdata, void *key); 63 | void (*valDestructor)(void *privdata, void *obj); 64 | } dictType; 65 | 66 | /* This is our hash table structure. Every dictionary has two of this as we 67 | * implement incremental rehashing, for the old to the new table. */ 68 | typedef struct dictht { 69 | dictEntry **table; 70 | unsigned long size; 71 | unsigned long sizemask; 72 | unsigned long used; 73 | } dictht; 74 | 75 | typedef struct dict { 76 | dictType *type; 77 | void *privdata; 78 | dictht ht[2]; 79 | int rehashidx; /* rehashing not in progress if rehashidx == -1 */ 80 | int iterators; /* number of iterators currently running */ 81 | } dict; 82 | 83 | /* If safe is set to 1 this is a safe iterator, that means, you can call 84 | * dictAdd, dictFind, and other functions against the dictionary even while 85 | * iterating. Otherwise it is a non safe iterator, and only dictNext() 86 | * should be called while iterating. */ 87 | typedef struct dictIterator { 88 | dict *d; 89 | int table, index, safe; 90 | dictEntry *entry, *nextEntry; 91 | } dictIterator; 92 | 93 | /* This is the initial size of every hash table */ 94 | #define DICT_HT_INITIAL_SIZE 4 95 | 96 | /* ------------------------------- Macros ------------------------------------*/ 97 | #define dictFreeVal(d, entry) \ 98 | if ((d)->type->valDestructor) \ 99 | (d)->type->valDestructor((d)->privdata, (entry)->v.val) 100 | 101 | #define dictSetVal(d, entry, _val_) do { \ 102 | if ((d)->type->valDup) \ 103 | entry->v.val = (d)->type->valDup((d)->privdata, _val_); \ 104 | else \ 105 | entry->v.val = (_val_); \ 106 | } while(0) 107 | 108 | #define dictSetSignedIntegerVal(entry, _val_) \ 109 | do { entry->v.s64 = _val_; } while(0) 110 | 111 | #define dictSetUnsignedIntegerVal(entry, _val_) \ 112 | do { entry->v.u64 = _val_; } while(0) 113 | 114 | #define dictFreeKey(d, entry) \ 115 | if ((d)->type->keyDestructor) \ 116 | (d)->type->keyDestructor((d)->privdata, (entry)->key) 117 | 118 | #define dictSetKey(d, entry, _key_) do { \ 119 | if ((d)->type->keyDup) \ 120 | entry->key = (d)->type->keyDup((d)->privdata, _key_); \ 121 | else \ 122 | entry->key = (_key_); \ 123 | } while(0) 124 | 125 | #define dictCompareKeys(d, key1, key2) \ 126 | (((d)->type->keyCompare) ? \ 127 | (d)->type->keyCompare((d)->privdata, key1, key2) : \ 128 | (key1) == (key2)) 129 | 130 | #define dictHashKey(d, key) (d)->type->hashFunction(key) 131 | #define dictGetKey(he) ((he)->key) 132 | #define dictGetVal(he) ((he)->v.val) 133 | #define dictGetSignedIntegerVal(he) ((he)->v.s64) 134 | #define dictGetUnsignedIntegerVal(he) ((he)->v.u64) 135 | #define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size) 136 | #define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used) 137 | #define dictIsRehashing(ht) ((ht)->rehashidx != -1) 138 | 139 | /* API */ 140 | dict *dictCreate(dictType *type, void *privDataPtr); 141 | int dictExpand(dict *d, unsigned long size); 142 | int dictAdd(dict *d, void *key, void *val); 143 | dictEntry *dictAddRaw(dict *d, void *key); 144 | int dictReplace(dict *d, void *key, void *val); 145 | dictEntry *dictReplaceRaw(dict *d, void *key); 146 | int dictDelete(dict *d, const void *key); 147 | int dictDeleteNoFree(dict *d, const void *key); 148 | void dictRelease(dict *d); 149 | dictEntry * dictFind(dict *d, const void *key); 150 | void *dictFetchValue(dict *d, const void *key); 151 | int dictResize(dict *d); 152 | dictIterator *dictGetIterator(dict *d); 153 | dictIterator *dictGetSafeIterator(dict *d); 154 | dictEntry *dictNext(dictIterator *iter); 155 | void dictReleaseIterator(dictIterator *iter); 156 | dictEntry *dictGetRandomKey(dict *d); 157 | void dictPrintStats(dict *d); 158 | unsigned int dictGenHashFunction(const void *key, int len); 159 | unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); 160 | void dictEmpty(dict *d); 161 | void dictEnableResize(void); 162 | void dictDisableResize(void); 163 | int dictRehash(dict *d, int n); 164 | int dictRehashMilliseconds(dict *d, int ms); 165 | void dictSetHashFunctionSeed(unsigned int initval); 166 | unsigned int dictGetHashFunctionSeed(void); 167 | 168 | /* Hash table types */ 169 | extern dictType dictTypeHeapStringCopyKey; 170 | extern dictType dictTypeHeapStrings; 171 | extern dictType dictTypeHeapStringCopyKeyValue; 172 | 173 | #endif /* __DICT_H */ 174 | -------------------------------------------------------------------------------- /performance/sds.c: -------------------------------------------------------------------------------- 1 | /* SDSLib, A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "sds.h" 37 | 38 | sds sdsnewlen(const void *init, size_t initlen) { 39 | struct sdshdr *sh; 40 | 41 | if (init) { 42 | sh = malloc(sizeof(struct sdshdr)+initlen+1); 43 | } else { 44 | sh = malloc(sizeof(struct sdshdr)+initlen+1); 45 | } 46 | if (sh == NULL) return NULL; 47 | sh->len = initlen; 48 | sh->free = 0; 49 | if (initlen && init) 50 | memcpy(sh->buf, init, initlen); 51 | sh->buf[initlen] = '\0'; 52 | return (char*)sh->buf; 53 | } 54 | 55 | sds sdsempty(void) { 56 | return sdsnewlen("",0); 57 | } 58 | 59 | sds sdsnew(const char *init) { 60 | size_t initlen = (init == NULL) ? 0 : strlen(init); 61 | return sdsnewlen(init, initlen); 62 | } 63 | 64 | sds sdsdup(const sds s) { 65 | return sdsnewlen(s, sdslen(s)); 66 | } 67 | 68 | void sdsfree(sds s) { 69 | if (s == NULL) return; 70 | free(s-sizeof(struct sdshdr)); 71 | } 72 | 73 | void sdsupdatelen(sds s) { 74 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 75 | int reallen = strlen(s); 76 | sh->free += (sh->len-reallen); 77 | sh->len = reallen; 78 | } 79 | 80 | void sdsclear(sds s) { 81 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 82 | sh->free += sh->len; 83 | sh->len = 0; 84 | sh->buf[0] = '\0'; 85 | } 86 | 87 | /* Enlarge the free space at the end of the sds string so that the caller 88 | * is sure that after calling this function can overwrite up to addlen 89 | * bytes after the end of the string, plus one more byte for nul term. 90 | * 91 | * Note: this does not change the *size* of the sds string as returned 92 | * by sdslen(), but only the free buffer space we have. */ 93 | sds sdsMakeRoomFor(sds s, size_t addlen) { 94 | struct sdshdr *sh, *newsh; 95 | size_t free = sdsavail(s); 96 | size_t len, newlen; 97 | 98 | if (free >= addlen) return s; 99 | len = sdslen(s); 100 | sh = (void*) (s-(sizeof(struct sdshdr))); 101 | newlen = (len+addlen); 102 | if (newlen < SDS_MAX_PREALLOC) 103 | newlen *= 2; 104 | else 105 | newlen += SDS_MAX_PREALLOC; 106 | newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1); 107 | if (newsh == NULL) return NULL; 108 | 109 | newsh->free = newlen - len; 110 | return newsh->buf; 111 | } 112 | 113 | /* Reallocate the sds string so that it has no free space at the end. The 114 | * contained string remains not altered, but next concatenation operations 115 | * will require a reallocation. */ 116 | sds sdsRemoveFreeSpace(sds s) { 117 | struct sdshdr *sh; 118 | 119 | sh = (void*) (s-(sizeof(struct sdshdr))); 120 | sh = realloc(sh, sizeof(struct sdshdr)+sh->len+1); 121 | sh->free = 0; 122 | return sh->buf; 123 | } 124 | 125 | size_t sdsAllocSize(sds s) { 126 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 127 | 128 | return sizeof(*sh)+sh->len+sh->free+1; 129 | } 130 | 131 | /* Increment the sds length and decrements the left free space at the 132 | * end of the string accordingly to 'incr'. Also set the null term 133 | * in the new end of the string. 134 | * 135 | * This function is used in order to fix the string length after the 136 | * user calls sdsMakeRoomFor(), writes something after the end of 137 | * the current string, and finally needs to set the new length. 138 | * 139 | * Note: it is possible to use a negative increment in order to 140 | * right-trim the string. 141 | * 142 | * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the 143 | * following schema to cat bytes coming from the kernel to the end of an 144 | * sds string new things without copying into an intermediate buffer: 145 | * 146 | * oldlen = sdslen(s); 147 | * s = sdsMakeRoomFor(s, BUFFER_SIZE); 148 | * nread = read(fd, s+oldlen, BUFFER_SIZE); 149 | * ... check for nread <= 0 and handle it ... 150 | * sdsIncrLen(s, nhread); 151 | */ 152 | void sdsIncrLen(sds s, int incr) { 153 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 154 | 155 | assert(sh->free >= incr); 156 | sh->len += incr; 157 | sh->free -= incr; 158 | assert(sh->free >= 0); 159 | s[sh->len] = '\0'; 160 | } 161 | 162 | /* Grow the sds to have the specified length. Bytes that were not part of 163 | * the original length of the sds will be set to zero. */ 164 | sds sdsgrowzero(sds s, size_t len) { 165 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 166 | size_t totlen, curlen = sh->len; 167 | 168 | if (len <= curlen) return s; 169 | s = sdsMakeRoomFor(s,len-curlen); 170 | if (s == NULL) return NULL; 171 | 172 | /* Make sure added region doesn't contain garbage */ 173 | sh = (void*)(s-(sizeof(struct sdshdr))); 174 | memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ 175 | totlen = sh->len+sh->free; 176 | sh->len = len; 177 | sh->free = totlen-sh->len; 178 | return s; 179 | } 180 | 181 | sds sdscatlen(sds s, const void *t, size_t len) { 182 | struct sdshdr *sh; 183 | size_t curlen = sdslen(s); 184 | 185 | s = sdsMakeRoomFor(s,len); 186 | if (s == NULL) return NULL; 187 | sh = (void*) (s-(sizeof(struct sdshdr))); 188 | memcpy(s+curlen, t, len); 189 | sh->len = curlen+len; 190 | sh->free = sh->free-len; 191 | s[curlen+len] = '\0'; 192 | return s; 193 | } 194 | 195 | sds sdscat(sds s, const char *t) { 196 | return sdscatlen(s, t, strlen(t)); 197 | } 198 | 199 | sds sdscatsds(sds s, const sds t) { 200 | return sdscatlen(s, t, sdslen(t)); 201 | } 202 | 203 | sds sdscpylen(sds s, const char *t, size_t len) { 204 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 205 | size_t totlen = sh->free+sh->len; 206 | 207 | if (totlen < len) { 208 | s = sdsMakeRoomFor(s,len-sh->len); 209 | if (s == NULL) return NULL; 210 | sh = (void*) (s-(sizeof(struct sdshdr))); 211 | totlen = sh->free+sh->len; 212 | } 213 | memcpy(s, t, len); 214 | s[len] = '\0'; 215 | sh->len = len; 216 | sh->free = totlen-len; 217 | return s; 218 | } 219 | 220 | sds sdscpy(sds s, const char *t) { 221 | return sdscpylen(s, t, strlen(t)); 222 | } 223 | 224 | sds sdscatvprintf(sds s, const char *fmt, va_list ap) { 225 | va_list cpy; 226 | char *buf, *t; 227 | size_t buflen = 16; 228 | 229 | while(1) { 230 | buf = malloc(buflen); 231 | if (buf == NULL) return NULL; 232 | buf[buflen-2] = '\0'; 233 | va_copy(cpy,ap); 234 | vsnprintf(buf, buflen, fmt, cpy); 235 | if (buf[buflen-2] != '\0') { 236 | free(buf); 237 | buflen *= 2; 238 | continue; 239 | } 240 | break; 241 | } 242 | t = sdscat(s, buf); 243 | free(buf); 244 | return t; 245 | } 246 | 247 | sds sdscatprintf(sds s, const char *fmt, ...) { 248 | va_list ap; 249 | char *t; 250 | va_start(ap, fmt); 251 | t = sdscatvprintf(s,fmt,ap); 252 | va_end(ap); 253 | return t; 254 | } 255 | 256 | sds sdstrim(sds s, const char *cset) { 257 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 258 | char *start, *end, *sp, *ep; 259 | size_t len; 260 | 261 | sp = start = s; 262 | ep = end = s+sdslen(s)-1; 263 | while(sp <= end && strchr(cset, *sp)) sp++; 264 | while(ep > start && strchr(cset, *ep)) ep--; 265 | len = (sp > ep) ? 0 : ((ep-sp)+1); 266 | if (sh->buf != sp) memmove(sh->buf, sp, len); 267 | sh->buf[len] = '\0'; 268 | sh->free = sh->free+(sh->len-len); 269 | sh->len = len; 270 | return s; 271 | } 272 | 273 | sds sdsrange(sds s, int start, int end) { 274 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 275 | size_t newlen, len = sdslen(s); 276 | 277 | if (len == 0) return s; 278 | if (start < 0) { 279 | start = len+start; 280 | if (start < 0) start = 0; 281 | } 282 | if (end < 0) { 283 | end = len+end; 284 | if (end < 0) end = 0; 285 | } 286 | newlen = (start > end) ? 0 : (end-start)+1; 287 | if (newlen != 0) { 288 | if (start >= (signed)len) { 289 | newlen = 0; 290 | } else if (end >= (signed)len) { 291 | end = len-1; 292 | newlen = (start > end) ? 0 : (end-start)+1; 293 | } 294 | } else { 295 | start = 0; 296 | } 297 | if (start && newlen) memmove(sh->buf, sh->buf+start, newlen); 298 | sh->buf[newlen] = 0; 299 | sh->free = sh->free+(sh->len-newlen); 300 | sh->len = newlen; 301 | return s; 302 | } 303 | 304 | void sdstolower(sds s) { 305 | int len = sdslen(s), j; 306 | 307 | for (j = 0; j < len; j++) s[j] = tolower(s[j]); 308 | } 309 | 310 | void sdstoupper(sds s) { 311 | int len = sdslen(s), j; 312 | 313 | for (j = 0; j < len; j++) s[j] = toupper(s[j]); 314 | } 315 | 316 | int sdscmp(const sds s1, const sds s2) { 317 | size_t l1, l2, minlen; 318 | int cmp; 319 | 320 | l1 = sdslen(s1); 321 | l2 = sdslen(s2); 322 | minlen = (l1 < l2) ? l1 : l2; 323 | cmp = memcmp(s1,s2,minlen); 324 | if (cmp == 0) return l1-l2; 325 | return cmp; 326 | } 327 | 328 | /* Split 's' with separator in 'sep'. An array 329 | * of sds strings is returned. *count will be set 330 | * by reference to the number of tokens returned. 331 | * 332 | * On out of memory, zero length string, zero length 333 | * separator, NULL is returned. 334 | * 335 | * Note that 'sep' is able to split a string using 336 | * a multi-character separator. For example 337 | * sdssplit("foo_-_bar","_-_"); will return two 338 | * elements "foo" and "bar". 339 | * 340 | * This version of the function is binary-safe but 341 | * requires length arguments. sdssplit() is just the 342 | * same function but for zero-terminated strings. 343 | */ 344 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { 345 | int elements = 0, slots = 5, start = 0, j; 346 | sds *tokens; 347 | 348 | if (seplen < 1 || len < 0) return NULL; 349 | 350 | tokens = malloc(sizeof(sds)*slots); 351 | if (tokens == NULL) return NULL; 352 | 353 | if (len == 0) { 354 | *count = 0; 355 | return tokens; 356 | } 357 | for (j = 0; j < (len-(seplen-1)); j++) { 358 | /* make sure there is room for the next element and the final one */ 359 | if (slots < elements+2) { 360 | sds *newtokens; 361 | 362 | slots *= 2; 363 | newtokens = realloc(tokens,sizeof(sds)*slots); 364 | if (newtokens == NULL) goto cleanup; 365 | tokens = newtokens; 366 | } 367 | /* search the separator */ 368 | if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { 369 | tokens[elements] = sdsnewlen(s+start,j-start); 370 | if (tokens[elements] == NULL) goto cleanup; 371 | elements++; 372 | start = j+seplen; 373 | j = j+seplen-1; /* skip the separator */ 374 | } 375 | } 376 | /* Add the final element. We are sure there is room in the tokens array. */ 377 | tokens[elements] = sdsnewlen(s+start,len-start); 378 | if (tokens[elements] == NULL) goto cleanup; 379 | elements++; 380 | *count = elements; 381 | return tokens; 382 | 383 | cleanup: 384 | { 385 | int i; 386 | for (i = 0; i < elements; i++) sdsfree(tokens[i]); 387 | free(tokens); 388 | *count = 0; 389 | return NULL; 390 | } 391 | } 392 | 393 | void sdsfreesplitres(sds *tokens, int count) { 394 | if (!tokens) return; 395 | while(count--) 396 | sdsfree(tokens[count]); 397 | free(tokens); 398 | } 399 | 400 | sds sdsfromlonglong(long long value) { 401 | char buf[32], *p; 402 | unsigned long long v; 403 | 404 | v = (value < 0) ? -value : value; 405 | p = buf+31; /* point to the last character */ 406 | do { 407 | *p-- = '0'+(v%10); 408 | v /= 10; 409 | } while(v); 410 | if (value < 0) *p-- = '-'; 411 | p++; 412 | return sdsnewlen(p,32-(p-buf)); 413 | } 414 | 415 | sds sdscatrepr(sds s, const char *p, size_t len) { 416 | s = sdscatlen(s,"\"",1); 417 | while(len--) { 418 | switch(*p) { 419 | case '\\': 420 | case '"': 421 | s = sdscatprintf(s,"\\%c",*p); 422 | break; 423 | case '\n': s = sdscatlen(s,"\\n",2); break; 424 | case '\r': s = sdscatlen(s,"\\r",2); break; 425 | case '\t': s = sdscatlen(s,"\\t",2); break; 426 | case '\a': s = sdscatlen(s,"\\a",2); break; 427 | case '\b': s = sdscatlen(s,"\\b",2); break; 428 | default: 429 | if (isprint(*p)) 430 | s = sdscatprintf(s,"%c",*p); 431 | else 432 | s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); 433 | break; 434 | } 435 | p++; 436 | } 437 | return sdscatlen(s,"\"",1); 438 | } 439 | 440 | /* Helper function for sdssplitargs() that returns non zero if 'c' 441 | * is a valid hex digit. */ 442 | int is_hex_digit(char c) { 443 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 444 | (c >= 'A' && c <= 'F'); 445 | } 446 | 447 | /* Helper function for sdssplitargs() that converts an hex digit into an 448 | * integer from 0 to 15 */ 449 | int hex_digit_to_int(char c) { 450 | switch(c) { 451 | case '0': return 0; 452 | case '1': return 1; 453 | case '2': return 2; 454 | case '3': return 3; 455 | case '4': return 4; 456 | case '5': return 5; 457 | case '6': return 6; 458 | case '7': return 7; 459 | case '8': return 8; 460 | case '9': return 9; 461 | case 'a': case 'A': return 10; 462 | case 'b': case 'B': return 11; 463 | case 'c': case 'C': return 12; 464 | case 'd': case 'D': return 13; 465 | case 'e': case 'E': return 14; 466 | case 'f': case 'F': return 15; 467 | default: return 0; 468 | } 469 | } 470 | 471 | /* Split a line into arguments, where every argument can be in the 472 | * following programming-language REPL-alike form: 473 | * 474 | * foo bar "newline are supported\n" and "\xff\x00otherstuff" 475 | * 476 | * The number of arguments is stored into *argc, and an array 477 | * of sds is returned. 478 | * 479 | * The caller should free the resulting array of sds strings with 480 | * sdsfreesplitres(). 481 | * 482 | * Note that sdscatrepr() is able to convert back a string into 483 | * a quoted string in the same format sdssplitargs() is able to parse. 484 | * 485 | * The function returns the allocated tokens on success, even when the 486 | * input string is empty, or NULL if the input contains unbalanced 487 | * quotes or closed quotes followed by non space characters 488 | * as in: "foo"bar or "foo' 489 | */ 490 | sds *sdssplitargs(const char *line, int *argc) { 491 | const char *p = line; 492 | char *current = NULL; 493 | char **vector = NULL; 494 | 495 | *argc = 0; 496 | while(1) { 497 | /* skip blanks */ 498 | while(*p && isspace(*p)) p++; 499 | if (*p) { 500 | /* get a token */ 501 | int inq=0; /* set to 1 if we are in "quotes" */ 502 | int insq=0; /* set to 1 if we are in 'single quotes' */ 503 | int done=0; 504 | 505 | if (current == NULL) current = sdsempty(); 506 | while(!done) { 507 | if (inq) { 508 | if (*p == '\\' && *(p+1) == 'x' && 509 | is_hex_digit(*(p+2)) && 510 | is_hex_digit(*(p+3))) 511 | { 512 | unsigned char byte; 513 | 514 | byte = (hex_digit_to_int(*(p+2))*16)+ 515 | hex_digit_to_int(*(p+3)); 516 | current = sdscatlen(current,(char*)&byte,1); 517 | p += 3; 518 | } else if (*p == '\\' && *(p+1)) { 519 | char c; 520 | 521 | p++; 522 | switch(*p) { 523 | case 'n': c = '\n'; break; 524 | case 'r': c = '\r'; break; 525 | case 't': c = '\t'; break; 526 | case 'b': c = '\b'; break; 527 | case 'a': c = '\a'; break; 528 | default: c = *p; break; 529 | } 530 | current = sdscatlen(current,&c,1); 531 | } else if (*p == '"') { 532 | /* closing quote must be followed by a space or 533 | * nothing at all. */ 534 | if (*(p+1) && !isspace(*(p+1))) goto err; 535 | done=1; 536 | } else if (!*p) { 537 | /* unterminated quotes */ 538 | goto err; 539 | } else { 540 | current = sdscatlen(current,p,1); 541 | } 542 | } else if (insq) { 543 | if (*p == '\\' && *(p+1) == '\'') { 544 | p++; 545 | current = sdscatlen(current,"'",1); 546 | } else if (*p == '\'') { 547 | /* closing quote must be followed by a space or 548 | * nothing at all. */ 549 | if (*(p+1) && !isspace(*(p+1))) goto err; 550 | done=1; 551 | } else if (!*p) { 552 | /* unterminated quotes */ 553 | goto err; 554 | } else { 555 | current = sdscatlen(current,p,1); 556 | } 557 | } else { 558 | switch(*p) { 559 | case ' ': 560 | case '\n': 561 | case '\r': 562 | case '\t': 563 | case '\0': 564 | done=1; 565 | break; 566 | case '"': 567 | inq=1; 568 | break; 569 | case '\'': 570 | insq=1; 571 | break; 572 | default: 573 | current = sdscatlen(current,p,1); 574 | break; 575 | } 576 | } 577 | if (*p) p++; 578 | } 579 | /* add the token to the vector */ 580 | vector = realloc(vector,((*argc)+1)*sizeof(char*)); 581 | vector[*argc] = current; 582 | (*argc)++; 583 | current = NULL; 584 | } else { 585 | /* Even on empty input string return something not NULL. */ 586 | if (vector == NULL) vector = malloc(sizeof(void*)); 587 | return vector; 588 | } 589 | } 590 | 591 | err: 592 | while((*argc)--) 593 | sdsfree(vector[*argc]); 594 | free(vector); 595 | if (current) sdsfree(current); 596 | *argc = 0; 597 | return NULL; 598 | } 599 | 600 | /* Modify the string substituting all the occurrences of the set of 601 | * characters specified in the 'from' string to the corresponding character 602 | * in the 'to' array. 603 | * 604 | * For instance: sdsmapchars(mystring, "ho", "01", 2) 605 | * will have the effect of turning the string "hello" into "0ell1". 606 | * 607 | * The function returns the sds string pointer, that is always the same 608 | * as the input pointer since no resize is needed. */ 609 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { 610 | size_t j, i, l = sdslen(s); 611 | 612 | for (j = 0; j < l; j++) { 613 | for (i = 0; i < setlen; i++) { 614 | if (s[j] == from[i]) { 615 | s[j] = to[i]; 616 | break; 617 | } 618 | } 619 | } 620 | return s; 621 | } 622 | 623 | #ifdef SDS_TEST_MAIN 624 | #include 625 | #include "testhelp.h" 626 | 627 | int main(void) { 628 | { 629 | struct sdshdr *sh; 630 | sds x = sdsnew("foo"), y; 631 | 632 | test_cond("Create a string and obtain the length", 633 | sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) 634 | 635 | sdsfree(x); 636 | x = sdsnewlen("foo",2); 637 | test_cond("Create a string with specified length", 638 | sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) 639 | 640 | x = sdscat(x,"bar"); 641 | test_cond("Strings concatenation", 642 | sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); 643 | 644 | x = sdscpy(x,"a"); 645 | test_cond("sdscpy() against an originally longer string", 646 | sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) 647 | 648 | x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); 649 | test_cond("sdscpy() against an originally shorter string", 650 | sdslen(x) == 33 && 651 | memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) 652 | 653 | sdsfree(x); 654 | x = sdscatprintf(sdsempty(),"%d",123); 655 | test_cond("sdscatprintf() seems working in the base case", 656 | sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) 657 | 658 | sdsfree(x); 659 | x = sdstrim(sdsnew("xxciaoyyy"),"xy"); 660 | test_cond("sdstrim() correctly trims characters", 661 | sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) 662 | 663 | y = sdsrange(sdsdup(x),1,1); 664 | test_cond("sdsrange(...,1,1)", 665 | sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) 666 | 667 | sdsfree(y); 668 | y = sdsrange(sdsdup(x),1,-1); 669 | test_cond("sdsrange(...,1,-1)", 670 | sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) 671 | 672 | sdsfree(y); 673 | y = sdsrange(sdsdup(x),-2,-1); 674 | test_cond("sdsrange(...,-2,-1)", 675 | sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) 676 | 677 | sdsfree(y); 678 | y = sdsrange(sdsdup(x),2,1); 679 | test_cond("sdsrange(...,2,1)", 680 | sdslen(y) == 0 && memcmp(y,"\0",1) == 0) 681 | 682 | sdsfree(y); 683 | y = sdsrange(sdsdup(x),1,100); 684 | test_cond("sdsrange(...,1,100)", 685 | sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) 686 | 687 | sdsfree(y); 688 | y = sdsrange(sdsdup(x),100,100); 689 | test_cond("sdsrange(...,100,100)", 690 | sdslen(y) == 0 && memcmp(y,"\0",1) == 0) 691 | 692 | sdsfree(y); 693 | sdsfree(x); 694 | x = sdsnew("foo"); 695 | y = sdsnew("foa"); 696 | test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) 697 | 698 | sdsfree(y); 699 | sdsfree(x); 700 | x = sdsnew("bar"); 701 | y = sdsnew("bar"); 702 | test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) 703 | 704 | sdsfree(y); 705 | sdsfree(x); 706 | x = sdsnew("aar"); 707 | y = sdsnew("bar"); 708 | test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) 709 | 710 | { 711 | int oldfree; 712 | 713 | sdsfree(x); 714 | x = sdsnew("0"); 715 | sh = (void*) (x-(sizeof(struct sdshdr))); 716 | test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0); 717 | x = sdsMakeRoomFor(x,1); 718 | sh = (void*) (x-(sizeof(struct sdshdr))); 719 | test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0); 720 | oldfree = sh->free; 721 | x[1] = '1'; 722 | sdsIncrLen(x,1); 723 | test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1'); 724 | test_cond("sdsIncrLen() -- len", sh->len == 2); 725 | test_cond("sdsIncrLen() -- free", sh->free == oldfree-1); 726 | } 727 | } 728 | test_report() 729 | return 0; 730 | } 731 | #endif 732 | -------------------------------------------------------------------------------- /performance/sds.h: -------------------------------------------------------------------------------- 1 | /* SDSLib, A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __SDS_H 32 | #define __SDS_H 33 | 34 | #define SDS_MAX_PREALLOC (1024*1024) 35 | 36 | #include 37 | #include 38 | 39 | typedef char *sds; 40 | 41 | struct sdshdr { 42 | int len; 43 | int free; 44 | char buf[]; 45 | }; 46 | 47 | static inline size_t sdslen(const sds s) { 48 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 49 | return sh->len; 50 | } 51 | 52 | static inline size_t sdsavail(const sds s) { 53 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 54 | return sh->free; 55 | } 56 | 57 | sds sdsnewlen(const void *init, size_t initlen); 58 | sds sdsnew(const char *init); 59 | sds sdsempty(); 60 | size_t sdslen(const sds s); 61 | sds sdsdup(const sds s); 62 | void sdsfree(sds s); 63 | size_t sdsavail(const sds s); 64 | sds sdsgrowzero(sds s, size_t len); 65 | sds sdscatlen(sds s, const void *t, size_t len); 66 | sds sdscat(sds s, const char *t); 67 | sds sdscatsds(sds s, const sds t); 68 | sds sdscpylen(sds s, const char *t, size_t len); 69 | sds sdscpy(sds s, const char *t); 70 | 71 | sds sdscatvprintf(sds s, const char *fmt, va_list ap); 72 | #ifdef __GNUC__ 73 | sds sdscatprintf(sds s, const char *fmt, ...) 74 | __attribute__((format(printf, 2, 3))); 75 | #else 76 | sds sdscatprintf(sds s, const char *fmt, ...); 77 | #endif 78 | 79 | sds sdstrim(sds s, const char *cset); 80 | sds sdsrange(sds s, int start, int end); 81 | void sdsupdatelen(sds s); 82 | void sdsclear(sds s); 83 | int sdscmp(const sds s1, const sds s2); 84 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); 85 | void sdsfreesplitres(sds *tokens, int count); 86 | void sdstolower(sds s); 87 | void sdstoupper(sds s); 88 | sds sdsfromlonglong(long long value); 89 | sds sdscatrepr(sds s, const char *p, size_t len); 90 | sds *sdssplitargs(const char *line, int *argc); 91 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); 92 | 93 | /* Low level functions exposed to the user API */ 94 | sds sdsMakeRoomFor(sds s, size_t addlen); 95 | void sdsIncrLen(sds s, int incr); 96 | sds sdsRemoveFreeSpace(sds s); 97 | size_t sdsAllocSize(sds s); 98 | 99 | #endif 100 | --------------------------------------------------------------------------------