mgtzm/src/Db.zig

166 lines
4.5 KiB
Zig

const c = @cImport({
@cInclude("tkrzw_langc.h");
});
const Self = @This();
const std = @import("std");
pub const free = c.free;
dbm: *c.TkrzwDBM,
const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const encode_len = 4;
pub fn numEncode(num: usize) ![encode_len]u8 {
return uintEncode(num, 4, chars);
}
pub fn numDecode(str: []const u8) usize {
return uintDecode(str, chars);
}
const EncodeError = error{
NumberTooBig,
};
fn uintEncode(num: usize, comptime bufflen: usize, comptime charset: []const u8) ![bufflen]u8 {
// Check if the number is bigger than the maximum possible number
var pow = comptime std.math.pow(usize, charset.len, bufflen);
if (num >= pow) return EncodeError.NumberTooBig;
var buff = [_]u8{charset[0]} ** bufflen;
var num_i: usize = num;
for (buff) |*pos| {
pow /= charset.len;
pos.* = charset[@divTrunc(num_i, pow)];
num_i %= pow;
}
return buff;
}
fn uintDecode(str: []const u8, comptime charset: []const u8) usize {
var ret: usize = 0;
for (str) |needle, i| {
for (charset) |blade, j| {
if (needle == blade) {
ret += std.math.pow(usize, charset.len, str.len - i - 1) * j;
break;
}
}
}
return ret;
}
pub fn open(path: [:0]const u8, writable: bool, params: [:0]const u8) Self {
return Self{
.dbm = c.tkrzw_dbm_open(path, writable, params),
};
}
// TODO: Return error
pub fn set(self: *Self, key: []const u8, value: []const u8) void {
const key_len = @intCast(i32, key.len);
const value_len = @intCast(i32, value.len);
_ = c.tkrzw_dbm_set(self.dbm, key.ptr, key_len, value.ptr, value_len, true);
}
// TODO: Return error
pub fn remove(self: *Self, key: []const u8) bool {
return c.tkrzw_dbm_remove(self.dbm, key.ptr, @intCast(i32, key.len));
}
// TODO: Return error
pub fn append(self: *Self, key: []const u8, value: []const u8, delim: []const u8) void {
const key_len = @intCast(i32, key.len);
const value_len = @intCast(i32, value.len);
const delim_len = @intCast(i32, delim.len);
_ = c.tkrzw_dbm_append(self.dbm, key.ptr, key_len, value.ptr, value_len, delim.ptr, delim_len);
}
pub fn check(self: *Self, key: []const u8) bool {
const key_len = @intCast(i32, key.len);
return c.tkrzw_dbm_check(self.dbm, key.ptr, key_len);
}
// You gotta call free on return
pub fn get(self: *Self, key: []const u8) ?[]u8 {
const key_len = @intCast(i32, key.len);
var len: i32 = 0;
var ret = c.tkrzw_dbm_get(self.dbm, key.ptr, key_len, &len);
return if (ret) |nnret| nnret[0..@intCast(usize, len)] else return null;
}
pub fn getList(self: *Self, key: []const u8, allocator: std.mem.Allocator) !?[][:0]u8 {
const list_str = self.get(key) orelse return null;
defer Self.free(list_str.ptr);
const count = std.mem.count(u8, list_str, " ") + 1;
var buffer = try allocator.alloc([:0]u8, count);
var iter = std.mem.split(u8, list_str, " ");
var i: usize = 0;
while (iter.next()) |unit| {
defer i += 1;
buffer[i] = try allocator.dupeZ(u8, unit);
}
return buffer;
}
/// Helper function to make it easier to find occurrences in lists
pub fn isInList(self: *Self, key: []const u8, needle: []const u8) bool {
if (self.get(key)) |haystack| {
defer Self.free(haystack.ptr);
if (isInStringDelim(haystack, needle, ' ')) return true;
}
return false;
}
// TODO: Return wether or not the list is void after this
pub fn removeFromList(self: *Self, key: []const u8, needle: []const u8) void {
// TODO: Better error handling, do not fail silently
const haystack = self.get(key) orelse return;
// If there's no start, somesthing's wrong
const start = std.mem.indexOf(u8, haystack, needle) orelse return;
const end = start + needle.len;
// Re-set the rest of the IDs
self.set(key, haystack[0..start]);
// We were deleting the last in the list, no need to append anything
if (end >= haystack.len) return;
// +1 to remove the space
self.append(key, haystack[(end + 1)..], " ");
return;
}
/// Generic function to find occurences of a string inside another one with delimiters
fn isInStringDelim(haystack: []const u8, needle: []const u8, delim: u8) bool {
var spliter = std.mem.split(u8, haystack, &[_]u8{delim});
while (spliter.next()) |blade| {
if (std.mem.eql(u8, blade, needle)) {
return true;
}
}
return false;
}