Merge branch 'wip/chergert/insertbeforelink' into 'master'

Add pre-allocated link helpers for GList and GQueue

See merge request GNOME/glib!476
This commit is contained in:
Philip Withnall 2019-05-01 22:47:15 +00:00
commit 375fa65b24
7 changed files with 248 additions and 6 deletions

View File

@ -2268,6 +2268,7 @@ g_list_append
g_list_prepend
g_list_insert
g_list_insert_before
g_list_insert_before_link
g_list_insert_sorted
g_list_remove
g_list_remove_link
@ -2389,7 +2390,9 @@ g_queue_index
g_queue_remove
g_queue_remove_all
g_queue_insert_before
g_queue_insert_before_link
g_queue_insert_after
g_queue_insert_after_link
g_queue_insert_sorted
g_queue_push_head_link
g_queue_push_tail_link

View File

@ -367,6 +367,64 @@ g_list_insert (GList *list,
return list;
}
/**
* g_list_insert_before_link:
* @list: a pointer to a #GList, this must point to the top of the list
* @sibling: (nullable): the list element before which the new element
* is inserted or %NULL to insert at the end of the list
* @link_: the list element to be added, which must not be part of
* any other list
*
* Inserts @link_ into the list before the given position.
*
* Returns: the (possibly changed) start of the #GList
*
* Since: 2.62
*/
GList *
g_list_insert_before_link (GList *list,
GList *sibling,
GList *link_)
{
g_return_val_if_fail (link_ != NULL, list);
g_return_val_if_fail (link_->prev == NULL, list);
g_return_val_if_fail (link_->next == NULL, list);
if (list == NULL)
{
g_return_val_if_fail (sibling == NULL, list);
return link_;
}
else if (sibling != NULL)
{
link_->prev = sibling->prev;
link_->next = sibling;
sibling->prev = link_;
if (link_->prev != NULL)
{
link_->prev->next = link_;
return list;
}
else
{
g_return_val_if_fail (sibling == list, link_);
return link_;
}
}
else
{
GList *last;
for (last = list; last->next != NULL; last = last->next) {}
last->next = link_;
last->next->prev = last;
last->next->next = NULL;
return list;
}
}
/**
* g_list_insert_before:
* @list: a pointer to a #GList, this must point to the top of the list
@ -383,14 +441,14 @@ g_list_insert_before (GList *list,
GList *sibling,
gpointer data)
{
if (!list)
if (list == NULL)
{
list = g_list_alloc ();
list->data = data;
g_return_val_if_fail (sibling == NULL, list);
return list;
}
else if (sibling)
else if (sibling != NULL)
{
GList *node;
@ -399,7 +457,7 @@ g_list_insert_before (GList *list,
node->prev = sibling->prev;
node->next = sibling;
sibling->prev = node;
if (node->prev)
if (node->prev != NULL)
{
node->prev->next = node;
return list;
@ -414,9 +472,7 @@ g_list_insert_before (GList *list,
{
GList *last;
last = list;
while (last->next)
last = last->next;
for (last = list; last->next != NULL; last = last->next) {}
last->next = _g_list_alloc ();
last->next->data = data;

View File

@ -78,6 +78,10 @@ GLIB_AVAILABLE_IN_ALL
GList* g_list_insert_before (GList *list,
GList *sibling,
gpointer data) G_GNUC_WARN_UNUSED_RESULT;
GLIB_AVAILABLE_IN_2_62
GList* g_list_insert_before_link (GList *list,
GList *sibling,
GList *link_) G_GNUC_WARN_UNUSED_RESULT;
GLIB_AVAILABLE_IN_ALL
GList* g_list_concat (GList *list1,
GList *list2) G_GNUC_WARN_UNUSED_RESULT;

View File

@ -1047,6 +1047,44 @@ g_queue_insert_before (GQueue *queue,
}
}
/**
* g_queue_insert_before_link:
* @queue: a #GQueue
* @sibling: (nullable): a #GList link that must be part of @queue, or %NULL to
* push at the tail of the queue.
* @link_: a #GList link to insert which must not be part of any other list.
*
* Inserts @link_ into @queue before @sibling.
*
* @sibling must be part of @queue.
*
* Since: 2.62
*/
void
g_queue_insert_before_link (GQueue *queue,
GList *sibling,
GList *link_)
{
g_return_if_fail (queue != NULL);
g_return_if_fail (link_ != NULL);
g_return_if_fail (link_->prev == NULL);
g_return_if_fail (link_->next == NULL);
if G_UNLIKELY (sibling == NULL)
{
/* We don't use g_list_insert_before_link() with a NULL sibling because it
* would be a O(n) operation and we would need to update manually the tail
* pointer.
*/
g_queue_push_tail_link (queue, link_);
}
else
{
queue->head = g_list_insert_before_link (queue->head, sibling, link_);
queue->length++;
}
}
/**
* g_queue_insert_after:
* @queue: a #GQueue
@ -1074,6 +1112,35 @@ g_queue_insert_after (GQueue *queue,
g_queue_insert_before (queue, sibling->next, data);
}
/**
* g_queue_insert_after_link:
* @queue: a #GQueue
* @sibling: (nullable): a #GList link that must be part of @queue, or %NULL to
* push at the head of the queue.
* @link_: a #GList link to insert which must not be part of any other list.
*
* Inserts @link_ into @queue after @sibling.
*
* @sibling must be part of @queue.
*
* Since: 2.62
*/
void
g_queue_insert_after_link (GQueue *queue,
GList *sibling,
GList *link_)
{
g_return_if_fail (queue != NULL);
g_return_if_fail (link_ != NULL);
g_return_if_fail (link_->prev == NULL);
g_return_if_fail (link_->next == NULL);
if G_UNLIKELY (sibling == NULL)
g_queue_push_head_link (queue, link_);
else
g_queue_insert_before_link (queue, sibling->next, link_);
}
/**
* g_queue_insert_sorted:
* @queue: a #GQueue

View File

@ -144,10 +144,20 @@ GLIB_AVAILABLE_IN_ALL
void g_queue_insert_before (GQueue *queue,
GList *sibling,
gpointer data);
GLIB_AVAILABLE_IN_2_62
void g_queue_insert_before_link
(GQueue *queue,
GList *sibling,
GList *link_);
GLIB_AVAILABLE_IN_ALL
void g_queue_insert_after (GQueue *queue,
GList *sibling,
gpointer data);
GLIB_AVAILABLE_IN_2_62
void g_queue_insert_after_link
(GQueue *queue,
GList *sibling,
GList *link_);
GLIB_AVAILABLE_IN_ALL
void g_queue_insert_sorted (GQueue *queue,
gpointer data,

View File

@ -548,6 +548,72 @@ test_double_free (void)
g_test_trap_assert_stderr ("*corrupted double-linked list detected*");
}
static void
test_list_insert_before_link (void)
{
GList a = {0};
GList b = {0};
GList c = {0};
GList d = {0};
GList e = {0};
GList *list;
list = g_list_insert_before_link (NULL, NULL, &a);
g_assert_nonnull (list);
g_assert_true (list == &a);
g_assert_null (a.prev);
g_assert_null (a.next);
g_assert_cmpint (g_list_length (list), ==, 1);
list = g_list_insert_before_link (list, &a, &b);
g_assert_nonnull (list);
g_assert_true (list == &b);
g_assert_null (b.prev);
g_assert_true (b.next == &a);
g_assert_true (a.prev == &b);
g_assert_null (a.next);
g_assert_cmpint (g_list_length (list), ==, 2);
list = g_list_insert_before_link (list, &a, &c);
g_assert_nonnull (list);
g_assert_true (list == &b);
g_assert_null (b.prev);
g_assert_true (b.next == &c);
g_assert_true (c.next == &a);
g_assert_true (c.prev == &b);
g_assert_true (a.prev == &c);
g_assert_null (a.next);
g_assert_cmpint (g_list_length (list), ==, 3);
list = g_list_insert_before_link (list, &b, &d);
g_assert_nonnull (list);
g_assert_true (list == &d);
g_assert_null (d.prev);
g_assert_true (b.prev == &d);
g_assert_true (c.prev == &b);
g_assert_true (a.prev == &c);
g_assert_true (d.next == &b);
g_assert_true (b.next == &c);
g_assert_true (c.next == &a);
g_assert_null (a.next);
g_assert_cmpint (g_list_length (list), ==, 4);
list = g_list_insert_before_link (list, NULL, &e);
g_assert_nonnull (list);
g_assert_true (list == &d);
g_assert_null (d.prev);
g_assert_true (b.prev == &d);
g_assert_true (c.prev == &b);
g_assert_true (a.prev == &c);
g_assert_true (d.next == &b);
g_assert_true (b.next == &c);
g_assert_true (c.next == &a);
g_assert_true (a.next == &e);
g_assert_true (e.prev == &a);
g_assert_null (e.next);
g_assert_cmpint (g_list_length (list), ==, 5);
}
int
main (int argc, char *argv[])
{
@ -562,6 +628,7 @@ main (int argc, char *argv[])
g_test_add_func ("/list/sort", test_list_sort);
g_test_add_func ("/list/sort-with-data", test_list_sort_with_data);
g_test_add_func ("/list/sort/stable", test_list_sort_stable);
g_test_add_func ("/list/insert-before-link", test_list_insert_before_link);
g_test_add_func ("/list/insert-sorted", test_list_insert_sorted);
g_test_add_func ("/list/insert-sorted-with-data", test_list_insert_sorted_with_data);
g_test_add_func ("/list/reverse", test_list_reverse);

View File

@ -1117,6 +1117,40 @@ test_free_full (void)
g_slice_free (QueueItem, three);
}
static void
test_insert_sibling_link (void)
{
GQueue q = G_QUEUE_INIT;
GList a = {0};
GList b = {0};
GList c = {0};
GList d = {0};
GList e = {0};
g_queue_push_head_link (&q, &a);
g_queue_insert_after_link (&q, &a, &d);
g_queue_insert_before_link (&q, &d, &b);
g_queue_insert_after_link (&q, &b, &c);
g_queue_insert_after_link (&q, NULL, &e);
g_assert_true (q.head == &e);
g_assert_true (q.tail == &d);
g_assert_null (e.prev);
g_assert_true (e.next == &a);
g_assert_true (a.prev == &e);
g_assert_true (a.next == &b);
g_assert_true (b.prev == &a);
g_assert_true (b.next == &c);
g_assert_true (c.prev == &b);
g_assert_true (c.next == &d);
g_assert_true (d.prev == &c);
g_assert_null (d.next);
}
int main (int argc, char *argv[])
{
@ -1133,6 +1167,7 @@ int main (int argc, char *argv[])
g_test_add_func ("/queue/clear", test_clear);
g_test_add_func ("/queue/free-full", test_free_full);
g_test_add_func ("/queue/clear-full", test_clear_full);
g_test_add_func ("/queue/insert-sibling-link", test_insert_sibling_link);
seed = g_test_rand_int_range (0, G_MAXINT);
path = g_strdup_printf ("/queue/random/seed:%u", seed);