diff options
| -rw-r--r-- | src/bplist.c | 2 | ||||
| -rw-r--r-- | src/hashtable.c | 40 | ||||
| -rw-r--r-- | src/hashtable.h | 7 | ||||
| -rw-r--r-- | src/plist.c | 87 | ||||
| -rw-r--r-- | src/plist.h | 1 |
5 files changed, 119 insertions, 18 deletions
diff --git a/src/bplist.c b/src/bplist.c index 9cc380c..124b024 100644 --- a/src/bplist.c +++ b/src/bplist.c | |||
| @@ -1156,7 +1156,7 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) | |||
| 1156 | //list of objects | 1156 | //list of objects |
| 1157 | objects = ptr_array_new(256); | 1157 | objects = ptr_array_new(256); |
| 1158 | //hashtable to write only once same nodes | 1158 | //hashtable to write only once same nodes |
| 1159 | ref_table = hash_table_new(plist_data_hash, plist_data_compare); | 1159 | ref_table = hash_table_new(plist_data_hash, plist_data_compare, free); |
| 1160 | 1160 | ||
| 1161 | //serialize plist | 1161 | //serialize plist |
| 1162 | ser_s.objects = objects; | 1162 | ser_s.objects = objects; |
diff --git a/src/hashtable.c b/src/hashtable.c index 3568f3c..dd6dbfc 100644 --- a/src/hashtable.c +++ b/src/hashtable.c | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | * hashtable.c | 2 | * hashtable.c |
| 3 | * really simple hash table implementation | 3 | * really simple hash table implementation |
| 4 | * | 4 | * |
| 5 | * Copyright (c) 2011 Nikias Bassen, All Rights Reserved. | 5 | * Copyright (c) 2011-2016 Nikias Bassen, All Rights Reserved. |
| 6 | * | 6 | * |
| 7 | * This library is free software; you can redistribute it and/or | 7 | * This library is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
| @@ -20,7 +20,7 @@ | |||
| 20 | */ | 20 | */ |
| 21 | #include "hashtable.h" | 21 | #include "hashtable.h" |
| 22 | 22 | ||
| 23 | hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func) | 23 | hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, free_func_t free_func) |
| 24 | { | 24 | { |
| 25 | hashtable_t* ht = (hashtable_t*)malloc(sizeof(hashtable_t)); | 25 | hashtable_t* ht = (hashtable_t*)malloc(sizeof(hashtable_t)); |
| 26 | int i; | 26 | int i; |
| @@ -30,6 +30,7 @@ hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func) | |||
| 30 | ht->count = 0; | 30 | ht->count = 0; |
| 31 | ht->hash_func = hash_func; | 31 | ht->hash_func = hash_func; |
| 32 | ht->compare_func = compare_func; | 32 | ht->compare_func = compare_func; |
| 33 | ht->free_func = free_func; | ||
| 33 | return ht; | 34 | return ht; |
| 34 | } | 35 | } |
| 35 | 36 | ||
| @@ -42,7 +43,9 @@ void hash_table_destroy(hashtable_t *ht) | |||
| 42 | if (ht->entries[i]) { | 43 | if (ht->entries[i]) { |
| 43 | hashentry_t* e = ht->entries[i]; | 44 | hashentry_t* e = ht->entries[i]; |
| 44 | while (e) { | 45 | while (e) { |
| 45 | free(e->value); | 46 | if (ht->free_func) { |
| 47 | ht->free_func(e->value); | ||
| 48 | } | ||
| 46 | hashentry_t* old = e; | 49 | hashentry_t* old = e; |
| 47 | e = e->next; | 50 | e = e->next; |
| 48 | free(old); | 51 | free(old); |
| @@ -104,3 +107,34 @@ void* hash_table_lookup(hashtable_t* ht, void *key) | |||
| 104 | } | 107 | } |
| 105 | return NULL; | 108 | return NULL; |
| 106 | } | 109 | } |
| 110 | |||
| 111 | void hash_table_remove(hashtable_t* ht, void *key) | ||
| 112 | { | ||
| 113 | if (!ht || !key) return; | ||
| 114 | |||
| 115 | unsigned int hash = ht->hash_func(key); | ||
| 116 | |||
| 117 | int idx0 = hash & 0xFFF; | ||
| 118 | |||
| 119 | // get the idx0 list | ||
| 120 | hashentry_t* e = ht->entries[idx0]; | ||
| 121 | hashentry_t* last = e; | ||
| 122 | while (e) { | ||
| 123 | if (ht->compare_func(e->key, key)) { | ||
| 124 | // found element, remove it from the list | ||
| 125 | hashentry_t* old = e; | ||
| 126 | if (e == ht->entries[idx0]) { | ||
| 127 | ht->entries[idx0] = e->next; | ||
| 128 | } else { | ||
| 129 | last->next = e->next; | ||
| 130 | } | ||
| 131 | if (ht->free_func) { | ||
| 132 | ht->free_func(old->value); | ||
| 133 | } | ||
| 134 | free(old); | ||
| 135 | return; | ||
| 136 | } | ||
| 137 | last = e; | ||
| 138 | e = e->next; | ||
| 139 | } | ||
| 140 | } | ||
diff --git a/src/hashtable.h b/src/hashtable.h index 60a40ab..42d7b93 100644 --- a/src/hashtable.h +++ b/src/hashtable.h | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | * hashtable.h | 2 | * hashtable.h |
| 3 | * header file for really simple hash table implementation | 3 | * header file for really simple hash table implementation |
| 4 | * | 4 | * |
| 5 | * Copyright (c) 2011 Nikias Bassen, All Rights Reserved. | 5 | * Copyright (c) 2011-2016 Nikias Bassen, All Rights Reserved. |
| 6 | * | 6 | * |
| 7 | * This library is free software; you can redistribute it and/or | 7 | * This library is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
| @@ -30,18 +30,21 @@ typedef struct hashentry_t { | |||
| 30 | 30 | ||
| 31 | typedef unsigned int(*hash_func_t)(const void* key); | 31 | typedef unsigned int(*hash_func_t)(const void* key); |
| 32 | typedef int (*compare_func_t)(const void *a, const void *b); | 32 | typedef int (*compare_func_t)(const void *a, const void *b); |
| 33 | typedef void (*free_func_t)(void *ptr); | ||
| 33 | 34 | ||
| 34 | typedef struct hashtable_t { | 35 | typedef struct hashtable_t { |
| 35 | hashentry_t *entries[4096]; | 36 | hashentry_t *entries[4096]; |
| 36 | size_t count; | 37 | size_t count; |
| 37 | hash_func_t hash_func; | 38 | hash_func_t hash_func; |
| 38 | compare_func_t compare_func; | 39 | compare_func_t compare_func; |
| 40 | free_func_t free_func; | ||
| 39 | } hashtable_t; | 41 | } hashtable_t; |
| 40 | 42 | ||
| 41 | hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func); | 43 | hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, free_func_t free_func); |
| 42 | void hash_table_destroy(hashtable_t *ht); | 44 | void hash_table_destroy(hashtable_t *ht); |
| 43 | 45 | ||
| 44 | void hash_table_insert(hashtable_t* ht, void *key, void *value); | 46 | void hash_table_insert(hashtable_t* ht, void *key, void *value); |
| 45 | void* hash_table_lookup(hashtable_t* ht, void *key); | 47 | void* hash_table_lookup(hashtable_t* ht, void *key); |
| 48 | void hash_table_remove(hashtable_t* ht, void *key); | ||
| 46 | 49 | ||
| 47 | #endif | 50 | #endif |
diff --git a/src/plist.c b/src/plist.c index a3d88b9..6a267eb 100644 --- a/src/plist.c +++ b/src/plist.c | |||
| @@ -37,6 +37,7 @@ | |||
| 37 | 37 | ||
| 38 | #include <node.h> | 38 | #include <node.h> |
| 39 | #include <node_iterator.h> | 39 | #include <node_iterator.h> |
| 40 | #include <hashtable.h> | ||
| 40 | 41 | ||
| 41 | extern void plist_xml_init(void); | 42 | extern void plist_xml_init(void); |
| 42 | extern void plist_xml_deinit(void); | 43 | extern void plist_xml_deinit(void); |
| @@ -148,6 +149,31 @@ plist_data_t plist_new_plist_data(void) | |||
| 148 | return data; | 149 | return data; |
| 149 | } | 150 | } |
| 150 | 151 | ||
| 152 | static unsigned int dict_key_hash(const void *data) | ||
| 153 | { | ||
| 154 | plist_data_t keydata = (plist_data_t)data; | ||
| 155 | unsigned int hash = 5381; | ||
| 156 | size_t i; | ||
| 157 | char *str = keydata->strval; | ||
| 158 | for (i = 0; i < keydata->length; str++, i++) { | ||
| 159 | hash = ((hash << 5) + hash) + *str; | ||
| 160 | } | ||
| 161 | return hash; | ||
| 162 | } | ||
| 163 | |||
| 164 | static int dict_key_compare(const void* a, const void* b) | ||
| 165 | { | ||
| 166 | plist_data_t data_a = (plist_data_t)a; | ||
| 167 | plist_data_t data_b = (plist_data_t)b; | ||
| 168 | if (data_a->strval == NULL || data_b->strval == NULL) { | ||
| 169 | return FALSE; | ||
| 170 | } | ||
| 171 | if (data_a->length != data_b->length) { | ||
| 172 | return FALSE; | ||
| 173 | } | ||
| 174 | return (strcmp(data_a->strval, data_b->strval) == 0) ? TRUE : FALSE; | ||
| 175 | } | ||
| 176 | |||
| 151 | void plist_free_data(plist_data_t data) | 177 | void plist_free_data(plist_data_t data) |
| 152 | { | 178 | { |
| 153 | if (data) | 179 | if (data) |
| @@ -161,6 +187,9 @@ void plist_free_data(plist_data_t data) | |||
| 161 | case PLIST_DATA: | 187 | case PLIST_DATA: |
| 162 | free(data->buff); | 188 | free(data->buff); |
| 163 | break; | 189 | break; |
| 190 | case PLIST_DICT: | ||
| 191 | hash_table_destroy(data->hashtable); | ||
| 192 | break; | ||
| 164 | default: | 193 | default: |
| 165 | break; | 194 | break; |
| 166 | } | 195 | } |
| @@ -483,20 +512,27 @@ PLIST_API plist_t plist_dict_get_item(plist_t node, const char* key) | |||
| 483 | 512 | ||
| 484 | if (node && PLIST_DICT == plist_get_node_type(node)) | 513 | if (node && PLIST_DICT == plist_get_node_type(node)) |
| 485 | { | 514 | { |
| 486 | 515 | plist_data_t data = plist_get_data(node); | |
| 487 | plist_t current = NULL; | 516 | hashtable_t *ht = (hashtable_t*)data->hashtable; |
| 488 | for (current = (plist_t)node_first_child(node); | 517 | if (ht) { |
| 518 | struct plist_data_s sdata; | ||
| 519 | sdata.strval = (char*)key; | ||
| 520 | sdata.length = strlen(key); | ||
| 521 | ret = (plist_t)hash_table_lookup(ht, &sdata); | ||
| 522 | } else { | ||
| 523 | plist_t current = NULL; | ||
| 524 | for (current = (plist_t)node_first_child(node); | ||
| 489 | current; | 525 | current; |
| 490 | current = (plist_t)node_next_sibling(node_next_sibling(current))) | 526 | current = (plist_t)node_next_sibling(node_next_sibling(current))) |
| 491 | { | ||
| 492 | |||
| 493 | plist_data_t data = plist_get_data(current); | ||
| 494 | assert( PLIST_KEY == plist_get_node_type(current) ); | ||
| 495 | |||
| 496 | if (data && !strcmp(key, data->strval)) | ||
| 497 | { | 527 | { |
| 498 | ret = (plist_t)node_next_sibling(current); | 528 | data = plist_get_data(current); |
| 499 | break; | 529 | assert( PLIST_KEY == plist_get_node_type(current) ); |
| 530 | |||
| 531 | if (data && !strcmp(key, data->strval)) | ||
| 532 | { | ||
| 533 | ret = (plist_t)node_next_sibling(current); | ||
| 534 | break; | ||
| 535 | } | ||
| 500 | } | 536 | } |
| 501 | } | 537 | } |
| 502 | } | 538 | } |
| @@ -507,6 +543,7 @@ PLIST_API void plist_dict_set_item(plist_t node, const char* key, plist_t item) | |||
| 507 | { | 543 | { |
| 508 | if (node && PLIST_DICT == plist_get_node_type(node)) { | 544 | if (node && PLIST_DICT == plist_get_node_type(node)) { |
| 509 | node_t* old_item = plist_dict_get_item(node, key); | 545 | node_t* old_item = plist_dict_get_item(node, key); |
| 546 | plist_t key_node = NULL; | ||
| 510 | if (old_item) { | 547 | if (old_item) { |
| 511 | int idx = plist_free_node(old_item); | 548 | int idx = plist_free_node(old_item); |
| 512 | if (idx < 0) { | 549 | if (idx < 0) { |
| @@ -514,10 +551,32 @@ PLIST_API void plist_dict_set_item(plist_t node, const char* key, plist_t item) | |||
| 514 | } else { | 551 | } else { |
| 515 | node_insert(node, idx, item); | 552 | node_insert(node, idx, item); |
| 516 | } | 553 | } |
| 554 | key_node = node_prev_sibling(item); | ||
| 517 | } else { | 555 | } else { |
| 518 | node_attach(node, plist_new_key(key)); | 556 | key_node = plist_new_key(key); |
| 557 | node_attach(node, key_node); | ||
| 519 | node_attach(node, item); | 558 | node_attach(node, item); |
| 520 | } | 559 | } |
| 560 | |||
| 561 | hashtable_t *ht = ((plist_data_t)((node_t*)node)->data)->hashtable; | ||
| 562 | if (ht) { | ||
| 563 | /* store pointer to item in hash table */ | ||
| 564 | hash_table_insert(ht, (plist_data_t)((node_t*)key_node)->data, item); | ||
| 565 | } else { | ||
| 566 | if (((node_t*)node)->count > 500) { | ||
| 567 | /* make new hash table */ | ||
| 568 | ht = hash_table_new(dict_key_hash, dict_key_compare, NULL); | ||
| 569 | /* calculate the hashes for all entries we have so far */ | ||
| 570 | plist_t current = NULL; | ||
| 571 | for (current = (plist_t)node_first_child(node); | ||
| 572 | ht && current; | ||
| 573 | current = (plist_t)node_next_sibling(node_next_sibling(current))) | ||
| 574 | { | ||
| 575 | hash_table_insert(ht, ((node_t*)current)->data, node_next_sibling(current)); | ||
| 576 | } | ||
| 577 | ((plist_data_t)((node_t*)node)->data)->hashtable = ht; | ||
| 578 | } | ||
| 579 | } | ||
| 521 | } | 580 | } |
| 522 | return; | 581 | return; |
| 523 | } | 582 | } |
| @@ -535,6 +594,10 @@ PLIST_API void plist_dict_remove_item(plist_t node, const char* key) | |||
| 535 | if (old_item) | 594 | if (old_item) |
| 536 | { | 595 | { |
| 537 | plist_t key_node = node_prev_sibling(old_item); | 596 | plist_t key_node = node_prev_sibling(old_item); |
| 597 | hashtable_t* ht = ((plist_data_t)((node_t*)node)->data)->hashtable; | ||
| 598 | if (ht) { | ||
| 599 | hash_table_remove(ht, ((node_t*)key_node)->data); | ||
| 600 | } | ||
| 538 | plist_free(key_node); | 601 | plist_free(key_node); |
| 539 | plist_free(old_item); | 602 | plist_free(old_item); |
| 540 | } | 603 | } |
diff --git a/src/plist.h b/src/plist.h index da8f9ca..7bf62a8 100644 --- a/src/plist.h +++ b/src/plist.h | |||
| @@ -56,6 +56,7 @@ struct plist_data_s | |||
| 56 | double realval; | 56 | double realval; |
| 57 | char *strval; | 57 | char *strval; |
| 58 | uint8_t *buff; | 58 | uint8_t *buff; |
| 59 | void *hashtable; | ||
| 59 | }; | 60 | }; |
| 60 | uint64_t length; | 61 | uint64_t length; |
| 61 | plist_type type; | 62 | plist_type type; |
