2
0

storage.zig 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. const std = @import("std");
  2. const index = @import("index.zig");
  3. const hashing = @import("hashing.zig");
  4. const persistence = @import("persistence.zig");
  5. const NUM_SHARDS = 64;
  6. const TOTAL_BUCKETS = 1_048_576;
  7. const BUCKETS_PER_SHARD = TOTAL_BUCKETS / NUM_SHARDS;
  8. const Entry = struct {
  9. key: []const u8,
  10. value: []const u8,
  11. hash: u32,
  12. next: ?*Entry,
  13. };
  14. const Shard = struct {
  15. buckets: []?*Entry,
  16. rwlock: std.Thread.RwLock,
  17. arena: std.heap.ArenaAllocator,
  18. allocator: std.mem.Allocator,
  19. };
  20. var shards: [NUM_SHARDS]Shard = undefined;
  21. var shards_initialized: bool = false;
  22. var init_mutex: std.Thread.Mutex = .{};
  23. fn getShardIndex(hash: u32) usize {
  24. return hash % NUM_SHARDS;
  25. }
  26. pub fn init() void {
  27. if (shards_initialized) return;
  28. init_mutex.lock();
  29. defer init_mutex.unlock();
  30. if (!shards_initialized) {
  31. for (&shards) |*shard| {
  32. shard.arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
  33. shard.allocator = shard.arena.allocator();
  34. shard.buckets = shard.allocator.alloc(?*Entry, BUCKETS_PER_SHARD) catch unreachable;
  35. @memset(shard.buckets, null);
  36. shard.rwlock = .{};
  37. }
  38. shards_initialized = true;
  39. }
  40. }
  41. pub fn restore(key: []const u8, value: []const u8) bool {
  42. const hash = hashing.hashKey(key);
  43. const shard_idx = getShardIndex(hash);
  44. var entry_key: []const u8 = undefined;
  45. {
  46. shards[shard_idx].rwlock.lock();
  47. defer shards[shard_idx].rwlock.unlock();
  48. const entry = writeVolatile(hash, key, value) orelse return false;
  49. entry_key = entry.key;
  50. }
  51. index.insert(entry_key);
  52. return true;
  53. }
  54. pub fn restoreDelete(key: []const u8) bool {
  55. const hash = hashing.hashKey(key);
  56. const shard_idx = getShardIndex(hash);
  57. const deleted = blk: {
  58. shards[shard_idx].rwlock.lock();
  59. defer shards[shard_idx].rwlock.unlock();
  60. break :blk deleteVolatile(hash, key);
  61. };
  62. if (deleted) {
  63. index.delete(key);
  64. return true;
  65. }
  66. return false;
  67. }
  68. pub fn writeVolatile(hash: u32, key: []const u8, value: []const u8) ?*Entry {
  69. const shard_idx = getShardIndex(hash);
  70. const bucketIdx = hash % shards[shard_idx].buckets.len;
  71. const alloc = shards[shard_idx].allocator;
  72. var current = shards[shard_idx].buckets[bucketIdx];
  73. while (current) |entry| {
  74. if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
  75. alloc.free(entry.value);
  76. entry.value = alloc.dupe(u8, value) catch return null;
  77. return entry;
  78. }
  79. current = entry.next;
  80. }
  81. const newEntry = alloc.create(Entry) catch return null;
  82. errdefer alloc.destroy(newEntry);
  83. newEntry.* = Entry{
  84. .key = alloc.dupe(u8, key) catch return null,
  85. .value = alloc.dupe(u8, value) catch return null,
  86. .hash = hash, // Cache hash value
  87. .next = shards[shard_idx].buckets[bucketIdx],
  88. };
  89. shards[shard_idx].buckets[bucketIdx] = newEntry;
  90. return newEntry;
  91. }
  92. pub fn write(key: []const u8, value: []const u8) bool {
  93. const hash = hashing.hashKey(key);
  94. const shard_idx = getShardIndex(hash);
  95. var entry_key: []const u8 = undefined;
  96. var entry_value: []const u8 = undefined;
  97. {
  98. shards[shard_idx].rwlock.lock();
  99. defer shards[shard_idx].rwlock.unlock();
  100. const entry = writeVolatile(hash, key, value) orelse return false;
  101. entry_key = entry.key;
  102. entry_value = entry.value;
  103. }
  104. index.insert(entry_key);
  105. persistence.persist('W', entry_key, entry_value);
  106. return true;
  107. }
  108. pub fn read(key: []const u8) ?[]const u8 {
  109. if (!shards_initialized) return null;
  110. const hash = hashing.hashKey(key);
  111. const shard_idx = getShardIndex(hash);
  112. shards[shard_idx].rwlock.lockShared();
  113. defer shards[shard_idx].rwlock.unlockShared();
  114. var current = shards[shard_idx].buckets[hash % shards[shard_idx].buckets.len];
  115. while (current) |entry| {
  116. if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
  117. return entry.value;
  118. }
  119. current = entry.next;
  120. }
  121. return null;
  122. }
  123. pub fn deleteVolatile(hash: u32, key: []const u8) bool {
  124. if (!shards_initialized) return false;
  125. const shard_idx = getShardIndex(hash);
  126. const bucketIdx = hash % shards[shard_idx].buckets.len;
  127. const alloc = shards[shard_idx].allocator;
  128. var current = shards[shard_idx].buckets[bucketIdx];
  129. var prev: ?*Entry = null;
  130. while (current) |entry| {
  131. if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
  132. if (prev) |p| {
  133. p.next = entry.next;
  134. } else {
  135. shards[shard_idx].buckets[bucketIdx] = entry.next;
  136. }
  137. alloc.free(entry.key);
  138. alloc.free(entry.value);
  139. alloc.destroy(entry);
  140. return true;
  141. }
  142. prev = entry;
  143. current = entry.next;
  144. }
  145. return false;
  146. }
  147. pub fn delete(key: []const u8) bool {
  148. const hash = hashing.hashKey(key);
  149. const shard_idx = getShardIndex(hash);
  150. const deleted = blk: {
  151. shards[shard_idx].rwlock.lock();
  152. defer shards[shard_idx].rwlock.unlock();
  153. break :blk deleteVolatile(hash, key);
  154. };
  155. if (deleted) {
  156. index.delete(key);
  157. persistence.persist('D', key, "");
  158. }
  159. return deleted;
  160. }