166 lines
4.5 KiB
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;
|
|
}
|