Skip to content

Commit e5eff13

Browse files
alichraghivoroskoi
andcommitted
std.sort: add pdqsort and heapsort
pdqsort (Pattern-defeating quicksort): A novel sorting algorithm that combines the fast average case of randomized quicksort with the fast worst case of heapsort, while achieving linear time on inputs with certain patterns. This implementation is based on https://github.com/zhangyunhao116/pdqsort which later replaced Go's original sort algorithm also: - compareFn now returns `std.math.Order` - moved *blocksort* (default stable algorithm) and *pdqsort* (default unstable algorithm) to lib/std/sort/ - made tests run for each algorithm Co-authored-by: VÖRÖSKŐI András <[email protected]>
1 parent 440b3df commit e5eff13

38 files changed

+1959
-1335
lines changed

lib/std/array_hash_map.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,8 +2291,8 @@ test "sort" {
22912291
const C = struct {
22922292
keys: []i32,
22932293

2294-
pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
2295-
return ctx.keys[a_index] < ctx.keys[b_index];
2294+
pub fn compare(ctx: @This(), a_index: usize, b_index: usize) std.math.Order {
2295+
return std.math.order(ctx.keys[a_index], ctx.keys[b_index]);
22962296
}
22972297
};
22982298

lib/std/compress/deflate/huffman_code.zig

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -346,17 +346,16 @@ pub fn generateFixedOffsetEncoding(allocator: Allocator) !HuffmanEncoder {
346346
return h;
347347
}
348348

349-
fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) bool {
349+
fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) math.Order {
350350
_ = context;
351-
return a.literal < b.literal;
351+
return math.order(a.literal, b.literal);
352352
}
353353

354-
fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
355-
_ = context;
354+
fn byFreq(context: void, a: LiteralNode, b: LiteralNode) math.Order {
356355
if (a.freq == b.freq) {
357-
return a.literal < b.literal;
356+
return byLiteral(context, a, b);
358357
}
359-
return a.freq < b.freq;
358+
return math.order(a.freq, b.freq);
360359
}
361360

362361
test "generate a Huffman code from an array of frequencies" {

lib/std/compress/zstandard/decode/huffman.zig

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.P
128128
LiteralsSection.HuffmanTree.PrefixedSymbol,
129129
weight_sorted_prefixed_symbols,
130130
weights,
131-
lessThanByWeight,
131+
compareByWeight,
132132
);
133133

134134
var prefix: u16 = 0;
@@ -222,13 +222,13 @@ pub fn decodeHuffmanTreeSlice(
222222
return buildHuffmanTree(&weights, symbol_count);
223223
}
224224

225-
fn lessThanByWeight(
225+
fn compareByWeight(
226226
weights: [256]u4,
227227
lhs: LiteralsSection.HuffmanTree.PrefixedSymbol,
228228
rhs: LiteralsSection.HuffmanTree.PrefixedSymbol,
229-
) bool {
229+
) std.math.Order {
230230
// NOTE: this function relies on the use of a stable sorting algorithm,
231231
// otherwise a special case of if (weights[lhs] == weights[rhs]) return lhs < rhs;
232232
// should be added
233-
return weights[lhs.symbol] < weights[rhs.symbol];
233+
return std.math.order(weights[lhs.symbol], weights[rhs.symbol]);
234234
}

lib/std/comptime_string_map.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ const mem = std.mem;
99
/// You can pass `struct { []const u8 }` (only keys) tuples if `V` is `void`.
1010
pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {
1111
const precomputed = comptime blk: {
12-
@setEvalBranchQuota(2000);
12+
@setEvalBranchQuota(4000);
1313
const KV = struct {
1414
key: []const u8,
1515
value: V,
1616
};
1717
var sorted_kvs: [kvs_list.len]KV = undefined;
1818
const lenAsc = (struct {
19-
fn lenAsc(context: void, a: KV, b: KV) bool {
19+
fn lenAsc(context: void, a: KV, b: KV) std.math.Order {
2020
_ = context;
21-
return a.key.len < b.key.len;
21+
return std.math.order(a.key.len, b.key.len);
2222
}
2323
}).lenAsc;
2424
for (kvs_list, 0..) |kv, i| {

lib/std/debug.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,7 +1211,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn
12111211
// Even though lld emits symbols in ascending order, this debug code
12121212
// should work for programs linked in any valid way.
12131213
// This sort is so that we can binary search later.
1214-
std.sort.sort(MachoSymbol, symbols, {}, MachoSymbol.addressLessThan);
1214+
std.sort.sort(MachoSymbol, symbols, {}, MachoSymbol.addressCompare);
12151215

12161216
return ModuleDebugInfo{
12171217
.base_address = undefined,
@@ -1269,9 +1269,9 @@ const MachoSymbol = struct {
12691269
return self.addr;
12701270
}
12711271

1272-
fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool {
1272+
fn addressCompare(context: void, lhs: MachoSymbol, rhs: MachoSymbol) std.math.Order {
12731273
_ = context;
1274-
return lhs.addr < rhs.addr;
1274+
return std.math.order(lhs.addr, rhs.addr);
12751275
}
12761276
};
12771277

lib/std/enums.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,9 +1279,9 @@ test "std.enums.ensureIndexer" {
12791279
});
12801280
}
12811281

1282-
fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool {
1282+
fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) std.math.Order {
12831283
_ = ctx;
1284-
return a.value < b.value;
1284+
return std.math.order(a.value, b.value);
12851285
}
12861286
pub fn EnumIndexer(comptime E: type) type {
12871287
if (!@typeInfo(E).Enum.is_exhaustive) {

lib/std/http/Headers.zig

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ pub const Field = struct {
4747
}
4848
}
4949

50-
fn lessThan(ctx: void, a: Field, b: Field) bool {
50+
fn compare(ctx: void, a: Field, b: Field) std.math.Order {
5151
_ = ctx;
52-
if (a.name.ptr == b.name.ptr) return false;
52+
if (a.name.ptr == b.name.ptr) return .eq;
5353

54-
return ascii.lessThanIgnoreCase(a.name, b.name);
54+
return ascii.orderIgnoreCase(a.name, b.name);
5555
}
5656
};
5757

@@ -202,7 +202,7 @@ pub const Headers = struct {
202202

203203
/// Sorts the headers in lexicographical order.
204204
pub fn sort(headers: *Headers) void {
205-
std.sort.sort(Field, headers.list.items, {}, Field.lessThan);
205+
std.sort.sort(Field, headers.list.items, {}, Field.compare);
206206
headers.rebuildIndex();
207207
}
208208

lib/std/multi_array_list.zig

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,12 @@ pub fn MultiArrayList(comptime T: type) type {
155155
};
156156
}
157157
const Sort = struct {
158-
fn lessThan(context: void, lhs: Data, rhs: Data) bool {
158+
fn compare(context: void, lhs: Data, rhs: Data) std.math.Order {
159159
_ = context;
160-
return lhs.alignment > rhs.alignment;
160+
return std.math.order(rhs.alignment, lhs.alignment);
161161
}
162162
};
163-
std.sort.sort(Data, &data, {}, Sort.lessThan);
163+
std.sort.sort(Data, &data, {}, Sort.compare);
164164
var sizes_bytes: [fields.len]usize = undefined;
165165
var field_indexes: [fields.len]usize = undefined;
166166
for (data, 0..) |elem, i| {
@@ -483,8 +483,8 @@ pub fn MultiArrayList(comptime T: type) type {
483483
}
484484
}
485485

486-
pub fn lessThan(sc: @This(), a_index: usize, b_index: usize) bool {
487-
return sc.sub_ctx.lessThan(a_index, b_index);
486+
pub fn compare(sc: @This(), a_index: usize, b_index: usize) std.math.Order {
487+
return sc.sub_ctx.compare(a_index, b_index);
488488
}
489489
};
490490

lib/std/net.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,7 @@ fn linuxLookupName(
10821082
key |= (MAXADDRS - @intCast(i32, i)) << DAS_ORDER_SHIFT;
10831083
addr.sortkey = key;
10841084
}
1085-
std.sort.sort(LookupAddr, addrs.items, {}, addrCmpLessThan);
1085+
std.sort.sort(LookupAddr, addrs.items, {}, addrCompare);
10861086
}
10871087

10881088
const Policy = struct {
@@ -1199,9 +1199,9 @@ fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool {
11991199
}
12001200

12011201
// Parameters `b` and `a` swapped to make this descending.
1202-
fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {
1202+
fn addrCompare(context: void, b: LookupAddr, a: LookupAddr) std.math.Order {
12031203
_ = context;
1204-
return a.sortkey < b.sortkey;
1204+
return std.math.order(a.sortkey, b.sortkey);
12051205
}
12061206

12071207
fn linuxLookupNameFromNull(

lib/std/priority_dequeue.zig

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -461,12 +461,12 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar
461461
};
462462
}
463463

464-
fn lessThanComparison(context: void, a: u32, b: u32) Order {
464+
fn compare(context: void, a: u32, b: u32) Order {
465465
_ = context;
466466
return std.math.order(a, b);
467467
}
468468

469-
const PDQ = PriorityDequeue(u32, void, lessThanComparison);
469+
const PDQ = PriorityDequeue(u32, void, compare);
470470

471471
test "std.PriorityDequeue: add and remove min" {
472472
var queue = PDQ.init(testing.allocator, {});
@@ -977,11 +977,11 @@ fn generateRandomSlice(allocator: std.mem.Allocator, rng: std.rand.Random, size:
977977
return array.toOwnedSlice();
978978
}
979979

980-
fn contextLessThanComparison(context: []const u32, a: usize, b: usize) Order {
980+
fn contextComparison(context: []const u32, a: usize, b: usize) Order {
981981
return std.math.order(context[a], context[b]);
982982
}
983983

984-
const CPDQ = PriorityDequeue(usize, []const u32, contextLessThanComparison);
984+
const CPDQ = PriorityDequeue(usize, []const u32, contextComparison);
985985

986986
test "std.PriorityDequeue: add and remove" {
987987
const context = [_]u32{ 5, 3, 4, 2, 2, 8, 0 };

lib/std/priority_queue.zig

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ fn lessThan(context: void, a: u32, b: u32) Order {
284284
}
285285

286286
fn greaterThan(context: void, a: u32, b: u32) Order {
287-
return lessThan(context, a, b).invert();
287+
_ = context;
288+
return std.math.order(b, a);
288289
}
289290

290291
const PQlt = PriorityQueue(u32, void, lessThan);
@@ -616,11 +617,11 @@ test "std.PriorityQueue: siftUp in remove" {
616617
}
617618
}
618619

619-
fn contextLessThan(context: []const u32, a: usize, b: usize) Order {
620+
fn contextCompare(context: []const u32, a: usize, b: usize) Order {
620621
return std.math.order(context[a], context[b]);
621622
}
622623

623-
const CPQlt = PriorityQueue(usize, []const u32, contextLessThan);
624+
const CPQlt = PriorityQueue(usize, []const u32, contextCompare);
624625

625626
test "std.PriorityQueue: add and remove min heap with contextful comparator" {
626627
const context = [_]u32{ 5, 3, 4, 2, 2, 8, 0 };

0 commit comments

Comments
 (0)