106 lines
2.9 KiB
Zig
106 lines
2.9 KiB
Zig
const std = @import("std");
|
|
|
|
pub const LRU = struct {
|
|
allocator: std.mem.Allocator,
|
|
map: std.StringHashMap(Entry),
|
|
max_entries: usize,
|
|
|
|
const Entry = struct {
|
|
value: []const u8,
|
|
expires: i64,
|
|
access_count: u64,
|
|
};
|
|
|
|
pub fn init(allocator: std.mem.Allocator, max_entries: usize) !LRU {
|
|
return LRU{
|
|
.allocator = allocator,
|
|
.map = std.StringHashMap(Entry).init(allocator),
|
|
.max_entries = max_entries,
|
|
};
|
|
}
|
|
|
|
pub fn get(self: *LRU, key: []const u8) ?[]const u8 {
|
|
var entry = self.map.getPtr(key) orelse return null;
|
|
|
|
const now = std.time.milliTimestamp();
|
|
if (now > entry.expires) {
|
|
self.remove(key);
|
|
return null;
|
|
}
|
|
|
|
entry.access_count += 1;
|
|
return entry.value;
|
|
}
|
|
|
|
pub fn put(self: *LRU, key: []const u8, value: []const u8, expires: i64) !void {
|
|
if (self.map.get(key)) |old_entry| {
|
|
self.allocator.free(old_entry.value);
|
|
_ = self.map.remove(key);
|
|
}
|
|
|
|
if (self.map.count() >= self.max_entries) {
|
|
self.evictOldest();
|
|
}
|
|
|
|
const key_copy = try self.allocator.dupe(u8, key);
|
|
const value_copy = try self.allocator.dupe(u8, value);
|
|
|
|
try self.map.put(key_copy, .{
|
|
.value = value_copy,
|
|
.expires = expires,
|
|
.access_count = 0,
|
|
});
|
|
}
|
|
|
|
fn evictOldest(self: *LRU) void {
|
|
var oldest_key: ?[]const u8 = null;
|
|
var oldest_access: u64 = std.math.maxInt(u64);
|
|
|
|
var it = self.map.iterator();
|
|
while (it.next()) |entry| {
|
|
if (entry.value_ptr.access_count < oldest_access) {
|
|
oldest_access = entry.value_ptr.access_count;
|
|
oldest_key = entry.key_ptr.*;
|
|
}
|
|
}
|
|
|
|
if (oldest_key) |key| {
|
|
self.remove(key);
|
|
}
|
|
}
|
|
|
|
fn remove(self: *LRU, key: []const u8) void {
|
|
if (self.map.fetchRemove(key)) |kv| {
|
|
self.allocator.free(kv.value.value);
|
|
self.allocator.free(kv.key);
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *LRU) void {
|
|
var it = self.map.iterator();
|
|
while (it.next()) |entry| {
|
|
self.allocator.free(entry.value_ptr.value);
|
|
self.allocator.free(entry.key_ptr.*);
|
|
}
|
|
self.map.deinit();
|
|
}
|
|
};
|
|
|
|
test "LRU basic operations" {
|
|
var lru = try LRU.init(std.testing.allocator, 3);
|
|
defer lru.deinit();
|
|
|
|
try lru.put("key1", "value1", 9999999999999);
|
|
try std.testing.expectEqualStrings("value1", lru.get("key1").?);
|
|
}
|
|
|
|
test "LRU eviction" {
|
|
var lru = try LRU.init(std.testing.allocator, 2);
|
|
defer lru.deinit();
|
|
|
|
try lru.put("key1", "value1", 9999999999999);
|
|
try lru.put("key2", "value2", 9999999999999);
|
|
try lru.put("key3", "value3", 9999999999999);
|
|
|
|
try std.testing.expect(lru.get("key1") == null);
|
|
}
|