Skip to content

Commit

Permalink
std.sort: add pdqsort and heapsort
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
2 people authored and andrewrk committed Apr 25, 2023
1 parent 8d88dcd commit b89f319
Show file tree
Hide file tree
Showing 35 changed files with 1,930 additions and 1,307 deletions.
4 changes: 2 additions & 2 deletions lib/std/array_hash_map.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2291,8 +2291,8 @@ test "sort" {
const C = struct {
keys: []i32,

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

Expand Down
11 changes: 5 additions & 6 deletions lib/std/compress/deflate/huffman_code.zig
Original file line number Diff line number Diff line change
Expand Up @@ -346,17 +346,16 @@ pub fn generateFixedOffsetEncoding(allocator: Allocator) !HuffmanEncoder {
return h;
}

fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) bool {
fn byLiteral(context: void, a: LiteralNode, b: LiteralNode) math.Order {
_ = context;
return a.literal < b.literal;
return math.order(a.literal, b.literal);
}

fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
_ = context;
fn byFreq(context: void, a: LiteralNode, b: LiteralNode) math.Order {
if (a.freq == b.freq) {
return a.literal < b.literal;
return byLiteral(context, a, b);
}
return a.freq < b.freq;
return math.order(a.freq, b.freq);
}

test "generate a Huffman code from an array of frequencies" {
Expand Down
8 changes: 4 additions & 4 deletions lib/std/compress/zstandard/decode/huffman.zig
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.P
LiteralsSection.HuffmanTree.PrefixedSymbol,
weight_sorted_prefixed_symbols,
weights,
lessThanByWeight,
compareByWeight,
);

var prefix: u16 = 0;
Expand Down Expand Up @@ -222,13 +222,13 @@ pub fn decodeHuffmanTreeSlice(
return buildHuffmanTree(&weights, symbol_count);
}

fn lessThanByWeight(
fn compareByWeight(
weights: [256]u4,
lhs: LiteralsSection.HuffmanTree.PrefixedSymbol,
rhs: LiteralsSection.HuffmanTree.PrefixedSymbol,
) bool {
) std.math.Order {
// NOTE: this function relies on the use of a stable sorting algorithm,
// otherwise a special case of if (weights[lhs] == weights[rhs]) return lhs < rhs;
// should be added
return weights[lhs.symbol] < weights[rhs.symbol];
return std.math.order(weights[lhs.symbol], weights[rhs.symbol]);
}
6 changes: 3 additions & 3 deletions lib/std/comptime_string_map.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ const mem = std.mem;
/// You can pass `struct { []const u8 }` (only keys) tuples if `V` is `void`.
pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {
const precomputed = comptime blk: {
@setEvalBranchQuota(2000);
@setEvalBranchQuota(4000);
const KV = struct {
key: []const u8,
value: V,
};
var sorted_kvs: [kvs_list.len]KV = undefined;
const lenAsc = (struct {
fn lenAsc(context: void, a: KV, b: KV) bool {
fn lenAsc(context: void, a: KV, b: KV) std.math.Order {
_ = context;
return a.key.len < b.key.len;
return std.math.order(a.key.len, b.key.len);
}
}).lenAsc;
for (kvs_list, 0..) |kv, i| {
Expand Down
6 changes: 3 additions & 3 deletions lib/std/debug.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1211,7 +1211,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn
// Even though lld emits symbols in ascending order, this debug code
// should work for programs linked in any valid way.
// This sort is so that we can binary search later.
std.sort.sort(MachoSymbol, symbols, {}, MachoSymbol.addressLessThan);
std.sort.sort(MachoSymbol, symbols, {}, MachoSymbol.addressCompare);

return ModuleDebugInfo{
.base_address = undefined,
Expand Down Expand Up @@ -1269,9 +1269,9 @@ const MachoSymbol = struct {
return self.addr;
}

fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool {
fn addressCompare(context: void, lhs: MachoSymbol, rhs: MachoSymbol) std.math.Order {
_ = context;
return lhs.addr < rhs.addr;
return std.math.order(lhs.addr, rhs.addr);
}
};

Expand Down
4 changes: 2 additions & 2 deletions lib/std/enums.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1279,9 +1279,9 @@ test "std.enums.ensureIndexer" {
});
}

fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool {
fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) std.math.Order {
_ = ctx;
return a.value < b.value;
return std.math.order(a.value, b.value);
}
pub fn EnumIndexer(comptime E: type) type {
if (!@typeInfo(E).Enum.is_exhaustive) {
Expand Down
8 changes: 4 additions & 4 deletions lib/std/http/Headers.zig
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ pub const Field = struct {
}
}

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

return ascii.lessThanIgnoreCase(a.name, b.name);
return ascii.orderIgnoreCase(a.name, b.name);
}
};

Expand Down Expand Up @@ -201,7 +201,7 @@ pub const Headers = struct {

/// Sorts the headers in lexicographical order.
pub fn sort(headers: *Headers) void {
std.sort.sort(Field, headers.list.items, {}, Field.lessThan);
std.sort.sort(Field, headers.list.items, {}, Field.compare);
headers.rebuildIndex();
}

Expand Down
10 changes: 5 additions & 5 deletions lib/std/multi_array_list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,12 @@ pub fn MultiArrayList(comptime T: type) type {
};
}
const Sort = struct {
fn lessThan(context: void, lhs: Data, rhs: Data) bool {
fn compare(context: void, lhs: Data, rhs: Data) std.math.Order {
_ = context;
return lhs.alignment > rhs.alignment;
return std.math.order(rhs.alignment, lhs.alignment);
}
};
std.sort.sort(Data, &data, {}, Sort.lessThan);
std.sort.sort(Data, &data, {}, Sort.compare);
var sizes_bytes: [fields.len]usize = undefined;
var field_indexes: [fields.len]usize = undefined;
for (data, 0..) |elem, i| {
Expand Down Expand Up @@ -484,8 +484,8 @@ pub fn MultiArrayList(comptime T: type) type {
}
}

pub fn lessThan(sc: @This(), a_index: usize, b_index: usize) bool {
return sc.sub_ctx.lessThan(a_index, b_index);
pub fn compare(sc: @This(), a_index: usize, b_index: usize) std.math.Order {
return sc.sub_ctx.compare(a_index, b_index);
}
};

Expand Down
4 changes: 2 additions & 2 deletions lib/std/net.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1199,9 +1199,9 @@ fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool {
}

// Parameters `b` and `a` swapped to make this descending.
fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {
fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) std.math.Order {
_ = context;
return a.sortkey < b.sortkey;
return std.math.order(a.sortkey, b.sortkey);
}

fn linuxLookupNameFromNull(
Expand Down
Loading

0 comments on commit b89f319

Please sign in to comment.