2 * sort.frag.h: Common implementation of linked-list sorting
5 * Raja R Harinath (rharinath@novell.com)
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 * (C) 2006 Novell, Inc.
30 * This code requires a typedef named 'list_node' for the list node. It
31 * is assumed that the list type is the type of a pointer to a list
32 * node, and that the node has a field named 'next' that implements to
33 * the linked list. No additional invariant is maintained (e.g. the
34 * 'prev' pointer of a doubly-linked list node is _not_ updated). Any
35 * invariant would require a post-processing pass to fix matters if
38 typedef list_node
*digit
;
41 * The maximum possible depth of the merge tree
42 * = ceiling (log2 (maximum number of list nodes))
43 * = ceiling (log2 (maximum possible memory size/size of each list node))
44 * = number of bits in 'size_t' - floor (log2 (sizeof digit))
45 * Also, each list in sort_info is at least 2 nodes long: we can reduce the depth by 1
47 #define FLOOR_LOG2(x) (((x)>=2) + ((x)>=4) + ((x)>=8) + ((x)>=16) + ((x)>=32) + ((x)>=64) + ((x)>=128))
48 #define MAX_RANKS ((sizeof (size_t) * 8) - FLOOR_LOG2(sizeof (list_node)) - 1)
52 int min_rank
, n_ranks
;
55 /* Invariant: ranks[i] == NULL || length(ranks[i]) >= 2**(i+1) */
56 list_node
*ranks
[MAX_RANKS
]; /* ~ 128 bytes on 32bit, ~ 512 bytes on 64bit */
60 init_sort_info (struct sort_info
*si
, GCompareFunc func
)
62 si
->min_rank
= si
->n_ranks
= 0;
64 /* we don't need to initialize si->ranks, since we never lookup past si->n_ranks. */
67 static inline list_node
*
68 merge_lists (list_node
*first
, list_node
*second
, GCompareFunc func
)
70 /* merge the two lists */
71 list_node
*list
= NULL
;
72 list_node
**pos
= &list
;
73 while (first
&& second
) {
74 if (func (first
->data
, second
->data
) > 0) {
76 second
= second
->next
;
81 pos
= &((*pos
)->next
);
83 *pos
= first
? first
: second
;
87 /* Pre-condition: upto <= si->n_ranks, list == NULL || length(list) == 1 */
88 static inline list_node
*
89 sweep_up (struct sort_info
*si
, list_node
*list
, int upto
)
91 #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)
93 * GCC incorrectly thinks we're writing below si->ranks array bounds.
95 #pragma GCC diagnostic push
96 #pragma GCC diagnostic ignored "-Warray-bounds"
100 for (i
= si
->min_rank
; i
< upto
; ++i
) {
101 list
= merge_lists (si
->ranks
[i
], list
, si
->func
);
102 si
->ranks
[i
] = NULL
;
106 #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)
107 #pragma GCC diagnostic pop
112 * The 'ranks' array essentially captures the recursion stack of a mergesort.
113 * The merge tree is built in a bottom-up manner. The control loop for
114 * updating the 'ranks' array is analogous to incrementing a binary integer,
115 * and the O(n) time for counting upto n translates to O(n) merges when
116 * inserting rank-0 lists. When we plug in the sizes of the lists involved in
117 * those merges, we get the O(n log n) time for the sort.
119 * Inserting higher-ranked lists reduce the height of the merge tree, and also
120 * eliminate a lot of redundant comparisons when merging two lists that would've
121 * been part of the same run. Adding a rank-i list is analogous to incrementing
122 * a binary integer by 2**i in one operation, thus sharing a similar speedup.
124 * When inserting higher-ranked lists, we choose to clear out the lower ranks
125 * in the interests of keeping the sort stable, but this makes analysis harder.
126 * Note that clearing the lower-ranked lists is O(length(list))-- thus it
127 * shouldn't affect the O(n log n) behaviour. IOW, inserting one rank-i list
128 * is equivalent to inserting 2**i rank-0 lists, thus even if we do i additional
129 * merges in the clearing-out (taking at most 2**i time) we are still fine.
132 #define stringify2(x) #x
133 #define stringify(x) stringify2(x)
135 /* Pre-condition: 2**(rank+1) <= length(list) < 2**(rank+2) (therefore: length(list) >= 2) */
137 insert_list (struct sort_info
*si
, list_node
* list
, int rank
)
139 #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)
141 * GCC incorrectly thinks we're writing below si->ranks array bounds.
143 #pragma GCC diagnostic push
144 #pragma GCC diagnostic ignored "-Warray-bounds"
149 if (rank
> si
->n_ranks
) {
150 if (rank
> MAX_RANKS
) {
151 g_warning ("Rank '%d' should not exceed " stringify (MAX_RANKS
), rank
);
154 list
= merge_lists (sweep_up (si
, NULL
, si
->n_ranks
), list
, si
->func
);
155 for (i
= si
->n_ranks
; i
< rank
; ++i
)
156 si
->ranks
[i
] = NULL
;
159 list
= merge_lists (sweep_up (si
, NULL
, rank
), list
, si
->func
);
160 for (i
= rank
; i
< si
->n_ranks
&& si
->ranks
[i
]; ++i
) {
161 list
= merge_lists (si
->ranks
[i
], list
, si
->func
);
162 si
->ranks
[i
] = NULL
;
166 if (i
== MAX_RANKS
) /* Will _never_ happen: so we can just devolve into quadratic ;-) */
168 if (i
>= si
->n_ranks
)
171 si
->ranks
[i
] = list
;
173 #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)
174 #pragma GCC diagnostic pop
183 /* A non-recursive mergesort */
185 do_sort (list_node
* list
, GCompareFunc func
)
189 init_sort_info (&si
, func
);
191 while (list
&& list
->next
) {
192 list_node
* next
= list
->next
;
193 list_node
* tail
= next
->next
;
195 if (func (list
->data
, next
->data
) > 0) {
202 insert_list (&si
, list
, 0);
207 return sweep_up (&si
, list
, si
.n_ranks
);