| 48 } |
48 } |
| 49 return cxReallocDefault(array, n); |
49 return cxReallocDefault(array, n); |
| 50 } |
50 } |
| 51 |
51 |
| 52 CxArrayReallocator cx_array_default_reallocator_impl = { |
52 CxArrayReallocator cx_array_default_reallocator_impl = { |
| 53 cx_array_default_realloc, NULL, NULL, 0, 0 |
53 cx_array_default_realloc, NULL, NULL |
| 54 }; |
54 }; |
| 55 |
55 |
| 56 CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl; |
56 CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl; |
| 57 |
57 |
| 58 // Stack-aware array reallocator |
58 // Stack-aware array reallocator |
| 70 errno = EOVERFLOW; |
70 errno = EOVERFLOW; |
| 71 return NULL; |
71 return NULL; |
| 72 } |
72 } |
| 73 |
73 |
| 74 // retrieve the pointer to the actual allocator |
74 // retrieve the pointer to the actual allocator |
| 75 const CxAllocator *al = alloc->ptr1; |
75 const CxAllocator *al = alloc->allocator; |
| 76 |
76 |
| 77 // check if the array is still located on the stack |
77 // check if the array is still located on the stack |
| 78 void *newmem; |
78 void *newmem; |
| 79 if (array == alloc->ptr2) { |
79 if (array == alloc->stack_ptr) { |
| 80 newmem = cxMalloc(al, n); |
80 newmem = cxMalloc(al, n); |
| 81 if (newmem != NULL && array != NULL) { |
81 if (newmem != NULL && array != NULL) { |
| 82 memcpy(newmem, array, old_capacity*elem_size); |
82 memcpy(newmem, array, old_capacity*elem_size); |
| 83 } |
83 } |
| 84 } else { |
84 } else { |
| 87 return newmem; |
87 return newmem; |
| 88 } |
88 } |
| 89 |
89 |
| 90 struct cx_array_reallocator_s cx_array_reallocator( |
90 struct cx_array_reallocator_s cx_array_reallocator( |
| 91 const struct cx_allocator_s *allocator, |
91 const struct cx_allocator_s *allocator, |
| 92 const void *stackmem |
92 const void *stack_ptr |
| 93 ) { |
93 ) { |
| 94 if (allocator == NULL) { |
94 if (allocator == NULL) { |
| 95 allocator = cxDefaultAllocator; |
95 allocator = cxDefaultAllocator; |
| 96 } |
96 } |
| 97 return (struct cx_array_reallocator_s) { |
97 return (struct cx_array_reallocator_s) { |
| 98 cx_array_advanced_realloc, |
98 cx_array_advanced_realloc, |
| 99 (void*) allocator, (void*) stackmem, |
99 allocator, stack_ptr, |
| 100 0, 0 |
|
| 101 }; |
100 }; |
| 102 } |
101 } |
| 103 |
102 |
| 104 // LOW LEVEL ARRAY LIST FUNCTIONS |
103 // LOW LEVEL ARRAY LIST FUNCTIONS |
| 105 |
104 |
| 106 static size_t cx_array_align_capacity( |
105 /** |
| 107 size_t cap, |
106 * Intelligently calculates a new capacity, reserving some more |
| 108 size_t alignment, |
107 * elements than required to prevent too many allocations. |
| 109 size_t max |
108 * |
| 110 ) { |
109 * @param current_capacity the current capacity of the array |
| 111 if (cap > max - alignment) { |
110 * @param needed_capacity the required capacity of the array |
| 112 return cap; |
111 * @param maximum_capacity the maximum capacity (given by the data type) |
| |
112 * @return the new capacity |
| |
113 */ |
| |
114 static size_t cx_array_grow_capacity( |
| |
115 size_t current_capacity, |
| |
116 size_t needed_capacity, |
| |
117 size_t maximum_capacity |
| |
118 ) { |
| |
119 if (current_capacity >= needed_capacity) { |
| |
120 return current_capacity; |
| |
121 } |
| |
122 size_t cap = needed_capacity; |
| |
123 size_t alignment; |
| |
124 if (cap < 128) alignment = 16; |
| |
125 else if (cap < 1024) alignment = 64; |
| |
126 else if (cap < 8192) alignment = 512; |
| |
127 else alignment = 1024; |
| |
128 |
| |
129 if (cap - 1 > maximum_capacity - alignment) { |
| |
130 return maximum_capacity; |
| 113 } else { |
131 } else { |
| 114 return cap - (cap % alignment) + alignment; |
132 return cap - (cap % alignment) + alignment; |
| 115 } |
133 } |
| 116 } |
134 } |
| 117 |
135 |
| 175 // determine new capacity |
193 // determine new capacity |
| 176 size_t newcap = oldsize + elem_count; |
194 size_t newcap = oldsize + elem_count; |
| 177 |
195 |
| 178 // reallocate if possible |
196 // reallocate if possible |
| 179 if (newcap > oldcap) { |
197 if (newcap > oldcap) { |
| 180 // calculate new capacity (next number divisible by 16) |
|
| 181 newcap = cx_array_align_capacity(newcap, 16, max_size); |
|
| 182 |
|
| 183 // perform reallocation |
|
| 184 void *newmem = reallocator->realloc( |
198 void *newmem = reallocator->realloc( |
| 185 *array, oldcap, newcap, elem_size, reallocator |
199 *array, oldcap, newcap, elem_size, reallocator |
| 186 ); |
200 ); |
| 187 if (newmem == NULL) { |
201 if (newmem == NULL) { |
| 188 return 1; // LCOV_EXCL_LINE |
202 return 1; // LCOV_EXCL_LINE |
| 268 errno = EOVERFLOW; |
282 errno = EOVERFLOW; |
| 269 return 1; |
283 return 1; |
| 270 } |
284 } |
| 271 |
285 |
| 272 // check if resize is required |
286 // check if resize is required |
| 273 size_t minsize = index + elem_count; |
287 const size_t minsize = index + elem_count; |
| 274 size_t newsize = oldsize < minsize ? minsize : oldsize; |
288 const size_t newsize = oldsize < minsize ? minsize : oldsize; |
| 275 |
289 |
| 276 // reallocate if possible |
290 // reallocate if necessary |
| 277 size_t newcap = oldcap; |
291 const size_t newcap = cx_array_grow_capacity(oldcap, newsize, max_size); |
| 278 if (newsize > oldcap) { |
292 if (newcap > oldcap) { |
| 279 // check, if we need to repair the src pointer |
293 // check if we need to repair the src pointer |
| 280 uintptr_t targetaddr = (uintptr_t) *target; |
294 uintptr_t targetaddr = (uintptr_t) *target; |
| 281 uintptr_t srcaddr = (uintptr_t) src; |
295 uintptr_t srcaddr = (uintptr_t) src; |
| 282 bool repairsrc = targetaddr <= srcaddr |
296 bool repairsrc = targetaddr <= srcaddr |
| 283 && srcaddr < targetaddr + oldcap * elem_size; |
297 && srcaddr < targetaddr + oldcap * elem_size; |
| 284 |
|
| 285 // calculate new capacity (next number divisible by 16) |
|
| 286 newcap = cx_array_align_capacity(newsize, 16, max_size); |
|
| 287 assert(newcap > newsize); |
|
| 288 |
298 |
| 289 // perform reallocation |
299 // perform reallocation |
| 290 void *newmem = reallocator->realloc( |
300 void *newmem = reallocator->realloc( |
| 291 *target, oldcap, newcap, elem_size, reallocator |
301 *target, oldcap, newcap, elem_size, reallocator |
| 292 ); |
302 ); |
| 366 errno = EOVERFLOW; |
376 errno = EOVERFLOW; |
| 367 return 1; |
377 return 1; |
| 368 } |
378 } |
| 369 |
379 |
| 370 // store some counts |
380 // store some counts |
| 371 size_t old_size = *size; |
381 const size_t old_size = *size; |
| 372 size_t old_capacity = *capacity; |
382 const size_t old_capacity = *capacity; |
| 373 // the necessary capacity is the worst case assumption, including duplicates |
383 // the necessary capacity is the worst case assumption, including duplicates |
| 374 size_t needed_capacity = old_size + elem_count; |
384 const size_t needed_capacity = cx_array_grow_capacity(old_capacity, |
| |
385 old_size + elem_count, SIZE_MAX); |
| 375 |
386 |
| 376 // if we need more than we have, try a reallocation |
387 // if we need more than we have, try a reallocation |
| 377 if (needed_capacity > old_capacity) { |
388 if (needed_capacity > old_capacity) { |
| 378 size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX); |
|
| 379 void *new_mem = reallocator->realloc( |
389 void *new_mem = reallocator->realloc( |
| 380 *target, old_capacity, new_capacity, elem_size, reallocator |
390 *target, old_capacity, needed_capacity, elem_size, reallocator |
| 381 ); |
391 ); |
| 382 if (new_mem == NULL) { |
392 if (new_mem == NULL) { |
| 383 // give it up right away, there is no contract |
393 // give it up right away, there is no contract |
| 384 // that requires us to insert as much as we can |
394 // that requires us to insert as much as we can |
| 385 return 1; // LCOV_EXCL_LINE |
395 return 1; // LCOV_EXCL_LINE |
| 386 } |
396 } |
| 387 *target = new_mem; |
397 *target = new_mem; |
| 388 *capacity = new_capacity; |
398 *capacity = needed_capacity; |
| 389 } |
399 } |
| 390 |
400 |
| 391 // now we have guaranteed that we can insert everything |
401 // now we have guaranteed that we can insert everything |
| 392 size_t new_size = old_size + elem_count; |
402 size_t new_size = old_size + elem_count; |
| 393 *size = new_size; |
403 *size = new_size; |
| 780 // get a correctly typed pointer to the list |
790 // get a correctly typed pointer to the list |
| 781 cx_array_list *arl = (cx_array_list *) list; |
791 cx_array_list *arl = (cx_array_list *) list; |
| 782 |
792 |
| 783 // guarantee enough capacity |
793 // guarantee enough capacity |
| 784 if (arl->capacity < list->collection.size + n) { |
794 if (arl->capacity < list->collection.size + n) { |
| 785 size_t new_capacity = list->collection.size + n; |
795 const size_t new_capacity = cx_array_grow_capacity(arl->capacity, |
| 786 new_capacity = new_capacity - (new_capacity % 16) + 16; |
796 list->collection.size + n, SIZE_MAX); |
| 787 if (cxReallocateArray( |
797 if (cxReallocateArray( |
| 788 list->collection.allocator, |
798 list->collection.allocator, |
| 789 &arl->data, new_capacity, |
799 &arl->data, new_capacity, |
| 790 list->collection.elem_size) |
800 list->collection.elem_size) |
| 791 ) { |
801 ) { |
| 879 static int cx_arl_insert_iter( |
889 static int cx_arl_insert_iter( |
| 880 struct cx_iterator_s *iter, |
890 struct cx_iterator_s *iter, |
| 881 const void *elem, |
891 const void *elem, |
| 882 int prepend |
892 int prepend |
| 883 ) { |
893 ) { |
| 884 struct cx_list_s *list = iter->src_handle.m; |
894 struct cx_list_s *list = iter->src_handle; |
| 885 if (iter->index < list->collection.size) { |
895 if (iter->index < list->collection.size) { |
| 886 if (cx_arl_insert_element(list, |
896 if (cx_arl_insert_element(list, |
| 887 iter->index + 1 - prepend, elem) == NULL) { |
897 iter->index + 1 - prepend, elem) == NULL) { |
| 888 return 1; |
898 return 1; |
| 889 } |
899 } |
| 1103 |
1113 |
| 1104 static void cx_arl_iter_next(void *it) { |
1114 static void cx_arl_iter_next(void *it) { |
| 1105 struct cx_iterator_s *iter = it; |
1115 struct cx_iterator_s *iter = it; |
| 1106 if (iter->base.remove) { |
1116 if (iter->base.remove) { |
| 1107 iter->base.remove = false; |
1117 iter->base.remove = false; |
| 1108 cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL); |
1118 cx_arl_remove(iter->src_handle, iter->index, 1, NULL); |
| 1109 iter->elem_count--; |
1119 iter->elem_count--; |
| 1110 } else { |
1120 } else { |
| 1111 iter->index++; |
1121 iter->index++; |
| 1112 iter->elem_handle = |
1122 iter->elem_handle = |
| 1113 ((char *) iter->elem_handle) |
1123 ((char *) iter->elem_handle) |
| 1114 + ((const struct cx_list_s *) iter->src_handle.c)->collection.elem_size; |
1124 + ((const struct cx_list_s *) iter->src_handle)->collection.elem_size; |
| 1115 } |
1125 } |
| 1116 } |
1126 } |
| 1117 |
1127 |
| 1118 static void cx_arl_iter_prev(void *it) { |
1128 static void cx_arl_iter_prev(void *it) { |
| 1119 struct cx_iterator_s *iter = it; |
1129 struct cx_iterator_s *iter = it; |
| 1120 const cx_array_list *list = iter->src_handle.c; |
|
| 1121 if (iter->base.remove) { |
1130 if (iter->base.remove) { |
| 1122 iter->base.remove = false; |
1131 iter->base.remove = false; |
| 1123 cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL); |
1132 cx_arl_remove(iter->src_handle, iter->index, 1, NULL); |
| 1124 iter->elem_count--; |
1133 iter->elem_count--; |
| 1125 } |
1134 } |
| 1126 iter->index--; |
1135 iter->index--; |
| |
1136 cx_array_list *list = iter->src_handle; |
| 1127 if (iter->index < list->base.collection.size) { |
1137 if (iter->index < list->base.collection.size) { |
| 1128 iter->elem_handle = ((char *) list->data) |
1138 iter->elem_handle = ((char *) list->data) |
| 1129 + iter->index * list->base.collection.elem_size; |
1139 + iter->index * list->base.collection.elem_size; |
| 1130 } |
1140 } |
| 1131 } |
1141 } |
| 1132 |
1142 |
| |
1143 static int cx_arl_change_capacity( |
| |
1144 struct cx_list_s *list, |
| |
1145 size_t new_capacity |
| |
1146 ) { |
| |
1147 cx_array_list *arl = (cx_array_list *)list; |
| |
1148 return cxReallocateArray(list->collection.allocator, |
| |
1149 &arl->data, new_capacity, list->collection.elem_size); |
| |
1150 } |
| 1133 |
1151 |
| 1134 static struct cx_iterator_s cx_arl_iterator( |
1152 static struct cx_iterator_s cx_arl_iterator( |
| 1135 const struct cx_list_s *list, |
1153 const struct cx_list_s *list, |
| 1136 size_t index, |
1154 size_t index, |
| 1137 bool backwards |
1155 bool backwards |
| 1138 ) { |
1156 ) { |
| 1139 struct cx_iterator_s iter; |
1157 struct cx_iterator_s iter; |
| 1140 |
1158 |
| 1141 iter.index = index; |
1159 iter.index = index; |
| 1142 iter.src_handle.c = list; |
1160 iter.src_handle = (void*)list; |
| 1143 iter.elem_handle = cx_arl_at(list, index); |
1161 iter.elem_handle = cx_arl_at(list, index); |
| 1144 iter.elem_size = list->collection.elem_size; |
1162 iter.elem_size = list->collection.elem_size; |
| 1145 iter.elem_count = list->collection.size; |
1163 iter.elem_count = list->collection.size; |
| 1146 iter.base.valid = cx_arl_iter_valid; |
1164 iter.base.valid = cx_arl_iter_valid; |
| 1147 iter.base.current = cx_arl_iter_current; |
1165 iter.base.current = cx_arl_iter_current; |
| 1148 iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; |
1166 iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; |
| 1149 iter.base.remove = false; |
1167 iter.base.remove = false; |
| 1150 iter.base.mutating = false; |
1168 iter.base.allow_remove = true; |
| 1151 |
1169 |
| 1152 return iter; |
1170 return iter; |
| 1153 } |
1171 } |
| 1154 |
1172 |
| 1155 static cx_list_class cx_array_list_class = { |
1173 static cx_list_class cx_array_list_class = { |