| |
1 /* |
| |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
| |
3 * |
| |
4 * Copyright 2021 Mike Becker, Olaf Wintermann 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 * 1. Redistributions of source code must retain the above copyright |
| |
10 * notice, this list of conditions and the following disclaimer. |
| |
11 * |
| |
12 * 2. Redistributions in binary form must reproduce the above copyright |
| |
13 * notice, this list of conditions and the following disclaimer in the |
| |
14 * documentation and/or other materials provided with the distribution. |
| |
15 * |
| |
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| |
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| |
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| |
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| |
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| |
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| |
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| |
26 * POSSIBILITY OF SUCH DAMAGE. |
| |
27 */ |
| |
28 |
| |
29 #include "cx/list.h" |
| |
30 |
| |
31 #include <string.h> |
| |
32 |
| |
33 // <editor-fold desc="Store Pointers Functionality"> |
| |
34 |
| |
35 static _Thread_local cx_compare_func cx_pl_cmpfunc_impl; |
| |
36 |
| |
37 static int cx_pl_cmpfunc( |
| |
38 void const *l, |
| |
39 void const *r |
| |
40 ) { |
| |
41 void *const *lptr = l; |
| |
42 void *const *rptr = r; |
| |
43 void const *left = lptr == NULL ? NULL : *lptr; |
| |
44 void const *right = rptr == NULL ? NULL : *rptr; |
| |
45 return cx_pl_cmpfunc_impl(left, right); |
| |
46 } |
| |
47 |
| |
48 static void cx_pl_hack_cmpfunc(struct cx_list_s const *list) { |
| |
49 // cast away const - this is the hacky thing |
| |
50 struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; |
| |
51 cx_pl_cmpfunc_impl = l->cmpfunc; |
| |
52 l->cmpfunc = cx_pl_cmpfunc; |
| |
53 } |
| |
54 |
| |
55 static void cx_pl_unhack_cmpfunc(struct cx_list_s const *list) { |
| |
56 // cast away const - this is the hacky thing |
| |
57 struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; |
| |
58 l->cmpfunc = cx_pl_cmpfunc_impl; |
| |
59 } |
| |
60 |
| |
61 static void cx_pl_destructor(struct cx_list_s *list) { |
| |
62 list->climpl->destructor(list); |
| |
63 } |
| |
64 |
| |
65 static int cx_pl_insert_element( |
| |
66 struct cx_list_s *list, |
| |
67 size_t index, |
| |
68 void const *element |
| |
69 ) { |
| |
70 return list->climpl->insert_element(list, index, &element); |
| |
71 } |
| |
72 |
| |
73 static size_t cx_pl_insert_array( |
| |
74 struct cx_list_s *list, |
| |
75 size_t index, |
| |
76 void const *array, |
| |
77 size_t n |
| |
78 ) { |
| |
79 return list->climpl->insert_array(list, index, array, n); |
| |
80 } |
| |
81 |
| |
82 static int cx_pl_insert_iter( |
| |
83 struct cx_iterator_s *iter, |
| |
84 void const *elem, |
| |
85 int prepend |
| |
86 ) { |
| |
87 struct cx_list_s *list = iter->src_handle.m; |
| |
88 return list->climpl->insert_iter(iter, &elem, prepend); |
| |
89 } |
| |
90 |
| |
91 static int cx_pl_remove( |
| |
92 struct cx_list_s *list, |
| |
93 size_t index |
| |
94 ) { |
| |
95 return list->climpl->remove(list, index); |
| |
96 } |
| |
97 |
| |
98 static void cx_pl_clear(struct cx_list_s *list) { |
| |
99 list->climpl->clear(list); |
| |
100 } |
| |
101 |
| |
102 static int cx_pl_swap( |
| |
103 struct cx_list_s *list, |
| |
104 size_t i, |
| |
105 size_t j |
| |
106 ) { |
| |
107 return list->climpl->swap(list, i, j); |
| |
108 } |
| |
109 |
| |
110 static void *cx_pl_at( |
| |
111 struct cx_list_s const *list, |
| |
112 size_t index |
| |
113 ) { |
| |
114 void **ptr = list->climpl->at(list, index); |
| |
115 return ptr == NULL ? NULL : *ptr; |
| |
116 } |
| |
117 |
| |
118 static ssize_t cx_pl_find_remove( |
| |
119 struct cx_list_s *list, |
| |
120 void const *elem, |
| |
121 bool remove |
| |
122 ) { |
| |
123 cx_pl_hack_cmpfunc(list); |
| |
124 ssize_t ret = list->climpl->find_remove(list, &elem, remove); |
| |
125 cx_pl_unhack_cmpfunc(list); |
| |
126 return ret; |
| |
127 } |
| |
128 |
| |
129 static void cx_pl_sort(struct cx_list_s *list) { |
| |
130 cx_pl_hack_cmpfunc(list); |
| |
131 list->climpl->sort(list); |
| |
132 cx_pl_unhack_cmpfunc(list); |
| |
133 } |
| |
134 |
| |
135 static int cx_pl_compare( |
| |
136 struct cx_list_s const *list, |
| |
137 struct cx_list_s const *other |
| |
138 ) { |
| |
139 cx_pl_hack_cmpfunc(list); |
| |
140 int ret = list->climpl->compare(list, other); |
| |
141 cx_pl_unhack_cmpfunc(list); |
| |
142 return ret; |
| |
143 } |
| |
144 |
| |
145 static void cx_pl_reverse(struct cx_list_s *list) { |
| |
146 list->climpl->reverse(list); |
| |
147 } |
| |
148 |
| |
149 static void *cx_pl_iter_current(void const *it) { |
| |
150 struct cx_iterator_s const *iter = it; |
| |
151 void **ptr = iter->base.current_impl(it); |
| |
152 return ptr == NULL ? NULL : *ptr; |
| |
153 } |
| |
154 |
| |
155 static struct cx_iterator_s cx_pl_iterator( |
| |
156 struct cx_list_s const *list, |
| |
157 size_t index, |
| |
158 bool backwards |
| |
159 ) { |
| |
160 struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards); |
| |
161 iter.base.current_impl = iter.base.current; |
| |
162 iter.base.current = cx_pl_iter_current; |
| |
163 return iter; |
| |
164 } |
| |
165 |
| |
166 static cx_list_class cx_pointer_list_class = { |
| |
167 cx_pl_destructor, |
| |
168 cx_pl_insert_element, |
| |
169 cx_pl_insert_array, |
| |
170 cx_pl_insert_iter, |
| |
171 cx_pl_remove, |
| |
172 cx_pl_clear, |
| |
173 cx_pl_swap, |
| |
174 cx_pl_at, |
| |
175 cx_pl_find_remove, |
| |
176 cx_pl_sort, |
| |
177 cx_pl_compare, |
| |
178 cx_pl_reverse, |
| |
179 cx_pl_iterator, |
| |
180 }; |
| |
181 |
| |
182 void cxListStoreObjects(CxList *list) { |
| |
183 list->collection.store_pointer = false; |
| |
184 if (list->climpl != NULL) { |
| |
185 list->cl = list->climpl; |
| |
186 list->climpl = NULL; |
| |
187 } |
| |
188 } |
| |
189 |
| |
190 void cxListStorePointers(CxList *list) { |
| |
191 list->collection.elem_size = sizeof(void *); |
| |
192 list->collection.store_pointer = true; |
| |
193 list->climpl = list->cl; |
| |
194 list->cl = &cx_pointer_list_class; |
| |
195 } |
| |
196 |
| |
197 // </editor-fold> |
| |
198 |
| |
199 // <editor-fold desc="empty list implementation"> |
| |
200 |
| |
201 static void cx_emptyl_noop(__attribute__((__unused__)) CxList *list) { |
| |
202 // this is a noop, but MUST be implemented |
| |
203 } |
| |
204 |
| |
205 static void *cx_emptyl_at( |
| |
206 __attribute__((__unused__)) struct cx_list_s const *list, |
| |
207 __attribute__((__unused__)) size_t index |
| |
208 ) { |
| |
209 return NULL; |
| |
210 } |
| |
211 |
| |
212 static ssize_t cx_emptyl_find_remove( |
| |
213 __attribute__((__unused__)) struct cx_list_s *list, |
| |
214 __attribute__((__unused__)) void const *elem, |
| |
215 __attribute__((__unused__)) bool remove |
| |
216 ) { |
| |
217 return -1; |
| |
218 } |
| |
219 |
| |
220 static int cx_emptyl_compare( |
| |
221 __attribute__((__unused__)) struct cx_list_s const *list, |
| |
222 struct cx_list_s const *other |
| |
223 ) { |
| |
224 if (other->collection.size == 0) return 0; |
| |
225 return -1; |
| |
226 } |
| |
227 |
| |
228 static bool cx_emptyl_iter_valid(__attribute__((__unused__)) void const *iter) { |
| |
229 return false; |
| |
230 } |
| |
231 |
| |
232 static CxIterator cx_emptyl_iterator( |
| |
233 struct cx_list_s const *list, |
| |
234 size_t index, |
| |
235 __attribute__((__unused__)) bool backwards |
| |
236 ) { |
| |
237 CxIterator iter = {0}; |
| |
238 iter.src_handle.c = list; |
| |
239 iter.index = index; |
| |
240 iter.base.valid = cx_emptyl_iter_valid; |
| |
241 return iter; |
| |
242 } |
| |
243 |
| |
244 static cx_list_class cx_empty_list_class = { |
| |
245 cx_emptyl_noop, |
| |
246 NULL, |
| |
247 NULL, |
| |
248 NULL, |
| |
249 NULL, |
| |
250 cx_emptyl_noop, |
| |
251 NULL, |
| |
252 cx_emptyl_at, |
| |
253 cx_emptyl_find_remove, |
| |
254 cx_emptyl_noop, |
| |
255 cx_emptyl_compare, |
| |
256 cx_emptyl_noop, |
| |
257 cx_emptyl_iterator, |
| |
258 }; |
| |
259 |
| |
260 CxList cx_empty_list = { |
| |
261 { |
| |
262 NULL, |
| |
263 NULL, |
| |
264 0, |
| |
265 0, |
| |
266 NULL, |
| |
267 NULL, |
| |
268 NULL, |
| |
269 false |
| |
270 }, |
| |
271 &cx_empty_list_class, |
| |
272 NULL |
| |
273 }; |
| |
274 |
| |
275 CxList *const cxEmptyList = &cx_empty_list; |
| |
276 |
| |
277 // </editor-fold> |
| |
278 |
| |
279 void cxListDestroy(CxList *list) { |
| |
280 list->cl->destructor(list); |
| |
281 } |
| |
282 |
| |
283 int cxListCompare( |
| |
284 CxList const *list, |
| |
285 CxList const *other |
| |
286 ) { |
| |
287 if ( |
| |
288 // if one is storing pointers but the other is not |
| |
289 (list->collection.store_pointer ^ other->collection.store_pointer) || |
| |
290 |
| |
291 // if one class is wrapped but the other is not |
| |
292 ((list->climpl == NULL) ^ (other->climpl == NULL)) || |
| |
293 |
| |
294 // if the resolved compare functions are not the same |
| |
295 ((list->climpl != NULL ? list->climpl->compare : list->cl->compare) != |
| |
296 (other->climpl != NULL ? other->climpl->compare : other->cl->compare)) |
| |
297 ) { |
| |
298 // lists are definitely different - cannot use internal compare function |
| |
299 if (list->collection.size == other->collection.size) { |
| |
300 CxIterator left = list->cl->iterator(list, 0, false); |
| |
301 CxIterator right = other->cl->iterator(other, 0, false); |
| |
302 for (size_t i = 0; i < list->collection.size; i++) { |
| |
303 void *leftValue = cxIteratorCurrent(left); |
| |
304 void *rightValue = cxIteratorCurrent(right); |
| |
305 int d = list->collection.cmpfunc(leftValue, rightValue); |
| |
306 if (d != 0) { |
| |
307 return d; |
| |
308 } |
| |
309 cxIteratorNext(left); |
| |
310 cxIteratorNext(right); |
| |
311 } |
| |
312 return 0; |
| |
313 } else { |
| |
314 return list->collection.size < other->collection.size ? -1 : 1; |
| |
315 } |
| |
316 } else { |
| |
317 // lists are compatible |
| |
318 return list->cl->compare(list, other); |
| |
319 } |
| |
320 } |
| |
321 |
| |
322 CxIterator cxListMutIteratorAt( |
| |
323 CxList *list, |
| |
324 size_t index |
| |
325 ) { |
| |
326 CxIterator it = list->cl->iterator(list, index, false); |
| |
327 it.base.mutating = true; |
| |
328 return it; |
| |
329 } |
| |
330 |
| |
331 CxIterator cxListMutBackwardsIteratorAt( |
| |
332 CxList *list, |
| |
333 size_t index |
| |
334 ) { |
| |
335 CxIterator it = list->cl->iterator(list, index, true); |
| |
336 it.base.mutating = true; |
| |
337 return it; |
| |
338 } |