varray.c (5125B)
1 #include <stdlib.h> 2 #include <string.h> 3 4 #include "varray.h" 5 6 #define _IDX(index) VARRAY_ELEMENT(va, index) 7 8 varray *varray_new(int element_size, int initial_allocation) 9 { 10 varray *va; 11 12 va = malloc(sizeof(varray)); 13 if (va == NULL) 14 return NULL; 15 16 if (varray_init(va, element_size, initial_allocation) == NULL) { 17 free(va); 18 return NULL; 19 } 20 21 return va; 22 } 23 24 varray *varray_init(varray *va, int element_size, int initial_allocation) 25 { 26 memset(va, 0, sizeof(varray)); 27 va->element_size = element_size; 28 va->allocation = initial_allocation; 29 30 if (initial_allocation) { 31 va->data = calloc(initial_allocation + 1, element_size); 32 if (va->data == NULL) { 33 return NULL; 34 } 35 } 36 37 return va; 38 } 39 40 void varray_destroy(varray *va) 41 { 42 int i; 43 44 if (va->destroy_func != NULL) { 45 for (i = 0; i < va->count; i++) { 46 va->destroy_func(_IDX(i)); 47 } 48 } 49 free(va->data); 50 memset(va, 0, sizeof(varray)); 51 } 52 53 void varray_free(varray *va) 54 { 55 if (va == NULL) 56 return; 57 58 varray_destroy(va); 59 free(va); 60 } 61 62 void *varray_extract_array(varray *va) 63 { 64 void *array; 65 66 if (va->count == 0) { 67 varray_destroy(va); 68 return NULL; 69 } 70 71 array = realloc(va->data, va->count * va->element_size); 72 memset(va, 0, sizeof(varray)); 73 return array; 74 } 75 76 int varray_get_index(varray *va, void *element_ptr) 77 { 78 if (va->data == NULL) 79 return -1; 80 81 if (element_ptr < va->data || element_ptr >= _IDX(va->count)) 82 return -1; 83 84 if ((element_ptr - va->data) % va->element_size != 0) 85 return -1; 86 87 return (element_ptr - va->data) / va->element_size; 88 } 89 90 static int grow_array(varray *va) 91 { 92 int new_allocation; 93 void *new_data; 94 95 if (va->allocation == 0) 96 new_allocation = 16; 97 else 98 new_allocation = va->allocation * 2; 99 100 new_data = realloc(va->data, va->element_size * (new_allocation + 1)); 101 if (new_data == NULL) 102 return 0; 103 104 va->data = new_data; 105 va->allocation = new_allocation; 106 return 1; 107 } 108 #define GROW_IF_NECESSARY(va, failure_retval) do { \ 109 if ((va)->count >= (va)->allocation) { \ 110 if (!grow_array(va)) \ 111 return (failure_retval); \ 112 } \ 113 } while (0) 114 115 void *varray_push(varray *va, void *element) 116 { 117 GROW_IF_NECESSARY(va, NULL); 118 if (element == NULL) { 119 memset(_IDX(va->count), 0, va->element_size); 120 if (va->init_func) { 121 if (va->init_func(_IDX(va->count)) == NULL) 122 return NULL; 123 } 124 } else { 125 memcpy(_IDX(va->count), element, va->element_size); 126 } 127 va->count++; 128 129 return _IDX(va->count - 1); 130 } 131 132 void *varray_insert(varray *va, void *element, int index) 133 { 134 if (index > va->count || index < 0) 135 return NULL; 136 137 GROW_IF_NECESSARY(va, NULL); 138 if (index < va->count) { 139 memmove(_IDX(index + 1), _IDX(index), va->element_size * (va->count - index)); 140 } 141 142 if (element == NULL) { 143 memset(_IDX(index), 0, va->element_size); 144 if (va->init_func) { 145 if (va->init_func(_IDX(index)) == NULL) { 146 varray_remove(va, index); 147 return NULL; 148 } 149 } 150 } else { 151 memcpy(_IDX(index), element, va->element_size); 152 } 153 154 va->count++; 155 return _IDX(index); 156 } 157 158 void *varray_pop(varray *va) 159 { 160 if (va->count == 0) 161 return NULL; 162 163 va->count--; 164 return _IDX(va->count); 165 } 166 167 void *varray_remove(varray *va, int index) 168 { 169 if (index >= va->count || index < 0) 170 return NULL; 171 172 if (index == va->count - 1) 173 return varray_pop(va); 174 175 memcpy(_IDX(va->allocation), _IDX(index), va->element_size); 176 memmove(_IDX(index), _IDX(index + 1), va->element_size * (va->count - index)); 177 va->count--; 178 179 memcpy(_IDX(va->count), _IDX(va->allocation), va->element_size); 180 return _IDX(va->count); 181 } 182 183 void varray_sort(varray *va) 184 { 185 qsort(va->data, va->count, va->element_size, va->sort_compar); 186 } 187 188 189 void *varray_sorted_search(const varray *va, const void *key) 190 { 191 return bsearch(key, va->data, va->count, va->element_size, va->search_compar ? va->search_compar : va->sort_compar); 192 } 193 194 static int get_sorted_insert_index(const varray *va, const void *key_or_element, int (*compar)(const void *, const void *)) 195 { 196 int low_idx = 0, high_idx = va->count; 197 198 while (low_idx < high_idx) { 199 int test_idx = (low_idx + high_idx) / 2; 200 int result = compar(key_or_element, _IDX(test_idx)); 201 202 if (result == 0) 203 return test_idx; 204 else if (result > 0) 205 low_idx = test_idx + 1; 206 else 207 high_idx = test_idx; 208 } 209 210 return low_idx; 211 } 212 213 void *varray_sorted_insert_ex(varray *va, void *element, int allow_dup) 214 { 215 int insert_idx = get_sorted_insert_index(va, element, va->sort_compar); 216 217 if (!allow_dup && insert_idx < va->count && va->search_compar(element, _IDX(insert_idx)) == 0) 218 return NULL; 219 220 return varray_insert(va, element, insert_idx); 221 } 222 223 void *varray_sorted_insert(varray *va, void *element) 224 { 225 return varray_sorted_insert_ex(va, element, 1); 226 } 227 228 void *varray_sorted_search_or_insert(varray *va, const void *key, int *found_existing) 229 { 230 int (*compar)(const void *, const void *); 231 int insert_idx; 232 233 compar = va->search_compar ? va->search_compar : va->sort_compar; 234 235 insert_idx = get_sorted_insert_index(va, key, compar); 236 237 if (insert_idx < va->count && compar(key, _IDX(insert_idx)) == 0) { 238 if (found_existing != NULL) 239 *found_existing = 1; 240 return _IDX(insert_idx); 241 } else { 242 if (found_existing != NULL) 243 *found_existing = 0; 244 return varray_insert(va, NULL, insert_idx); 245 } 246 }