about summary refs log tree commit diff
path: root/fs.lua
diff options
context:
space:
mode:
authorequa <equaa@protonmail.com>2022-03-07 19:14:15 +0000
committerequa <equaa@protonmail.com>2022-03-07 19:14:15 +0000
commit224e8f43c50a513bae78af2152c12c0a5f9564f9 (patch)
tree7748d6ffeba50a43d3a7f55a3c85983d07969776 /fs.lua
initial commit
Diffstat (limited to 'fs.lua')
-rw-r--r--fs.lua153
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