| /* Sequential list data type implemented by a linked list. | |
| Copyright (C) 2006-2026 Free Software Foundation, Inc. | |
| Written by Bruno Haible <bruno@clisp.org>, 2006. | |
| This file is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as | |
| published by the Free Software Foundation; either version 2.1 of the | |
| License, or (at your option) any later version. | |
| This file is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| /* Common code of gl_linked_list.c and gl_linkedhash_list.c. */ | |
| /* If the symbol SIGNAL_SAFE_LIST is defined, the code is compiled in such | |
| a way that a gl_list_t data structure may be used from within a signal | |
| handler. The operations allowed in the signal handler are: | |
| gl_list_iterator, gl_list_iterator_next, gl_list_iterator_free. | |
| The list and node fields that are therefore accessed from the signal handler | |
| are: | |
| list->root, node->next, node->value. | |
| We are careful to make modifications to these fields only in an order | |
| that maintains the consistency of the list data structure at any moment, | |
| and we use 'volatile' assignments to prevent the compiler from reordering | |
| such assignments. */ | |
| #ifdef SIGNAL_SAFE_LIST | |
| # define ASYNCSAFE(type) *(type volatile *)& | |
| #else | |
| # define ASYNCSAFE(type) | |
| #endif | |
| /* -------------------------- gl_list_t Data Type -------------------------- */ | |
| static gl_list_t | |
| gl_linked_nx_create_empty (gl_list_implementation_t implementation, | |
| gl_listelement_equals_fn equals_fn, | |
| gl_listelement_hashcode_fn hashcode_fn, | |
| gl_listelement_dispose_fn dispose_fn, | |
| bool allow_duplicates) | |
| { | |
| struct gl_list_impl *list = | |
| (struct gl_list_impl *) malloc (sizeof (struct gl_list_impl)); | |
| if (list == NULL) | |
| return NULL; | |
| list->base.vtable = implementation; | |
| list->base.equals_fn = equals_fn; | |
| list->base.hashcode_fn = hashcode_fn; | |
| list->base.dispose_fn = dispose_fn; | |
| list->base.allow_duplicates = allow_duplicates; | |
| #if WITH_HASHTABLE | |
| list->table_size = 11; | |
| list->table = | |
| (gl_hash_entry_t *) calloc (list->table_size, sizeof (gl_hash_entry_t)); | |
| if (list->table == NULL) | |
| goto fail; | |
| #endif | |
| list->root.next = &list->root; | |
| list->root.prev = &list->root; | |
| list->count = 0; | |
| return list; | |
| #if WITH_HASHTABLE | |
| fail: | |
| free (list); | |
| return NULL; | |
| #endif | |
| } | |
| static gl_list_t | |
| gl_linked_nx_create (gl_list_implementation_t implementation, | |
| gl_listelement_equals_fn equals_fn, | |
| gl_listelement_hashcode_fn hashcode_fn, | |
| gl_listelement_dispose_fn dispose_fn, | |
| bool allow_duplicates, | |
| size_t count, const void **contents) | |
| { | |
| struct gl_list_impl *list = | |
| (struct gl_list_impl *) malloc (sizeof (struct gl_list_impl)); | |
| gl_list_node_t tail; | |
| if (list == NULL) | |
| return NULL; | |
| list->base.vtable = implementation; | |
| list->base.equals_fn = equals_fn; | |
| list->base.hashcode_fn = hashcode_fn; | |
| list->base.dispose_fn = dispose_fn; | |
| list->base.allow_duplicates = allow_duplicates; | |
| #if WITH_HASHTABLE | |
| { | |
| size_t estimate = xsum (count, count / 2); /* 1.5 * count */ | |
| if (estimate < 10) | |
| estimate = 10; | |
| list->table_size = next_prime (estimate); | |
| if (size_overflow_p (xtimes (list->table_size, sizeof (gl_hash_entry_t)))) | |
| goto fail1; | |
| list->table = | |
| (gl_hash_entry_t *) calloc (list->table_size, sizeof (gl_hash_entry_t)); | |
| if (list->table == NULL) | |
| goto fail1; | |
| } | |
| #endif | |
| list->count = count; | |
| tail = &list->root; | |
| for (; count > 0; contents++, count--) | |
| { | |
| gl_list_node_t node = | |
| (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); | |
| if (node == NULL) | |
| goto fail2; | |
| node->value = *contents; | |
| #if WITH_HASHTABLE | |
| node->h.hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (node->value) | |
| : (size_t)(uintptr_t) node->value); | |
| /* Add node to the hash table. */ | |
| if (add_to_bucket (list, node) < 0) | |
| { | |
| free (node); | |
| goto fail2; | |
| } | |
| #endif | |
| /* Add node to the list. */ | |
| node->prev = tail; | |
| tail->next = node; | |
| tail = node; | |
| } | |
| tail->next = &list->root; | |
| list->root.prev = tail; | |
| return list; | |
| fail2: | |
| for (gl_list_node_t node = tail; node != &list->root; ) | |
| { | |
| gl_list_node_t prev = node->prev; | |
| free (node); | |
| node = prev; | |
| } | |
| #if WITH_HASHTABLE | |
| free (list->table); | |
| fail1: | |
| #endif | |
| free (list); | |
| return NULL; | |
| } | |
| static size_t _GL_ATTRIBUTE_PURE | |
| gl_linked_size (gl_list_t list) | |
| { | |
| return list->count; | |
| } | |
| static const void * _GL_ATTRIBUTE_PURE | |
| gl_linked_node_value (gl_list_t _GL_UNNAMED (list), gl_list_node_t node) | |
| { | |
| return node->value; | |
| } | |
| static int | |
| gl_linked_node_nx_set_value (_GL_ATTRIBUTE_MAYBE_UNUSED gl_list_t list, | |
| gl_list_node_t node, | |
| const void *elt) | |
| { | |
| #if WITH_HASHTABLE | |
| if (elt != node->value) | |
| { | |
| size_t new_hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (elt) | |
| : (size_t)(uintptr_t) elt); | |
| if (new_hashcode != node->h.hashcode) | |
| { | |
| remove_from_bucket (list, node); | |
| node->value = elt; | |
| node->h.hashcode = new_hashcode; | |
| if (add_to_bucket (list, node) < 0) | |
| { | |
| /* Out of memory. We removed node from a bucket but cannot add | |
| it to another bucket. In order to avoid inconsistencies, we | |
| must remove node entirely from the list. */ | |
| gl_list_node_t before_removed = node->prev; | |
| gl_list_node_t after_removed = node->next; | |
| ASYNCSAFE(gl_list_node_t) before_removed->next = after_removed; | |
| after_removed->prev = before_removed; | |
| list->count--; | |
| free (node); | |
| return -1; | |
| } | |
| } | |
| else | |
| node->value = elt; | |
| } | |
| #else | |
| node->value = elt; | |
| #endif | |
| return 0; | |
| } | |
| static gl_list_node_t _GL_ATTRIBUTE_PURE | |
| gl_linked_next_node (gl_list_t list, gl_list_node_t node) | |
| { | |
| return (node->next != &list->root ? node->next : NULL); | |
| } | |
| static gl_list_node_t _GL_ATTRIBUTE_PURE | |
| gl_linked_previous_node (gl_list_t list, gl_list_node_t node) | |
| { | |
| return (node->prev != &list->root ? node->prev : NULL); | |
| } | |
| static gl_list_node_t _GL_ATTRIBUTE_PURE | |
| gl_linked_first_node (gl_list_t list) | |
| { | |
| if (list->count > 0) | |
| return list->root.next; | |
| else | |
| return NULL; | |
| } | |
| static gl_list_node_t _GL_ATTRIBUTE_PURE | |
| gl_linked_last_node (gl_list_t list) | |
| { | |
| if (list->count > 0) | |
| return list->root.prev; | |
| else | |
| return NULL; | |
| } | |
| static const void * _GL_ATTRIBUTE_PURE | |
| gl_linked_get_at (gl_list_t list, size_t position) | |
| { | |
| size_t count = list->count; | |
| if (!(position < count)) | |
| /* Invalid argument. */ | |
| abort (); | |
| /* Here we know count > 0. */ | |
| gl_list_node_t node; | |
| if (position <= ((count - 1) / 2)) | |
| { | |
| node = list->root.next; | |
| for (; position > 0; position--) | |
| node = node->next; | |
| } | |
| else | |
| { | |
| position = count - 1 - position; | |
| node = list->root.prev; | |
| for (; position > 0; position--) | |
| node = node->prev; | |
| } | |
| return node->value; | |
| } | |
| static gl_list_node_t | |
| gl_linked_nx_set_at (gl_list_t list, size_t position, const void *elt) | |
| { | |
| size_t count = list->count; | |
| if (!(position < count)) | |
| /* Invalid argument. */ | |
| abort (); | |
| /* Here we know count > 0. */ | |
| gl_list_node_t node; | |
| if (position <= ((count - 1) / 2)) | |
| { | |
| node = list->root.next; | |
| for (; position > 0; position--) | |
| node = node->next; | |
| } | |
| else | |
| { | |
| position = count - 1 - position; | |
| node = list->root.prev; | |
| for (; position > 0; position--) | |
| node = node->prev; | |
| } | |
| #if WITH_HASHTABLE | |
| if (elt != node->value) | |
| { | |
| size_t new_hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (elt) | |
| : (size_t)(uintptr_t) elt); | |
| if (new_hashcode != node->h.hashcode) | |
| { | |
| remove_from_bucket (list, node); | |
| node->value = elt; | |
| node->h.hashcode = new_hashcode; | |
| if (add_to_bucket (list, node) < 0) | |
| { | |
| /* Out of memory. We removed node from a bucket but cannot add | |
| it to another bucket. In order to avoid inconsistencies, we | |
| must remove node entirely from the list. */ | |
| gl_list_node_t before_removed = node->prev; | |
| gl_list_node_t after_removed = node->next; | |
| ASYNCSAFE(gl_list_node_t) before_removed->next = after_removed; | |
| after_removed->prev = before_removed; | |
| list->count--; | |
| free (node); | |
| return NULL; | |
| } | |
| } | |
| else | |
| node->value = elt; | |
| } | |
| #else | |
| node->value = elt; | |
| #endif | |
| return node; | |
| } | |
| static gl_list_node_t _GL_ATTRIBUTE_PURE | |
| gl_linked_search_from_to (gl_list_t list, size_t start_index, size_t end_index, | |
| const void *elt) | |
| { | |
| size_t count = list->count; | |
| if (!(start_index <= end_index && end_index <= count)) | |
| /* Invalid arguments. */ | |
| abort (); | |
| { | |
| #if WITH_HASHTABLE | |
| size_t hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (elt) | |
| : (size_t)(uintptr_t) elt); | |
| size_t bucket = hashcode % list->table_size; | |
| gl_listelement_equals_fn equals = list->base.equals_fn; | |
| if (!list->base.allow_duplicates) | |
| { | |
| /* Look for the first match in the hash bucket. */ | |
| gl_list_node_t found = NULL; | |
| for (gl_list_node_t node = (gl_list_node_t) list->table[bucket]; | |
| node != NULL; | |
| node = (gl_list_node_t) node->h.hash_next) | |
| if (node->h.hashcode == hashcode | |
| && (equals != NULL | |
| ? equals (elt, node->value) | |
| : elt == node->value)) | |
| { | |
| found = node; | |
| break; | |
| } | |
| if (start_index > 0) | |
| /* Look whether found's index is < start_index. */ | |
| for (gl_list_node_t node = list->root.next; ; node = node->next) | |
| { | |
| if (node == found) | |
| return NULL; | |
| if (--start_index == 0) | |
| break; | |
| } | |
| if (end_index < count) | |
| /* Look whether found's index is >= end_index. */ | |
| { | |
| end_index = count - end_index; | |
| for (gl_list_node_t node = list->root.prev; ; node = node->prev) | |
| { | |
| if (node == found) | |
| return NULL; | |
| if (--end_index == 0) | |
| break; | |
| } | |
| } | |
| return found; | |
| } | |
| else | |
| { | |
| /* Look whether there is more than one match in the hash bucket. */ | |
| bool multiple_matches = false; | |
| gl_list_node_t first_match = NULL; | |
| for (gl_list_node_t node = (gl_list_node_t) list->table[bucket]; | |
| node != NULL; | |
| node = (gl_list_node_t) node->h.hash_next) | |
| if (node->h.hashcode == hashcode | |
| && (equals != NULL | |
| ? equals (elt, node->value) | |
| : elt == node->value)) | |
| { | |
| if (first_match == NULL) | |
| first_match = node; | |
| else | |
| { | |
| multiple_matches = true; | |
| break; | |
| } | |
| } | |
| if (multiple_matches) | |
| { | |
| /* We need the match with the smallest index. But we don't have | |
| a fast mapping node -> index. So we have to walk the list. */ | |
| end_index -= start_index; | |
| gl_list_node_t node = list->root.next; | |
| for (; start_index > 0; start_index--) | |
| node = node->next; | |
| for (; | |
| end_index > 0; | |
| node = node->next, end_index--) | |
| if (node->h.hashcode == hashcode | |
| && (equals != NULL | |
| ? equals (elt, node->value) | |
| : elt == node->value)) | |
| return node; | |
| /* The matches must have all been at indices < start_index or | |
| >= end_index. */ | |
| return NULL; | |
| } | |
| else | |
| { | |
| if (start_index > 0) | |
| /* Look whether first_match's index is < start_index. */ | |
| for (gl_list_node_t node = list->root.next; node != &list->root; node = node->next) | |
| { | |
| if (node == first_match) | |
| return NULL; | |
| if (--start_index == 0) | |
| break; | |
| } | |
| if (end_index < list->count) | |
| /* Look whether first_match's index is >= end_index. */ | |
| { | |
| end_index = list->count - end_index; | |
| for (gl_list_node_t node = list->root.prev; ; node = node->prev) | |
| { | |
| if (node == first_match) | |
| return NULL; | |
| if (--end_index == 0) | |
| break; | |
| } | |
| } | |
| return first_match; | |
| } | |
| } | |
| #else | |
| end_index -= start_index; | |
| gl_list_node_t node = list->root.next; | |
| for (; start_index > 0; start_index--) | |
| node = node->next; | |
| gl_listelement_equals_fn equals = list->base.equals_fn; | |
| if (equals != NULL) | |
| { | |
| for (; end_index > 0; node = node->next, end_index--) | |
| if (equals (elt, node->value)) | |
| return node; | |
| } | |
| else | |
| { | |
| for (; end_index > 0; node = node->next, end_index--) | |
| if (elt == node->value) | |
| return node; | |
| } | |
| return NULL; | |
| #endif | |
| } | |
| } | |
| static size_t _GL_ATTRIBUTE_PURE | |
| gl_linked_indexof_from_to (gl_list_t list, size_t start_index, size_t end_index, | |
| const void *elt) | |
| { | |
| size_t count = list->count; | |
| if (!(start_index <= end_index && end_index <= count)) | |
| /* Invalid arguments. */ | |
| abort (); | |
| { | |
| #if WITH_HASHTABLE | |
| /* Here the hash table doesn't help much. It only allows us to minimize | |
| the number of equals() calls, by looking up first the node and then | |
| its index. */ | |
| size_t hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (elt) | |
| : (size_t)(uintptr_t) elt); | |
| size_t bucket = hashcode % list->table_size; | |
| gl_listelement_equals_fn equals = list->base.equals_fn; | |
| /* First step: Look up the node. */ | |
| gl_list_node_t node; | |
| if (!list->base.allow_duplicates) | |
| { | |
| /* Look for the first match in the hash bucket. */ | |
| for (node = (gl_list_node_t) list->table[bucket]; | |
| node != NULL; | |
| node = (gl_list_node_t) node->h.hash_next) | |
| if (node->h.hashcode == hashcode | |
| && (equals != NULL | |
| ? equals (elt, node->value) | |
| : elt == node->value)) | |
| break; | |
| } | |
| else | |
| { | |
| /* Look whether there is more than one match in the hash bucket. */ | |
| bool multiple_matches = false; | |
| gl_list_node_t first_match = NULL; | |
| for (node = (gl_list_node_t) list->table[bucket]; | |
| node != NULL; | |
| node = (gl_list_node_t) node->h.hash_next) | |
| if (node->h.hashcode == hashcode | |
| && (equals != NULL | |
| ? equals (elt, node->value) | |
| : elt == node->value)) | |
| { | |
| if (first_match == NULL) | |
| first_match = node; | |
| else | |
| { | |
| multiple_matches = true; | |
| break; | |
| } | |
| } | |
| if (multiple_matches) | |
| { | |
| /* We need the match with the smallest index. But we don't have | |
| a fast mapping node -> index. So we have to walk the list. */ | |
| size_t index = start_index; | |
| node = list->root.next; | |
| for (; start_index > 0; start_index--) | |
| node = node->next; | |
| for (; | |
| index < end_index; | |
| node = node->next, index++) | |
| if (node->h.hashcode == hashcode | |
| && (equals != NULL | |
| ? equals (elt, node->value) | |
| : elt == node->value)) | |
| return index; | |
| /* The matches must have all been at indices < start_index or | |
| >= end_index. */ | |
| return (size_t)(-1); | |
| } | |
| node = first_match; | |
| } | |
| /* Second step: Look up the index of the node. */ | |
| if (node == NULL) | |
| return (size_t)(-1); | |
| else | |
| { | |
| size_t index = 0; | |
| for (; node->prev != &list->root; node = node->prev) | |
| index++; | |
| if (index >= start_index && index < end_index) | |
| return index; | |
| else | |
| return (size_t)(-1); | |
| } | |
| #else | |
| size_t index = start_index; | |
| gl_list_node_t node = list->root.next; | |
| for (; start_index > 0; start_index--) | |
| node = node->next; | |
| gl_listelement_equals_fn equals = list->base.equals_fn; | |
| if (equals != NULL) | |
| { | |
| for (; | |
| index < end_index; | |
| node = node->next, index++) | |
| if (equals (elt, node->value)) | |
| return index; | |
| } | |
| else | |
| { | |
| for (; | |
| index < end_index; | |
| node = node->next, index++) | |
| if (elt == node->value) | |
| return index; | |
| } | |
| return (size_t)(-1); | |
| #endif | |
| } | |
| } | |
| static gl_list_node_t | |
| gl_linked_nx_add_first (gl_list_t list, const void *elt) | |
| { | |
| gl_list_node_t node = | |
| (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); | |
| if (node == NULL) | |
| return NULL; | |
| ASYNCSAFE(const void *) node->value = elt; | |
| #if WITH_HASHTABLE | |
| node->h.hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (node->value) | |
| : (size_t)(uintptr_t) node->value); | |
| /* Add node to the hash table. */ | |
| if (add_to_bucket (list, node) < 0) | |
| { | |
| free (node); | |
| return NULL; | |
| } | |
| #endif | |
| /* Add node to the list. */ | |
| node->prev = &list->root; | |
| ASYNCSAFE(gl_list_node_t) node->next = list->root.next; | |
| node->next->prev = node; | |
| ASYNCSAFE(gl_list_node_t) list->root.next = node; | |
| list->count++; | |
| #if WITH_HASHTABLE | |
| hash_resize_after_add (list); | |
| #endif | |
| return node; | |
| } | |
| static gl_list_node_t | |
| gl_linked_nx_add_last (gl_list_t list, const void *elt) | |
| { | |
| gl_list_node_t node = | |
| (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); | |
| if (node == NULL) | |
| return NULL; | |
| ASYNCSAFE(const void *) node->value = elt; | |
| #if WITH_HASHTABLE | |
| node->h.hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (node->value) | |
| : (size_t)(uintptr_t) node->value); | |
| /* Add node to the hash table. */ | |
| if (add_to_bucket (list, node) < 0) | |
| { | |
| free (node); | |
| return NULL; | |
| } | |
| #endif | |
| /* Add node to the list. */ | |
| ASYNCSAFE(gl_list_node_t) node->next = &list->root; | |
| node->prev = list->root.prev; | |
| ASYNCSAFE(gl_list_node_t) node->prev->next = node; | |
| list->root.prev = node; | |
| list->count++; | |
| #if WITH_HASHTABLE | |
| hash_resize_after_add (list); | |
| #endif | |
| return node; | |
| } | |
| static gl_list_node_t | |
| gl_linked_nx_add_before (gl_list_t list, gl_list_node_t node, const void *elt) | |
| { | |
| gl_list_node_t new_node = | |
| (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); | |
| if (new_node == NULL) | |
| return NULL; | |
| ASYNCSAFE(const void *) new_node->value = elt; | |
| #if WITH_HASHTABLE | |
| new_node->h.hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (new_node->value) | |
| : (size_t)(uintptr_t) new_node->value); | |
| /* Add new_node to the hash table. */ | |
| if (add_to_bucket (list, new_node) < 0) | |
| { | |
| free (new_node); | |
| return NULL; | |
| } | |
| #endif | |
| /* Add new_node to the list. */ | |
| ASYNCSAFE(gl_list_node_t) new_node->next = node; | |
| new_node->prev = node->prev; | |
| ASYNCSAFE(gl_list_node_t) new_node->prev->next = new_node; | |
| node->prev = new_node; | |
| list->count++; | |
| #if WITH_HASHTABLE | |
| hash_resize_after_add (list); | |
| #endif | |
| return new_node; | |
| } | |
| static gl_list_node_t | |
| gl_linked_nx_add_after (gl_list_t list, gl_list_node_t node, const void *elt) | |
| { | |
| gl_list_node_t new_node = | |
| (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); | |
| if (new_node == NULL) | |
| return NULL; | |
| ASYNCSAFE(const void *) new_node->value = elt; | |
| #if WITH_HASHTABLE | |
| new_node->h.hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (new_node->value) | |
| : (size_t)(uintptr_t) new_node->value); | |
| /* Add new_node to the hash table. */ | |
| if (add_to_bucket (list, new_node) < 0) | |
| { | |
| free (new_node); | |
| return NULL; | |
| } | |
| #endif | |
| /* Add new_node to the list. */ | |
| new_node->prev = node; | |
| ASYNCSAFE(gl_list_node_t) new_node->next = node->next; | |
| new_node->next->prev = new_node; | |
| ASYNCSAFE(gl_list_node_t) node->next = new_node; | |
| list->count++; | |
| #if WITH_HASHTABLE | |
| hash_resize_after_add (list); | |
| #endif | |
| return new_node; | |
| } | |
| static gl_list_node_t | |
| gl_linked_nx_add_at (gl_list_t list, size_t position, const void *elt) | |
| { | |
| size_t count = list->count; | |
| gl_list_node_t new_node; | |
| if (!(position <= count)) | |
| /* Invalid argument. */ | |
| abort (); | |
| new_node = (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); | |
| if (new_node == NULL) | |
| return NULL; | |
| ASYNCSAFE(const void *) new_node->value = elt; | |
| #if WITH_HASHTABLE | |
| new_node->h.hashcode = | |
| (list->base.hashcode_fn != NULL | |
| ? list->base.hashcode_fn (new_node->value) | |
| : (size_t)(uintptr_t) new_node->value); | |
| /* Add new_node to the hash table. */ | |
| if (add_to_bucket (list, new_node) < 0) | |
| { | |
| free (new_node); | |
| return NULL; | |
| } | |
| #endif | |
| /* Add new_node to the list. */ | |
| if (position <= (count / 2)) | |
| { | |
| gl_list_node_t node = &list->root; | |
| for (; position > 0; position--) | |
| node = node->next; | |
| new_node->prev = node; | |
| ASYNCSAFE(gl_list_node_t) new_node->next = node->next; | |
| new_node->next->prev = new_node; | |
| ASYNCSAFE(gl_list_node_t) node->next = new_node; | |
| } | |
| else | |
| { | |
| position = count - position; | |
| gl_list_node_t node = &list->root; | |
| for (; position > 0; position--) | |
| node = node->prev; | |
| ASYNCSAFE(gl_list_node_t) new_node->next = node; | |
| new_node->prev = node->prev; | |
| ASYNCSAFE(gl_list_node_t) new_node->prev->next = new_node; | |
| node->prev = new_node; | |
| } | |
| list->count++; | |
| #if WITH_HASHTABLE | |
| hash_resize_after_add (list); | |
| #endif | |
| return new_node; | |
| } | |
| static bool | |
| gl_linked_remove_node (gl_list_t list, gl_list_node_t node) | |
| { | |
| #if WITH_HASHTABLE | |
| /* Remove node from the hash table. */ | |
| remove_from_bucket (list, node); | |
| #endif | |
| /* Remove node from the list. */ | |
| gl_list_node_t prev = node->prev; | |
| gl_list_node_t next = node->next; | |
| ASYNCSAFE(gl_list_node_t) prev->next = next; | |
| next->prev = prev; | |
| list->count--; | |
| if (list->base.dispose_fn != NULL) | |
| list->base.dispose_fn (node->value); | |
| free (node); | |
| return true; | |
| } | |
| static bool | |
| gl_linked_remove_at (gl_list_t list, size_t position) | |
| { | |
| size_t count = list->count; | |
| if (!(position < count)) | |
| /* Invalid argument. */ | |
| abort (); | |
| /* Here we know count > 0. */ | |
| gl_list_node_t removed_node; | |
| if (position <= ((count - 1) / 2)) | |
| { | |
| gl_list_node_t node = &list->root; | |
| for (; position > 0; position--) | |
| node = node->next; | |
| removed_node = node->next; | |
| gl_list_node_t after_removed = node->next->next; | |
| ASYNCSAFE(gl_list_node_t) node->next = after_removed; | |
| after_removed->prev = node; | |
| } | |
| else | |
| { | |
| position = count - 1 - position; | |
| gl_list_node_t node = &list->root; | |
| for (; position > 0; position--) | |
| node = node->prev; | |
| removed_node = node->prev; | |
| gl_list_node_t before_removed = node->prev->prev; | |
| node->prev = before_removed; | |
| ASYNCSAFE(gl_list_node_t) before_removed->next = node; | |
| } | |
| #if WITH_HASHTABLE | |
| remove_from_bucket (list, removed_node); | |
| #endif | |
| list->count--; | |
| if (list->base.dispose_fn != NULL) | |
| list->base.dispose_fn (removed_node->value); | |
| free (removed_node); | |
| return true; | |
| } | |
| static bool | |
| gl_linked_remove (gl_list_t list, const void *elt) | |
| { | |
| gl_list_node_t node = gl_linked_search_from_to (list, 0, list->count, elt); | |
| if (node != NULL) | |
| return gl_linked_remove_node (list, node); | |
| else | |
| return false; | |
| } | |
| static void | |
| gl_linked_list_free (gl_list_t list) | |
| { | |
| gl_listelement_dispose_fn dispose = list->base.dispose_fn; | |
| for (gl_list_node_t node = list->root.next; node != &list->root; ) | |
| { | |
| gl_list_node_t next = node->next; | |
| if (dispose != NULL) | |
| dispose (node->value); | |
| free (node); | |
| node = next; | |
| } | |
| #if WITH_HASHTABLE | |
| free (list->table); | |
| #endif | |
| free (list); | |
| } | |
| /* --------------------- gl_list_iterator_t Data Type --------------------- */ | |
| static gl_list_iterator_t _GL_ATTRIBUTE_PURE | |
| gl_linked_iterator (gl_list_t list) | |
| { | |
| gl_list_iterator_t result; | |
| result.vtable = list->base.vtable; | |
| result.list = list; | |
| result.p = list->root.next; | |
| result.q = &list->root; | |
| #if defined GCC_LINT || defined lint | |
| result.i = 0; | |
| result.j = 0; | |
| result.count = 0; | |
| #endif | |
| return result; | |
| } | |
| static gl_list_iterator_t _GL_ATTRIBUTE_PURE | |
| gl_linked_iterator_from_to (gl_list_t list, | |
| size_t start_index, size_t end_index) | |
| { | |
| gl_list_iterator_t result; | |
| size_t n1, n2, n3; | |
| if (!(start_index <= end_index && end_index <= list->count)) | |
| /* Invalid arguments. */ | |
| abort (); | |
| result.vtable = list->base.vtable; | |
| result.list = list; | |
| n1 = start_index; | |
| n2 = end_index - start_index; | |
| n3 = list->count - end_index; | |
| /* Find the maximum among n1, n2, n3, so as to reduce the number of | |
| loop iterations to n1 + n2 + n3 - max(n1,n2,n3). */ | |
| if (n1 > n2 && n1 > n3) | |
| { | |
| /* n1 is the maximum, use n2 and n3. */ | |
| gl_list_node_t node = &list->root; | |
| for (size_t i = n3; i > 0; i--) | |
| node = node->prev; | |
| result.q = node; | |
| for (size_t i = n2; i > 0; i--) | |
| node = node->prev; | |
| result.p = node; | |
| } | |
| else if (n2 > n3) | |
| { | |
| /* n2 is the maximum, use n1 and n3. */ | |
| gl_list_node_t node = list->root.next; | |
| for (size_t i = n1; i > 0; i--) | |
| node = node->next; | |
| result.p = node; | |
| node = &list->root; | |
| for (size_t i = n3; i > 0; i--) | |
| node = node->prev; | |
| result.q = node; | |
| } | |
| else | |
| { | |
| /* n3 is the maximum, use n1 and n2. */ | |
| gl_list_node_t node = list->root.next; | |
| for (size_t i = n1; i > 0; i--) | |
| node = node->next; | |
| result.p = node; | |
| for (size_t i = n2; i > 0; i--) | |
| node = node->next; | |
| result.q = node; | |
| } | |
| #if defined GCC_LINT || defined lint | |
| result.i = 0; | |
| result.j = 0; | |
| result.count = 0; | |
| #endif | |
| return result; | |
| } | |
| static bool | |
| gl_linked_iterator_next (gl_list_iterator_t *iterator, | |
| const void **eltp, gl_list_node_t *nodep) | |
| { | |
| if (iterator->p != iterator->q) | |
| { | |
| gl_list_node_t node = (gl_list_node_t) iterator->p; | |
| *eltp = node->value; | |
| if (nodep != NULL) | |
| *nodep = node; | |
| iterator->p = node->next; | |
| return true; | |
| } | |
| else | |
| return false; | |
| } | |
| static void | |
| gl_linked_iterator_free (gl_list_iterator_t *_GL_UNNAMED (iterator)) | |
| { | |
| } | |
| /* ---------------------- Sorted gl_list_t Data Type ---------------------- */ | |
| static gl_list_node_t _GL_ATTRIBUTE_PURE | |
| gl_linked_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar, | |
| const void *elt) | |
| { | |
| for (gl_list_node_t node = list->root.next; node != &list->root; node = node->next) | |
| { | |
| int cmp = compar (node->value, elt); | |
| if (cmp > 0) | |
| break; | |
| if (cmp == 0) | |
| return node; | |
| } | |
| return NULL; | |
| } | |
| static gl_list_node_t _GL_ATTRIBUTE_PURE | |
| gl_linked_sortedlist_search_from_to (gl_list_t list, | |
| gl_listelement_compar_fn compar, | |
| size_t low, size_t high, | |
| const void *elt) | |
| { | |
| size_t count = list->count; | |
| if (!(low <= high && high <= list->count)) | |
| /* Invalid arguments. */ | |
| abort (); | |
| high -= low; | |
| if (high > 0) | |
| { | |
| /* Here we know low < count. */ | |
| size_t position = low; | |
| gl_list_node_t node; | |
| if (position <= ((count - 1) / 2)) | |
| { | |
| node = list->root.next; | |
| for (; position > 0; position--) | |
| node = node->next; | |
| } | |
| else | |
| { | |
| position = count - 1 - position; | |
| node = list->root.prev; | |
| for (; position > 0; position--) | |
| node = node->prev; | |
| } | |
| do | |
| { | |
| int cmp = compar (node->value, elt); | |
| if (cmp > 0) | |
| break; | |
| if (cmp == 0) | |
| return node; | |
| node = node->next; | |
| } | |
| while (--high > 0); | |
| } | |
| return NULL; | |
| } | |
| static size_t _GL_ATTRIBUTE_PURE | |
| gl_linked_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar, | |
| const void *elt) | |
| { | |
| gl_list_node_t node; | |
| size_t index; | |
| for (node = list->root.next, index = 0; | |
| node != &list->root; | |
| node = node->next, index++) | |
| { | |
| int cmp = compar (node->value, elt); | |
| if (cmp > 0) | |
| break; | |
| if (cmp == 0) | |
| return index; | |
| } | |
| return (size_t)(-1); | |
| } | |
| static size_t _GL_ATTRIBUTE_PURE | |
| gl_linked_sortedlist_indexof_from_to (gl_list_t list, | |
| gl_listelement_compar_fn compar, | |
| size_t low, size_t high, | |
| const void *elt) | |
| { | |
| size_t count = list->count; | |
| if (!(low <= high && high <= list->count)) | |
| /* Invalid arguments. */ | |
| abort (); | |
| high -= low; | |
| if (high > 0) | |
| { | |
| /* Here we know low < count. */ | |
| size_t index = low; | |
| size_t position = low; | |
| gl_list_node_t node; | |
| if (position <= ((count - 1) / 2)) | |
| { | |
| node = list->root.next; | |
| for (; position > 0; position--) | |
| node = node->next; | |
| } | |
| else | |
| { | |
| position = count - 1 - position; | |
| node = list->root.prev; | |
| for (; position > 0; position--) | |
| node = node->prev; | |
| } | |
| do | |
| { | |
| int cmp = compar (node->value, elt); | |
| if (cmp > 0) | |
| break; | |
| if (cmp == 0) | |
| return index; | |
| node = node->next; | |
| index++; | |
| } | |
| while (--high > 0); | |
| } | |
| return (size_t)(-1); | |
| } | |
| static gl_list_node_t | |
| gl_linked_sortedlist_nx_add (gl_list_t list, gl_listelement_compar_fn compar, | |
| const void *elt) | |
| { | |
| for (gl_list_node_t node = list->root.next; node != &list->root; node = node->next) | |
| if (compar (node->value, elt) >= 0) | |
| return gl_linked_nx_add_before (list, node, elt); | |
| return gl_linked_nx_add_last (list, elt); | |
| } | |
| static bool | |
| gl_linked_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar, | |
| const void *elt) | |
| { | |
| for (gl_list_node_t node = list->root.next; node != &list->root; node = node->next) | |
| { | |
| int cmp = compar (node->value, elt); | |
| if (cmp > 0) | |
| break; | |
| if (cmp == 0) | |
| return gl_linked_remove_node (list, node); | |
| } | |
| return false; | |
| } |