ucx/array_list.c

changeset 113
dde28a806552
parent 112
c3f2f16fa4b8
equal deleted inserted replaced
112:c3f2f16fa4b8 113:dde28a806552
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 }
1090 } 1100 }
1091 } 1101 }
1092 1102
1093 static bool cx_arl_iter_valid(const void *it) { 1103 static bool cx_arl_iter_valid(const void *it) {
1094 const struct cx_iterator_s *iter = it; 1104 const struct cx_iterator_s *iter = it;
1095 const struct cx_list_s *list = iter->src_handle.c; 1105 const struct cx_list_s *list = iter->src_handle;
1096 return iter->index < list->collection.size; 1106 return iter->index < list->collection.size;
1097 } 1107 }
1098 1108
1099 static void *cx_arl_iter_current(const void *it) { 1109 static void *cx_arl_iter_current(const void *it) {
1100 const struct cx_iterator_s *iter = it; 1110 const struct cx_iterator_s *iter = it;
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 = {
1165 cx_arl_at, 1183 cx_arl_at,
1166 cx_arl_find_remove, 1184 cx_arl_find_remove,
1167 cx_arl_sort, 1185 cx_arl_sort,
1168 cx_arl_compare, 1186 cx_arl_compare,
1169 cx_arl_reverse, 1187 cx_arl_reverse,
1188 cx_arl_change_capacity,
1170 cx_arl_iterator, 1189 cx_arl_iterator,
1171 }; 1190 };
1172 1191
1173 CxList *cxArrayListCreate( 1192 CxList *cxArrayListCreate(
1174 const CxAllocator *allocator, 1193 const CxAllocator *allocator,

mercurial