hc
2023-12-04 f33f61bdb7ca6d5ebe7a78f9d8694b91360279ac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// SPDX-License-Identifier: GPL-2.0
/*
 * A fast, small, non-recursive O(nlog n) sort for the Linux kernel
 *
 * Jan 23 2005  Matt Mackall <mpm@selenic.com>
 */
 
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
#include <linux/types.h>
#include <linux/export.h>
#include <linux/sort.h>
 
static int alignment_ok(const void *base, int align)
{
   return IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ||
       ((unsigned long)base & (align - 1)) == 0;
}
 
static void u32_swap(void *a, void *b, int size)
{
   u32 t = *(u32 *)a;
   *(u32 *)a = *(u32 *)b;
   *(u32 *)b = t;
}
 
static void u64_swap(void *a, void *b, int size)
{
   u64 t = *(u64 *)a;
   *(u64 *)a = *(u64 *)b;
   *(u64 *)b = t;
}
 
static void generic_swap(void *a, void *b, int size)
{
   char t;
 
   do {
       t = *(char *)a;
       *(char *)a++ = *(char *)b;
       *(char *)b++ = t;
   } while (--size > 0);
}
 
/**
 * sort - sort an array of elements
 * @base: pointer to data to sort
 * @num: number of elements
 * @size: size of each element
 * @cmp_func: pointer to comparison function
 * @swap_func: pointer to swap function or NULL
 *
 * This function does a heapsort on the given array. You may provide a
 * swap_func function optimized to your element type.
 *
 * Sorting time is O(n log n) both on average and worst-case. While
 * qsort is about 20% faster on average, it suffers from exploitable
 * O(n*n) worst-case behavior and extra memory requirements that make
 * it less suitable for kernel use.
 */
 
void sort(void *base, size_t num, size_t size,
     int (*cmp_func)(const void *, const void *),
     void (*swap_func)(void *, void *, int size))
{
   /* pre-scale counters for performance */
   int i = (num/2 - 1) * size, n = num * size, c, r;
 
   if (!swap_func) {
       if (size == 4 && alignment_ok(base, 4))
           swap_func = u32_swap;
       else if (size == 8 && alignment_ok(base, 8))
           swap_func = u64_swap;
       else
           swap_func = generic_swap;
   }
 
   /* heapify */
   for ( ; i >= 0; i -= size) {
       for (r = i; r * 2 + size < n; r  = c) {
           c = r * 2 + size;
           if (c < n - size &&
                   cmp_func(base + c, base + c + size) < 0)
               c += size;
           if (cmp_func(base + r, base + c) >= 0)
               break;
           swap_func(base + r, base + c, size);
       }
   }
 
   /* sort */
   for (i = n - size; i > 0; i -= size) {
       swap_func(base, base + i, size);
       for (r = 0; r * 2 + size < i; r = c) {
           c = r * 2 + size;
           if (c < i - size &&
                   cmp_func(base + c, base + c + size) < 0)
               c += size;
           if (cmp_func(base + r, base + c) >= 0)
               break;
           swap_func(base + r, base + c, size);
       }
   }
}
 
EXPORT_SYMBOL(sort);