aboutsummaryrefslogtreecommitdiff
path: root/lib/std/array_hash_map.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-03-09 22:50:27 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-03-10 13:13:17 -0500
commit6e49ba77f3001fe04d4b2177a5c37d28bd422758 (patch)
treecfd55e3566fbf651e339529ff03693c75484907b /lib/std/array_hash_map.zig
parentf736cde397a6abb1399827ed5988c43001706580 (diff)
downloadzig-6e49ba77f3001fe04d4b2177a5c37d28bd422758.tar.gz
zig-6e49ba77f3001fe04d4b2177a5c37d28bd422758.zip
std: add sort method to ArrayHashMap and MultiArrayList
This also adds `std.sort.sortContext` and `std.sort.insertionSortContext` which are more advanced methods that allow overriding the `swap` method. The former calls the latter for now because reworking the main sort implementation is a big task that can be done later without any changes to the API.
Diffstat (limited to 'lib/std/array_hash_map.zig')
-rw-r--r--lib/std/array_hash_map.zig57
1 files changed, 57 insertions, 0 deletions
diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig
index 4359c30083..3ddaac49eb 100644
--- a/lib/std/array_hash_map.zig
+++ b/lib/std/array_hash_map.zig
@@ -408,6 +408,13 @@ pub fn ArrayHashMap(
return self.unmanaged.reIndexContext(self.allocator, self.ctx);
}
+ /// Sorts the entries and then rebuilds the index.
+ /// `sort_ctx` must have this method:
+ /// `fn lessThan(ctx: @TypeOf(ctx), a_index: usize, b_index: usize) bool`
+ pub fn sort(self: *Self, sort_ctx: anytype) void {
+ return self.unmanaged.sortContext(sort_ctx, self.ctx);
+ }
+
/// Shrinks the underlying `Entry` array to `new_len` elements and discards any associated
/// index entries. Keeps capacity the same.
pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
@@ -1169,6 +1176,22 @@ pub fn ArrayHashMapUnmanaged(
self.index_header = new_header;
}
+ /// Sorts the entries and then rebuilds the index.
+ /// `sort_ctx` must have this method:
+ /// `fn lessThan(ctx: @TypeOf(ctx), a_index: usize, b_index: usize) bool`
+ pub inline fn sort(self: *Self, sort_ctx: anytype) void {
+ if (@sizeOf(ByIndexContext) != 0)
+ @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call sortContext instead.");
+ return self.sortContext(sort_ctx, undefined);
+ }
+
+ pub fn sortContext(self: *Self, sort_ctx: anytype, ctx: Context) void {
+ self.entries.sort(sort_ctx);
+ const header = self.index_header orelse return;
+ header.reset();
+ self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, header);
+ }
+
/// Shrinks the underlying `Entry` array to `new_len` elements and discards any associated
/// index entries. Keeps capacity the same.
pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
@@ -1868,6 +1891,14 @@ const IndexHeader = struct {
allocator.free(slice);
}
+ /// Puts an IndexHeader into the state that it would be in after being freshly allocated.
+ fn reset(header: *IndexHeader) void {
+ const index_size = hash_map.capacityIndexSize(header.bit_index);
+ const ptr = @ptrCast([*]align(@alignOf(IndexHeader)) u8, header);
+ const nbytes = @sizeOf(IndexHeader) + header.length() * index_size;
+ @memset(ptr + @sizeOf(IndexHeader), 0xff, nbytes - @sizeOf(IndexHeader));
+ }
+
// Verify that the header has sufficient alignment to produce aligned arrays.
comptime {
if (@alignOf(u32) > @alignOf(IndexHeader))
@@ -2218,6 +2249,32 @@ test "auto store_hash" {
try testing.expect(meta.fieldInfo(HasExpensiveEqlUn.Data, .hash).field_type != void);
}
+test "sort" {
+ var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
+ defer map.deinit();
+
+ for ([_]i32{ 8, 3, 12, 10, 2, 4, 9, 5, 6, 13, 14, 15, 16, 1, 11, 17, 7 }) |x| {
+ try map.put(x, x * 3);
+ }
+
+ 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];
+ }
+ };
+
+ map.sort(C{ .keys = map.keys() });
+
+ var x: i32 = 1;
+ for (map.keys()) |key, i| {
+ try testing.expect(key == x);
+ try testing.expect(map.values()[i] == x * 3);
+ x += 1;
+ }
+}
+
pub fn getHashPtrAddrFn(comptime K: type, comptime Context: type) (fn (Context, K) u32) {
return struct {
fn hash(ctx: Context, key: K) u32 {