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); }