| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- const std = @import("std");
- const RecordRef = @import("keydir.zig").RecordRef;
- pub const Node = struct {
- key: []u8,
- inline_key: [32]u8 = undefined,
- external_key: bool,
- record: RecordRef,
- priority: u64,
- parent: ?*Node = null,
- left: ?*Node = null,
- right: ?*Node = null,
- };
- pub const Prepared = struct {
- node: ?*Node,
- };
- pub const OrderedIndex = struct {
- allocator: std.mem.Allocator,
- root: ?*Node = null,
- count: usize = 0,
- allocated_bytes: usize = 0,
- pub fn init(allocator: std.mem.Allocator) OrderedIndex {
- return .{ .allocator = allocator };
- }
- pub fn deinit(self: *OrderedIndex) void {
- while (self.root) |node| _ = self.remove(node.key);
- self.* = undefined;
- }
- fn priority(key: []const u8) u64 {
- var value: u64 = 14695981039346656037;
- for (key) |byte| {
- value ^= byte;
- value *%= 1099511628211;
- }
- value ^= value >> 30;
- value *%= 0xbf58476d1ce4e5b9;
- value ^= value >> 27;
- value *%= 0x94d049bb133111eb;
- return value ^ (value >> 31);
- }
- fn rotateLeft(self: *OrderedIndex, node: *Node) void {
- const child = node.right.?;
- node.right = child.left;
- if (child.left) |left| left.parent = node;
- child.parent = node.parent;
- if (node.parent) |parent| {
- if (parent.left == node) parent.left = child else parent.right = child;
- } else self.root = child;
- child.left = node;
- node.parent = child;
- }
- fn rotateRight(self: *OrderedIndex, node: *Node) void {
- const child = node.left.?;
- node.left = child.right;
- if (child.right) |right| right.parent = node;
- child.parent = node.parent;
- if (node.parent) |parent| {
- if (parent.left == node) parent.left = child else parent.right = child;
- } else self.root = child;
- child.right = node;
- node.parent = child;
- }
- pub fn put(self: *OrderedIndex, key: []const u8, record: RecordRef) !void {
- var parent: ?*Node = null;
- var current = self.root;
- var order: std.math.Order = .eq;
- while (current) |node| {
- order = std.mem.order(u8, key, node.key);
- if (order == .eq) {
- node.record = record;
- return;
- }
- parent = node;
- current = if (order == .lt) node.left else node.right;
- }
- var prepared = try self.prepare(key);
- const node = prepared.node.?;
- prepared.node = null;
- node.record = record;
- node.parent = parent;
- if (parent) |value| {
- if (order == .lt) value.left = node else value.right = node;
- } else self.root = node;
- self.count += 1;
- self.allocated_bytes += @sizeOf(Node) + if (node.external_key) key.len else 0;
- while (node.parent) |value| {
- if (value.priority <= node.priority) break;
- if (value.left == node) self.rotateRight(value) else self.rotateLeft(value);
- }
- }
- pub fn prepare(self: *OrderedIndex, key: []const u8) !Prepared {
- const node = try self.allocator.create(Node);
- errdefer self.allocator.destroy(node);
- node.* = .{ .key = undefined, .external_key = key.len > 32, .record = undefined, .priority = priority(key) };
- if (node.external_key) {
- node.key = try self.allocator.dupe(u8, key);
- } else {
- @memcpy(node.inline_key[0..key.len], key);
- node.key = node.inline_key[0..key.len];
- }
- return .{ .node = node };
- }
- pub fn discard(self: *OrderedIndex, prepared: *Prepared) void {
- const node = prepared.node orelse return;
- if (node.external_key) self.allocator.free(node.key);
- self.allocator.destroy(node);
- prepared.node = null;
- }
- pub fn putPrepared(self: *OrderedIndex, prepared: *Prepared, record: RecordRef) void {
- const node = prepared.node.?;
- var parent: ?*Node = null;
- var current = self.root;
- var order: std.math.Order = .eq;
- while (current) |existing| {
- order = std.mem.order(u8, node.key, existing.key);
- if (order == .eq) {
- existing.record = record;
- self.discard(prepared);
- return;
- }
- parent = existing;
- current = if (order == .lt) existing.left else existing.right;
- }
- prepared.node = null;
- node.record = record;
- node.parent = parent;
- if (parent) |value| {
- if (order == .lt) value.left = node else value.right = node;
- } else self.root = node;
- self.count += 1;
- self.allocated_bytes += @sizeOf(Node) + if (node.external_key) node.key.len else 0;
- while (node.parent) |value| {
- if (value.priority <= node.priority) break;
- if (value.left == node) self.rotateRight(value) else self.rotateLeft(value);
- }
- }
- pub fn remove(self: *OrderedIndex, key: []const u8) bool {
- const node = self.find(key) orelse return false;
- while (node.left != null or node.right != null) {
- if (node.left == null) {
- self.rotateLeft(node);
- } else if (node.right == null) {
- self.rotateRight(node);
- } else if (node.left.?.priority < node.right.?.priority) {
- self.rotateRight(node);
- } else {
- self.rotateLeft(node);
- }
- }
- if (node.parent) |parent| {
- if (parent.left == node) parent.left = null else parent.right = null;
- } else self.root = null;
- self.count -= 1;
- self.allocated_bytes -= @sizeOf(Node) + if (node.external_key) node.key.len else 0;
- if (node.external_key) self.allocator.free(node.key);
- self.allocator.destroy(node);
- return true;
- }
- pub fn find(self: *const OrderedIndex, key: []const u8) ?*Node {
- var current = self.root;
- while (current) |node| switch (std.mem.order(u8, key, node.key)) {
- .eq => return node,
- .lt => current = node.left,
- .gt => current = node.right,
- };
- return null;
- }
- pub fn lowerBound(self: *const OrderedIndex, key: []const u8) ?*Node {
- var current = self.root;
- var result: ?*Node = null;
- while (current) |node| {
- if (std.mem.order(u8, node.key, key) == .lt) {
- current = node.right;
- } else {
- result = node;
- current = node.left;
- }
- }
- return result;
- }
- pub fn next(node: *Node) ?*Node {
- if (node.right) |right| {
- var current = right;
- while (current.left) |left| current = left;
- return current;
- }
- var current = node;
- while (current.parent) |parent| {
- if (parent.left == current) return parent;
- current = parent;
- }
- return null;
- }
- pub fn records(self: *const OrderedIndex, allocator: std.mem.Allocator) ![]RecordRef {
- const result = try allocator.alloc(RecordRef, self.count);
- var node = self.lowerBound("");
- var index: usize = 0;
- while (node) |value| {
- result[index] = value.record;
- index += 1;
- node = next(value);
- }
- return result;
- }
- };
- test "ordered insert update remove and lower bound" {
- var index = OrderedIndex.init(std.testing.allocator);
- defer index.deinit();
- try index.put("b", .{ .hash = 2, .lsn = 1, .key_offset = 0, .value_offset = 0, .key_len = 1, .value_len = 0 });
- try index.put("a", .{ .hash = 1, .lsn = 1, .key_offset = 0, .value_offset = 0, .key_len = 1, .value_len = 0 });
- try index.put("c", .{ .hash = 3, .lsn = 1, .key_offset = 0, .value_offset = 0, .key_len = 1, .value_len = 0 });
- try index.put("b", .{ .hash = 2, .lsn = 2, .key_offset = 0, .value_offset = 0, .key_len = 1, .value_len = 0 });
- try std.testing.expectEqual(@as(usize, 3), index.count);
- try std.testing.expectEqualStrings("b", index.lowerBound("az").?.key);
- try std.testing.expectEqual(@as(u64, 2), index.find("b").?.record.lsn);
- try std.testing.expect(index.remove("b"));
- try std.testing.expectEqualStrings("c", index.lowerBound("b").?.key);
- try std.testing.expectEqual(@as(usize, 2), index.count);
- }
|