LCOV - code coverage report
Current view: top level - tommyds - tommychain.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 69 69 100.0 %
Date: 2026-04-29 15:04:44 Functions: 5 5 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: BSD-2-Clause
       2             : // Copyright (C) 2010 Andrea Mazzoleni
       3             : 
       4             : /** \file
       5             :  * Chain of nodes.
       6             :  * A chain of nodes is an abstraction used to implement complex list operations
       7             :  * like sorting.
       8             :  *
       9             :  * Do not use this directly. Use lists instead.
      10             :  */
      11             : 
      12             : #ifndef __TOMMYCHAIN_H
      13             : #define __TOMMYCHAIN_H
      14             : 
      15             : #include "tommytypes.h"
      16             : 
      17             : /******************************************************************************/
      18             : /* chain */
      19             : 
      20             : /**
      21             :  * Chain of nodes.
      22             :  * A chain of nodes is a sequence of nodes with the following properties:
      23             :  * - It contains at least one node. A chain of zero nodes cannot exist.
      24             :  * - The next field of the tail is of *undefined* value.
      25             :  * - The prev field of the head is of *undefined* value.
      26             :  * - All the other inner prev and next fields are correctly set.
      27             :  */
      28             : typedef struct tommy_chain_struct {
      29             :         tommy_node* head; /**< Pointer to the head of the chain. */
      30             :         tommy_node* tail; /**< Pointer to the tail of the chain. */
      31             : } tommy_chain;
      32             : 
      33             : /**
      34             :  * Splices a chain in the middle of another chain.
      35             :  * \param first_before Node before the splice point.
      36             :  * \param first_after Node after the splice point.
      37             :  * \param second_head Head of the chain to splice in.
      38             :  * \param second_tail Tail of the chain to splice in.
      39             :  */
      40     5001488 : tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail)
      41             : {
      42             :         /* set the prev list */
      43     5001488 :         first_after->prev = second_tail;
      44     5001488 :         second_head->prev = first_before;
      45             : 
      46             :         /* set the next list */
      47     5001488 :         first_before->next = second_head;
      48     5001488 :         second_tail->next = first_after;
      49     5001488 : }
      50             : 
      51             : /**
      52             :  * Concatenates two chains.
      53             :  * \param first_tail Tail of the first chain.
      54             :  * \param second_head Head of the second chain.
      55             :  */
      56     1441395 : tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head)
      57             : {
      58             :         /* set the prev list */
      59     1441395 :         second_head->prev = first_tail;
      60             : 
      61             :         /* set the next list */
      62     1441395 :         first_tail->next = second_head;
      63     1441395 : }
      64             : 
      65             : /**
      66             :  * Merges two chains.
      67             :  * \param first First chain (will contain the result).
      68             :  * \param second Second chain (will be empty after the merge).
      69             :  * \param cmp Comparison function.
      70             :  */
      71      527839 : tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
      72             : {
      73      527839 :         tommy_node* first_i = first->head;
      74      527839 :         tommy_node* second_i = second->head;
      75             : 
      76             :         /* merge */
      77             :         while (1) {
      78    10934182 :                 if (cmp(first_i->data, second_i->data) > 0) {
      79     5267884 :                         tommy_node* next = second_i->next;
      80     5267884 :                         if (first_i == first->head) {
      81      266396 :                                 tommy_chain_concat(second_i, first_i);
      82      266396 :                                 first->head = second_i;
      83             :                         } else {
      84     5001488 :                                 tommy_chain_splice(first_i->prev, first_i, second_i, second_i);
      85             :                         }
      86     5267884 :                         if (second_i == second->tail)
      87      264306 :                                 break;
      88     5003578 :                         second_i = next;
      89             :                 } else {
      90     5666298 :                         if (first_i == first->tail) {
      91      263533 :                                 tommy_chain_concat(first_i, second_i);
      92      263533 :                                 first->tail = second->tail;
      93      263533 :                                 break;
      94             :                         }
      95     5402765 :                         first_i = first_i->next;
      96             :                 }
      97             :         }
      98      527839 : }
      99             : 
     100             : /**
     101             :  * Merges two chains managing special degenerated cases.
     102             :  * It's functionally equivalent to tommy_chain_merge() but faster with already ordered chains.
     103             :  * \param first First chain (will contain the result).
     104             :  * \param second Second chain (will be empty after the merge).
     105             :  * \param cmp Comparison function.
     106             :  */
     107     1439305 : tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
     108             : {
     109             :         /* identify the condition first <= second */
     110     1439305 :         if (cmp(first->tail->data, second->head->data) <= 0) {
     111      533313 :                 tommy_chain_concat(first->tail, second->head);
     112      533313 :                 first->tail = second->tail;
     113      533313 :                 return;
     114             :         }
     115             : 
     116             :         /* identify the condition second < first */
     117             :         /* here we must be strict on comparison to keep the sort stable */
     118      905992 :         if (cmp(second->tail->data, first->head->data) < 0) {
     119      378153 :                 tommy_chain_concat(second->tail, first->head);
     120      378153 :                 first->head = second->head;
     121      378153 :                 return;
     122             :         }
     123             : 
     124      527839 :         tommy_chain_merge(first, second, cmp);
     125             : }
     126             : 
     127             : /**
     128             :  * Sorts a chain.
     129             :  * It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity,
     130             :  * similar to the one used in the SGI STL libraries and in the Linux Kernel,
     131             :  * but faster on degenerated cases like already ordered lists.
     132             :  *
     133             :  * SGI STL stl_list.h
     134             :  * http://www.sgi.com/tech/stl/stl_list.h
     135             :  *
     136             :  * Linux Kernel lib/list_sort.c
     137             :  * http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c
     138             :  *
     139             :  * \param chain Chain to sort.
     140             :  * \param cmp Comparison function.
     141             :  */
     142        2427 : tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp)
     143             : {
     144             :         /*
     145             :          * Bit buckets of chains.
     146             :          * Each bucket contains 2^i nodes or it's empty.
     147             :          * The chain at address TOMMY_BIT_MAX is an independent variable operating as "carry".
     148             :          * We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck.
     149             :          */
     150             :         tommy_chain bit[TOMMY_SIZE_BIT + 1];
     151             : 
     152             :         /**
     153             :          * Value stored inside the bit bucket.
     154             :          * It's used to know which bucket is empty or full.
     155             :          */
     156             :         tommy_size_t counter;
     157        2427 :         tommy_node* node = chain->head;
     158        2427 :         tommy_node* tail = chain->tail;
     159             :         tommy_size_t mask;
     160             :         tommy_size_t i;
     161             : 
     162        2427 :         counter = 0;
     163     1439305 :         while (1) {
     164             :                 tommy_node* next;
     165             :                 tommy_chain* last;
     166             : 
     167             :                 /* carry bit to add */
     168     1441732 :                 last = &bit[TOMMY_SIZE_BIT];
     169     1441732 :                 bit[TOMMY_SIZE_BIT].head = node;
     170     1441732 :                 bit[TOMMY_SIZE_BIT].tail = node;
     171     1441732 :                 next = node->next;
     172             : 
     173             :                 /* add the bit, propagating the carry */
     174     1441732 :                 i = 0;
     175     1441732 :                 mask = counter;
     176     2873997 :                 while ((mask & 1) != 0) {
     177     1432265 :                         tommy_chain_merge_degenerated(&bit[i], last, cmp);
     178     1432265 :                         mask >>= 1;
     179     1432265 :                         last = &bit[i];
     180     1432265 :                         ++i;
     181             :                 }
     182             : 
     183             :                 /* copy the carry in the first empty bit */
     184     1441732 :                 bit[i] = *last;
     185             : 
     186             :                 /* add the carry in the counter */
     187     1441732 :                 ++counter;
     188             : 
     189     1441732 :                 if (node == tail)
     190        2427 :                         break;
     191     1439305 :                 node = next;
     192             :         }
     193             : 
     194             :         /* merge the buckets */
     195        2427 :         i = tommy_ctz(counter);
     196        2427 :         mask = counter >> i;
     197       13101 :         while (mask != 1) {
     198       10674 :                 mask >>= 1;
     199       10674 :                 if (mask & 1)
     200        7040 :                         tommy_chain_merge_degenerated(&bit[i + 1], &bit[i], cmp);
     201             :                 else
     202        3634 :                         bit[i + 1] = bit[i];
     203       10674 :                 ++i;
     204             :         }
     205             : 
     206        2427 :         *chain = bit[i];
     207        2427 : }
     208             : 
     209             : #endif

Generated by: LCOV version 1.0