You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

370 lines
9.7 KiB

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2011-2013, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the COPYING file in this distribution for license details.
  7. * ----------------------------------------------------------------------
  8. */
  9. #include <assert.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include "libcork/core/types.h"
  13. #include "libcork/ds/array.h"
  14. #include "libcork/helpers/errors.h"
  15. #ifndef CORK_ARRAY_DEBUG
  16. #define CORK_ARRAY_DEBUG 0
  17. #endif
  18. #if CORK_ARRAY_DEBUG
  19. #include <stdio.h>
  20. #define DEBUG(...) \
  21. do { \
  22. fprintf(stderr, __VA_ARGS__); \
  23. fprintf(stderr, "\n"); \
  24. } while (0)
  25. #else
  26. #define DEBUG(...) /* nothing */
  27. #endif
  28. /*-----------------------------------------------------------------------
  29. * Resizable arrays
  30. */
  31. struct cork_array_priv {
  32. size_t allocated_count;
  33. size_t allocated_size;
  34. size_t element_size;
  35. size_t initialized_count;
  36. void *user_data;
  37. cork_free_f free_user_data;
  38. cork_init_f init;
  39. cork_done_f done;
  40. cork_init_f reuse;
  41. cork_done_f remove;
  42. };
  43. void
  44. cork_raw_array_init(struct cork_raw_array *array, size_t element_size)
  45. {
  46. array->items = NULL;
  47. array->size = 0;
  48. array->priv = cork_new(struct cork_array_priv);
  49. array->priv->allocated_count = 0;
  50. array->priv->allocated_size = 0;
  51. array->priv->element_size = element_size;
  52. array->priv->initialized_count = 0;
  53. array->priv->user_data = NULL;
  54. array->priv->free_user_data = NULL;
  55. array->priv->init = NULL;
  56. array->priv->done = NULL;
  57. array->priv->reuse = NULL;
  58. array->priv->remove = NULL;
  59. }
  60. void
  61. cork_raw_array_done(struct cork_raw_array *array)
  62. {
  63. if (array->priv->done != NULL) {
  64. size_t i;
  65. char *element = array->items;
  66. for (i = 0; i < array->priv->initialized_count; i++) {
  67. array->priv->done(array->priv->user_data, element);
  68. element += array->priv->element_size;
  69. }
  70. }
  71. if (array->items != NULL) {
  72. free(array->items);
  73. }
  74. cork_free_user_data(array->priv);
  75. free(array->priv);
  76. }
  77. void
  78. cork_raw_array_set_callback_data(struct cork_raw_array *array,
  79. void *user_data, cork_free_f free_user_data)
  80. {
  81. array->priv->user_data = user_data;
  82. array->priv->free_user_data = free_user_data;
  83. }
  84. void
  85. cork_raw_array_set_init(struct cork_raw_array *array, cork_init_f init)
  86. {
  87. array->priv->init = init;
  88. }
  89. void
  90. cork_raw_array_set_done(struct cork_raw_array *array, cork_done_f done)
  91. {
  92. array->priv->done = done;
  93. }
  94. void
  95. cork_raw_array_set_reuse(struct cork_raw_array *array, cork_init_f reuse)
  96. {
  97. array->priv->reuse = reuse;
  98. }
  99. void
  100. cork_raw_array_set_remove(struct cork_raw_array *array, cork_done_f remove)
  101. {
  102. array->priv->remove = remove;
  103. }
  104. size_t
  105. cork_raw_array_element_size(const struct cork_raw_array *array)
  106. {
  107. return array->priv->element_size;
  108. }
  109. void
  110. cork_raw_array_clear(struct cork_raw_array *array)
  111. {
  112. if (array->priv->remove != NULL) {
  113. size_t i;
  114. char *element = array->items;
  115. for (i = 0; i < array->priv->initialized_count; i++) {
  116. array->priv->remove(array->priv->user_data, element);
  117. element += array->priv->element_size;
  118. }
  119. }
  120. array->size = 0;
  121. }
  122. void *
  123. cork_raw_array_elements(const struct cork_raw_array *array)
  124. {
  125. return array->items;
  126. }
  127. void *
  128. cork_raw_array_at(const struct cork_raw_array *array, size_t index)
  129. {
  130. return ((char *) array->items) + (array->priv->element_size * index);
  131. }
  132. size_t
  133. cork_raw_array_size(const struct cork_raw_array *array)
  134. {
  135. return array->size;
  136. }
  137. bool
  138. cork_raw_array_is_empty(const struct cork_raw_array *array)
  139. {
  140. return (array->size == 0);
  141. }
  142. void
  143. cork_raw_array_ensure_size(struct cork_raw_array *array, size_t desired_count)
  144. {
  145. size_t desired_size;
  146. DEBUG("--- Array %p: Ensure %zu %zu-byte elements",
  147. array, desired_count, array->priv->element_size);
  148. desired_size = desired_count * array->priv->element_size;
  149. if (desired_size > array->priv->allocated_size) {
  150. size_t new_count = array->priv->allocated_count * 2;
  151. size_t new_size = array->priv->allocated_size * 2;
  152. if (desired_size > new_size) {
  153. new_count = desired_count;
  154. new_size = desired_size;
  155. }
  156. DEBUG("--- Array %p: Reallocating %zu->%zu bytes",
  157. array, array->priv->allocated_size, new_size);
  158. array->items = cork_realloc(array->items, new_size);
  159. array->priv->allocated_count = new_count;
  160. array->priv->allocated_size = new_size;
  161. }
  162. }
  163. void *
  164. cork_raw_array_append(struct cork_raw_array *array)
  165. {
  166. size_t index;
  167. void *element;
  168. index = array->size++;
  169. cork_raw_array_ensure_size(array, array->size);
  170. element = cork_raw_array_at(array, index);
  171. /* Call the init or reset callback, depending on whether this entry has been
  172. * initialized before. */
  173. /* Since we can currently only add elements by appending them one at a time,
  174. * then this entry is either already initialized, or is the first
  175. * uninitialized entry. */
  176. assert(index <= array->priv->initialized_count);
  177. if (index == array->priv->initialized_count) {
  178. /* This element has not been initialized yet. */
  179. array->priv->initialized_count++;
  180. if (array->priv->init != NULL) {
  181. array->priv->init(array->priv->user_data, element);
  182. }
  183. } else {
  184. /* This element has already been initialized. */
  185. if (array->priv->reuse != NULL) {
  186. array->priv->reuse(array->priv->user_data, element);
  187. }
  188. }
  189. return element;
  190. }
  191. int
  192. cork_raw_array_copy(struct cork_raw_array *dest,
  193. const struct cork_raw_array *src,
  194. cork_copy_f copy, void *user_data)
  195. {
  196. size_t i;
  197. size_t reuse_count;
  198. char *dest_element;
  199. DEBUG("--- Copying %zu elements (%zu bytes) from %p to %p",
  200. src->size, src->size * dest->priv->element_size, src, dest);
  201. assert(dest->priv->element_size == src->priv->element_size);
  202. cork_array_clear(dest);
  203. cork_array_ensure_size(dest, src->size);
  204. /* Initialize enough elements to hold the contents of src */
  205. reuse_count = dest->priv->initialized_count;
  206. if (src->size < reuse_count) {
  207. reuse_count = src->size;
  208. }
  209. dest_element = dest->items;
  210. if (dest->priv->reuse != NULL) {
  211. DEBUG(" Calling reuse on elements 0-%zu", reuse_count);
  212. for (i = 0; i < reuse_count; i++) {
  213. dest->priv->reuse(dest->priv->user_data, dest_element);
  214. dest_element += dest->priv->element_size;
  215. }
  216. } else {
  217. dest_element += reuse_count * dest->priv->element_size;
  218. }
  219. if (dest->priv->init != NULL) {
  220. DEBUG(" Calling init on elements %zu-%zu", reuse_count, src->size);
  221. for (i = reuse_count; i < src->size; i++) {
  222. dest->priv->init(dest->priv->user_data, dest_element);
  223. dest_element += dest->priv->element_size;
  224. }
  225. }
  226. if (src->size > dest->priv->initialized_count) {
  227. dest->priv->initialized_count = src->size;
  228. }
  229. /* If the caller provided a copy function, let it copy each element in turn.
  230. * Otherwise, bulk copy everything using memcpy. */
  231. if (copy == NULL) {
  232. memcpy(dest->items, src->items, src->size * dest->priv->element_size);
  233. } else {
  234. const char *src_element = src->items;
  235. dest_element = dest->items;
  236. for (i = 0; i < src->size; i++) {
  237. rii_check(copy(user_data, dest_element, src_element));
  238. dest_element += dest->priv->element_size;
  239. src_element += dest->priv->element_size;
  240. }
  241. }
  242. dest->size = src->size;
  243. return 0;
  244. }
  245. /*-----------------------------------------------------------------------
  246. * Pointer arrays
  247. */
  248. struct cork_pointer_array {
  249. cork_free_f free;
  250. };
  251. static void
  252. pointer__init(void *user_data, void *vvalue)
  253. {
  254. void **value = vvalue;
  255. *value = NULL;
  256. }
  257. static void
  258. pointer__done(void *user_data, void *vvalue)
  259. {
  260. struct cork_pointer_array *ptr_array = user_data;
  261. void **value = vvalue;
  262. if (*value != NULL) {
  263. ptr_array->free(*value);
  264. }
  265. }
  266. static void
  267. pointer__remove(void *user_data, void *vvalue)
  268. {
  269. struct cork_pointer_array *ptr_array = user_data;
  270. void **value = vvalue;
  271. if (*value != NULL) {
  272. ptr_array->free(*value);
  273. }
  274. *value = NULL;
  275. }
  276. void
  277. cork_raw_pointer_array_init(struct cork_raw_array *array, cork_free_f free_ptr)
  278. {
  279. struct cork_pointer_array *ptr_array = cork_new(struct cork_pointer_array);
  280. ptr_array->free = free_ptr;
  281. cork_raw_array_init(array, sizeof(void *));
  282. cork_array_set_callback_data(array, ptr_array, free);
  283. cork_array_set_init(array, pointer__init);
  284. cork_array_set_done(array, pointer__done);
  285. cork_array_set_remove(array, pointer__remove);
  286. }
  287. /*-----------------------------------------------------------------------
  288. * String arrays
  289. */
  290. void
  291. cork_string_array_init(struct cork_string_array *array)
  292. {
  293. cork_raw_pointer_array_init
  294. ((struct cork_raw_array *) array, (cork_free_f) cork_strfree);
  295. }
  296. void
  297. cork_string_array_append(struct cork_string_array *array, const char *str)
  298. {
  299. const char *copy = cork_strdup(str);
  300. cork_array_append(array, copy);
  301. }
  302. static int
  303. string__copy(void *user_data, void *vdest, const void *vsrc)
  304. {
  305. const char **dest = vdest;
  306. const char **src = (const char **) vsrc;
  307. *dest = cork_strdup(*src);
  308. return 0;
  309. }
  310. void
  311. cork_string_array_copy(struct cork_string_array *dest,
  312. const struct cork_string_array *src)
  313. {
  314. CORK_ATTR_UNUSED int rc;
  315. rc = cork_array_copy(dest, src, string__copy, NULL);
  316. /* cork_array_copy can only fail if the copy callback fails, and ours
  317. * doesn't! */
  318. assert(rc == 0);
  319. }