/* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** \file * Chain of nodes. * A chain of nodes is an abstraction used to implements complex list operations * like sorting. * * Do not use this directly. Use lists instead. */ #ifndef __TOMMYCHAIN_H #define __TOMMYCHAIN_H #include "tommytypes.h" /******************************************************************************/ /* chain */ /** * Chain of nodes. * A chain of nodes is a sequence of nodes with the following properties: * - It contains at least one node. A chains of zero nodes cannot exist. * - The next field of the tail is of *undefined* value. * - The prev field of the head is of *undefined* value. * - All the other inner prev and next fields are correctly set. */ typedef struct tommy_chain_struct { tommy_node* head; /**< Pointer to the head of the chain. */ tommy_node* tail; /**< Pointer to the tail of the chain. */ } tommy_chain; /** * Splices a chain in the middle of another chain. */ tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail) { /* set the prev list */ first_after->prev = second_tail; second_head->prev = first_before; /* set the next list */ first_before->next = second_head; second_tail->next = first_after; } /** * Concats two chains. */ tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head) { /* set the prev list */ second_head->prev = first_tail; /* set the next list */ first_tail->next = second_head; } /** * Merges two chains. */ tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) { tommy_node* first_i = first->head; tommy_node* second_i = second->head; /* merge */ while (1) { if (cmp(first_i->data, second_i->data) > 0) { tommy_node* next = second_i->next; if (first_i == first->head) { tommy_chain_concat(second_i, first_i); first->head = second_i; } else { tommy_chain_splice(first_i->prev, first_i, second_i, second_i); } if (second_i == second->tail) break; second_i = next; } else { if (first_i == first->tail) { tommy_chain_concat(first_i, second_i); first->tail = second->tail; break; } first_i = first_i->next; } } } /** * Merges two chains managing special degenerated cases. * It's functionally equivalent at tommy_chain_merge() but faster with already ordered chains. */ tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) { /* identify the condition first <= second */ if (cmp(first->tail->data, second->head->data) <= 0) { tommy_chain_concat(first->tail, second->head); first->tail = second->tail; return; } /* identify the condition second < first */ /* here we must be strict on comparison to keep the sort stable */ if (cmp(second->tail->data, first->head->data) < 0) { tommy_chain_concat(second->tail, first->head); first->head = second->head; return; } tommy_chain_merge(first, second, cmp); } /** * Sorts a chain. * It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity, * similar at the one used in the SGI STL libraries and in the Linux Kernel, * but faster on degenerated cases like already ordered lists. * * SGI STL stl_list.h * http://www.sgi.com/tech/stl/stl_list.h * * Linux Kernel lib/list_sort.c * http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c */ tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp) { /* * Bit buckets of chains. * Each bucket contains 2^i nodes or it's empty. * The chain at address TOMMY_BIT_MAX is an independent variable operating as "carry". * We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck. */ tommy_chain bit[TOMMY_SIZE_BIT + 1]; /** * Value stored inside the bit bucket. * It's used to know which bucket is empty of full. */ tommy_size_t counter; tommy_node* node = chain->head; tommy_node* tail = chain->tail; tommy_size_t mask; tommy_size_t i; counter = 0; while (1) { tommy_node* next; tommy_chain* last; /* carry bit to add */ last = &bit[TOMMY_SIZE_BIT]; bit[TOMMY_SIZE_BIT].head = node; bit[TOMMY_SIZE_BIT].tail = node; next = node->next; /* add the bit, propagating the carry */ i = 0; mask = counter; while ((mask & 1) != 0) { tommy_chain_merge_degenerated(&bit[i], last, cmp); mask >>= 1; last = &bit[i]; ++i; } /* copy the carry in the first empty bit */ bit[i] = *last; /* add the carry in the counter */ ++counter; if (node == tail) break; node = next; } /* merge the buckets */ i = tommy_ctz(counter); mask = counter >> i; while (mask != 1) { mask >>= 1; if (mask & 1) tommy_chain_merge_degenerated(&bit[i + 1], &bit[i], cmp); else bit[i + 1] = bit[i]; ++i; } *chain = bit[i]; } #endif