random_access_set.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/common/allocator.h>
  6. #include <aws/common/device_random.h>
  7. #include <aws/http/private/random_access_set.h>
  8. struct aws_random_access_set_impl {
  9. struct aws_allocator *allocator;
  10. struct aws_array_list list; /* Always store the pointer of the element. */
  11. struct aws_hash_table map; /* Map from the element to the index in the array. */
  12. aws_hash_callback_destroy_fn *destroy_element_fn;
  13. };
  14. static void s_impl_destroy(struct aws_random_access_set_impl *impl) {
  15. if (!impl) {
  16. return;
  17. }
  18. aws_array_list_clean_up(&impl->list);
  19. aws_hash_table_clean_up(&impl->map);
  20. aws_mem_release(impl->allocator, impl);
  21. }
  22. static struct aws_random_access_set_impl *s_impl_new(
  23. struct aws_allocator *allocator,
  24. aws_hash_fn *hash_fn,
  25. aws_hash_callback_eq_fn *equals_fn,
  26. aws_hash_callback_destroy_fn *destroy_element_fn,
  27. size_t initial_item_allocation) {
  28. struct aws_random_access_set_impl *impl = aws_mem_calloc(allocator, 1, sizeof(struct aws_random_access_set_impl));
  29. impl->allocator = allocator;
  30. /* Will always store the pointer of the element. */
  31. if (aws_array_list_init_dynamic(&impl->list, allocator, initial_item_allocation, sizeof(void *))) {
  32. s_impl_destroy(impl);
  33. return NULL;
  34. }
  35. if (aws_hash_table_init(
  36. &impl->map, allocator, initial_item_allocation, hash_fn, equals_fn, destroy_element_fn, NULL)) {
  37. s_impl_destroy(impl);
  38. return NULL;
  39. }
  40. impl->destroy_element_fn = destroy_element_fn;
  41. return impl;
  42. }
  43. int aws_random_access_set_init(
  44. struct aws_random_access_set *set,
  45. struct aws_allocator *allocator,
  46. aws_hash_fn *hash_fn,
  47. aws_hash_callback_eq_fn *equals_fn,
  48. aws_hash_callback_destroy_fn *destroy_element_fn,
  49. size_t initial_item_allocation) {
  50. AWS_FATAL_PRECONDITION(set);
  51. AWS_FATAL_PRECONDITION(allocator);
  52. AWS_FATAL_PRECONDITION(hash_fn);
  53. AWS_FATAL_PRECONDITION(equals_fn);
  54. struct aws_random_access_set_impl *impl =
  55. s_impl_new(allocator, hash_fn, equals_fn, destroy_element_fn, initial_item_allocation);
  56. if (!impl) {
  57. return AWS_OP_ERR;
  58. }
  59. set->impl = impl;
  60. return AWS_OP_SUCCESS;
  61. }
  62. void aws_random_access_set_clean_up(struct aws_random_access_set *set) {
  63. if (!set) {
  64. return;
  65. }
  66. s_impl_destroy(set->impl);
  67. }
  68. int aws_random_access_set_add(struct aws_random_access_set *set, const void *element, bool *added) {
  69. AWS_PRECONDITION(set);
  70. AWS_PRECONDITION(element);
  71. AWS_PRECONDITION(added);
  72. bool exist = false;
  73. if (aws_random_access_set_exist(set, element, &exist) || exist) {
  74. *added = false;
  75. return AWS_OP_SUCCESS;
  76. }
  77. /* deep copy the pointer of element to store at the array list */
  78. if (aws_array_list_push_back(&set->impl->list, (void *)&element)) {
  79. goto list_push_error;
  80. }
  81. if (aws_hash_table_put(&set->impl->map, element, (void *)(aws_array_list_length(&set->impl->list) - 1), NULL)) {
  82. goto error;
  83. }
  84. *added = true;
  85. return AWS_OP_SUCCESS;
  86. error:
  87. aws_array_list_pop_back(&set->impl->list);
  88. list_push_error:
  89. *added = false;
  90. return AWS_OP_ERR;
  91. }
  92. int aws_random_access_set_remove(struct aws_random_access_set *set, const void *element) {
  93. AWS_PRECONDITION(set);
  94. AWS_PRECONDITION(element);
  95. size_t current_length = aws_array_list_length(&set->impl->list);
  96. if (current_length == 0) {
  97. /* Nothing to remove */
  98. return AWS_OP_SUCCESS;
  99. }
  100. struct aws_hash_element *find = NULL;
  101. /* find and remove the element from table */
  102. if (aws_hash_table_find(&set->impl->map, element, &find)) {
  103. return AWS_OP_ERR;
  104. }
  105. if (!find) {
  106. /* It's removed already */
  107. return AWS_OP_SUCCESS;
  108. }
  109. size_t index_to_remove = (size_t)find->value;
  110. if (aws_hash_table_remove_element(&set->impl->map, find)) {
  111. return AWS_OP_ERR;
  112. }
  113. /* If assert code failed, we won't be recovered from the failure */
  114. int assert_re = AWS_OP_SUCCESS;
  115. (void)assert_re;
  116. /* Nothing else can fail after here. */
  117. if (index_to_remove != current_length - 1) {
  118. /* It's not the last element, we need to swap it with the end of the list and remove the last element */
  119. void *last_element = NULL;
  120. /* The last element is a pointer of pointer of element. */
  121. assert_re = aws_array_list_get_at_ptr(&set->impl->list, &last_element, current_length - 1);
  122. AWS_ASSERT(assert_re == AWS_OP_SUCCESS);
  123. /* Update the last element index in the table */
  124. struct aws_hash_element *element_to_update = NULL;
  125. assert_re = aws_hash_table_find(&set->impl->map, *(void **)last_element, &element_to_update);
  126. AWS_ASSERT(assert_re == AWS_OP_SUCCESS);
  127. AWS_ASSERT(element_to_update != NULL);
  128. element_to_update->value = (void *)index_to_remove;
  129. /* Swap the last element with the element to remove in the list */
  130. aws_array_list_swap(&set->impl->list, index_to_remove, current_length - 1);
  131. }
  132. /* Remove the current last element from the list */
  133. assert_re = aws_array_list_pop_back(&set->impl->list);
  134. AWS_ASSERT(assert_re == AWS_OP_SUCCESS);
  135. if (set->impl->destroy_element_fn) {
  136. set->impl->destroy_element_fn((void *)element);
  137. }
  138. return AWS_OP_SUCCESS;
  139. }
  140. int aws_random_access_set_random_get_ptr(const struct aws_random_access_set *set, void **out) {
  141. AWS_PRECONDITION(set);
  142. AWS_PRECONDITION(out != NULL);
  143. size_t length = aws_array_list_length(&set->impl->list);
  144. if (length == 0) {
  145. return aws_raise_error(AWS_ERROR_LIST_EMPTY);
  146. }
  147. uint64_t random_64_bit_num = 0;
  148. aws_device_random_u64(&random_64_bit_num);
  149. size_t index = (size_t)random_64_bit_num % length;
  150. /* The array list stores the pointer of the element. */
  151. return aws_array_list_get_at(&set->impl->list, (void *)out, index);
  152. }
  153. size_t aws_random_access_set_get_size(const struct aws_random_access_set *set) {
  154. return aws_array_list_length(&set->impl->list);
  155. }
  156. int aws_random_access_set_exist(const struct aws_random_access_set *set, const void *element, bool *exist) {
  157. AWS_PRECONDITION(set);
  158. AWS_PRECONDITION(element);
  159. AWS_PRECONDITION(exist);
  160. struct aws_hash_element *find = NULL;
  161. int re = aws_hash_table_find(&set->impl->map, element, &find);
  162. *exist = find != NULL;
  163. return re;
  164. }
  165. int aws_random_access_set_random_get_ptr_index(const struct aws_random_access_set *set, void **out, size_t index) {
  166. AWS_PRECONDITION(set);
  167. AWS_PRECONDITION(out != NULL);
  168. return aws_array_list_get_at(&set->impl->list, (void *)out, index);
  169. }