#ifndef AWS_COMMON_LINKED_LIST_INL #define AWS_COMMON_LINKED_LIST_INL /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include AWS_EXTERN_C_BEGIN /** * Set node's next and prev pointers to NULL. */ AWS_STATIC_IMPL void aws_linked_list_node_reset(struct aws_linked_list_node *node) { AWS_PRECONDITION(node != NULL); AWS_ZERO_STRUCT(*node); AWS_POSTCONDITION(AWS_IS_ZEROED(*node)); } /** * These functions need to be defined first as they are used in pre * and post conditions. */ /** * Tests if the list is empty. */ AWS_STATIC_IMPL bool aws_linked_list_empty(const struct aws_linked_list *list) { AWS_PRECONDITION(list); return list->head.next == &list->tail; } /** * Checks that a linked list is valid. */ AWS_STATIC_IMPL bool aws_linked_list_is_valid(const struct aws_linked_list *list) { if (list && list->head.next && list->head.prev == NULL && list->tail.prev && list->tail.next == NULL) { #if (AWS_DEEP_CHECKS == 1) return aws_linked_list_is_valid_deep(list); #else return true; #endif } return false; } /** * Checks that the prev of the next pointer of a node points to the * node. As this checks whether the [next] connection of a node is * bidirectional, it returns false if used for the list tail. */ AWS_STATIC_IMPL bool aws_linked_list_node_next_is_valid(const struct aws_linked_list_node *node) { return node && node->next && node->next->prev == node; } /** * Checks that the next of the prev pointer of a node points to the * node. Similarly to the above, this returns false if used for the * head of a list. */ AWS_STATIC_IMPL bool aws_linked_list_node_prev_is_valid(const struct aws_linked_list_node *node) { return node && node->prev && node->prev->next == node; } /** * Checks that a linked list satisfies double linked list connectivity * constraints. This check is O(n) as it traverses the whole linked * list to ensure that tail is reachable from head (and vice versa) * and that every connection is bidirectional. * * Note: This check *cannot* go into an infinite loop, because we * ensure that the connection to the next node is * bidirectional. Therefore, if a node's [a] a.next is a previous node * [b] in the list, b.prev != &a and so this check would fail, thus * terminating the loop. */ AWS_STATIC_IMPL bool aws_linked_list_is_valid_deep(const struct aws_linked_list *list) { if (!list) { return false; } /* This could go into an infinite loop for a circular list */ const struct aws_linked_list_node *temp = &list->head; /* Head must reach tail by following next pointers */ bool head_reaches_tail = false; /* By satisfying the above and that edges are bidirectional, we * also guarantee that tail reaches head by following prev * pointers */ while (temp) { if (temp == &list->tail) { head_reaches_tail = true; break; } else if (!aws_linked_list_node_next_is_valid(temp)) { /* Next and prev pointers should connect the same nodes */ return false; } temp = temp->next; } return head_reaches_tail; } /** * Initializes the list. List will be empty after this call. */ AWS_STATIC_IMPL void aws_linked_list_init(struct aws_linked_list *list) { AWS_PRECONDITION(list); list->head.next = &list->tail; list->head.prev = NULL; list->tail.prev = &list->head; list->tail.next = NULL; AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(aws_linked_list_empty(list)); } /** * Returns an iteration pointer for the first element in the list. */ AWS_STATIC_IMPL struct aws_linked_list_node *aws_linked_list_begin(const struct aws_linked_list *list) { AWS_PRECONDITION(aws_linked_list_is_valid(list)); struct aws_linked_list_node *rval = list->head.next; AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(rval == list->head.next); return rval; } /** * Returns an iteration pointer for one past the last element in the list. */ AWS_STATIC_IMPL const struct aws_linked_list_node *aws_linked_list_end(const struct aws_linked_list *list) { AWS_PRECONDITION(aws_linked_list_is_valid(list)); const struct aws_linked_list_node *rval = &list->tail; AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(rval == &list->tail); return rval; } /** * Returns a pointer for the last element in the list. * Used to begin iterating the list in reverse. Ex: * for (i = aws_linked_list_rbegin(list); i != aws_linked_list_rend(list); i = aws_linked_list_prev(i)) {...} */ AWS_STATIC_IMPL struct aws_linked_list_node *aws_linked_list_rbegin(const struct aws_linked_list *list) { AWS_PRECONDITION(aws_linked_list_is_valid(list)); struct aws_linked_list_node *rval = list->tail.prev; AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(rval == list->tail.prev); return rval; } /** * Returns the pointer to one before the first element in the list. * Used to end iterating the list in reverse. */ AWS_STATIC_IMPL const struct aws_linked_list_node *aws_linked_list_rend(const struct aws_linked_list *list) { AWS_PRECONDITION(aws_linked_list_is_valid(list)); const struct aws_linked_list_node *rval = &list->head; AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(rval == &list->head); return rval; } /** * Returns the next element in the list. */ AWS_STATIC_IMPL struct aws_linked_list_node *aws_linked_list_next(const struct aws_linked_list_node *node) { AWS_PRECONDITION(aws_linked_list_node_next_is_valid(node)); struct aws_linked_list_node *rval = node->next; AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(node)); AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(rval)); AWS_POSTCONDITION(rval == node->next); return rval; } /** * Returns the previous element in the list. */ AWS_STATIC_IMPL struct aws_linked_list_node *aws_linked_list_prev(const struct aws_linked_list_node *node) { AWS_PRECONDITION(aws_linked_list_node_prev_is_valid(node)); struct aws_linked_list_node *rval = node->prev; AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(node)); AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(rval)); AWS_POSTCONDITION(rval == node->prev); return rval; } /** * Inserts to_add immediately after after. */ AWS_STATIC_IMPL void aws_linked_list_insert_after( struct aws_linked_list_node *after, struct aws_linked_list_node *to_add) { AWS_PRECONDITION(aws_linked_list_node_next_is_valid(after)); AWS_PRECONDITION(to_add != NULL); to_add->prev = after; to_add->next = after->next; after->next->prev = to_add; after->next = to_add; AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(after)); AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(to_add)); AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(to_add)); AWS_POSTCONDITION(after->next == to_add); } /** * Swaps the order two nodes in the linked list. */ AWS_STATIC_IMPL void aws_linked_list_swap_nodes(struct aws_linked_list_node *a, struct aws_linked_list_node *b) { AWS_PRECONDITION(aws_linked_list_node_prev_is_valid(a)); AWS_PRECONDITION(aws_linked_list_node_next_is_valid(a)); AWS_PRECONDITION(aws_linked_list_node_prev_is_valid(b)); AWS_PRECONDITION(aws_linked_list_node_next_is_valid(b)); if (a == b) { return; } /* snapshot b's value to avoid clobbering its next/prev pointers if a/b are adjacent */ struct aws_linked_list_node tmp = *b; a->prev->next = b; a->next->prev = b; tmp.prev->next = a; tmp.next->prev = a; tmp = *a; *a = *b; *b = tmp; AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(a)); AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(a)); AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(b)); AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(b)); } /** * Inserts to_add immediately before before. */ AWS_STATIC_IMPL void aws_linked_list_insert_before( struct aws_linked_list_node *before, struct aws_linked_list_node *to_add) { AWS_PRECONDITION(aws_linked_list_node_prev_is_valid(before)); AWS_PRECONDITION(to_add != NULL); to_add->next = before; to_add->prev = before->prev; before->prev->next = to_add; before->prev = to_add; AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(before)); AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(to_add)); AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(to_add)); AWS_POSTCONDITION(before->prev == to_add); } /** * Removes the specified node from the list (prev/next point to each other) and * returns the next node in the list. */ AWS_STATIC_IMPL void aws_linked_list_remove(struct aws_linked_list_node *node) { AWS_PRECONDITION(aws_linked_list_node_prev_is_valid(node)); AWS_PRECONDITION(aws_linked_list_node_next_is_valid(node)); node->prev->next = node->next; node->next->prev = node->prev; aws_linked_list_node_reset(node); AWS_POSTCONDITION(node->next == NULL && node->prev == NULL); } /** * Append new_node. */ AWS_STATIC_IMPL void aws_linked_list_push_back(struct aws_linked_list *list, struct aws_linked_list_node *node) { AWS_PRECONDITION(aws_linked_list_is_valid(list)); AWS_PRECONDITION(node != NULL); aws_linked_list_insert_before(&list->tail, node); AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(list->tail.prev == node, "[node] is the new last element of [list]"); } /** * Returns the element in the back of the list. */ AWS_STATIC_IMPL struct aws_linked_list_node *aws_linked_list_back(const struct aws_linked_list *list) { AWS_PRECONDITION(aws_linked_list_is_valid(list)); AWS_PRECONDITION(!aws_linked_list_empty(list)); struct aws_linked_list_node *rval = list->tail.prev; AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(rval)); AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(rval)); return rval; } /** * Returns the element in the back of the list and removes it */ AWS_STATIC_IMPL struct aws_linked_list_node *aws_linked_list_pop_back(struct aws_linked_list *list) { AWS_PRECONDITION(!aws_linked_list_empty(list)); AWS_PRECONDITION(aws_linked_list_is_valid(list)); struct aws_linked_list_node *back = aws_linked_list_back(list); aws_linked_list_remove(back); AWS_POSTCONDITION(back->next == NULL && back->prev == NULL); AWS_POSTCONDITION(aws_linked_list_is_valid(list)); return back; } /** * Prepend new_node. */ AWS_STATIC_IMPL void aws_linked_list_push_front(struct aws_linked_list *list, struct aws_linked_list_node *node) { AWS_PRECONDITION(aws_linked_list_is_valid(list)); AWS_PRECONDITION(node != NULL); aws_linked_list_insert_before(list->head.next, node); AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(list->head.next == node, "[node] is the new first element of [list]"); } /** * Returns the element in the front of the list. */ AWS_STATIC_IMPL struct aws_linked_list_node *aws_linked_list_front(const struct aws_linked_list *list) { AWS_PRECONDITION(aws_linked_list_is_valid(list)); AWS_PRECONDITION(!aws_linked_list_empty(list)); struct aws_linked_list_node *rval = list->head.next; AWS_POSTCONDITION(aws_linked_list_is_valid(list)); AWS_POSTCONDITION(aws_linked_list_node_prev_is_valid(rval)); AWS_POSTCONDITION(aws_linked_list_node_next_is_valid(rval)); return rval; } /** * Returns the element in the front of the list and removes it */ AWS_STATIC_IMPL struct aws_linked_list_node *aws_linked_list_pop_front(struct aws_linked_list *list) { AWS_PRECONDITION(!aws_linked_list_empty(list)); AWS_PRECONDITION(aws_linked_list_is_valid(list)); struct aws_linked_list_node *front = aws_linked_list_front(list); aws_linked_list_remove(front); AWS_POSTCONDITION(front->next == NULL && front->prev == NULL); AWS_POSTCONDITION(aws_linked_list_is_valid(list)); return front; } AWS_STATIC_IMPL void aws_linked_list_swap_contents( struct aws_linked_list *AWS_RESTRICT a, struct aws_linked_list *AWS_RESTRICT b) { AWS_PRECONDITION(aws_linked_list_is_valid(a)); AWS_PRECONDITION(aws_linked_list_is_valid(b)); AWS_PRECONDITION(a != b); struct aws_linked_list_node *a_first = a->head.next; struct aws_linked_list_node *a_last = a->tail.prev; /* Move B's contents into A */ if (aws_linked_list_empty(b)) { aws_linked_list_init(a); } else { a->head.next = b->head.next; a->head.next->prev = &a->head; a->tail.prev = b->tail.prev; a->tail.prev->next = &a->tail; } /* Move A's old contents into B */ if (a_first == &a->tail) { aws_linked_list_init(b); } else { b->head.next = a_first; b->head.next->prev = &b->head; b->tail.prev = a_last; b->tail.prev->next = &b->tail; } AWS_POSTCONDITION(aws_linked_list_is_valid(a)); AWS_POSTCONDITION(aws_linked_list_is_valid(b)); } AWS_STATIC_IMPL void aws_linked_list_move_all_back( struct aws_linked_list *AWS_RESTRICT dst, struct aws_linked_list *AWS_RESTRICT src) { AWS_PRECONDITION(aws_linked_list_is_valid(src)); AWS_PRECONDITION(aws_linked_list_is_valid(dst)); AWS_PRECONDITION(dst != src); if (!aws_linked_list_empty(src)) { /* splice src nodes into dst, between the back and tail nodes */ struct aws_linked_list_node *dst_back = dst->tail.prev; struct aws_linked_list_node *src_front = src->head.next; struct aws_linked_list_node *src_back = src->tail.prev; dst_back->next = src_front; src_front->prev = dst_back; dst->tail.prev = src_back; src_back->next = &dst->tail; /* reset src */ src->head.next = &src->tail; src->tail.prev = &src->head; } AWS_POSTCONDITION(aws_linked_list_is_valid(src)); AWS_POSTCONDITION(aws_linked_list_is_valid(dst)); } AWS_STATIC_IMPL void aws_linked_list_move_all_front( struct aws_linked_list *AWS_RESTRICT dst, struct aws_linked_list *AWS_RESTRICT src) { AWS_PRECONDITION(aws_linked_list_is_valid(src)); AWS_PRECONDITION(aws_linked_list_is_valid(dst)); AWS_PRECONDITION(dst != src); if (!aws_linked_list_empty(src)) { /* splice src nodes into dst, between the head and front nodes */ struct aws_linked_list_node *dst_front = dst->head.next; struct aws_linked_list_node *src_front = src->head.next; struct aws_linked_list_node *src_back = src->tail.prev; dst->head.next = src_front; src_front->prev = &dst->head; src_back->next = dst_front; dst_front->prev = src_back; /* reset src */ src->head.next = &src->tail; src->tail.prev = &src->head; } AWS_POSTCONDITION(aws_linked_list_is_valid(src)); AWS_POSTCONDITION(aws_linked_list_is_valid(dst)); } AWS_EXTERN_C_END #endif /* AWS_COMMON_LINKED_LIST_INL */