| 1 | /* |
| 2 | Copyright (c) 2012, Broadcom Europe Ltd |
| 3 | All rights reserved. |
| 4 | |
| 5 | Redistribution and use in source and binary forms, with or without |
| 6 | modification, are permitted provided that the following conditions are met: |
| 7 | * Redistributions of source code must retain the above copyright |
| 8 | notice, this list of conditions and the following disclaimer. |
| 9 | * Redistributions in binary form must reproduce the above copyright |
| 10 | notice, this list of conditions and the following disclaimer in the |
| 11 | documentation and/or other materials provided with the distribution. |
| 12 | * Neither the name of the copyright holder nor the |
| 13 | names of its contributors may be used to endorse or promote products |
| 14 | derived from this software without specific prior written permission. |
| 15 | |
| 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
| 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | #include <stdlib.h> |
| 28 | #include <stdio.h> |
| 29 | #include <string.h> |
| 30 | |
| 31 | #include "containers/core/containers_common.h" |
| 32 | #include "containers/core/containers_list.h" |
| 33 | |
| 34 | /****************************************************************************** |
| 35 | Defines and constants. |
| 36 | ******************************************************************************/ |
| 37 | |
| 38 | /****************************************************************************** |
| 39 | Type definitions |
| 40 | ******************************************************************************/ |
| 41 | |
| 42 | /****************************************************************************** |
| 43 | Function prototypes |
| 44 | ******************************************************************************/ |
| 45 | |
| 46 | /****************************************************************************** |
| 47 | Local Functions |
| 48 | ******************************************************************************/ |
| 49 | |
| 50 | /** Find an entry in the list, or the insertion point. |
| 51 | * Uses binary sub-division to find the search item. If index is not NULL, the |
| 52 | * index of the matching entry, or the point at which to insert if not found, is |
| 53 | * written to that address. |
| 54 | * |
| 55 | * \param list The list to be searched. |
| 56 | * \param entry The entry for which to search. |
| 57 | * \param index Set to index of match, or insertion point if not found. May be NULL. |
| 58 | * \return True if a match was found, false if not. */ |
| 59 | static bool vc_containers_list_find_index(const VC_CONTAINERS_LIST_T *list, |
| 60 | const void *entry, |
| 61 | uint32_t *index) |
| 62 | { |
| 63 | const char *entries = (const char *)list->entries; |
| 64 | size_t entry_size = list->entry_size; |
| 65 | VC_CONTAINERS_LIST_COMPARATOR_T comparator = list->comparator; |
| 66 | uint32_t start = 0, end = list->size; |
| 67 | uint32_t mid = end >> 1; |
| 68 | bool match = false; |
| 69 | |
| 70 | while (mid < end) |
| 71 | { |
| 72 | int comparison = comparator(entry, entries + mid * entry_size); |
| 73 | |
| 74 | if (comparison < 0) |
| 75 | end = mid; |
| 76 | else if (comparison > 0) |
| 77 | start = mid + 1; |
| 78 | else { |
| 79 | match = true; |
| 80 | break; |
| 81 | } |
| 82 | |
| 83 | mid = (start + end) >> 1; |
| 84 | } |
| 85 | |
| 86 | if (index) *index = mid; |
| 87 | return match; |
| 88 | } |
| 89 | |
| 90 | /****************************************************************************** |
| 91 | Functions exported as part of the API |
| 92 | ******************************************************************************/ |
| 93 | |
| 94 | /*****************************************************************************/ |
| 95 | VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity, |
| 96 | size_t entry_size, |
| 97 | VC_CONTAINERS_LIST_COMPARATOR_T comparator) |
| 98 | { |
| 99 | VC_CONTAINERS_LIST_T *list; |
| 100 | |
| 101 | list = (VC_CONTAINERS_LIST_T *)malloc(sizeof(VC_CONTAINERS_LIST_T)); |
| 102 | if (!list) |
| 103 | return NULL; |
| 104 | |
| 105 | /* Ensure non-zero capacity, as that signifies a read-only list */ |
| 106 | if (!capacity) capacity = 1; |
| 107 | |
| 108 | list->entries = malloc(capacity * entry_size); |
| 109 | if (!list->entries) |
| 110 | { |
| 111 | free(list); |
| 112 | return NULL; |
| 113 | } |
| 114 | |
| 115 | list->size = 0; |
| 116 | list->capacity = capacity; |
| 117 | list->entry_size = entry_size; |
| 118 | list->comparator = comparator; |
| 119 | |
| 120 | return list; |
| 121 | } |
| 122 | |
| 123 | /*****************************************************************************/ |
| 124 | void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list) |
| 125 | { |
| 126 | /* Avoid trying to destroy read-only lists */ |
| 127 | if (list && list->capacity) |
| 128 | { |
| 129 | if (list->entries) |
| 130 | free(list->entries); |
| 131 | free(list); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | /*****************************************************************************/ |
| 136 | void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list) |
| 137 | { |
| 138 | /* Avoid trying to reset read-only lists */ |
| 139 | if (list && list->capacity) |
| 140 | list->size = 0; |
| 141 | } |
| 142 | |
| 143 | /*****************************************************************************/ |
| 144 | bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list, |
| 145 | void *new_entry, |
| 146 | bool allow_duplicates) |
| 147 | { |
| 148 | uint32_t insert_idx; |
| 149 | char *insert_ptr; |
| 150 | size_t entry_size; |
| 151 | bool match; |
| 152 | |
| 153 | if (!list || !list->capacity) return false; |
| 154 | |
| 155 | entry_size = list->entry_size; |
| 156 | match = vc_containers_list_find_index(list, new_entry, &insert_idx); |
| 157 | insert_ptr = (char *)list->entries + entry_size * insert_idx; |
| 158 | |
| 159 | if (!match || allow_duplicates) |
| 160 | { |
| 161 | /* Ensure there is space for the new entry */ |
| 162 | if (list->size == list->capacity) |
| 163 | { |
| 164 | void *new_entries = realloc(list->entries, (list->size + 1) * entry_size); |
| 165 | |
| 166 | if (!new_entries) |
| 167 | return false; |
| 168 | list->entries = new_entries; |
| 169 | list->capacity++; |
| 170 | } |
| 171 | |
| 172 | /* Move up anything above the insertion point */ |
| 173 | if (insert_idx < list->size) |
| 174 | memmove(insert_ptr + entry_size, insert_ptr, (list->size - insert_idx) * entry_size); |
| 175 | |
| 176 | list->size++; |
| 177 | } |
| 178 | |
| 179 | /* Copy in the new entry (overwriting the old one if necessary) */ |
| 180 | memcpy(insert_ptr, new_entry, list->entry_size); |
| 181 | |
| 182 | return true; |
| 183 | } |
| 184 | |
| 185 | /*****************************************************************************/ |
| 186 | bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list, |
| 187 | void *entry) |
| 188 | { |
| 189 | uint32_t index; |
| 190 | size_t entry_size; |
| 191 | |
| 192 | if (!vc_containers_list_find_index(list, entry, &index)) |
| 193 | return false; |
| 194 | |
| 195 | entry_size = list->entry_size; |
| 196 | memcpy(entry, (const char *)list->entries + entry_size * index, entry_size); |
| 197 | |
| 198 | return true; |
| 199 | } |
| 200 | |
| 201 | /*****************************************************************************/ |
| 202 | void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list) |
| 203 | { |
| 204 | uint32_t ii, entry_size; |
| 205 | const uint8_t *entry_ptr; |
| 206 | |
| 207 | vc_container_assert(list); |
| 208 | vc_container_assert(!list->capacity || list->size <= list->capacity); |
| 209 | vc_container_assert(list->entry_size); |
| 210 | vc_container_assert(list->comparator); |
| 211 | vc_container_assert(list->entries); |
| 212 | |
| 213 | /* Check all entries are in sorted order */ |
| 214 | entry_ptr = (const uint8_t *)list->entries; |
| 215 | entry_size = list->entry_size; |
| 216 | for (ii = 1; ii < list->size; ii++) |
| 217 | { |
| 218 | vc_container_assert(list->comparator(entry_ptr, entry_ptr + entry_size) <= 0); |
| 219 | entry_ptr += entry_size; |
| 220 | } |
| 221 | } |
| 222 | |