|
@@ -4,27 +4,67 @@ const index = @import("index.zig");
|
|
|
const hashing = @import("hashing.zig");
|
|
const hashing = @import("hashing.zig");
|
|
|
const persistence = @import("persistence.zig");
|
|
const persistence = @import("persistence.zig");
|
|
|
|
|
|
|
|
-const MAX_RECORDS = 10_000_000;
|
|
|
|
|
|
|
+const INITIAL_BUCKETS = 1_048_576;
|
|
|
const Entry = struct {
|
|
const Entry = struct {
|
|
|
key: []const u8,
|
|
key: []const u8,
|
|
|
value: []const u8,
|
|
value: []const u8,
|
|
|
|
|
+ hash: u32,
|
|
|
next: ?*Entry,
|
|
next: ?*Entry,
|
|
|
};
|
|
};
|
|
|
-var buf: [MAX_RECORDS]?*Entry = undefined;
|
|
|
|
|
|
|
+
|
|
|
|
|
+var buckets: []?*Entry = undefined;
|
|
|
|
|
+var buckets_initialized: bool = false;
|
|
|
|
|
|
|
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
|
const allocator = arena.allocator();
|
|
const allocator = arena.allocator();
|
|
|
|
|
|
|
|
-const EMPTY = "";
|
|
|
|
|
-var mutex: std.Thread.Mutex = .{};
|
|
|
|
|
|
|
+var rwlock: std.Thread.RwLock = .{};
|
|
|
|
|
+var init_mutex: std.Thread.Mutex = .{};
|
|
|
|
|
+
|
|
|
|
|
+pub fn init() void {
|
|
|
|
|
+ if (buckets_initialized) return;
|
|
|
|
|
+
|
|
|
|
|
+ init_mutex.lock();
|
|
|
|
|
+ defer init_mutex.unlock();
|
|
|
|
|
+
|
|
|
|
|
+ if (!buckets_initialized) {
|
|
|
|
|
+ buckets = allocator.alloc(?*Entry, INITIAL_BUCKETS) catch unreachable;
|
|
|
|
|
+ @memset(buckets, null);
|
|
|
|
|
+ buckets_initialized = true;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+pub fn restore(key: []const u8, value: []const u8) bool {
|
|
|
|
|
+ rwlock.lock();
|
|
|
|
|
+ defer rwlock.unlock();
|
|
|
|
|
+
|
|
|
|
|
+ const entry = writeVolatile(key, value);
|
|
|
|
|
+ if (entry != null) {
|
|
|
|
|
+ index.insert(entry.?.key);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+pub fn restoreDelete(key: []const u8) bool {
|
|
|
|
|
+ rwlock.lock();
|
|
|
|
|
+ defer rwlock.unlock();
|
|
|
|
|
+
|
|
|
|
|
+ const deleted = deleteVolatile(key);
|
|
|
|
|
+ if (deleted) {
|
|
|
|
|
+ index.delete(key);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
pub fn writeVolatile(key: []const u8, value: []const u8) ?*Entry {
|
|
pub fn writeVolatile(key: []const u8, value: []const u8) ?*Entry {
|
|
|
const hash = hashing.hashKey(key);
|
|
const hash = hashing.hashKey(key);
|
|
|
- const bufIdx = hash % buf.len;
|
|
|
|
|
|
|
+ const bucketIdx = hash % buckets.len;
|
|
|
|
|
|
|
|
- var current = buf[bufIdx];
|
|
|
|
|
|
|
+ var current = buckets[bucketIdx];
|
|
|
while (current) |entry| {
|
|
while (current) |entry| {
|
|
|
- if (std.mem.eql(u8, entry.key, key)) {
|
|
|
|
|
|
|
+ if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
|
|
|
allocator.free(entry.value);
|
|
allocator.free(entry.value);
|
|
|
entry.value = allocator.dupe(u8, value) catch return null;
|
|
entry.value = allocator.dupe(u8, value) catch return null;
|
|
|
return entry;
|
|
return entry;
|
|
@@ -38,35 +78,38 @@ pub fn writeVolatile(key: []const u8, value: []const u8) ?*Entry {
|
|
|
newEntry.* = Entry{
|
|
newEntry.* = Entry{
|
|
|
.key = allocator.dupe(u8, key) catch return null,
|
|
.key = allocator.dupe(u8, key) catch return null,
|
|
|
.value = allocator.dupe(u8, value) catch return null,
|
|
.value = allocator.dupe(u8, value) catch return null,
|
|
|
- .next = buf[bufIdx],
|
|
|
|
|
|
|
+ .hash = hash, // Cache hash value
|
|
|
|
|
+ .next = buckets[bucketIdx],
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- buf[bufIdx] = newEntry;
|
|
|
|
|
- index.insert(key);
|
|
|
|
|
|
|
+ buckets[bucketIdx] = newEntry;
|
|
|
return newEntry;
|
|
return newEntry;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub fn write(key: []const u8, value: []const u8) bool {
|
|
pub fn write(key: []const u8, value: []const u8) bool {
|
|
|
- mutex.lock();
|
|
|
|
|
- defer mutex.unlock();
|
|
|
|
|
|
|
+ rwlock.lock();
|
|
|
|
|
+ defer rwlock.unlock();
|
|
|
|
|
|
|
|
const entry = writeVolatile(key, value);
|
|
const entry = writeVolatile(key, value);
|
|
|
if (entry == null) {
|
|
if (entry == null) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ index.insert(entry.?.key);
|
|
|
persistence.persist('W', entry.?.key, entry.?.value);
|
|
persistence.persist('W', entry.?.key, entry.?.value);
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub fn read(key: []const u8) ?[]const u8 {
|
|
pub fn read(key: []const u8) ?[]const u8 {
|
|
|
- mutex.lock();
|
|
|
|
|
- defer mutex.unlock();
|
|
|
|
|
|
|
+ rwlock.lockShared();
|
|
|
|
|
+ defer rwlock.unlockShared();
|
|
|
|
|
+
|
|
|
|
|
+ if (!buckets_initialized) return null;
|
|
|
|
|
|
|
|
const hash = hashing.hashKey(key);
|
|
const hash = hashing.hashKey(key);
|
|
|
- var current = buf[hash % buf.len];
|
|
|
|
|
|
|
+ var current = buckets[hash % buckets.len];
|
|
|
while (current) |entry| {
|
|
while (current) |entry| {
|
|
|
- if (std.mem.eql(u8, entry.key, key)) {
|
|
|
|
|
|
|
+ if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
|
|
|
return entry.value;
|
|
return entry.value;
|
|
|
}
|
|
}
|
|
|
current = entry.next;
|
|
current = entry.next;
|
|
@@ -76,21 +119,21 @@ pub fn read(key: []const u8) ?[]const u8 {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub fn deleteVolatile(key: []const u8) bool {
|
|
pub fn deleteVolatile(key: []const u8) bool {
|
|
|
|
|
+ if (!buckets_initialized) return false;
|
|
|
const hash = hashing.hashKey(key);
|
|
const hash = hashing.hashKey(key);
|
|
|
- const bufIdx = hash % buf.len;
|
|
|
|
|
|
|
+ const bucketIdx = hash % buckets.len;
|
|
|
|
|
|
|
|
- var current = buf[bufIdx];
|
|
|
|
|
|
|
+ var current = buckets[bucketIdx];
|
|
|
var prev: ?*Entry = null;
|
|
var prev: ?*Entry = null;
|
|
|
|
|
|
|
|
while (current) |entry| {
|
|
while (current) |entry| {
|
|
|
- if (std.mem.eql(u8, entry.key, key)) {
|
|
|
|
|
|
|
+ if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
|
|
|
if (prev) |p| {
|
|
if (prev) |p| {
|
|
|
p.next = entry.next;
|
|
p.next = entry.next;
|
|
|
} else {
|
|
} else {
|
|
|
- buf[bufIdx] = entry.next;
|
|
|
|
|
|
|
+ buckets[bucketIdx] = entry.next;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- index.delete(key);
|
|
|
|
|
allocator.free(entry.key);
|
|
allocator.free(entry.key);
|
|
|
allocator.free(entry.value);
|
|
allocator.free(entry.value);
|
|
|
allocator.destroy(entry);
|
|
allocator.destroy(entry);
|
|
@@ -105,11 +148,12 @@ pub fn deleteVolatile(key: []const u8) bool {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub fn delete(key: []const u8) bool {
|
|
pub fn delete(key: []const u8) bool {
|
|
|
- mutex.lock();
|
|
|
|
|
- defer mutex.unlock();
|
|
|
|
|
|
|
+ rwlock.lock();
|
|
|
|
|
+ defer rwlock.unlock();
|
|
|
|
|
|
|
|
const deleted = deleteVolatile(key);
|
|
const deleted = deleteVolatile(key);
|
|
|
if (deleted) {
|
|
if (deleted) {
|
|
|
|
|
+ index.delete(key);
|
|
|
persistence.persist('D', key, "");
|
|
persistence.persist('D', key, "");
|
|
|
}
|
|
}
|
|
|
|
|
|