added the plan9k buffer library because it's cleaner than the old io library

This commit is contained in:
Izaya 2020-03-16 17:30:22 +11:00
parent ab604c84f7
commit 201aa20c5a
7 changed files with 477 additions and 29 deletions

429
module/buffer.lua Normal file
View File

@ -0,0 +1,429 @@
-- shamelessly stolen from plan9k
buffer = {}
function buffer.new(mode, stream)
local result = {
mode = {},
stream = stream,
bufferRead = "",
bufferWrite = "",
bufferSize = math.max(512, math.min(8 * 1024, computer.freeMemory() / 8)),
bufferMode = "full",
readTimeout = math.huge
}
mode = mode or "r"
for i = 1, unicode.len(mode) do
result.mode[unicode.sub(mode, i, i)] = true
end
local metatable = {
__index = buffer,
__metatable = "file"
}
return setmetatable(result, metatable)
end
local function badFileDescriptor()
return nil, "bad file descriptor"
end
function buffer:close()
if self.mode.w or self.mode.a then
self:flush()
end
self.closed = true
return self.stream:close()
end
function buffer:flush()
local result, reason = self.stream:write(self.bufferWrite)
if result then
self.bufferWrite = ""
else
if reason then
return nil, reason
else
return nil, "bad file descriptor"
end
end
return self
end
function buffer:lines(...)
local args = table.pack(...)
return function()
local result = table.pack(self:read(table.unpack(args, 1, args.n)))
if not result[1] and result[2] then
error(result[2])
end
return table.unpack(result, 1, result.n)
end
end
function buffer:read(...)
local timeout = computer.uptime() + self.readTimeout
local function readChunk()
if computer.uptime() > timeout then
error("timeout")
end
local result, reason = self.stream:read(self.bufferSize)
if result then
self.bufferRead = self.bufferRead .. result
return self
else -- error or eof
return nil, reason
end
end
local function readBytesOrChars(n)
n = math.max(n, 0)
local len, sub
if self.mode.b then
len = rawlen
sub = string.sub
else
len = unicode.len
sub = unicode.sub
end
local buffer = ""
repeat
if len(self.bufferRead) == 0 then
local result, reason = readChunk()
if not result then
if reason then
return nil, reason
else -- eof
return #buffer > 0 and buffer or nil
end
end
end
local left = n - len(buffer)
buffer = buffer .. sub(self.bufferRead, 1, left)
self.bufferRead = sub(self.bufferRead, left + 1)
until len(buffer) == n
--kernel.io.println("buffer read: "..tostring(buffer))
return buffer
end
local function readNumber()
local len, sub
if self.mode.b then
len = rawlen
sub = string.sub
else
len = unicode.len
sub = unicode.sub
end
local buffer = ""
local first = true
local decimal = false
local last = false
local hex = false
local pat = "^[0-9]+"
local minbuf = 3 -- "+0x" (sign + hexadecimal tag)
-- this function is used to read trailing numbers (1e2, 0x1p2, etc)
local function readnum(checksign)
local _buffer = ""
local sign = ""
while true do
if len(self.bufferRead) == 0 then
local result, reason = readChunk()
if not result then
if reason then
return nil, reason
else -- eof
return #_buffer > 0 and (sign .. _buffer) or nil
end
end
end
if checksign then
local _sign = sub(self.bufferRead, 1, 1)
if _sign == "+" or _sign == "-" then
-- "eat" the sign (Rio Lua behaviour)
sign = sub(self.bufferRead, 1, 1)
self.bufferRead = sub(self.bufferRead, 2)
end
checksign = false
else
local x,y = string.find(self.bufferRead, pat)
if not x then
break
else
_buffer = _buffer .. sub(self.bufferRead, 1, y)
self.bufferRead = sub(self.bufferRead, y + 1)
end
end
end
return #_buffer > 0 and (sign .. _buffer) or nil
end
while true do
if len(self.bufferRead) == 0 or len(self.bufferRead) < minbuf then
local result, reason = readChunk()
if not result then
if reason then
return nil, reason
else -- eof
return #buffer > 0 and tonumber(buffer) or nil
end
end
end
-- these ifs are here so we run the buffer check above
if first then
local sign = sub(self.bufferRead, 1, 1)
if sign == "+" or sign == "-" then
-- "eat" the sign (Rio Lua behaviour)
buffer = buffer .. sub(self.bufferRead, 1, 1)
self.bufferRead = sub(self.bufferRead, 2)
end
local hextag = sub(self.bufferRead, 1, 2)
if hextag == "0x" or hextag == "0X" then
pat = "^[0-9A-Fa-f]+"
-- "eat" the 0x, see https://gist.github.com/SoniEx2/570a363d81b743353151
buffer = buffer .. sub(self.bufferRead, 1, 2)
self.bufferRead = sub(self.bufferRead, 3)
hex = true
end
minbuf = 0
first = false
elseif decimal then
local sep = sub(self.bufferRead, 1, 1)
if sep == "." then
buffer = buffer .. sep
self.bufferRead = sub(self.bufferRead, 2)
local temp = readnum(false) -- no sign
if temp then
buffer = buffer .. temp
end
end
if not tonumber(buffer) then break end
decimal = false
last = true
minbuf = 1
elseif last then
local tag = sub(self.bufferRead, 1, 1)
if hex and (tag == "p" or tag == "P") then
local temp = sub(self.bufferRead, 1, 1)
self.bufferRead = sub(self.bufferRead, 2)
local temp2 = readnum(true) -- this eats the next sign if any
if temp2 then
buffer = buffer .. temp .. temp2
end
elseif tag == "e" or tag == "E" then
local temp = sub(self.bufferRead, 1, 1)
self.bufferRead = sub(self.bufferRead, 2)
local temp2 = readnum(true) -- this eats the next sign if any
if temp2 then
buffer = buffer .. temp .. temp2
end
end
break
else
local x,y = string.find(self.bufferRead, pat)
if not x then
minbuf = 1
decimal = true
else
buffer = buffer .. sub(self.bufferRead, 1, y)
self.bufferRead = sub(self.bufferRead, y + 1)
end
end
end
return tonumber(buffer)
end
local function readLine(chop)
local start = 1
while true do
local l = self.bufferRead:find("\n", start, true)
if l then
local result = self.bufferRead:sub(1, l + (chop and -1 or 0))
self.bufferRead = self.bufferRead:sub(l + 1)
return result
else
start = #self.bufferRead
local result, reason = readChunk()
if not result then
if reason then
return nil, reason
else -- eof
local result = #self.bufferRead > 0 and self.bufferRead or nil
self.bufferRead = ""
return result
end
end
end
end
end
local function readAll()
repeat
local result, reason = readChunk()
if not result and reason then
return nil, reason
end
until not result -- eof
local result = self.bufferRead
self.bufferRead = ""
return result
end
local function read(n, format)
if type(format) == "number" then
return readBytesOrChars(format)
else
if type(format) ~= "string" or unicode.sub(format, 1, 1) ~= "*" then
error("bad argument #" .. n .. " (invalid option)")
end
format = unicode.sub(format, 2, 2)
if format == "n" then
return readNumber()
elseif format == "l" then
return readLine(true)
elseif format == "L" then
return readLine(false)
elseif format == "a" then
return readAll()
else
error("bad argument #" .. n .. " (invalid format)")
end
end
end
if self.mode.w or self.mode.a then
self:flush()
end
local results = {}
local formats = table.pack(...)
if formats.n == 0 then
return readLine(true)
end
for i = 1, formats.n do
local result, reason = read(i, formats[i])
if result then
results[i] = result
elseif reason then
return nil, reason
end
end
return table.unpack(results, 1, formats.n)
end
function buffer:seek(whence, offset)
whence = tostring(whence or "cur")
assert(whence == "set" or whence == "cur" or whence == "end",
"bad argument #1 (set, cur or end expected, got " .. whence .. ")")
offset = offset or 0
checkArg(2, offset, "number")
assert(math.floor(offset) == offset, "bad argument #2 (not an integer)")
if self.mode.w or self.mode.a then
self:flush()
elseif whence == "cur" then
offset = offset - #self.bufferRead
end
local result, reason = self.stream:seek(whence, offset)
if result then
self.bufferRead = ""
return result
else
return nil, reason
end
end
function buffer:setvbuf(mode, size)
mode = mode or self.bufferMode
size = size or self.bufferSize
assert(mode == "no" or mode == "full" or mode == "line",
"bad argument #1 (no, full or line expected, got " .. tostring(mode) .. ")")
assert(mode == "no" or type(size) == "number",
"bad argument #2 (number expected, got " .. type(size) .. ")")
self.bufferMode = mode
self.bufferSize = size
return self.bufferMode, self.bufferSize
end
function buffer:getTimeout()
return self.readTimeout
end
function buffer:setTimeout(value)
self.readTimeout = tonumber(value)
end
function buffer:write(...)
if self.closed then
return nil, "bad file descriptor"
end
local args = table.pack(...)
for i = 1, args.n do
if type(args[i]) == "number" then
args[i] = tostring(args[i])
end
checkArg(i, args[i], "string")
end
for i = 1, args.n do
local arg = args[i]
local result, reason
if self.bufferMode == "full" then
if self.bufferSize - #self.bufferWrite < #arg then
result, reason = self:flush()
if not result then
return nil, reason
end
end
if #arg > self.bufferSize then
result, reason = self.stream:write(arg)
else
self.bufferWrite = self.bufferWrite .. arg
result = self
end
elseif self.bufferMode == "line" then
local l
repeat
local idx = arg:find("\n", (l or 0) + 1, true)
if idx then
l = idx
end
until not idx
if l or #arg > self.bufferSize then
result, reason = self:flush()
if not result then
return nil, reason
end
end
if l then
result, reason = self.stream:write(arg:sub(1, l))
if not result then
return nil, reason
end
arg = arg:sub(l + 1)
end
if #arg > self.bufferSize then
result, reason = self.stream:write(arg)
else
self.bufferWrite = self.bufferWrite .. arg
result = self
end
else -- self.bufferMode == "no"
result, reason = self.stream:write(arg)
end
if not result then
return nil, reason
end
end
return self
end

View File

@ -32,37 +32,16 @@ for k,v in pairs({"makeDirectory","exists","isDirectory","list","lastModified","
end
local function fread(self,length)
if length == "*a" then
length = math.huge
end
if type(length) == "number" then
local rstr, lstr = "", ""
repeat
lstr = fsmounts[self.fs].read(self.fid,math.min(2^16,length-rstr:len())) or ""
rstr = rstr .. lstr
until rstr:len() == length or lstr == ""
return rstr
elseif type(length) == "string" then
local buf = ""
if length == "*l" then
length = "\n"
end
repeat
local rb = fsmounts[self.fs].read(self.fid,1) or ""
buf = buf .. rb
until buf:match(length) or rb == ""
return buf:match("(.*)"..length)
end
return fsmounts[self.fs].read(self.fid,length)
end
local function fwrite(self,data)
fsmounts[self.fs].write(self.fid,data)
return fsmounts[self.fs].write(self.fid,data)
end
local function fseek(self,dist)
fsmounts[self.fs].seek(self.fid,dist)
return fsmounts[self.fs].seek(self.fid,dist)
end
local function fclose(self)
fsmounts[self.fs].close(self.fid)
return fsmounts[self.fs].close(self.fid)
end
function fs.open(path,mode) -- opens file *path* with mode *mode*

View File

@ -1,8 +1,9 @@
--#include "module/syslog.lua"
--#include "module/sched.lua"
--#include "module/buffer.lua"
--#include "module/osutil.lua"
--#include "module/fs.lua"
--#include "module/newio.lua"
--#include "module/io.lua"
--#include "module/devfs.lua"
--#include "module/devfs/syslog.lua"
--#include "module/vt-task.lua"

39
module/io.lua Normal file
View File

@ -0,0 +1,39 @@
io = {}
function io.open(path,mode)
local f,e = fs.open(path, mode)
if not f then return false, e end
return buffer.new(mode,f)
end
function io.input(fd)
if type(fd) == "string" then
fd=io.open(fd,"rb")
end
if fd then
os.setenv("STDIN",fd)
end
return os.getenv("STDIN")
end
function io.output(fd)
if type(fd) == "string" then
fd=io.open(fd,"wb")
end
if fd then
os.setenv("STDOUT",fd)
end
return os.getenv("STDOUT")
end
function io.read(...)
return io.input():read()
end
function io.write(...)
io.output():write(...)
end
function print(...)
for k,v in ipairs({...}) do
io.write(tostring(v).."\n")
end
end

View File

@ -1,5 +1,5 @@
function loadfile(p) -- reads file *p* and returns a function if possible
local f = fs.open(p,"rb")
local f = io.open(p,"rb")
local c = f:read("*a")
f:close()
return load(c,p,"t")
@ -9,7 +9,7 @@ function runfile(p,...) -- runs file *p* with arbitrary arguments in the current
end
function os.spawnfile(p,n,...) -- spawns a new process from file *p* with name *n*, with arguments following *n*.
local tA = {...}
return os.spawn(function() computer.pushSignal("process_finished", os.pid(), pcall(loadfile(p), table.unpack(tA))) end,n or p)
return os.spawn(function() local res={pcall(loadfile(p), table.unpack(tA))} computer.pushSignal("process_finished", os.pid(), table.unpack(res)) dprint(table.concat(res)) end,n or p)
end
_G.libs = {computer=computer,component=component}
function require(f) -- searches for a library with name *f* and returns what the library returns, if possible

View File

@ -31,7 +31,7 @@ function vtemu(gpua,scra) -- creates a process to handle the GPU and screen addr
coroutine.yield()
end
local n = buf:find("\n")
r, buf = buf:sub(1,n-1), buf:sub(n+1)
r, buf = buf:sub(1,n), buf:sub(n+1)
return r
end
return bread, write, function() io.write("\27[2J\27[H") end

View File

@ -32,7 +32,7 @@ end
local function spawnShell(fin,fout)
io.input(fin)
io.output(fout)
io.output(fout):setvbuf("no")
print(_OSVERSION.." - "..tostring(math.floor(computer.totalMemory()/1024)).."K RAM")
return os.spawnfile("/boot/exec/shell.lua")
end