mirror of
https://github.com/google/pebble.git
synced 2025-11-22 15:30:55 -05:00
Import of the watch repository from Pebble
This commit is contained in:
356
src/libutil/list.c
Normal file
356
src/libutil/list.c
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "util/list.h"
|
||||
#include "util/assert.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void list_init(ListNode* node) {
|
||||
node->next = NULL;
|
||||
node->prev = NULL;
|
||||
}
|
||||
|
||||
ListNode* list_insert_after(ListNode* node, ListNode* new_node) {
|
||||
if (node == NULL) {
|
||||
return new_node;
|
||||
}
|
||||
new_node->next = node->next;
|
||||
new_node->prev = node;
|
||||
|
||||
if (node->next) {
|
||||
node->next->prev = new_node;
|
||||
}
|
||||
node->next = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
ListNode* list_insert_before(ListNode* node, ListNode* new_node) {
|
||||
if (node == NULL) {
|
||||
return new_node;
|
||||
}
|
||||
new_node->next = node;
|
||||
new_node->prev = node->prev;
|
||||
|
||||
if (node->prev) {
|
||||
node->prev->next = new_node;
|
||||
}
|
||||
node->prev = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
ListNode* list_pop_head(ListNode *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode *head = list_get_head(node);
|
||||
ListNode *new_head = head->next;
|
||||
list_remove(head, NULL, NULL);
|
||||
return new_head;
|
||||
}
|
||||
|
||||
ListNode* list_pop_tail(ListNode *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode* tail = list_get_tail(node);
|
||||
ListNode* new_tail = tail->prev;
|
||||
list_remove(tail, NULL, NULL);
|
||||
return new_tail;
|
||||
}
|
||||
|
||||
void list_remove(ListNode* node, ListNode **head, ListNode **tail) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
if (head && *head == node) {
|
||||
*head = node->next;
|
||||
}
|
||||
if (tail && *tail == node) {
|
||||
*tail = node->prev;
|
||||
}
|
||||
if (node->next) {
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
if (node->prev) {
|
||||
node->prev->next = node->next;
|
||||
}
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
}
|
||||
|
||||
ListNode* list_append(ListNode* node, ListNode* new_node) {
|
||||
return list_insert_after(list_get_tail(node), new_node);
|
||||
}
|
||||
|
||||
ListNode* list_prepend(ListNode* node, ListNode* new_node) {
|
||||
return list_insert_before(list_get_head(node), new_node);
|
||||
}
|
||||
|
||||
ListNode* list_get_next(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return node->next;
|
||||
}
|
||||
|
||||
ListNode* list_get_prev(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return node->prev;
|
||||
}
|
||||
|
||||
ListNode* list_get_tail(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
while (node->next != NULL) {
|
||||
node = node->next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
ListNode* list_get_head(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
while (node->prev != NULL) {
|
||||
node = node->prev;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
bool list_is_head(const ListNode *node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
return !node->prev;
|
||||
}
|
||||
|
||||
bool list_is_tail(const ListNode *node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
return !node->next;
|
||||
}
|
||||
|
||||
uint32_t list_count_to_tail_from(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t count = 1;
|
||||
while ((node = node->next) != NULL) {
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t list_count_to_head_from(ListNode *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t count = 1;
|
||||
while ((node = node->prev) != NULL) {
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t list_count(ListNode* node) {
|
||||
return list_count_to_tail_from(list_get_head(node));
|
||||
}
|
||||
|
||||
ListNode* list_get_at(ListNode *node, int32_t index) {
|
||||
while (node != NULL && index != 0) {
|
||||
if (index > 0) {
|
||||
node = node->next;
|
||||
index--;
|
||||
} else {
|
||||
node = node->prev;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
ListNode* list_sorted_add(ListNode *node, ListNode *new_node, Comparator comparator, bool ascending) {
|
||||
if (node == NULL) {
|
||||
return new_node;
|
||||
}
|
||||
if (new_node == NULL) {
|
||||
return node;
|
||||
}
|
||||
ListNode * const head = node;
|
||||
for(;;) {
|
||||
int order = comparator(node, new_node);
|
||||
if (!ascending) {
|
||||
order = -order;
|
||||
}
|
||||
|
||||
if (order < 0) {
|
||||
list_insert_before(node, new_node);
|
||||
if (node == head) {
|
||||
return new_node;
|
||||
} else {
|
||||
return head;
|
||||
}
|
||||
}
|
||||
ListNode *next = node->next;
|
||||
if (next == NULL) {
|
||||
list_insert_after(node, new_node);
|
||||
return head;
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool list_contains(const ListNode *node, const ListNode *node_to_search) {
|
||||
if (node == NULL || node_to_search == NULL) {
|
||||
return false;
|
||||
}
|
||||
while (node) {
|
||||
if (node == node_to_search) {
|
||||
return true;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ListNode* list_find(ListNode *node, ListFilterCallback filter_callback, void *data) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode *cursor = node;
|
||||
do {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
} while ((cursor = cursor->next));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode* list_find_next(ListNode *node, ListFilterCallback filter_callback, bool wrap_around, void *data) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode *cursor = node;
|
||||
while ((cursor = cursor->next)) {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
if (wrap_around == false) {
|
||||
return NULL;
|
||||
}
|
||||
cursor = list_get_head(node);
|
||||
while (cursor) {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
// We're back at where we started and even <node> itself doesn't match the filter
|
||||
if (cursor == node) {
|
||||
return NULL;
|
||||
}
|
||||
cursor = cursor->next;
|
||||
}
|
||||
UTIL_ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode* list_find_prev(ListNode *node, ListFilterCallback filter_callback, bool wrap_around, void *data) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode *cursor = node;
|
||||
while ((cursor = cursor->prev)) {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
if (wrap_around == false) {
|
||||
return NULL;
|
||||
}
|
||||
cursor = list_get_tail(node);
|
||||
while (cursor) {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
// We're back at where we started and even <node> itself doesn't match the filter
|
||||
if (cursor == node) {
|
||||
return NULL;
|
||||
}
|
||||
cursor = cursor->prev;
|
||||
}
|
||||
UTIL_ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode *list_concatenate(ListNode *restrict list_a, ListNode *restrict list_b) {
|
||||
ListNode *head_a = list_get_head(list_a);
|
||||
if (list_b == NULL) {
|
||||
return head_a;
|
||||
}
|
||||
|
||||
ListNode *head_b = list_get_head(list_b);
|
||||
if (list_a == NULL) {
|
||||
return head_b;
|
||||
}
|
||||
|
||||
if (head_a == head_b) {
|
||||
// list b is already in list a!
|
||||
return head_a;
|
||||
}
|
||||
|
||||
ListNode *tail_a = list_get_tail(list_a);
|
||||
|
||||
head_b->prev = tail_a;
|
||||
tail_a->next = head_b;
|
||||
|
||||
return head_a;
|
||||
}
|
||||
|
||||
void list_foreach(ListNode *head, ListForEachCallback each_cb, void *context) {
|
||||
if (!each_cb) {
|
||||
return;
|
||||
}
|
||||
|
||||
ListNode *iter = head;
|
||||
while (iter) {
|
||||
// Save off a pointer so the client to this function can destroy the node (useful for deinits)
|
||||
ListNode *next = iter->next;
|
||||
if (!each_cb(iter, context)) {
|
||||
return;
|
||||
}
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
void list_debug_dump(ListNode *head) {
|
||||
ListNode *iter = head;
|
||||
char buffer[30];
|
||||
while (iter) {
|
||||
snprintf(buffer, sizeof(buffer), "node %p (%p, %p)", iter, iter->prev, iter->next);
|
||||
UTIL_LOG(buffer);
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user