Software /
code /
prosody
Diff
util/datamanager.lua @ 13183:33b114fbb5de
util.datamanager: Add way to efficiently remove first items in a list
Copying data without parsing it should be more performant than parsing
it serializing back.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Wed, 12 Jul 2023 11:42:41 +0200 |
parent | 13182:c48ae06e24d6 |
child | 13185:b57f45165e1e |
line wrap: on
line diff
--- a/util/datamanager.lua Mon Jul 10 17:19:05 2023 +0200 +++ b/util/datamanager.lua Wed Jul 12 11:42:41 2023 +0200 @@ -506,6 +506,97 @@ return setmetatable({ file = file; index = index }, indexed_list_mt); end +local function shift_index(index_filename, index, trim_to, offset) + local index_scratch = index_filename .. "~"; + local new_index, err = io_open(index_scratch, "w"); + if not new_index then + os_remove(index_filename); + return "deleted", err; + end + + local ok, err = new_index:write(index_magic); + if not ok then + new_index:close(); + os_remove(index_filename); + os_remove(index_scratch); + return "deleted", err; + end + + if not index.file or not index.file:seek("set", index_item_size * trim_to) then + new_index:close(); + os_remove(index_filename); + os_remove(index_scratch); + return "deleted"; + else + local pack, unpack = string.pack, string.unpack; + for item in index.file:lines(index_item_size) do + local ok, err = new_index:write(pack(index_fmt, unpack(index_fmt, item) - offset)); + if not ok then + os_remove(index_filename); + os_remove(index_scratch); + return "deleted", err; + end + end + local ok, err = new_index:close(); + if not ok then + os_remove(index_filename); + os_remove(index_scratch); + return "deleted", err; + end + return os_rename(index_scratch, index_filename); + end +end + +local function list_shift(username, host, datastore, trim_to) + if trim_to == 1 then + return true + end + if type(trim_to) ~= "number" or trim_to < 1 then + return nil, "invalid-argument"; + end + local list_filename = getpath(username, host, datastore, "list"); + local index_filename = getpath(username, host, datastore, "lidx"); + local index, err = get_list_index(username, host, datastore); + if not index then + return nil, err; + end + + local new_first = index[trim_to]; + if not new_first then + os_remove(index_filename); + return os_remove(list_filename); + end + + local offset = new_first.start; + if offset == 0 then + return true; + end + + local r, err = io_open(list_filename, "r"); + if not r then + return nil, err; + end + local w, err = io_open(list_filename .. "~", "w"); + if not w then + return nil, err; + end + r:seek("set", offset); + for block in r:lines(0x1000) do + local ok, err = w:write(block); + if not ok then + return nil, err; + end + end + r:close(); + local ok, err = w:close(); + if not ok then + return nil, err; + end + shift_index(index_filename, index, trim_to, offset) + return os_rename(list_filename .. "~", list_filename); +end + + local type_map = { keyval = "dat"; list = "list"; @@ -609,4 +700,5 @@ build_list_index = build_list_index; list_open = list_open; + list_shift = list_shift; };