1/* $NetBSD: linux_list_sort.c,v 1.2 2014/03/18 18:20:43 riastradh Exp $ */
2
3/*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: linux_list_sort.c,v 1.2 2014/03/18 18:20:43 riastradh Exp $");
34
35#include <sys/systm.h>
36
37#include <machine/limits.h>
38
39#include <linux/list.h>
40#include <linux/list_sort.h>
41
42static struct list_head *
43 list_sort_merge(struct list_head *, struct list_head *,
44 int (*)(void *, struct list_head *, struct list_head *),
45 void *);
46static void list_sort_merge_into(struct list_head *,
47 struct list_head *, struct list_head *,
48 int (*)(void *, struct list_head *, struct list_head *),
49 void *);
50
51void
52list_sort(void *arg, struct list_head *list,
53 int (*compare)(void *, struct list_head *, struct list_head *))
54{
55 /*
56 * Array of sorted sublists, counting in binary: accumulator[i]
57 * is sorted, and either is NULL or has length 2^i.
58 */
59 struct list_head *accumulator[64];
60
61 /* Indices into accumulator. */
62 unsigned int logn, max_logn = 0;
63
64 /* The sorted list we're currently working on. */
65 struct list_head *sorted;
66
67 /* The remainder of the unsorted list. */
68 struct list_head *next;
69
70 /* Make sure we can't possibly have more than 2^64-element lists. */
71 __CTASSERT((CHAR_BIT * sizeof(struct list_head *)) <= 64);
72
73 for (logn = 0; logn < __arraycount(accumulator); logn++)
74 accumulator[logn] = NULL;
75
76 list_for_each_safe(sorted, next, list) {
77 /* Pick off a single element, always sorted. */
78 sorted->next = NULL;
79
80 /* Add one and propagate the carry. */
81 for (logn = 0; accumulator[logn] != NULL; logn++) {
82 /*
83 * Merge, preferring previously accumulated
84 * elements to make the sort stable.
85 */
86 sorted = list_sort_merge(accumulator[logn], sorted,
87 compare, arg);
88 accumulator[logn] = NULL;
89 KASSERT((logn + 1) < __arraycount(accumulator));
90 }
91
92 /* Remember the highest index seen so far. */
93 if (logn > max_logn)
94 max_logn = logn;
95
96 /*
97 * logn = log_2(length(sorted)), and accumulator[logn]
98 * is now empty, so save the sorted sublist there.
99 */
100 accumulator[logn] = sorted;
101 }
102
103 /*
104 * Merge ~half of everything we have accumulated.
105 */
106 sorted = NULL;
107 for (logn = 0; logn < max_logn; logn++)
108 sorted = list_sort_merge(accumulator[logn], sorted, compare,
109 arg);
110
111 /*
112 * Merge the last ~halves back into the list, and fix the back
113 * pointers.
114 */
115 list_sort_merge_into(list, accumulator[max_logn], sorted, compare,
116 arg);
117}
118
119/*
120 * Merge the NULL-terminated lists starting at nodes `a' and `b',
121 * breaking ties by choosing nodes in `a' first, and returning
122 * whichever node has the least element.
123 */
124static struct list_head *
125list_sort_merge(struct list_head *a, struct list_head *b,
126 int (*compare)(void *, struct list_head *, struct list_head *), void *arg)
127{
128 struct list_head head, *tail = &head;
129
130 /*
131 * Merge while elements in both remain.
132 */
133 while ((a != NULL) && (b != NULL)) {
134 struct list_head **const first = ((*compare)(arg, a, b) <= 0?
135 &a : &b);
136
137 tail = tail->next = *first;
138 *first = (*first)->next;
139 }
140
141 /*
142 * Attach whatever remains.
143 */
144 tail->next = (a != NULL? a : b);
145 return head.next;
146}
147
148/*
149 * Merge the NULL-terminated lists starting at nodes `a' and `b' into
150 * the (uninitialized) list head `list', breaking ties by choosing
151 * nodes in `a' first, and setting the `prev' pointers as we go.
152 */
153static void
154list_sort_merge_into(struct list_head *list,
155 struct list_head *a, struct list_head *b,
156 int (*compare)(void *, struct list_head *, struct list_head *), void *arg)
157{
158 struct list_head *prev = list;
159
160 /*
161 * Merge while elements in both remain.
162 */
163 while ((a != NULL) && (b != NULL)) {
164 struct list_head **const first = ((*compare)(arg, a, b) <= 0?
165 &a : &b);
166
167 (*first)->prev = prev;
168 prev = prev->next = *first;
169 *first = (*first)->next;
170 }
171
172 /*
173 * Attach whichever of a and b remains, and fix up the prev
174 * pointers all the way down the rest of the list.
175 */
176 struct list_head *tail = (a == NULL? b : a);
177 while (tail != NULL) {
178 prev->next = tail;
179 tail->prev = prev;
180 prev = prev->next;
181 tail = tail->next;
182 }
183
184 /*
185 * Finally, finish the cycle.
186 */
187 prev->next = list;
188 list->prev = prev;
189}
190