| 38 void *array, |
38 void *array, |
| 39 size_t capacity, |
39 size_t capacity, |
| 40 size_t elem_size, |
40 size_t elem_size, |
| 41 cx_attr_unused CxArrayReallocator *alloc |
41 cx_attr_unused CxArrayReallocator *alloc |
| 42 ) { |
42 ) { |
| 43 return realloc(array, capacity * elem_size); |
43 size_t n; |
| |
44 if (cx_szmul(capacity, elem_size, &n)) { |
| |
45 errno = EOVERFLOW; |
| |
46 return NULL; |
| |
47 } |
| |
48 return realloc(array, n); |
| 44 } |
49 } |
| 45 |
50 |
| 46 CxArrayReallocator cx_array_default_reallocator_impl = { |
51 CxArrayReallocator cx_array_default_reallocator_impl = { |
| 47 cx_array_default_realloc, NULL, NULL, 0, 0 |
52 cx_array_default_realloc, NULL, NULL, 0, 0 |
| 48 }; |
53 }; |
| 55 void *array, |
60 void *array, |
| 56 size_t capacity, |
61 size_t capacity, |
| 57 size_t elem_size, |
62 size_t elem_size, |
| 58 cx_attr_unused CxArrayReallocator *alloc |
63 cx_attr_unused CxArrayReallocator *alloc |
| 59 ) { |
64 ) { |
| |
65 // check for overflow |
| |
66 size_t n; |
| |
67 if (cx_szmul(capacity, elem_size, &n)) { |
| |
68 errno = EOVERFLOW; |
| |
69 return NULL; |
| |
70 } |
| |
71 |
| 60 // retrieve the pointer to the actual allocator |
72 // retrieve the pointer to the actual allocator |
| 61 const CxAllocator *al = alloc->ptr1; |
73 const CxAllocator *al = alloc->ptr1; |
| 62 |
74 |
| 63 // check if the array is still located on the stack |
75 // check if the array is still located on the stack |
| 64 void *newmem; |
76 void *newmem; |
| 65 if (array == alloc->ptr2) { |
77 if (array == alloc->ptr2) { |
| 66 newmem = cxMalloc(al, capacity * elem_size); |
78 newmem = cxMalloc(al, n); |
| 67 if (newmem != NULL && array != NULL) { |
79 if (newmem != NULL && array != NULL) { |
| 68 memcpy(newmem, array, capacity * elem_size); |
80 memcpy(newmem, array, n); |
| 69 } |
81 } |
| 70 } else { |
82 } else { |
| 71 newmem = cxRealloc(al, array, capacity * elem_size); |
83 newmem = cxRealloc(al, array, n); |
| 72 } |
84 } |
| 73 return newmem; |
85 return newmem; |
| 74 } |
86 } |
| 75 |
87 |
| 76 struct cx_array_reallocator_s cx_array_reallocator( |
88 struct cx_array_reallocator_s cx_array_reallocator( |
| 134 } |
158 } |
| 135 |
159 |
| 136 // assert that the array is allocated when it has capacity |
160 // assert that the array is allocated when it has capacity |
| 137 assert(*array != NULL || oldcap == 0); |
161 assert(*array != NULL || oldcap == 0); |
| 138 |
162 |
| |
163 // check for overflow |
| |
164 if (elem_count > max_size - oldsize) { |
| |
165 errno = EOVERFLOW; |
| |
166 return 1; |
| |
167 } |
| |
168 |
| 139 // determine new capacity |
169 // determine new capacity |
| 140 size_t newcap = oldsize + elem_count; |
170 size_t newcap = oldsize + elem_count; |
| 141 |
|
| 142 // check for overflow |
|
| 143 if (newcap > max_size) { |
|
| 144 errno = EOVERFLOW; |
|
| 145 return 1; |
|
| 146 } |
|
| 147 |
171 |
| 148 // reallocate if possible |
172 // reallocate if possible |
| 149 if (newcap > oldcap) { |
173 if (newcap > oldcap) { |
| 150 // calculate new capacity (next number divisible by 16) |
174 // calculate new capacity (next number divisible by 16) |
| 151 newcap = newcap - (newcap % 16) + 16; |
175 newcap = cx_array_align_capacity(newcap, 16, max_size); |
| 152 |
176 |
| 153 // perform reallocation |
177 // perform reallocation |
| 154 void *newmem = reallocator->realloc( |
178 void *newmem = reallocator->realloc( |
| 155 *array, newcap, elem_size, reallocator |
179 *array, newcap, elem_size, reallocator |
| 156 ); |
180 ); |
| 227 } |
251 } |
| 228 |
252 |
| 229 // assert that the array is allocated when it has capacity |
253 // assert that the array is allocated when it has capacity |
| 230 assert(*target != NULL || oldcap == 0); |
254 assert(*target != NULL || oldcap == 0); |
| 231 |
255 |
| |
256 // check for overflow |
| |
257 if (index > max_size || elem_count > max_size - index) { |
| |
258 errno = EOVERFLOW; |
| |
259 return 1; |
| |
260 } |
| |
261 |
| 232 // check if resize is required |
262 // check if resize is required |
| 233 size_t minsize = index + elem_count; |
263 size_t minsize = index + elem_count; |
| 234 size_t newsize = oldsize < minsize ? minsize : oldsize; |
264 size_t newsize = oldsize < minsize ? minsize : oldsize; |
| 235 |
|
| 236 // check for overflow |
|
| 237 if (newsize > max_size) { |
|
| 238 errno = EOVERFLOW; |
|
| 239 return 1; |
|
| 240 } |
|
| 241 |
265 |
| 242 // reallocate if possible |
266 // reallocate if possible |
| 243 size_t newcap = oldcap; |
267 size_t newcap = oldcap; |
| 244 if (newsize > oldcap) { |
268 if (newsize > oldcap) { |
| 245 // check, if we need to repair the src pointer |
269 // check, if we need to repair the src pointer |
| 247 uintptr_t srcaddr = (uintptr_t) src; |
271 uintptr_t srcaddr = (uintptr_t) src; |
| 248 bool repairsrc = targetaddr <= srcaddr |
272 bool repairsrc = targetaddr <= srcaddr |
| 249 && srcaddr < targetaddr + oldcap * elem_size; |
273 && srcaddr < targetaddr + oldcap * elem_size; |
| 250 |
274 |
| 251 // calculate new capacity (next number divisible by 16) |
275 // calculate new capacity (next number divisible by 16) |
| 252 newcap = newsize - (newsize % 16) + 16; |
276 newcap = cx_array_align_capacity(newsize, 16, max_size); |
| 253 assert(newcap > newsize); |
277 assert(newcap > newsize); |
| 254 |
278 |
| 255 // perform reallocation |
279 // perform reallocation |
| 256 void *newmem = reallocator->realloc( |
280 void *newmem = reallocator->realloc( |
| 257 *target, newcap, elem_size, reallocator |
281 *target, newcap, elem_size, reallocator |
| 272 // determine target pointer |
296 // determine target pointer |
| 273 char *start = *target; |
297 char *start = *target; |
| 274 start += index * elem_size; |
298 start += index * elem_size; |
| 275 |
299 |
| 276 // copy elements and set new size |
300 // copy elements and set new size |
| |
301 // note: no overflow check here, b/c we cannot get here w/o allocation |
| 277 memmove(start, src, elem_count * elem_size); |
302 memmove(start, src, elem_count * elem_size); |
| 278 |
303 |
| 279 // if any of size or capacity changed, store them back |
304 // if any of size or capacity changed, store them back |
| 280 if (newsize != oldsize || newcap != oldcap) { |
305 if (newsize != oldsize || newcap != oldcap) { |
| 281 if (width == 0 || width == CX_WORDSIZE) { |
306 if (width == 0 || width == CX_WORDSIZE) { |
| 319 assert(reallocator != NULL); |
344 assert(reallocator != NULL); |
| 320 |
345 |
| 321 // corner case |
346 // corner case |
| 322 if (elem_count == 0) return 0; |
347 if (elem_count == 0) return 0; |
| 323 |
348 |
| |
349 // overflow check |
| |
350 if (elem_count > SIZE_MAX - *size) { |
| |
351 errno = EOVERFLOW; |
| |
352 return 1; |
| |
353 } |
| |
354 |
| 324 // store some counts |
355 // store some counts |
| 325 size_t old_size = *size; |
356 size_t old_size = *size; |
| 326 size_t needed_capacity = old_size + elem_count; |
357 size_t needed_capacity = old_size + elem_count; |
| 327 |
358 |
| 328 // if we need more than we have, try a reallocation |
359 // if we need more than we have, try a reallocation |
| 329 if (needed_capacity > *capacity) { |
360 if (needed_capacity > *capacity) { |
| 330 size_t new_capacity = needed_capacity - (needed_capacity % 16) + 16; |
361 size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX); |
| 331 void *new_mem = reallocator->realloc( |
362 void *new_mem = reallocator->realloc( |
| 332 *target, new_capacity, elem_size, reallocator |
363 *target, new_capacity, elem_size, reallocator |
| 333 ); |
364 ); |
| 334 if (new_mem == NULL) { |
365 if (new_mem == NULL) { |
| 335 // give it up right away, there is no contract |
366 // give it up right away, there is no contract |