linked_hash_table.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/common/linked_hash_table.h>
  6. static void s_element_destroy(void *value) {
  7. struct aws_linked_hash_table_node *node = value;
  8. if (node->table->user_on_value_destroy) {
  9. node->table->user_on_value_destroy(node->value);
  10. }
  11. aws_linked_list_remove(&node->node);
  12. aws_mem_release(node->table->allocator, node);
  13. }
  14. int aws_linked_hash_table_init(
  15. struct aws_linked_hash_table *table,
  16. struct aws_allocator *allocator,
  17. aws_hash_fn *hash_fn,
  18. aws_hash_callback_eq_fn *equals_fn,
  19. aws_hash_callback_destroy_fn *destroy_key_fn,
  20. aws_hash_callback_destroy_fn *destroy_value_fn,
  21. size_t initial_item_count) {
  22. AWS_ASSERT(table);
  23. AWS_ASSERT(allocator);
  24. AWS_ASSERT(hash_fn);
  25. AWS_ASSERT(equals_fn);
  26. table->allocator = allocator;
  27. table->user_on_value_destroy = destroy_value_fn;
  28. table->user_on_key_destroy = destroy_key_fn;
  29. aws_linked_list_init(&table->list);
  30. return aws_hash_table_init(
  31. &table->table, allocator, initial_item_count, hash_fn, equals_fn, destroy_key_fn, s_element_destroy);
  32. }
  33. void aws_linked_hash_table_clean_up(struct aws_linked_hash_table *table) {
  34. /* clearing the table will remove all elements. That will also deallocate
  35. * any table entries we currently have. */
  36. aws_hash_table_clean_up(&table->table);
  37. AWS_ZERO_STRUCT(*table);
  38. }
  39. int aws_linked_hash_table_find(struct aws_linked_hash_table *table, const void *key, void **p_value) {
  40. struct aws_hash_element *element = NULL;
  41. int err_val = aws_hash_table_find(&table->table, key, &element);
  42. if (err_val || !element) {
  43. *p_value = NULL;
  44. return err_val;
  45. }
  46. struct aws_linked_hash_table_node *linked_node = element->value;
  47. *p_value = linked_node->value;
  48. return AWS_OP_SUCCESS;
  49. }
  50. int aws_linked_hash_table_find_and_move_to_back(struct aws_linked_hash_table *table, const void *key, void **p_value) {
  51. struct aws_hash_element *element = NULL;
  52. int err_val = aws_hash_table_find(&table->table, key, &element);
  53. if (err_val || !element) {
  54. *p_value = NULL;
  55. return err_val;
  56. }
  57. struct aws_linked_hash_table_node *linked_node = element->value;
  58. *p_value = linked_node->value;
  59. /* on access, remove from current place in list and move it to the back. */
  60. aws_linked_hash_table_move_node_to_end_of_list(table, linked_node);
  61. return AWS_OP_SUCCESS;
  62. }
  63. int aws_linked_hash_table_put(struct aws_linked_hash_table *table, const void *key, void *p_value) {
  64. struct aws_linked_hash_table_node *node =
  65. aws_mem_calloc(table->allocator, 1, sizeof(struct aws_linked_hash_table_node));
  66. if (!node) {
  67. return AWS_OP_ERR;
  68. }
  69. struct aws_hash_element *element = NULL;
  70. int was_added = 0;
  71. int err_val = aws_hash_table_create(&table->table, key, &element, &was_added);
  72. if (err_val) {
  73. aws_mem_release(table->allocator, node);
  74. return err_val;
  75. }
  76. if (element->value) {
  77. AWS_ASSERT(!was_added);
  78. /*
  79. * There's an existing element with a key that is "equal" to the submitted key. We need to destroy that
  80. * existing element's value if applicable.
  81. */
  82. s_element_destroy(element->value);
  83. /*
  84. * We're reusing an old element. The keys might be different references but "equal" via comparison. In that
  85. * case we need to destroy the key (if appropriate) and point the element to the new key. This underhanded
  86. * mutation of the element is safe with respect to the hash table because the keys are "equal."
  87. */
  88. if (table->user_on_key_destroy && element->key != key) {
  89. table->user_on_key_destroy((void *)element->key);
  90. }
  91. /*
  92. * Potentially a NOOP, but under certain circumstances (when the key and value are a part of the same structure
  93. * and we're overwriting the existing entry, for example), this is necessary. Equality via function does not
  94. * imply equal pointers.
  95. */
  96. element->key = key;
  97. }
  98. node->value = p_value;
  99. node->key = key;
  100. node->table = table;
  101. element->value = node;
  102. aws_linked_list_push_back(&table->list, &node->node);
  103. return AWS_OP_SUCCESS;
  104. }
  105. int aws_linked_hash_table_remove(struct aws_linked_hash_table *table, const void *key) {
  106. /* allocated table memory and the linked list entry will be removed in the
  107. * callback. */
  108. return aws_hash_table_remove(&table->table, key, NULL, NULL);
  109. }
  110. void aws_linked_hash_table_clear(struct aws_linked_hash_table *table) {
  111. /* clearing the table will remove all elements. That will also deallocate
  112. * any entries we currently have. */
  113. aws_hash_table_clear(&table->table);
  114. }
  115. size_t aws_linked_hash_table_get_element_count(const struct aws_linked_hash_table *table) {
  116. return aws_hash_table_get_entry_count(&table->table);
  117. }
  118. void aws_linked_hash_table_move_node_to_end_of_list(
  119. struct aws_linked_hash_table *table,
  120. struct aws_linked_hash_table_node *node) {
  121. aws_linked_list_remove(&node->node);
  122. aws_linked_list_push_back(&table->list, &node->node);
  123. }
  124. const struct aws_linked_list *aws_linked_hash_table_get_iteration_list(const struct aws_linked_hash_table *table) {
  125. return &table->list;
  126. }