diff --git a/example/build.zig.zon b/example/build.zig.zon index 0e20237..99cf357 100644 --- a/example/build.zig.zon +++ b/example/build.zig.zon @@ -24,8 +24,8 @@ // internet connectivity. .dependencies = .{ .@"boltdb-zig" = .{ - .url = "git+https://github.com/laohanlinux/boltdb-zig.git/?ref=align#f2ae8cd9a8d4396fec3fbbed3adbd9a14e0fa1a3", - .hash = "12203cc7a802ae6cd01c6b856ff96ddfe26cb9612533ca116a0e30178d5251e173bd", + .url = "git+https://github.com/laohanlinux/boltdb-zig.git/?ref=align#3ec605eca6a9f85959f81900542fea59cc3eaef6", + .hash = "122056bd20aba7365380a500c315f4a03aa5ea2134685b11fa7e32e312344c28175c", }, }, .paths = .{ diff --git a/src/bucket.zig b/src/bucket.zig index b659f5e..ce4c5d9 100644 --- a/src/bucket.zig +++ b/src/bucket.zig @@ -19,8 +19,6 @@ const NodeSet = std.AutoHashMap(*Node, void); // A set of aligned values that will be freed by the bucket. pub const AutoFreeObject = struct { isFreed: bool = false, - // A set of bytes that will be freed by the bucket. - autoFreeBytes: std.AutoHashMap(u64, []u8), // A set of nodes that will be freed by the bucket. // 1: Note, the bucket.nodes is not in the autoFreeObject, so we need to destroy it manually. // But the bucket.rootNode is in the autoFreeObject, so we don't need to destroy it manually. @@ -28,7 +26,6 @@ pub const AutoFreeObject = struct { // So, we need to destroy it manually. // 2: the nodes of autoFreeNodes is a new node that created after tx.commit(Copy on Write), their are is a spill node, a snapshot node, a new node. autoFreeNodes: NodeSet, - freePtrs: std.AutoArrayHashMap(u64, isize), allocSize: usize = 0, allocator: std.mem.Allocator, @@ -37,40 +34,15 @@ pub const AutoFreeObject = struct { return .{ .autoFreeNodes = NodeSet.init(allocator), .allocator = allocator, - .freePtrs = std.AutoArrayHashMap(u64, isize).init(allocator), - .autoFreeBytes = std.AutoHashMap(u64, []u8).init(allocator), }; } /// Add a node to the auto free object. pub fn addNode(self: *AutoFreeObject, node: *Node) void { - const key = node.key orelse ""; self.allocSize += node.size(); const gop = self.autoFreeNodes.getOrPut(node) catch unreachable; const ptr = @intFromPtr(node); - // assert(gop.found_existing == false, "the node({}: 0x{x}, {d}) is already in the auto free nodes", .{ node.pgid, ptr, node.id }); - if (gop.found_existing) { - std.log.debug("the node({s}, {}: 0x{x}, {d}) is already in the auto free nodes", .{ key, node.pgid, ptr, node.id }); - } - std.log.info("add node to the auto free nodes, key: {s}, pgid: {d}, ptr: 0x{x}, id: {d}, allocSize: {d}", .{ key, node.pgid, ptr, node.id, self.allocSize }); - } - - /// Add a byte slice to the auto free object. - pub fn addAutoFreeBytes(self: *AutoFreeObject, value: []u8) void { - const ptr = @intFromPtr(value.ptr); - const got = self.autoFreeBytes.getOrPut(ptr) catch unreachable; - if (got.found_existing) { - // std.log.debug("the auto free bytes({}: 0x{x}) is already in the auto free bytes", .{ value.len, ptr }); - } else { - got.value_ptr.* = value; - self.allocSize += value.len; - // std.log.info("add auto free bytes, size: {d}, ptr: 0x{x}, allocSize: {d}", .{ value.len, ptr, self.allocSize }); - } - } - - /// Get the alloc size. - pub fn getAllocSize(self: *AutoFreeObject) usize { - return self.allocSize; + assert(gop.found_existing == false, "the node({}: 0x{x}, {d}) is already in the auto free nodes", .{ node.pgid, ptr, node.id }); } /// Deinit the auto free object. @@ -80,14 +52,10 @@ pub const AutoFreeObject = struct { { var it = self.autoFreeNodes.keyIterator(); while (it.next()) |node| { - const ptr = @intFromPtr(node); node.*.deinit(); - self.freePtrs.put(ptr, 1) catch unreachable; } self.autoFreeNodes.deinit(); } - - self.freePtrs.deinit(); } }; diff --git a/src/cursor.zig b/src/cursor.zig index 85e5a48..ad406a0 100644 --- a/src/cursor.zig +++ b/src/cursor.zig @@ -372,7 +372,7 @@ pub const Cursor = struct { // // _ = printNodes; // printNodes(n); assert(n.inodes.items.len > 0, "the node is empty", .{}); - var indexRef = n.searchInodes2(key); + var indexRef = n.searchInodes(key); if (!indexRef.exact) { indexRef.index -= 1; } diff --git a/src/node.zig b/src/node.zig index 41fbee8..78afdc5 100644 --- a/src/node.zig +++ b/src/node.zig @@ -253,7 +253,7 @@ pub const Node = struct { pub fn del(self: *Self, key: []const u8) ?usize { // std.log.debug("del key: {any} at node: {d}", .{ key, self.pgid }); // Find index of key. - const indexRef = self.searchInodes2(key); + const indexRef = self.searchInodes(key); var inode = self.inodes.orderedRemove(indexRef.index); assert(indexRef.exact, "the key is not found, key: {s}, index: {d}, node len: {d}, node key: {s}", .{ key, indexRef.index, self.inodes.items.len, inode.key.? }); // free the inode @@ -431,7 +431,7 @@ pub const Node = struct { self.parent = Node.init(self.getAllocator()); self.parent.?.bucket = self.bucket; self.parent.?.children.append(self) catch unreachable; // children also is you! - self.bucket.?.tx.?.autoFreeNodes.?.addNode(self.parent.?); + // self.bucket.?.tx.?.autoFreeNodes.?.addNode(self.parent.?); } // Create a new node and add it to the parent. @@ -439,7 +439,6 @@ pub const Node = struct { next.bucket = self.bucket; next.isLeaf = self.isLeaf; next.parent = self.parent; - // TODO: maybe here is a bug self.parent.?.children.append(next) catch unreachable; next.inodes.ensureTotalCapacity(self.inodes.items.len - _splitIndex) catch unreachable; next.inodes.appendSlice(self.inodes.items[_splitIndex..]) catch unreachable; @@ -506,20 +505,18 @@ pub const Node = struct { // Spill child nodes first. Child nodes can materialize sibling nodes in // the case of split-merge so we cannot use a range loop. We have to check // the children size on every loop iteration. - const lessFn = struct { - fn less(_: void, a: *Node, b: *Node) bool { - return std.mem.order(u8, a.key.?, b.key.?) == .lt; - } - }.less; std.mem.sort( *Node, self.children.items, {}, - lessFn, + struct { + fn lessFn(_: void, a: *Node, b: *Node) bool { + return std.mem.order(u8, a.key.?, b.key.?) == .lt; + } + }.lessFn, ); for (0..self.children.items.len) |i| { const child = self.children.items[i]; - // std.log.debug("spill child node: {d}, index: {d}", .{ child.pgid, i }); try child.spill(); } // We no longer need the children list because it's only used for spilling tracking. @@ -530,11 +527,9 @@ pub const Node = struct { defer nodes.deinit(); // log.debug("pgid: {d}, nodeid: 0x{x}, nodes size: {d}, key: {s}", .{ self.pgid, self.nodePtrInt(), nodes.items.len, self.key orelse "empty" }); for (nodes.items) |node| { - // log.debug("spill node: {d}, count:{}, index: {d}", .{ node.pgid, nodes.items.len, i }); // Add node's page to the freelist if it's not new. // (it is the first one, because split node from left to right!) if (node.pgid > 0) { - // log.debug("free a page to freelist, pgid: {}", .{node.pgid}); try _db.freelist.free(_tx.meta.txid, _tx.getPage(node.pgid)); // reset the pgid to 0, so the node will be a new node. node.pgid = 0; @@ -562,21 +557,18 @@ pub const Node = struct { // Update the statistics. _tx.stats.spill += 1; } - // self.safeCheck(); // If the root node split and created a new root then we need to spill that // as well. We'll clear out the children to make sure it doesn't try to respill. if (self.parent != null and self.parent.?.pgid == 0) { self.children.clearAndFree(); return self.parent.?.spill(); } - // log.debug("Try to spill parent, pgid: {d}", .{self.pgid}); } /// Attempts to combine the node with sibling nodes if the node fill /// size is below a threshold or if there are not enough keys. pub fn rebalance(self: *Self) void { if (!self.unbalance) { - // std.log.debug("i has rebalance, pgid: {}", .{self.pgid}); return; } self.unbalance = false; @@ -588,7 +580,6 @@ pub const Node = struct { // Ignore if node is above threshold (25%) and has enough keys. const threshold = self.bucket.?.tx.?.db.?.pageSize / 4; if (self.size() > threshold and self.inodes.items.len > self.minKeys()) { - // std.log.debug("the node size is too large, so don't rebalance: {d}", .{self.pgid}); return; } @@ -629,10 +620,7 @@ pub const Node = struct { } // std.log.debug("nothing need to rebalance at root: {d}, key={s}, isLeaf: {}, inodes len: {d}", .{ self.pgid, self.key orelse "empty", self.isLeaf, self.inodes.items.len }); return; - } else { - // std.log.debug("the node parent is not null, so rebalance: {d}, parent: {d}", .{ self.pgid, self.parent.?.pgid }); } - // If node has no keys then just remove it. if (self.numChildren() == 0) { // remove self from parent reference. @@ -643,13 +631,9 @@ pub const Node = struct { const exists = self.bucket.?.nodes.?.remove(self.pgid); assert(exists, "rebalance: node({d}) not found in nodes map", .{self.pgid}); // free reference page to db. - const oldPgid = self.pgid; self.free(); // continue reblance parent. self.parent.?.rebalance(); - // destroy self. - const key = if (self.key == null) "" else self.key.?; - std.log.info("destroy self, key={any}, id: {d}, pgid: {d}, ptr: 0x{x}, keyPtr: 0x{x}, parentPtr: 0x{x}, parentInodesLen: {d}", .{ key, self.id, oldPgid, self.nodePtrInt(), @intFromPtr(self.key.?.ptr), self.parent.?.nodePtrInt(), self.parent.?.inodes.items.len }); self.deinitAndDestroy(); return; } @@ -710,7 +694,6 @@ pub const Node = struct { fn removeChild(self: *Self, target: *Node) void { for (self.children.items, 0..) |child, i| { if (child == target) { - // TODO mybey we should check the child is in the children list. const childNode = self.children.orderedRemove(i); assert(childNode.nodePtrInt() == target.nodePtrInt(), "the child is not in the children list", .{}); return; @@ -723,20 +706,17 @@ pub const Node = struct { pub fn dereference(self: *Self) void { if (self.key != null) { const cpKey = self.arenaAllocator.allocator().dupe(u8, self.key.?) catch unreachable; - // self.allocator.free(self.key.?); self.key = cpKey; assert(self.pgid == 0 or self.key != null and self.key.?.len > 0, "deference: zero-length node key on existing node", .{}); } for (self.inodes.items) |*inode| { const newKey = self.arenaAllocator.allocator().dupe(u8, inode.key.?) catch unreachable; - // self.allocator.free(inode.key.?); inode.key = newKey; assert(inode.key != null and inode.key.?.len > 0, "deference: zero-length inode key on existing node", .{}); // If the value is not null if (inode.value) |value| { const newValue = self.arenaAllocator.allocator().dupe(u8, value) catch unreachable; - // self.allocator.free(value); inode.value = newValue; assert(inode.value != null and inode.value.?.len > 0, "deference: zero-length inode value on existing node", .{}); } @@ -766,64 +746,13 @@ pub const Node = struct { /// get the allocator of the node pub fn getAllocator(self: *Self) std.mem.Allocator { - // std.log.err("arena allocator capacity: {d}", .{self.arenaAllocator.queryCapacity()}); return self.arenaAllocator.allocator(); } - /// binary search the key in the inodes - pub fn binarySearchInodes(self: *const Self, key: []const u8) ?usize { - const findFn = struct { - fn find(context: []const u8, item: INode) std.math.Order { - return std.mem.order(u8, context, item.key.?); - } - }.find; - return std.sort.binarySearch(INode, self.inodes.items, key, findFn); - } - - /// Returns the index of the first element in `items` that is greater than or equal to `context`, - /// if no such element exists, returns `items.len`. - pub fn lowerBoundInodes(self: *const Self, key: []const u8) usize { - const lowerBoundFn = struct { - fn lower(context: []const u8, item: INode) std.math.Order { - return std.mem.order(u8, context, item.key.?); - } - }.lower; - return std.sort.lowerBound(INode, self.inodes.items, key, lowerBoundFn); - } - - /// Returns the index of the first element in `items` that is greater than `context`, - /// if no such element exists, returns `items.len`. - pub fn upperBoundInodes(self: *const Self, key: []const u8) usize { - const upperBoundFn = struct { - fn upper(context: []const u8, item: INode) std.math.Order { - return std.mem.order(u8, context, item.key.?); - } - }.upper; - return std.sort.upperBound(INode, self.inodes.items, key, upperBoundFn); - } - /// search the key in the inodes, if found, return the index and exact, if not found, return the position of the first element that is greater than the key pub fn searchInodes(self: *const Self, key: []const u8) struct { index: usize, exact: bool } { var left: usize = 0; var right: usize = self.inodes.items.len; - while (left < right) { - const mid = left + (right - left) / 2; - const element = self.inodes.items[mid]; - const cmp = std.mem.order(u8, element.key.?, key); - switch (cmp) { - .eq => return .{ .index = mid, .exact = true }, - .lt => left = mid + 1, - .gt => right = mid, - } - } - return .{ .index = left, .exact = false }; - } - - /// search the key in the inodes, if found, return the index and exact, if not found, return the position of the first element that is greater than the key - pub fn searchInodes2(self: *const Self, key: []const u8) struct { index: usize, exact: bool } { - var left: usize = 0; - var right: usize = self.inodes.items.len; - while (left < right) { const mid = left + (right - left) / 2; const cmp = std.mem.order(u8, key, self.inodes.items[mid].key.?); diff --git a/src/root.zig b/src/root.zig index 63bc5ba..a76aef2 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1,20 +1,20 @@ -const std = @import("std"); -const testing = std.testing; - -export fn add(a: i32, b: i32) i32 { - return a + b; -} - -test "basic add functionality" { - std.testing.log_level = .debug; - std.log.warn("run test", .{}); - try testing.expect(add(3, 7) == 10); -} - -test { - _ = @import("cursor_test.zig"); - _ = @import("node_test.zig"); - _ = @import("bucket_test.zig"); - _ = @import("tx_test.zig"); - _ = @import("page.zig"); -} +const std = @import("std"); +const testing = std.testing; + +export fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "basic add functionality" { + std.testing.log_level = .debug; + std.log.warn("run test", .{}); + try testing.expect(add(3, 7) == 10); +} + +test { + _ = @import("cursor_test.zig"); + _ = @import("node_test.zig"); + _ = @import("bucket_test.zig"); + _ = @import("tx_test.zig"); + _ = @import("page.zig"); +}