aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/restricted/aws/aws-c-http/source/random_access_set.c
blob: 20fc12309fab479c7d3b07460639d6fcb238d497 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

#include <aws/common/allocator.h>
#include <aws/common/device_random.h>
#include <aws/http/private/random_access_set.h>

struct aws_random_access_set_impl {
    struct aws_allocator *allocator;
    struct aws_array_list list; /* Always store the pointer of the element. */
    struct aws_hash_table map;  /* Map from the element to the index in the array. */
    aws_hash_callback_destroy_fn *destroy_element_fn;
};

static void s_impl_destroy(struct aws_random_access_set_impl *impl) {
    if (!impl) {
        return;
    }
    aws_array_list_clean_up(&impl->list);
    aws_hash_table_clean_up(&impl->map);
    aws_mem_release(impl->allocator, impl);
}

static struct aws_random_access_set_impl *s_impl_new(
    struct aws_allocator *allocator,
    aws_hash_fn *hash_fn,
    aws_hash_callback_eq_fn *equals_fn,
    aws_hash_callback_destroy_fn *destroy_element_fn,
    size_t initial_item_allocation) {
    struct aws_random_access_set_impl *impl = aws_mem_calloc(allocator, 1, sizeof(struct aws_random_access_set_impl));
    impl->allocator = allocator;
    /* Will always store the pointer of the element. */
    if (aws_array_list_init_dynamic(&impl->list, allocator, initial_item_allocation, sizeof(void *))) {
        s_impl_destroy(impl);
        return NULL;
    }

    if (aws_hash_table_init(
            &impl->map, allocator, initial_item_allocation, hash_fn, equals_fn, destroy_element_fn, NULL)) {
        s_impl_destroy(impl);
        return NULL;
    }
    impl->destroy_element_fn = destroy_element_fn;
    return impl;
}

int aws_random_access_set_init(
    struct aws_random_access_set *set,
    struct aws_allocator *allocator,
    aws_hash_fn *hash_fn,
    aws_hash_callback_eq_fn *equals_fn,
    aws_hash_callback_destroy_fn *destroy_element_fn,
    size_t initial_item_allocation) {
    AWS_FATAL_PRECONDITION(set);
    AWS_FATAL_PRECONDITION(allocator);
    AWS_FATAL_PRECONDITION(hash_fn);
    AWS_FATAL_PRECONDITION(equals_fn);

    struct aws_random_access_set_impl *impl =
        s_impl_new(allocator, hash_fn, equals_fn, destroy_element_fn, initial_item_allocation);
    if (!impl) {
        return AWS_OP_ERR;
    }
    set->impl = impl;
    return AWS_OP_SUCCESS;
}

void aws_random_access_set_clean_up(struct aws_random_access_set *set) {
    if (!set) {
        return;
    }
    s_impl_destroy(set->impl);
}

int aws_random_access_set_add(struct aws_random_access_set *set, const void *element, bool *added) {
    AWS_PRECONDITION(set);
    AWS_PRECONDITION(element);
    AWS_PRECONDITION(added);
    bool exist = false;
    if (aws_random_access_set_exist(set, element, &exist) || exist) {
        *added = false;
        return AWS_OP_SUCCESS;
    }
    /* deep copy the pointer of element to store at the array list */
    if (aws_array_list_push_back(&set->impl->list, (void *)&element)) {
        goto list_push_error;
    }
    if (aws_hash_table_put(&set->impl->map, element, (void *)(aws_array_list_length(&set->impl->list) - 1), NULL)) {
        goto error;
    }
    *added = true;
    return AWS_OP_SUCCESS;
error:
    aws_array_list_pop_back(&set->impl->list);
list_push_error:
    *added = false;
    return AWS_OP_ERR;
}

int aws_random_access_set_remove(struct aws_random_access_set *set, const void *element) {
    AWS_PRECONDITION(set);
    AWS_PRECONDITION(element);
    size_t current_length = aws_array_list_length(&set->impl->list);
    if (current_length == 0) {
        /* Nothing to remove */
        return AWS_OP_SUCCESS;
    }
    struct aws_hash_element *find = NULL;
    /* find and remove the element from table */
    if (aws_hash_table_find(&set->impl->map, element, &find)) {
        return AWS_OP_ERR;
    }
    if (!find) {
        /* It's removed already */
        return AWS_OP_SUCCESS;
    }

    size_t index_to_remove = (size_t)find->value;
    if (aws_hash_table_remove_element(&set->impl->map, find)) {
        return AWS_OP_ERR;
    }
    /* If assert code failed, we won't be recovered from the failure */
    int assert_re = AWS_OP_SUCCESS;
    (void)assert_re;
    /* Nothing else can fail after here. */
    if (index_to_remove != current_length - 1) {
        /* It's not the last element, we need to swap it with the end of the list and remove the last element */
        void *last_element = NULL;
        /* The last element is a pointer of pointer of element. */
        assert_re = aws_array_list_get_at_ptr(&set->impl->list, &last_element, current_length - 1);
        AWS_ASSERT(assert_re == AWS_OP_SUCCESS);
        /* Update the last element index in the table */
        struct aws_hash_element *element_to_update = NULL;
        assert_re = aws_hash_table_find(&set->impl->map, *(void **)last_element, &element_to_update);
        AWS_ASSERT(assert_re == AWS_OP_SUCCESS);
        AWS_ASSERT(element_to_update != NULL);
        element_to_update->value = (void *)index_to_remove;
        /* Swap the last element with the element to remove in the list */
        aws_array_list_swap(&set->impl->list, index_to_remove, current_length - 1);
    }
    /* Remove the current last element from the list */
    assert_re = aws_array_list_pop_back(&set->impl->list);
    AWS_ASSERT(assert_re == AWS_OP_SUCCESS);
    if (set->impl->destroy_element_fn) {
        set->impl->destroy_element_fn((void *)element);
    }
    return AWS_OP_SUCCESS;
}

int aws_random_access_set_random_get_ptr(const struct aws_random_access_set *set, void **out) {
    AWS_PRECONDITION(set);
    AWS_PRECONDITION(out != NULL);
    size_t length = aws_array_list_length(&set->impl->list);
    if (length == 0) {
        return aws_raise_error(AWS_ERROR_LIST_EMPTY);
    }

    uint64_t random_64_bit_num = 0;
    aws_device_random_u64(&random_64_bit_num);

    size_t index = (size_t)random_64_bit_num % length;
    /* The array list stores the pointer of the element. */
    return aws_array_list_get_at(&set->impl->list, (void *)out, index);
}

size_t aws_random_access_set_get_size(const struct aws_random_access_set *set) {
    return aws_array_list_length(&set->impl->list);
}

int aws_random_access_set_exist(const struct aws_random_access_set *set, const void *element, bool *exist) {
    AWS_PRECONDITION(set);
    AWS_PRECONDITION(element);
    AWS_PRECONDITION(exist);
    struct aws_hash_element *find = NULL;
    int re = aws_hash_table_find(&set->impl->map, element, &find);
    *exist = find != NULL;
    return re;
}

int aws_random_access_set_random_get_ptr_index(const struct aws_random_access_set *set, void **out, size_t index) {
    AWS_PRECONDITION(set);
    AWS_PRECONDITION(out != NULL);
    return aws_array_list_get_at(&set->impl->list, (void *)out, index);
}