summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2016-11-18 03:22:25 +0100
committerGravatar Nikias Bassen2016-11-18 03:22:25 +0100
commit582c59bf7dcf37270c2fd7e99b4982ebc9bcbc74 (patch)
tree91b8c594acd89e4c53a8c00bd730446bbfc0c6cf /src
parentf1f2bcebc8690c9b420288aeede2e52c5bf18ccd (diff)
downloadlibplist-582c59bf7dcf37270c2fd7e99b4982ebc9bcbc74.tar.gz
libplist-582c59bf7dcf37270c2fd7e99b4982ebc9bcbc74.tar.bz2
Improve plist_dict_set_item performance for large dictionaries with hash table
Diffstat (limited to 'src')
-rw-r--r--src/bplist.c2
-rw-r--r--src/hashtable.c40
-rw-r--r--src/hashtable.h7
-rw-r--r--src/plist.c87
-rw-r--r--src/plist.h1
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
23hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func) 23hashtable_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
111void 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
31typedef unsigned int(*hash_func_t)(const void* key); 31typedef unsigned int(*hash_func_t)(const void* key);
32typedef int (*compare_func_t)(const void *a, const void *b); 32typedef int (*compare_func_t)(const void *a, const void *b);
33typedef void (*free_func_t)(void *ptr);
33 34
34typedef struct hashtable_t { 35typedef 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
41hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func); 43hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, free_func_t free_func);
42void hash_table_destroy(hashtable_t *ht); 44void hash_table_destroy(hashtable_t *ht);
43 45
44void hash_table_insert(hashtable_t* ht, void *key, void *value); 46void hash_table_insert(hashtable_t* ht, void *key, void *value);
45void* hash_table_lookup(hashtable_t* ht, void *key); 47void* hash_table_lookup(hashtable_t* ht, void *key);
48void 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
41extern void plist_xml_init(void); 42extern void plist_xml_init(void);
42extern void plist_xml_deinit(void); 43extern 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
152static 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
164static 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
151void plist_free_data(plist_data_t data) 177void 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;