2
0

wal.zig 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. const std = @import("std");
  2. var wal_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
  3. const wal_allocator = wal_arena.allocator();
  4. pub fn compactUPKVFile(filename: []const u8) !void {
  5. const extension = std.fs.path.extension(filename);
  6. if (!std.mem.eql(u8, extension, "upkv")) {
  7. std.debug.print("Invalid file extension: {s}\n", .{extension});
  8. return;
  9. }
  10. const records = try getRecordsFromFile(filename);
  11. const compactedRecords = compact(records);
  12. for (compactedRecords) |record| {
  13. std.debug.print("{s}\n", .{record});
  14. }
  15. }
  16. test "compactUPKVFile test" {}
  17. pub fn getRecordsFromFile(filename: []const u8) ![]const []const u8 {
  18. const cwd = std.fs.cwd();
  19. var upkv: ?std.fs.File = null;
  20. upkv = cwd.openFile(filename, .{ .mode = .read_only }) catch |err| {
  21. std.debug.print("Failed to open file: {any}\n", .{err});
  22. return err;
  23. };
  24. const upkvData = upkv.?.readToEndAlloc(wal_allocator, 10_000_000 * 100) catch |err| {
  25. std.debug.print("Failed to read file: {any}\n", .{err});
  26. return err;
  27. };
  28. var records = std.ArrayListUnmanaged([]const u8){};
  29. var it = std.mem.splitScalar(u8, upkvData, '\r');
  30. while (it.next()) |record| {
  31. if (record.len > 0) {
  32. try records.append(wal_allocator, record);
  33. }
  34. }
  35. return records.items;
  36. }
  37. test "getRecordsFromFile test" {
  38. var tmp_dir = std.testing.tmpDir(.{});
  39. defer tmp_dir.cleanup();
  40. const file = try tmp_dir.dir.createFile("test.upkv", .{});
  41. defer file.close();
  42. try file.writeAll("W|key1|value1\rD|key2|\rW|key3|value3");
  43. var path_buf: [std.fs.max_path_bytes]u8 = undefined;
  44. const tmp_path = try tmp_dir.dir.realpath(".", &path_buf);
  45. const full_path = try std.fmt.allocPrint(std.testing.allocator, "{s}/test.upkv", .{tmp_path});
  46. defer std.testing.allocator.free(full_path);
  47. const records = try getRecordsFromFile(full_path);
  48. try std.testing.expect(records.len == 3);
  49. try std.testing.expectEqualStrings("W|key1|value1", records[0]);
  50. try std.testing.expectEqualStrings("D|key2|", records[1]);
  51. try std.testing.expectEqualStrings("W|key3|value3", records[2]);
  52. }
  53. pub fn compact(records: []const []const u8) []const []const u8 {
  54. var compactMap = std.StringHashMap([]const u8).init(wal_allocator);
  55. for (records) |record| {
  56. var parts = std.mem.splitScalar(u8, record, '|');
  57. const opcode = parts.next() orelse continue;
  58. const key = parts.next() orelse continue;
  59. const value = parts.next() orelse "";
  60. if (std.mem.eql(u8, opcode, "D")) {
  61. _ = compactMap.remove(key);
  62. } else if (std.mem.eql(u8, opcode, "W")) {
  63. compactMap.put(key, value) catch continue;
  64. }
  65. }
  66. var compactedRecords = std.ArrayListUnmanaged([]const u8){};
  67. var it = compactMap.iterator();
  68. while (it.next()) |entry| {
  69. const record = std.fmt.allocPrint(wal_allocator, "W|{s}|{s}", .{ entry.key_ptr.*, entry.value_ptr.* }) catch continue;
  70. compactedRecords.append(wal_allocator, record) catch continue;
  71. }
  72. return compactedRecords.items;
  73. }
  74. test "compaction test" {
  75. const records = &[_][]const u8{
  76. "W|key1|value1v1",
  77. "W|key1|value1v2",
  78. "W|key2|value2v1",
  79. "D|key2|",
  80. "W|key3|value3v1",
  81. "W|key4|value4v1",
  82. "D|key4|",
  83. };
  84. const compactedExpected = &[_][]const u8{
  85. "W|key1|value1v2",
  86. "W|key3|value3v1",
  87. };
  88. const compacted = compact(records[0..]);
  89. for (compacted, 0..) |record, i| {
  90. try std.testing.expectEqualStrings(compactedExpected[i], record);
  91. }
  92. }