LCOV - code coverage report
Current view: top level - tommyds/tommyds - tommychain.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 69 69 100.0 %
Date: 2025-12-03 00:44:19 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
       3             :  *
       4             :  * Redistribution and use in source and binary forms, with or without
       5             :  * modification, are permitted provided that the following conditions
       6             :  * are met:
       7             :  *
       8             :  * 1. Redistributions of source code must retain the above copyright
       9             :  *    notice, this list of conditions and the following disclaimer.
      10             :  *
      11             :  * 2. Redistributions in binary form must reproduce the above copyright
      12             :  *    notice, this list of conditions and the following disclaimer in the
      13             :  *    documentation and/or other materials provided with the distribution.
      14             :  *
      15             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
      16             :  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      17             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      18             :  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
      19             :  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      20             :  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      21             :  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      22             :  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      23             :  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      24             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      25             :  * POSSIBILITY OF SUCH DAMAGE.
      26             :  */
      27             : 
      28             : /** \file
      29             :  * Chain of nodes.
      30             :  * A chain of nodes is an abstraction used to implement complex list operations
      31             :  * like sorting.
      32             :  *
      33             :  * Do not use this directly. Use lists instead.
      34             :  */
      35             : 
      36             : #ifndef __TOMMYCHAIN_H
      37             : #define __TOMMYCHAIN_H
      38             : 
      39             : #include "tommytypes.h"
      40             : 
      41             : /******************************************************************************/
      42             : /* chain */
      43             : 
      44             : /**
      45             :  * Chain of nodes.
      46             :  * A chain of nodes is a sequence of nodes with the following properties:
      47             :  * - It contains at least one node. A chain of zero nodes cannot exist.
      48             :  * - The next field of the tail is of *undefined* value.
      49             :  * - The prev field of the head is of *undefined* value.
      50             :  * - All the other inner prev and next fields are correctly set.
      51             :  */
      52             : typedef struct tommy_chain_struct {
      53             :         tommy_node* head; /**< Pointer to the head of the chain. */
      54             :         tommy_node* tail; /**< Pointer to the tail of the chain. */
      55             : } tommy_chain;
      56             : 
      57             : /**
      58             :  * Splices a chain in the middle of another chain.
      59             :  * \param first_before Node before the splice point.
      60             :  * \param first_after Node after the splice point.
      61             :  * \param second_head Head of the chain to splice in.
      62             :  * \param second_tail Tail of the chain to splice in.
      63             :  */
      64    23430489 : tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail)
      65             : {
      66             :         /* set the prev list */
      67    23430489 :         first_after->prev = second_tail;
      68    23430489 :         second_head->prev = first_before;
      69             : 
      70             :         /* set the next list */
      71    23430489 :         first_before->next = second_head;
      72    23430489 :         second_tail->next = first_after;
      73    23430489 : }
      74             : 
      75             : /**
      76             :  * Concatenates two chains.
      77             :  * \param first_tail Tail of the first chain.
      78             :  * \param second_head Head of the second chain.
      79             :  */
      80     4998191 : tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head)
      81             : {
      82             :         /* set the prev list */
      83     4998191 :         second_head->prev = first_tail;
      84             : 
      85             :         /* set the next list */
      86     4998191 :         first_tail->next = second_head;
      87     4998191 : }
      88             : 
      89             : /**
      90             :  * Merges two chains.
      91             :  * \param first First chain (will contain the result).
      92             :  * \param second Second chain (will be empty after the merge).
      93             :  * \param cmp Comparison function.
      94             :  */
      95      859955 : tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
      96             : {
      97      859955 :         tommy_node* first_i = first->head;
      98      859955 :         tommy_node* second_i = second->head;
      99             : 
     100             :         /* merge */
     101             :         while (1) {
     102    49016800 :                 if (cmp(first_i->data, second_i->data) > 0) {
     103    23857535 :                         tommy_node* next = second_i->next;
     104    23857535 :                         if (first_i == first->head) {
     105      427046 :                                 tommy_chain_concat(second_i, first_i);
     106      427046 :                                 first->head = second_i;
     107             :                         } else {
     108    23430489 :                                 tommy_chain_splice(first_i->prev, first_i, second_i, second_i);
     109             :                         }
     110    23857535 :                         if (second_i == second->tail)
     111      428850 :                                 break;
     112    23428685 :                         second_i = next;
     113             :                 } else {
     114    25159265 :                         if (first_i == first->tail) {
     115      431105 :                                 tommy_chain_concat(first_i, second_i);
     116      431105 :                                 first->tail = second->tail;
     117      431105 :                                 break;
     118             :                         }
     119    24728160 :                         first_i = first_i->next;
     120             :                 }
     121             :         }
     122      859955 : }
     123             : 
     124             : /**
     125             :  * Merges two chains managing special degenerated cases.
     126             :  * It's functionally equivalent to tommy_chain_merge() but faster with already ordered chains.
     127             :  * \param first First chain (will contain the result).
     128             :  * \param second Second chain (will be empty after the merge).
     129             :  * \param cmp Comparison function.
     130             :  */
     131     4999995 : tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
     132             : {
     133             :         /* identify the condition first <= second */
     134     4999995 :         if (cmp(first->tail->data, second->head->data) <= 0) {
     135     2548313 :                 tommy_chain_concat(first->tail, second->head);
     136     2548313 :                 first->tail = second->tail;
     137     2548313 :                 return;
     138             :         }
     139             : 
     140             :         /* identify the condition second < first */
     141             :         /* here we must be strict on comparison to keep the sort stable */
     142     2451682 :         if (cmp(second->tail->data, first->head->data) < 0) {
     143     1591727 :                 tommy_chain_concat(second->tail, first->head);
     144     1591727 :                 first->head = second->head;
     145     1591727 :                 return;
     146             :         }
     147             : 
     148      859955 :         tommy_chain_merge(first, second, cmp);
     149             : }
     150             : 
     151             : /**
     152             :  * Sorts a chain.
     153             :  * It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity,
     154             :  * similar to the one used in the SGI STL libraries and in the Linux Kernel,
     155             :  * but faster on degenerated cases like already ordered lists.
     156             :  *
     157             :  * SGI STL stl_list.h
     158             :  * http://www.sgi.com/tech/stl/stl_list.h
     159             :  *
     160             :  * Linux Kernel lib/list_sort.c
     161             :  * http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c
     162             :  *
     163             :  * \param chain Chain to sort.
     164             :  * \param cmp Comparison function.
     165             :  */
     166           5 : tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp)
     167             : {
     168             :         /*
     169             :          * Bit buckets of chains.
     170             :          * Each bucket contains 2^i nodes or it's empty.
     171             :          * The chain at address TOMMY_BIT_MAX is an independent variable operating as "carry".
     172             :          * We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck.
     173             :          */
     174             :         tommy_chain bit[TOMMY_SIZE_BIT + 1];
     175             : 
     176             :         /**
     177             :          * Value stored inside the bit bucket.
     178             :          * It's used to know which bucket is empty or full.
     179             :          */
     180             :         tommy_size_t counter;
     181           5 :         tommy_node* node = chain->head;
     182           5 :         tommy_node* tail = chain->tail;
     183             :         tommy_size_t mask;
     184             :         tommy_size_t i;
     185             : 
     186           5 :         counter = 0;
     187     4999995 :         while (1) {
     188             :                 tommy_node* next;
     189             :                 tommy_chain* last;
     190             : 
     191             :                 /* carry bit to add */
     192     5000000 :                 last = &bit[TOMMY_SIZE_BIT];
     193     5000000 :                 bit[TOMMY_SIZE_BIT].head = node;
     194     5000000 :                 bit[TOMMY_SIZE_BIT].tail = node;
     195     5000000 :                 next = node->next;
     196             : 
     197             :                 /* add the bit, propagating the carry */
     198     5000000 :                 i = 0;
     199     5000000 :                 mask = counter;
     200     9999965 :                 while ((mask & 1) != 0) {
     201     4999965 :                         tommy_chain_merge_degenerated(&bit[i], last, cmp);
     202     4999965 :                         mask >>= 1;
     203     4999965 :                         last = &bit[i];
     204     4999965 :                         ++i;
     205             :                 }
     206             : 
     207             :                 /* copy the carry in the first empty bit */
     208     5000000 :                 bit[i] = *last;
     209             : 
     210             :                 /* add the carry in the counter */
     211     5000000 :                 ++counter;
     212             : 
     213     5000000 :                 if (node == tail)
     214           5 :                         break;
     215     4999995 :                 node = next;
     216             :         }
     217             : 
     218             :         /* merge the buckets */
     219           5 :         i = tommy_ctz(counter);
     220           5 :         mask = counter >> i;
     221          70 :         while (mask != 1) {
     222          65 :                 mask >>= 1;
     223          65 :                 if (mask & 1)
     224          30 :                         tommy_chain_merge_degenerated(&bit[i + 1], &bit[i], cmp);
     225             :                 else
     226          35 :                         bit[i + 1] = bit[i];
     227          65 :                 ++i;
     228             :         }
     229             : 
     230           5 :         *chain = bit[i];
     231           5 : }
     232             : 
     233             : #endif

Generated by: LCOV version 1.0