diff options
author | equa <equaa@protonmail.com> | 2022-03-07 19:14:15 +0000 |
---|---|---|
committer | equa <equaa@protonmail.com> | 2022-03-07 19:14:15 +0000 |
commit | 224e8f43c50a513bae78af2152c12c0a5f9564f9 (patch) | |
tree | 7748d6ffeba50a43d3a7f55a3c85983d07969776 /fs.lua |
initial commit
Diffstat (limited to 'fs.lua')
-rw-r--r-- | fs.lua | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/fs.lua b/fs.lua new file mode 100644 index 0000000..2e7638f --- /dev/null +++ b/fs.lua @@ -0,0 +1,153 @@ +-- cqueue filesystem with luaposix +-- TODO: closing files +local unistd = require("posix.unistd") +local cqueues = require("cqueues") +local dkjson = require("dkjson") + +local file = {} + +function file.new(fd, mode) + if not mode then mode = "r" end + local f = setmetatable({}, { __index = file }) + f.pollfd = fd + f.mode = mode + if string.match(mode, "r") then + f.rbuf = {} + end + + if string.match(mode, "w") then + f.wbuf = {} + end + + return f +end + +function file.events(f) + return f.mode +end + +-- input/output buffers +-- +-- buffers are arrays of objects containing "data" (a string) and "index" (their position) +-- indexing allows us to avoid recreating string objects + +function buffer_length(buf) + local total = 0 + for _, entry in ipairs(buf) do + total = total + #entry.data - entry.index + 1 + end + return total +end + +-- returns nil on empty length +function buffer_get(buf, length) + local out = {} + if not length then length = buffer_length(buf) end + while length > 0 do + assert(#buf > 0) + + local x = string.sub(buf[1].data, buf[1].index, buf[1].index + length - 1) + table.insert(out, x) + if #buf[1].data - buf[1].index + 1 <= length then + table.remove(buf, 1) + else + buf[1].index = buf[1].index + length + end + length = length - #x + end + if out[1] then return table.concat(out) else return nil end +end + +function buffer_char_index(buf, char) + local num = 1 + for _, entry in ipairs(buf) do + if string.find(entry.data, char, entry.index, true) then + return num + string.find(entry.data, char, entry.index, true) - entry.index + else + num = num + #entry.data - entry.index + 1 + end + end + + return nil +end + +do + local test_buf = { { data = "abcde", index = 2 }, { data = "\na", index = 1 } } + assert(buffer_length(test_buf) == 6) + assert(buffer_char_index(test_buf, "\n") == 5) + assert(buffer_get(test_buf, 5) == "bcde\n") + assert(buffer_length(test_buf) == 1) +end + +function try_read(buffer, fd, max) + cqueues.poll({ pollfd = fd, events = function () return "r" end }) + + local data, _, errno = unistd.read(fd, max) + + if not data then return nil, errno end + + table.insert(buffer, { data = data, index = 1}) + + return #data +end + +function file.read(f, what) + assert(f.rbuf) + + if (type(what) == "number" and what > 0) then + while buffer_length(f.rbuf) < what do + if try_read(f.rbuf, f.pollfd, what - buffer_length(f.rbuf)) == 0 then + break + end + end + + return buffer_get(f.rbuf, math.min(what, buffer_length(f.rbuf))) + elseif (type(what) == "number" and what < 0) then + while buffer_length(f.rbuf) == 0 do + if try_read(f.rbuf, f.pollfd, 0 - what) == 0 then return nil end + end + return buffer_get(f.rbuf, math.min(0 - what, buffer_length(f.rbuf))) + elseif what == "*l" or what == "*L" then + while not buffer_char_index(f.rbuf, "\n") do + -- TODO: constantize this + if try_read(f.rbuf, f.pollfd, 1024) == 0 then + break + end + end + + local line = buffer_get(f.rbuf, buffer_char_index(f.rbuf, "\n")) + + if not line then + return nil + end + local ret = string.gsub( + line, + "\n", + (what == "*l" and "" or "\n") + ) + return ret + else + -- TODO + end +end + +function file.lines(f, what) + return function () return file.read(f, what or "*l") end +end + +function file.write(f, data) + local index = 1 + while index <= #data do + cqueues.poll({ pollfd = f.pollfd, events = function () return "w" end }) + local nb, _, errno = unistd.write(f.pollfd, string.sub(data, index)) + if nb >= 0 then + index = index + nb + else + return nil, errno -- TODO? + end + end + + return f +end + +return file |