25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 * POSSIBILITY OF SUCH DAMAGE. |
26 * POSSIBILITY OF SUCH DAMAGE. |
27 */ |
27 */ |
28 |
28 |
29 #include "cx/kv_list.h" |
29 #include "cx/kv_list.h" |
|
30 #include "cx/hash_map.h" |
|
31 #include "cx/linked_list.h" |
|
32 |
|
33 typedef struct cx_kv_list_s cx_kv_list; |
|
34 |
|
35 struct cx_kv_list_map_s { |
|
36 struct cx_hash_map_s map_base; |
|
37 /** Back-reference to the list. */ |
|
38 cx_kv_list *list; |
|
39 }; |
|
40 |
|
41 /** The list aspect (must have the same layout as the normal linked list). */ |
|
42 struct cx_kv_list_list_s { |
|
43 struct cx_list_s list_base; |
|
44 void *begin; |
|
45 void *end; |
|
46 }; |
|
47 |
|
48 struct cx_kv_list_s { |
|
49 struct cx_kv_list_list_s list; |
|
50 /** The lookup map - stores pointers to the nodes. */ |
|
51 struct cx_kv_list_map_s *map; |
|
52 const cx_list_class *list_methods; |
|
53 const cx_map_class *map_methods; |
|
54 }; |
|
55 |
|
56 static void cx_kvl_deallocate(struct cx_list_s *list) { |
|
57 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
58 // free the map first |
|
59 kv_list->map_methods->deallocate(&kv_list->map->map_base.base); |
|
60 kv_list->list_methods->deallocate(list); |
|
61 } |
|
62 |
|
63 static void *cx_kvl_insert_element( |
|
64 struct cx_list_s *list, |
|
65 size_t index, |
|
66 const void *data |
|
67 ) { |
|
68 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
69 // TODO: trick the base method by adding the required space for the key to the elem_size |
|
70 return kv_list->list_methods->insert_element(list, index, data); |
|
71 } |
|
72 |
|
73 static size_t cx_kvl_insert_array( |
|
74 struct cx_list_s *list, |
|
75 size_t index, |
|
76 const void *data, |
|
77 size_t n |
|
78 ) { |
|
79 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
80 // TODO: trick the base method by adding the required space for the key to the elem_size |
|
81 return kv_list->list_methods->insert_array(list, index, data, n); |
|
82 } |
|
83 |
|
84 static size_t cx_kvl_insert_sorted( |
|
85 struct cx_list_s *list, |
|
86 const void *sorted_data, |
|
87 size_t n |
|
88 ) { |
|
89 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
90 // TODO: trick the base method by adding the required space for the key to the elem_size |
|
91 return kv_list->list_methods->insert_sorted(list, sorted_data, n); |
|
92 } |
|
93 |
|
94 static int cx_kvl_insert_iter( |
|
95 struct cx_iterator_s *iter, |
|
96 const void *elem, |
|
97 int prepend |
|
98 ) { |
|
99 cx_kv_list *kv_list = (cx_kv_list*)iter->src_handle.m; |
|
100 // TODO: trick the base method by adding the required space for the key to the elem_size |
|
101 return kv_list->list_methods->insert_iter(iter, elem, prepend); |
|
102 } |
|
103 |
|
104 static size_t cx_kvl_remove( |
|
105 struct cx_list_s *list, |
|
106 size_t index, |
|
107 size_t num, |
|
108 void *targetbuf |
|
109 ) { |
|
110 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
111 // TODO: always use the target buffer to get the element first, |
|
112 // then obtain the key, remove it from the map, |
|
113 // and finally call any destructors manually |
|
114 return kv_list->list_methods->remove(list, index, num, targetbuf); |
|
115 } |
|
116 |
|
117 static void cx_kvl_clear(struct cx_list_s *list) { |
|
118 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
119 kv_list->list_methods->clear(list); |
|
120 // also clear all lookup entries |
|
121 cxMapClear(&kv_list->map->map_base.base); |
|
122 } |
|
123 |
|
124 static int cx_kvl_swap( |
|
125 struct cx_list_s *list, |
|
126 size_t i, |
|
127 size_t j |
|
128 ) { |
|
129 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
130 return kv_list->list_methods->swap(list, i, j); |
|
131 } |
|
132 |
|
133 static void *cx_kvl_at( |
|
134 const struct cx_list_s *list, |
|
135 size_t index |
|
136 ) { |
|
137 const cx_kv_list *kv_list = (const cx_kv_list*)list; |
|
138 return kv_list->list_methods->at(list, index); |
|
139 } |
|
140 |
|
141 static size_t cx_kvl_find_remove( |
|
142 struct cx_list_s *list, |
|
143 const void *elem, |
|
144 bool remove |
|
145 ) { |
|
146 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
147 // TODO: implement removal of the key in the map |
|
148 return kv_list->list_methods->find_remove(list, elem, remove); |
|
149 } |
|
150 |
|
151 static void cx_kvl_sort(struct cx_list_s *list) { |
|
152 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
153 kv_list->list_methods->sort(list); |
|
154 } |
|
155 |
|
156 static int cx_kvl_compare( |
|
157 const struct cx_list_s *list, |
|
158 const struct cx_list_s *other |
|
159 ) { |
|
160 // TODO: make it so that comparison with normal linked lists is also optimized |
|
161 const cx_kv_list *kv_list = (const cx_kv_list*)list; |
|
162 return kv_list->list_methods->compare(list, other); |
|
163 } |
|
164 |
|
165 static void cx_kvl_reverse(struct cx_list_s *list) { |
|
166 cx_kv_list *kv_list = (cx_kv_list*)list; |
|
167 kv_list->list_methods->reverse(list); |
|
168 } |
|
169 |
|
170 static struct cx_iterator_s cx_kvl_iterator( |
|
171 const struct cx_list_s *list, |
|
172 size_t index, |
|
173 bool backward |
|
174 ) { |
|
175 const cx_kv_list *kv_list = (const cx_kv_list*)list; |
|
176 // TODO: cannot really forward, because mutating iterators must be able to remove the element |
|
177 return kv_list->list_methods->iterator(list, index, backward); |
|
178 } |
|
179 |
|
180 static cx_list_class cx_kv_list_class = { |
|
181 cx_kvl_deallocate, |
|
182 cx_kvl_insert_element, |
|
183 cx_kvl_insert_array, |
|
184 cx_kvl_insert_sorted, |
|
185 cx_kvl_insert_iter, |
|
186 cx_kvl_remove, |
|
187 cx_kvl_clear, |
|
188 cx_kvl_swap, |
|
189 cx_kvl_at, |
|
190 cx_kvl_find_remove, |
|
191 cx_kvl_sort, |
|
192 cx_kvl_compare, |
|
193 cx_kvl_reverse, |
|
194 cx_kvl_iterator, |
|
195 }; |
30 |
196 |
31 CxList *cxKvListCreate( |
197 CxList *cxKvListCreate( |
32 const CxAllocator *allocator, |
198 const CxAllocator *allocator, |
33 cx_compare_func comparator, |
199 cx_compare_func comparator, |
34 size_t elem_size |
200 size_t elem_size |
35 ) { |
201 ) { |
36 return NULL; |
202 if (allocator == NULL) { |
|
203 allocator = cxDefaultAllocator; |
|
204 } |
|
205 |
|
206 // create a normal linked list and a normal hash map, first |
|
207 CxList *list = cxLinkedListCreate(allocator, comparator, elem_size); |
|
208 if (list == NULL) return NULL; // LCOV_EXCL_LINE |
|
209 CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0); |
|
210 if (map == NULL) { // LCOV_EXCL_START |
|
211 cxListFree(list); |
|
212 return NULL; |
|
213 } // LCOV_EXCL_STOP |
|
214 |
|
215 // reallocate the map to add memory for the list back-reference |
|
216 struct cx_kv_list_map_s *kv_map = cxRealloc(allocator, map, sizeof(struct cx_kv_list_map_s)); |
|
217 |
|
218 // reallocate the list to add memory for storing the metadata |
|
219 cx_kv_list *kv_list = cxRealloc(allocator, list, sizeof(cx_kv_list)); |
|
220 |
|
221 // if any of the reallocations failed, we bail out |
|
222 if (kv_map != NULL && kv_list != NULL) { |
|
223 map = (CxMap*) kv_map; |
|
224 list = (CxList*) kv_list; |
|
225 } else { // LCOV_EXCL_START |
|
226 cxListFree(list); |
|
227 cxMapFree(map); |
|
228 return NULL; |
|
229 } // LCOV_EXCL_STOP |
|
230 |
|
231 // combine the list and the map aspect |
|
232 kv_list->map = kv_map; |
|
233 kv_map->list = kv_list; |
|
234 |
|
235 // remember the base methods and override them |
|
236 kv_list->list_methods = list->cl; |
|
237 kv_list->map_methods = map->cl; |
|
238 list->cl = &cx_kv_list_class; |
|
239 // TODO: override all map members |
|
240 // and remember to set the sorted flag of the list to false in the put/emplace methods! |
|
241 |
|
242 return list; |
37 } |
243 } |
38 |
244 |
39 CxMap *cxKvListCreateAsMap( |
245 CxMap *cxKvListCreateAsMap( |
40 const CxAllocator *allocator, |
246 const CxAllocator *allocator, |
41 cx_compare_func comparator, |
247 cx_compare_func comparator, |