/* GLIB - Library of useful routines for C programming * Copyright (C) 2002 Soeren Sandmann (sandmann@daimi.au.dk) * * This library 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 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "gtksequence.h" typedef struct _GtkSequenceNode GtkSequenceNode; struct _GtkSequence { GtkSequenceNode *node; /* does not necessarily point to the root. * You can splay it if you want it to */ GDestroyNotify data_destroy_notify; }; struct _GtkSequenceNode { guint is_end : 1; gint n_nodes : 31; /* number of nodes below this node, * including this node */ GtkSequenceNode *parent; GtkSequenceNode *left; GtkSequenceNode *right; GtkSequence *sequence; gpointer data; }; static GtkSequenceNode *_gtk_sequence_node_new (gpointer data); static GtkSequenceNode *_gtk_sequence_node_find_first (GtkSequenceNode *node); static GtkSequenceNode *_gtk_sequence_node_find_last (GtkSequenceNode *node); static GtkSequenceNode *_gtk_sequence_node_find_by_pos (GtkSequenceNode *node, gint pos); static GtkSequenceNode *_gtk_sequence_node_prev (GtkSequenceNode *node); static GtkSequenceNode *_gtk_sequence_node_next (GtkSequenceNode *node); static gint _gtk_sequence_node_get_pos (GtkSequenceNode *node); static GtkSequence *_gtk_sequence_node_get_sequence (GtkSequenceNode *node); static GtkSequenceNode *_gtk_sequence_node_find_closest (GtkSequenceNode *node, GtkSequenceNode *other, GCompareDataFunc cmp, gpointer data); static gint _gtk_sequence_node_get_length (GtkSequenceNode *node); static void _gtk_sequence_node_free (GtkSequenceNode *node, GDestroyNotify destroy); #if 0 static gboolean _gtk_sequence_node_is_singleton (GtkSequenceNode *node); #endif static void _gtk_sequence_node_split (GtkSequenceNode *node, GtkSequenceNode **left, GtkSequenceNode **right); static void _gtk_sequence_node_insert_before (GtkSequenceNode *node, GtkSequenceNode *new); static void _gtk_sequence_node_remove (GtkSequenceNode *node); static void _gtk_sequence_node_insert_sorted (GtkSequenceNode *node, GtkSequenceNode *new, GCompareDataFunc cmp_func, gpointer cmp_data); /* GtkSequence */ GtkSequence * _gtk_sequence_new (GDestroyNotify data_destroy) { GtkSequence *seq = g_new (GtkSequence, 1); seq->data_destroy_notify = data_destroy; seq->node = _gtk_sequence_node_new (NULL); seq->node->is_end = TRUE; seq->node->sequence = seq; return seq; } void _gtk_sequence_foreach (GtkSequence *seq, GFunc func, gpointer data) { GtkSequencePtr ptr; g_return_if_fail (seq != NULL); g_return_if_fail (func != NULL); ptr = _gtk_sequence_get_begin_ptr (seq); while (!_gtk_sequence_ptr_is_end (ptr)) { GtkSequenceNode *node = ptr; func (node->data, data); ptr = _gtk_sequence_ptr_next (ptr); } } void _gtk_sequence_free (GtkSequence *seq) { g_return_if_fail (seq != NULL); _gtk_sequence_node_free (seq->node, seq->data_destroy_notify); g_free (seq); } #if 0 static void flatten_nodes (GtkSequenceNode *node, GList **list) { g_print ("flatten %p\n", node); if (!node) return; else if (_gtk_sequence_node_is_singleton (node)) *list = g_list_prepend (*list, node); else { GtkSequenceNode *left; GtkSequenceNode *right; _gtk_sequence_node_split (node, &left, &right); flatten_nodes (left, list); flatten_nodes (right, list); } } #endif typedef struct SortInfo SortInfo; struct SortInfo { GCompareDataFunc cmp; gpointer data; }; static gint node_compare (gconstpointer n1, gconstpointer n2, gpointer data) { SortInfo *info = data; const GtkSequenceNode *node1 = n1; const GtkSequenceNode *node2 = n2; if (node1->is_end) return 1; else if (node2->is_end) return -1; else return (* info->cmp) (node1, node2, info->data); } void _gtk_sequence_append (GtkSequence *seq, gpointer data) { GtkSequenceNode *node, *last; g_return_if_fail (seq != NULL); node = _gtk_sequence_node_new (data); node->sequence = seq; last = _gtk_sequence_node_find_last (seq->node); _gtk_sequence_node_insert_before (last, node); } void _gtk_sequence_prepend (GtkSequence *seq, gpointer data) { GtkSequenceNode *node, *second; g_return_if_fail (seq != NULL); node = _gtk_sequence_node_new (data); node->sequence = seq; second = _gtk_sequence_node_next (_gtk_sequence_node_find_first (seq->node)); _gtk_sequence_node_insert_before (second, node); } GtkSequencePtr _gtk_sequence_insert (GtkSequencePtr ptr, gpointer data) { GtkSequenceNode *node; g_return_val_if_fail (ptr != NULL, NULL); node = _gtk_sequence_node_new (data); node->sequence = ptr->sequence; _gtk_sequence_node_insert_before (ptr, node); return node; } static void _gtk_sequence_unlink (GtkSequence *seq, GtkSequenceNode *node) { g_assert (!node->is_end); seq->node = _gtk_sequence_node_next (node); g_assert (seq->node); g_assert (seq->node != node); _gtk_sequence_node_remove (node); } void _gtk_sequence_remove (GtkSequencePtr ptr) { GtkSequence *seq; g_return_if_fail (ptr != NULL); g_return_if_fail (!ptr->is_end); seq = _gtk_sequence_node_get_sequence (ptr); _gtk_sequence_unlink (seq, ptr); _gtk_sequence_node_free (ptr, seq->data_destroy_notify); } void _gtk_sequence_sort (GtkSequence *seq, GCompareDataFunc cmp_func, gpointer cmp_data) { GtkSequence *tmp; GtkSequenceNode *begin, *end; g_return_if_fail (seq != NULL); g_return_if_fail (cmp_func != NULL); begin = _gtk_sequence_get_begin_ptr (seq); end = _gtk_sequence_get_end_ptr (seq); _gtk_sequence_remove_range (begin, end, &tmp); while (_gtk_sequence_get_length (tmp) > 0) { GtkSequenceNode *node = _gtk_sequence_get_begin_ptr (tmp); _gtk_sequence_unlink (tmp, node); _gtk_sequence_node_insert_sorted (seq->node, node, cmp_func, cmp_data); } _gtk_sequence_free (tmp); } gpointer _gtk_sequence_ptr_get_data (GtkSequencePtr ptr) { g_return_val_if_fail (ptr != NULL, NULL); g_return_val_if_fail (!ptr->is_end, NULL); return ptr->data; } GtkSequencePtr _gtk_sequence_insert_sorted (GtkSequence *seq, gpointer data, GCompareDataFunc cmp_func, gpointer cmp_data) { GtkSequenceNode *new_node = _gtk_sequence_node_new (data); new_node->sequence = seq; _gtk_sequence_node_insert_sorted (seq->node, new_node, cmp_func, cmp_data); return new_node; } void _gtk_sequence_insert_sequence (GtkSequencePtr ptr, GtkSequence *other_seq) { GtkSequenceNode *last; g_return_if_fail (other_seq != NULL); g_return_if_fail (ptr != NULL); last = _gtk_sequence_node_find_last (other_seq->node); _gtk_sequence_node_insert_before (ptr, last); _gtk_sequence_node_remove (last); _gtk_sequence_node_free (last, NULL); other_seq->node = NULL; _gtk_sequence_free (other_seq); } void _gtk_sequence_concatenate (GtkSequence *seq1, GtkSequence *seq2) { GtkSequenceNode *last; g_return_if_fail (seq1 != NULL); g_return_if_fail (seq2 != NULL); last = _gtk_sequence_node_find_last (seq1->node); _gtk_sequence_insert_sequence (last, seq2); } /* * The new sequence inherits the destroy notify from the sequence that * beign and end comes from */ void _gtk_sequence_remove_range (GtkSequencePtr begin, GtkSequencePtr end, GtkSequence **removed) { GtkSequence *seq; GtkSequenceNode *s1, *s2, *s3; seq = _gtk_sequence_node_get_sequence (begin); g_assert (end != NULL); g_return_if_fail (seq == _gtk_sequence_node_get_sequence (end)); _gtk_sequence_node_split (begin, &s1, &s2); _gtk_sequence_node_split (end, NULL, &s3); if (s1) _gtk_sequence_node_insert_before (s3, s1); seq->node = s3; if (removed) { *removed = _gtk_sequence_new (seq->data_destroy_notify); _gtk_sequence_node_insert_before ((*removed)->node, s2); } else { _gtk_sequence_node_free (s2, seq->data_destroy_notify); } } gint _gtk_sequence_get_length (GtkSequence *seq) { return _gtk_sequence_node_get_length (seq->node) - 1; } GtkSequencePtr _gtk_sequence_get_end_ptr (GtkSequence *seq) { g_return_val_if_fail (seq != NULL, NULL); return _gtk_sequence_node_find_last (seq->node); } GtkSequencePtr _gtk_sequence_get_begin_ptr (GtkSequence *seq) { g_return_val_if_fail (seq != NULL, NULL); return _gtk_sequence_node_find_first (seq->node); } /* * if pos > number of items or -1, will return end pointer */ GtkSequencePtr _gtk_sequence_get_ptr_at_pos (GtkSequence *seq, gint pos) { gint len; g_return_val_if_fail (seq != NULL, NULL); len = _gtk_sequence_get_length (seq); if (pos > len || pos == -1) pos = len; return _gtk_sequence_node_find_by_pos (seq->node, pos); } /* GtkSequencePtr */ gboolean _gtk_sequence_ptr_is_end (GtkSequencePtr ptr) { g_return_val_if_fail (ptr != NULL, FALSE); return ptr->is_end; } gboolean _gtk_sequence_ptr_is_begin (GtkSequencePtr ptr) { return (_gtk_sequence_node_prev (ptr) == ptr); } gint _gtk_sequence_ptr_get_position (GtkSequencePtr ptr) { g_return_val_if_fail (ptr != NULL, -1); return _gtk_sequence_node_get_pos (ptr); } GtkSequencePtr _gtk_sequence_ptr_next (GtkSequencePtr ptr) { g_return_val_if_fail (ptr != NULL, NULL); return _gtk_sequence_node_next (ptr); } GtkSequencePtr _gtk_sequence_ptr_prev (GtkSequencePtr ptr) { g_return_val_if_fail (ptr != NULL, NULL); return _gtk_sequence_node_prev (ptr); } GtkSequencePtr _gtk_sequence_ptr_move (GtkSequencePtr ptr, guint delta) { gint new_pos; g_return_val_if_fail (ptr != NULL, NULL); new_pos = _gtk_sequence_node_get_pos (ptr) + delta; return _gtk_sequence_node_find_by_pos (ptr, new_pos); } void _gtk_sequence_sort_changed (GtkSequencePtr ptr, GCompareDataFunc cmp_func, gpointer cmp_data) { GtkSequence *seq; g_return_if_fail (!ptr->is_end); seq = _gtk_sequence_node_get_sequence (ptr); _gtk_sequence_unlink (seq, ptr); _gtk_sequence_node_insert_sorted (seq->node, ptr, cmp_func, cmp_data); } /* search * * The only restriction on the search function is that it * must not delete any nodes. It is permitted to insert new nodes, * but the caller should "know what he is doing" */ void _gtk_sequence_search (GtkSequence *seq, GtkSequenceSearchFunc f, gpointer data) { GQueue *intervals = g_queue_new (); g_queue_push_tail (intervals, _gtk_sequence_node_find_first (seq->node)); g_queue_push_tail (intervals, _gtk_sequence_node_find_last (seq->node)); while (!g_queue_is_empty (intervals)) { GtkSequenceNode *begin = g_queue_pop_head (intervals); GtkSequenceNode *end = g_queue_pop_head (intervals); if (f (begin, end, data)) { gint begin_pos = _gtk_sequence_node_get_pos (begin); gint end_pos = _gtk_sequence_node_get_pos (end); if (end_pos - begin_pos > 1) { GtkSequenceNode *mid; gint mid_pos; mid_pos = begin_pos + (end_pos - begin_pos) / 2; mid = _gtk_sequence_node_find_by_pos (begin, mid_pos); g_queue_push_tail (intervals, begin); g_queue_push_tail (intervals, mid); g_queue_push_tail (intervals, mid); g_queue_push_tail (intervals, end); } } } g_queue_free (intervals); } #if 0 /* aggregates */ void _gtk_sequence_add_aggregate (GtkSequence *seq, const gchar *aggregate, GtkSequenceAggregateFunc f, gpointer data, GDestroyNotify destroy) { /* FIXME */ } void _gtk_sequence_remove_aggregate (GtkSequence *seq, const gchar aggregate) { /* FIXME */ } void _gtk_sequence_set_aggregate_data (GtkSequencePtr ptr, const gchar *aggregate, gpointer data) { /* FIXME */ } gpointer _gtk_sequence_get_aggregate_data (GtkSequencePtr begin, GtkSequencePtr end, const gchar *aggregate) { g_assert_not_reached(); return NULL; } #endif /* Nodes */ static void _gtk_sequence_node_update_fields (GtkSequenceNode *node) { g_assert (node != NULL); node->n_nodes = 1; if (node->left) node->n_nodes += node->left->n_nodes; if (node->right) node->n_nodes += node->right->n_nodes; #if 0 if (node->left || node->right) g_assert (node->n_nodes > 1); #endif } #define NODE_LEFT_CHILD(n) (((n)->parent) && ((n)->parent->left) == (n)) #define NODE_RIGHT_CHILD(n) (((n)->parent) && ((n)->parent->right) == (n)) static void _gtk_sequence_node_rotate (GtkSequenceNode *node) { GtkSequenceNode *tmp, *old; g_assert (node->parent); g_assert (node->parent != node); if (NODE_LEFT_CHILD (node)) { /* rotate right */ tmp = node->right; node->right = node->parent; node->parent = node->parent->parent; if (node->parent) { if (node->parent->left == node->right) node->parent->left = node; else node->parent->right = node; } g_assert (node->right); node->right->parent = node; node->right->left = tmp; if (node->right->left) node->right->left->parent = node->right; old = node->right; } else { /* rotate left */ tmp = node->left; node->left = node->parent; node->parent = node->parent->parent; if (node->parent) { if (node->parent->right == node->left) node->parent->right = node; else node->parent->left = node; } g_assert (node->left); node->left->parent = node; node->left->right = tmp; if (node->left->right) node->left->right->parent = node->left; old = node->left; } _gtk_sequence_node_update_fields (old); _gtk_sequence_node_update_fields (node); } static GtkSequenceNode * splay (GtkSequenceNode *node) { while (node->parent) { if (!node->parent->parent) { /* zig */ _gtk_sequence_node_rotate (node); } else if ((NODE_LEFT_CHILD (node) && NODE_LEFT_CHILD (node->parent)) || (NODE_RIGHT_CHILD (node) && NODE_RIGHT_CHILD (node->parent))) { /* zig-zig */ _gtk_sequence_node_rotate (node->parent); _gtk_sequence_node_rotate (node); } else { /* zig-zag */ _gtk_sequence_node_rotate (node); _gtk_sequence_node_rotate (node); } } return node; } static GtkSequenceNode * _gtk_sequence_node_new (gpointer data) { GtkSequenceNode *node = g_new0 (GtkSequenceNode, 1); node->parent = NULL; node->left = NULL; node->right = NULL; node->data = data; node->is_end = FALSE; node->n_nodes = 1; return node; } static GtkSequenceNode * find_min (GtkSequenceNode *node) { splay (node); while (node->left) node = node->left; return node; } static GtkSequenceNode * find_max (GtkSequenceNode *node) { splay (node); while (node->right) node = node->right; return node; } static GtkSequenceNode * _gtk_sequence_node_find_first (GtkSequenceNode *node) { return splay (find_min (node)); } static GtkSequenceNode * _gtk_sequence_node_find_last (GtkSequenceNode *node) { return splay (find_max (node)); } static gint get_n_nodes (GtkSequenceNode *node) { if (node) return node->n_nodes; else return 0; } static GtkSequenceNode * _gtk_sequence_node_find_by_pos (GtkSequenceNode *node, gint pos) { gint i; g_assert (node != NULL); splay (node); while ((i = get_n_nodes (node->left)) != pos) { if (i < pos) { node = node->right; pos -= (i + 1); } else { node = node->left; g_assert (node->parent != NULL); } } return splay (node); } static GtkSequenceNode * _gtk_sequence_node_prev (GtkSequenceNode *node) { splay (node); if (node->left) { node = node->left; while (node->right) node = node->right; } return splay (node); } static GtkSequenceNode * _gtk_sequence_node_next (GtkSequenceNode *node) { splay (node); if (node->right) { node = node->right; while (node->left) node = node->left; } return splay (node); } static gint _gtk_sequence_node_get_pos (GtkSequenceNode *node) { splay (node); return get_n_nodes (node->left); } static GtkSequence * _gtk_sequence_node_get_sequence (GtkSequenceNode *node) { splay (node); return node->sequence; } static GtkSequenceNode * _gtk_sequence_node_find_closest (GtkSequenceNode *node, GtkSequenceNode *other, GCompareDataFunc cmp, gpointer data) { GtkSequenceNode *best; gint c; splay (node); do { best = node; if ((c = cmp (node, other, data)) != 0) { if (c < 0) node = node->right; else node = node->left; } } while (c != 0 && node != NULL); return best; } static void _gtk_sequence_node_free (GtkSequenceNode *node, GDestroyNotify destroy) { /* FIXME: * * This is to avoid excessively deep recursions. A splay tree is not necessarily * balanced at all. * * I _think_ this is still linear in the number of nodes, but I'd like to * do something more efficient. */ while (node) { GtkSequenceNode *next; node = splay (find_min (node)); next = node->right; if (next) next->parent = NULL; if (destroy && !node->is_end) destroy (node->data); g_free (node); node = next; } } #if 0 static gboolean _gtk_sequence_node_is_singleton (GtkSequenceNode *node) { splay (node); if (node->left || node->right) return FALSE; return TRUE; } #endif static void _gtk_sequence_node_split (GtkSequenceNode *node, GtkSequenceNode **left, GtkSequenceNode **right) { GtkSequenceNode *left_tree; splay (node); left_tree = node->left; if (left_tree) { left_tree->parent = NULL; _gtk_sequence_node_update_fields (left_tree); } node->left = NULL; _gtk_sequence_node_update_fields (node); if (left) *left = left_tree; if (right) *right = node; } static void _gtk_sequence_node_insert_before (GtkSequenceNode *node, GtkSequenceNode *new) { g_assert (node != NULL); g_assert (new != NULL); splay (node); new = splay (find_min (new)); g_assert (new->left == NULL); if (node->left) node->left->parent = new; new->left = node->left; new->parent = node; node->left = new; _gtk_sequence_node_update_fields (new); _gtk_sequence_node_update_fields (node); } static gint _gtk_sequence_node_get_length (GtkSequenceNode *node) { g_assert (node != NULL); splay (node); return node->n_nodes; } static void _gtk_sequence_node_remove (GtkSequenceNode *node) { GtkSequenceNode *right, *left; splay (node); left = node->left; right = node->right; node->left = node->right = NULL; if (right) { right->parent = NULL; right = _gtk_sequence_node_find_first (right); g_assert (right->left == NULL); right->left = left; if (left) { left->parent = right; _gtk_sequence_node_update_fields (right); } } else if (left) left->parent = NULL; } #if 0 /* debug func */ static gint _gtk_sequence_node_calc_height (GtkSequenceNode *node) { /* breadth first traversal */ gint height = 0; GQueue *nodes = g_queue_new (); g_queue_push_tail (nodes, node); while (!g_queue_is_empty (nodes)) { GQueue *tmp = g_queue_new (); height++; while (!g_queue_is_empty (nodes)) { GtkSequenceNode *node = g_queue_pop_head (nodes); if (node->left) g_queue_push_tail (tmp, node->left); if (node->right) g_queue_push_tail (tmp, node->right); } g_queue_free (nodes); nodes = tmp; } g_queue_free (nodes); return height; } #endif static void _gtk_sequence_node_insert_sorted (GtkSequenceNode *node, GtkSequenceNode *new, GCompareDataFunc cmp_func, gpointer cmp_data) { SortInfo info; GtkSequenceNode *closest; info.cmp = cmp_func; info.data = cmp_data; closest = _gtk_sequence_node_find_closest (node, new, node_compare, &info); g_assert (closest != new); if (node_compare (new, closest, &info) > 0) closest = _gtk_sequence_node_next (closest); /* this can never fail since we have a bigger-than-everything * end-node */ g_assert (node_compare (new, closest, &info) <= 0); _gtk_sequence_node_insert_before (closest, new); } static gint _gtk_sequence_node_calc_height (GtkSequenceNode *node) { gint left_height; gint right_height; if (node) { left_height = 0; right_height = 0; if (node->left) left_height = _gtk_sequence_node_calc_height (node->left); if (node->right) right_height = _gtk_sequence_node_calc_height (node->right); return MAX (left_height, right_height) + 1; } return 0; } gint _gtk_sequence_calc_tree_height (GtkSequence *seq) { GtkSequenceNode *node = seq->node; gint r, l; while (node->parent) node = node->parent; if (node) { r = _gtk_sequence_node_calc_height (node->right); l = _gtk_sequence_node_calc_height (node->left); return MAX (r, l) + 1; } else return 0; } GtkSequence * _gtk_sequence_ptr_get_sequence (GtkSequencePtr ptr) { GtkSequenceNode *node = ptr; return node->sequence; } void _gtk_sequence_swap (GtkSequencePtr a, GtkSequencePtr b) { GtkSequenceNode temp; gpointer temp_data; g_return_if_fail (!_gtk_sequence_ptr_is_end (a)); g_return_if_fail (!_gtk_sequence_ptr_is_end (b)); /* swap contents of the nodes */ temp = *a; *a = *b; *b = temp; /* swap data back */ temp_data = a->data; a->data = b->data; b->data = temp_data; } void _gtk_sequence_move (GtkSequencePtr ptr, GtkSequencePtr new_pos) { _gtk_sequence_unlink (ptr->sequence, ptr); _gtk_sequence_node_insert_before (new_pos, ptr); } /* Overwrites the existing pointer. */ void _gtk_sequence_set (GtkSequencePtr ptr, gpointer data) { GtkSequence *seq; g_return_if_fail (!_gtk_sequence_ptr_is_end (ptr)); seq = _gtk_sequence_node_get_sequence (ptr); if (seq->data_destroy_notify) seq->data_destroy_notify (ptr->data); ptr->data = data; }