ref: 8728342de349e7029312ddbd33c2710d2d03fff2
parent: 35bde60621ecf5cbcf07b743cafd9724f87faccb
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Oct 4 06:59:36 EDT 2015
Make the interface for libbio expose errors. Filling in all the error conditions correctly is only lightly tested, but it seems to work.
--- a/lib/bio/bio.myr
+++ b/lib/bio/bio.myr
@@ -10,6 +10,7 @@
/* backing fd */
fd : std.fd
mode : mode
+ haderr : bool
/* read buffer */
rbuf : byte[:]
@@ -21,6 +22,12 @@
wend : std.size
;;
+ type status(@a) = union
+ `Eof
+ `Ok @a
+ `Err
+ ;;
+
/* creation */
const mkfile : (fd : std.fd, mode : mode -> file#)
const open : (path : byte[:], mode : mode -> std.result(file#, byte[:]))
@@ -30,8 +37,8 @@
const free : (f : file# -> void)
/* basic i/o. Returns sub-buffer when applicable. */
- const write : (f : file#, src : byte[:] -> std.size)
- const read : (f : file#, dst : byte[:] -> std.option(byte[:]))
+ const write : (f : file#, src : byte[:] -> status(std.size))
+ const read : (f : file#, dst : byte[:] -> status(byte[:]))
const flush : (f : file# -> bool)
/* seeking */
@@ -38,27 +45,28 @@
const seek : (f : file#, std.off -> std.off)
/* single unit operations */
- const putb : (f : file#, b : byte -> std.size)
- const putc : (f : file#, c : char -> std.size)
- const getb : (f : file# -> std.option(byte))
- const getc : (f : file# -> std.option(char))
+ const putb : (f : file#, b : byte -> status(std.size))
+ const putc : (f : file#, c : char -> status(std.size))
+ const getb : (f : file# -> status(byte))
+ const getc : (f : file# -> status(char))
/* peeking */
- const peekb : (f : file# -> std.option(byte))
- const peekc : (f : file# -> std.option(char))
+ const peekb : (f : file# -> status(byte))
+ const peekc : (f : file# -> status(char))
/* delimited read; returns freshly allocated buffer. */
- const readln : (f : file# -> std.option(byte[:]))
- const readto : (f : file#, delim : byte[:] -> std.option(byte[:]))
+ const readln : (f : file# -> status(byte[:]))
+ const readto : (f : file#, delim : byte[:] -> status(byte[:]))
const skipto : (f : file#, delim : byte[:] -> bool)
const skipspace : (f : file# -> void)
/* formatted i/o */
- const put : (f : file#, fmt : byte[:], args : ... -> std.size)
+ const put : (f : file#, fmt : byte[:], args : ... -> status(std.size))
/* pkg funcs */
- pkglocal const ensureread : (f : file#, n : std.size -> bool)
- pkglocal const ensurewrite : (f : file#, n : std.size -> bool)
+ pkglocal const ensureread : (f : file#, n : std.size -> status(std.size))
+ pkglocal const ensurewrite : (f : file#, n : std.size -> status(std.size))
+ pkglocal const writebuf : (fd : std.fd, b : byte[:] -> status(std.size))
;;
const Bufsz = 16*std.KiB
@@ -162,7 +170,7 @@
if src.len < (f.wbuf.len - f.wend)
std.slcp(f.wbuf[f.wend:f.wend+src.len], src)
f.wend += src.len
- -> src.len
+ -> `Ok src.len
else
flush(f)
-> writebuf(f.fd, src)
@@ -178,6 +186,11 @@
var d
var count
+ /* Clear the error state so we can retry */
+ if f.haderr
+ f.haderr = false
+ -> `Err
+ ;;
std.assert(f.mode & Rd != 0, "File is not in read mode")
/*
* small reads should try to fill, so we don't have to make a
@@ -201,17 +214,23 @@
d = dst[count:]
while dst.len > 0
n = std.read(f.fd, d)
- if n <= 0
- goto readdone
+ if n == 0
+ break
+ elif n < 0
+ if count > 0
+ f.haderr = true
+ ;;
+ break
;;
count += n
d = d[n:]
;;
-:readdone
- if count > 0
- -> `std.Some dst[:count]
+ if n < 0 && count == 0
+ -> `Err
+ elif count == 0
+ -> `Eof
else
- -> `std.None
+ -> `Ok dst[:count]
;;
}
@@ -221,8 +240,13 @@
ret = true
if f.mode & Wr != 0
- ret = (writebuf(f.fd, f.wbuf[:f.wend]) == f.wend)
- f.wend = 0
+ match writebuf(f.fd, f.wbuf[:f.wend])
+ | `Ok n:
+ ret = (n == f.wend)
+ f.wend = 0
+ | _:
+ -> false
+ ;;
;;
-> ret
}
@@ -234,9 +258,13 @@
/* writes a single byte to the output stream */
const putb = {f, b
- ensurewrite(f, 1)
- f.wbuf[f.wend++] = b
- -> 1
+ match ensurewrite(f, 1)
+ | `Err: -> `Err
+ | `Eof: -> `Eof
+ | `Ok n:
+ f.wbuf[f.wend++] = b
+ -> `Ok 1
+ ;;
}
/* writes a single character to the output stream, encoded in utf8 */
@@ -244,18 +272,24 @@
var sz
sz = std.charlen(c)
- ensurewrite(f, sz)
- std.encode(f.wbuf[f.wend:], c)
- f.wend += sz
- -> sz
+ match ensurewrite(f, sz)
+ | `Err: -> `Err
+ | `Eof: -> `Eof
+ | `Ok n:
+ std.encode(f.wbuf[f.wend:], c)
+ f.wend += sz
+ -> `Ok sz
+ ;;
}
/* reads a single byte from the input stream */
const getb = {f
- if ensureread(f, 1)
- -> `std.Some f.rbuf[f.rstart++]
+ match ensureread(f, 1)
+ | `Eof: -> `Eof
+ | `Err: -> `Err
+ | `Ok n:
+ -> `Ok f.rbuf[f.rstart++]
;;
- -> `std.None
}
/* reads a single character from the input stream, encoded in utf8 */
@@ -262,35 +296,39 @@
const getc = {f
var c
- if ensurecodepoint(f)
+ match ensurecodepoint(f)
+ | `Err: -> `Err
+ | `Eof: -> `Eof
+ | `Ok n:
c = std.decode(f.rbuf[f.rstart:f.rend])
f.rstart += std.charlen(c)
- -> `std.Some c
+ -> `Ok c
;;
- -> `std.None
}
/* ensures we have enough to read a single codepoint in the buffer */
-const ensurecodepoint = {f
+const ensurecodepoint : (f : file# -> status(std.size)) = {f
var b
var len
- if !ensureread(f, 1)
- -> false
+ match ensureread(f, 1)
+ | `Err: -> `Err
+ | `Eof: -> `Eof
+ | `Ok n:
+ b = f.rbuf[f.rstart]
+ if b & 0x80 == 0 /* 0b0xxx_xxxx */
+ len = 1
+ elif b & 0xe0 == 0xc0 /* 0b110x_xxxx */
+ len = 2
+ elif b & 0xf0 == 0xe0 /* 0b1110_xxxx */
+ len = 3
+ elif b & 0xf8 == 0xf0 /* 0b1111_0xxx */
+ len = 4
+ else
+ len = 1 /* invalid unicode char */
+ ;;
+ -> ensureread(f, len)
;;
- b = f.rbuf[f.rstart]
- if b & 0x80 == 0 /* 0b0xxx_xxxx */
- len = 1
- elif b & 0xe0 == 0xc0 /* 0b110x_xxxx */
- len = 2
- elif b & 0xf0 == 0xe0 /* 0b1110_xxxx */
- len = 3
- elif b & 0xf8 == 0xf0 /* 0b1111_0xxx */
- len = 4
- else
- len = 1 /* invalid unicode char */
- ;;
- -> ensureread(f, len)
}
/*
@@ -319,19 +357,21 @@
/* peeks a single byte from an input stream */
const peekb = {f
- if !ensureread(f, 1)
- -> `std.None
- else
- -> `std.Some f.rbuf[f.rstart]
+ match ensureread(f, 1)
+ | `Eof: -> `Eof
+ | `Err: -> `Err
+ | `Ok n:
+ -> `Ok f.rbuf[f.rstart]
;;
}
/* peeks a single character from a utf8 encoded input stream */
const peekc = {f
- if !ensurecodepoint(f)
- -> `std.None
- else
- -> `std.Some std.decode(f.rbuf[f.rstart:f.rend])
+ match ensurecodepoint(f)
+ | `Eof: -> `Eof
+ | `Err: -> `Err
+ | `Ok n:
+ -> `Ok std.decode(f.rbuf[f.rstart:f.rend])
;;
}
@@ -351,8 +391,9 @@
/* same as readto, but drops the read data. */
const skipto = {f, delim
match readdelim(f, delim, true)
- | `std.Some ret: -> true
- | `std.None: -> false
+ | `Ok ign: -> true
+ | `Eof: -> false
+ | `Err: -> false
;;
}
@@ -359,13 +400,15 @@
const skipspace = {f
while true
match bio.peekc(f)
- | `std.Some c:
+ | `Ok c:
if std.isspace(c)
break
;;
bio.getc(f)
- | `std.None:
+ | `Eof:
break
+ | `Err:
+ break
;;
;;
}
@@ -377,12 +420,15 @@
ret = [][:]
while true
/* get at least delimiter count of characters */
- if !ensureread(f, 1)
+ match ensureread(f, 1)
+ | `Ok _:
+ | `Err: -> `Err
+ | `Eof:
ret = readinto(f, ret, f.rend - f.rstart)
if ret.len > 0
- -> `std.Some ret
+ -> `Ok ret
else
- -> `std.None
+ -> `Eof
;;
;;
/* scan for delimiter */
@@ -391,12 +437,12 @@
if c == '\r' || c == '\n'
n = 1
/* if we have '\r', we can get '\r\n'. */
- if c == '\r' && std.getv(peekc(f), -1) == '\n'
+ if c == '\r' && unwrapc(peekc(f), -1) == '\n'
n = 2
;;
ret = readinto(f, ret, i - f.rstart)
f.rstart += n
- -> `std.Some ret
+ -> `Ok ret
;;
:nextitergetln
;;
@@ -405,6 +451,13 @@
std.die("unreachable")
}
+const unwrapc = {cc, v
+ match cc
+ | `Ok c: -> c
+ | _: -> v
+ ;;
+}
+
const readdelim = {f, delim, drop
var ret
@@ -411,14 +464,17 @@
ret = [][:]
while true
/* get at least delimiter count of characters */
- if !ensureread(f, delim.len)
+ match ensureread(f, 1)
+ | `Ok _:
+ | `Err: -> `Err
+ | `Eof:
if !drop
ret = readinto(f, ret, f.rend - f.rstart)
;;
if ret.len > 0
- -> `std.Some ret
+ -> `Ok ret
else
- -> `std.None
+ -> `Err
;;
;;
for var i = f.rstart; i < f.rend; i++
@@ -432,7 +488,7 @@
ret = readinto(f, ret, i - f.rstart)
;;
f.rstart += delim.len
- -> `std.Some ret
+ -> `Ok ret
;;
:nextiterread
;;
@@ -476,9 +532,15 @@
const ensurewrite = {f, n
std.assert(n < f.wbuf.len, "ensured write capacity > buffer size")
if n > f.wbuf.len - f.wend
- -> flush(f)
+ match writebuf(f.fd, f.wbuf[:f.wend])
+ | `Ok len:
+ f.wend = 0
+ -> `Ok len
+ | _:
+ -> `Err
+ ;;
;;
- -> true
+ -> `Ok n
}
/*
@@ -499,9 +561,18 @@
f.rstart = 0
f.rend = cap
;;
- -> fill(f, n) > n
+ match fill(f, n)
+ | `Eof: -> `Eof
+ | `Err: -> `Err
+ | `Ok len:
+ if len > n
+ -> `Ok len
+ else
+ -> `Eof
+ ;;
+ ;;
else
- -> true
+ -> `Ok n
;;
}
@@ -513,14 +584,16 @@
count = 0
while src.len != 0
n = std.write(fd, src)
- if n <= 0
- goto writedone
+ if n == 0
+ -> `Eof
+ elif n < 0
+ -> `Err
;;
count += n
src = src[n:]
;;
:writedone
- -> count
+ -> `Ok count
}
@@ -534,16 +607,37 @@
var count
count = 0
+ /* Clear the error state so we can retry */
+ if f.haderr
+ f.haderr = false
+ -> `Err
+ ;;
while count < min
n = std.read(f.fd, f.rbuf[f.rend:])
- if n <= 0
- goto filldone
+ /*
+ If we've already read data, we don't want to
+ throw it away, so we report a successful short
+ read, and then error on the next read.
+ */
+ if n == 0
+ break
+ elif n < 0
+ if count > 0
+ f.haderr = true
+ ;;
+ break
;;
count += n
f.rend += n
;;
-:filldone
- -> count
+
+ if n < 0 && count == 0
+ -> `Err
+ elif count == 0
+ -> `Eof
+ else
+ -> `Ok count
+ ;;
}
--- a/lib/bio/geti.myr
+++ b/lib/bio/geti.myr
@@ -4,16 +4,16 @@
pkg bio =
/* unsigned big endian */
- generic getbe8 : (f : file# -> std.option(@a::(numeric,integral)))
- generic getbe16 : (f : file# -> std.option(@a::(numeric,integral)))
- generic getbe32 : (f : file# -> std.option(@a::(numeric,integral)))
- generic getbe64 : (f : file# -> std.option(@a::(numeric,integral)))
+ generic getbe8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe64 : (f : file# -> status(@a::(numeric,integral)))
/* signed big endian */
- generic getle8 : (f : file# -> std.option(@a::(numeric,integral)))
- generic getle16 : (f : file# -> std.option(@a::(numeric,integral)))
- generic getle32 : (f : file# -> std.option(@a::(numeric,integral)))
- generic getle64 : (f : file# -> std.option(@a::(numeric,integral)))
+ generic getle8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle64 : (f : file# -> status(@a::(numeric,integral)))
;;
/*
@@ -20,17 +20,19 @@
reads a single integer-like value to the output stream, in
little endian format
*/
-generic getle = {f, n -> std.option(@a::(numeric,integral))
+generic getle = {f, n -> status(@a::(numeric,integral))
var v, i
v = 0
- if !ensureread(f, n)
- -> `std.None
+ match ensureread(f, n)
+ | `Eof: -> `Eof
+ | `Err: -> `Err
+ | `Ok _:
+ for i = 0; i < n; i++
+ v |= (f.rbuf[f.rstart++] castto(uint64)) << (8*(i castto(uint64)))
+ ;;
+ -> `Ok v castto(@a::(numeric,integral))
;;
- for i = 0; i < n; i++
- v |= (f.rbuf[f.rstart++] castto(uint64)) << (8*(i castto(uint64)))
- ;;
- -> `std.Some v castto(@a::(numeric,integral))
}
/*
@@ -37,18 +39,20 @@
reads a single integer-like value to the output stream, in
big endian format
*/
-generic getbe = {f, n -> std.option(@a::(numeric,integral))
+generic getbe = {f, n -> status(@a::(numeric,integral))
var v, i
v = 0
- if !ensureread(f,n)
- -> `std.None
+ match ensureread(f, n)
+ | `Eof: -> `Eof
+ | `Err: -> `Err
+ | `Ok _:
+ for i = 0; i < n; i++
+ v <<= 8
+ v |= (f.rbuf[f.rstart++] castto(uint64))
+ ;;
+ -> `Ok v castto(@a::(numeric,integral))
;;
- for i = 0; i < n; i++
- v <<= 8
- v |= (f.rbuf[f.rstart++] castto(uint64))
- ;;
- -> `std.Some v castto(@a::(numeric,integral))
}
generic getbe8 = {f; -> getbe(f, 1)}
--- a/lib/bio/puti.myr
+++ b/lib/bio/puti.myr
@@ -4,16 +4,16 @@
pkg bio =
/* unsigned big endian */
- generic putbe8 : (f : file#, v : @a::(numeric,integral) -> std.size)
- generic putbe16 : (f : file#, v : @a::(numeric,integral) -> std.size)
- generic putbe32 : (f : file#, v : @a::(numeric,integral) -> std.size)
- generic putbe64 : (f : file#, v : @a::(numeric,integral) -> std.size)
+ generic putbe8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
/* unsigned little endian */
- generic putle8 : (f : file#, v : @a::(numeric,integral) -> std.size)
- generic putle16 : (f : file#, v : @a::(numeric,integral) -> std.size)
- generic putle32 : (f : file#, v : @a::(numeric,integral) -> std.size)
- generic putle64 : (f : file#, v : @a::(numeric,integral) -> std.size)
+ generic putle8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
;;
generic putbe8 = {f, v; -> putbe(f, v castto(uint64), 1)}
@@ -29,34 +29,38 @@
const putle = {f, v, n
var buf : byte[8]
- if !ensurewrite(f, n)
- -> 0
+ match ensurewrite(f, n)
+ | `Err: -> `Err
+ | `Eof: -> `Eof
+ | `Ok _:
+ buf[0] = (v >> 0) & 0xff castto(byte)
+ buf[1] = (v >> 8) & 0xff castto(byte)
+ buf[2] = (v >> 16) & 0xff castto(byte)
+ buf[3] = (v >> 24) & 0xff castto(byte)
+ buf[4] = (v >> 32) & 0xff castto(byte)
+ buf[5] = (v >> 40) & 0xff castto(byte)
+ buf[6] = (v >> 48) & 0xff castto(byte)
+ buf[7] = (v >> 56) & 0xff castto(byte)
+ -> write(f, buf[:n])
;;
- buf[0] = (v >> 0) & 0xff castto(byte)
- buf[1] = (v >> 8) & 0xff castto(byte)
- buf[2] = (v >> 16) & 0xff castto(byte)
- buf[3] = (v >> 24) & 0xff castto(byte)
- buf[4] = (v >> 32) & 0xff castto(byte)
- buf[5] = (v >> 40) & 0xff castto(byte)
- buf[6] = (v >> 48) & 0xff castto(byte)
- buf[7] = (v >> 56) & 0xff castto(byte)
- -> write(f, buf[:n])
}
const putbe = {f, v, n
var buf : byte[8]
- if !ensurewrite(f, n)
- -> 0
+ match ensurewrite(f, n)
+ | `Err: -> `Err
+ | `Eof: -> `Eof
+ | `Ok _:
+ buf[0] = (v >> 56) & 0xff castto(byte)
+ buf[1] = (v >> 48) & 0xff castto(byte)
+ buf[2] = (v >> 40) & 0xff castto(byte)
+ buf[3] = (v >> 32) & 0xff castto(byte)
+ buf[4] = (v >> 24) & 0xff castto(byte)
+ buf[5] = (v >> 16) & 0xff castto(byte)
+ buf[6] = (v >> 8) & 0xff castto(byte)
+ buf[7] = (v >> 0) & 0xff castto(byte)
+ -> write(f, buf[8-n:])
;;
- buf[0] = (v >> 56) & 0xff castto(byte)
- buf[1] = (v >> 48) & 0xff castto(byte)
- buf[2] = (v >> 40) & 0xff castto(byte)
- buf[3] = (v >> 32) & 0xff castto(byte)
- buf[4] = (v >> 24) & 0xff castto(byte)
- buf[5] = (v >> 16) & 0xff castto(byte)
- buf[6] = (v >> 8) & 0xff castto(byte)
- buf[7] = (v >> 0) & 0xff castto(byte)
- -> write(f, buf[8-n:])
}
--- a/lib/regex/redump.myr
+++ b/lib/regex/redump.myr
@@ -57,10 +57,13 @@
const dump = {re, fd
while true
match bio.readln(fd)
- | `std.Some ln:
+ | `bio.Ok ln:
show(re, ln, regex.exec(re, ln))
std.slfree(ln)
- | `std.None:
+ | `bio.Eof:
+ break
+ | `bio.Err:
+ std.put("error reading from input")
break
;;
;;
--- a/mbld/deps.myr
+++ b/mbld/deps.myr
@@ -215,11 +215,11 @@
while true
lnum++
match bio.readln(f)
- | `std.Some ln:
+ | `bio.Err: std.fatal("unable to read {}\n", path)
+ | `bio.Eof: break
+ | `bio.Ok ln:
(cflags, libs) = getcflags(ln, cflags, libs)
std.slfree(ln)
- | `std.None:
- break
;;
;;
bio.close(f)
@@ -251,7 +251,7 @@
-> (cflags, libs)
}
-const getdeps = {b : build#, ds : depscan#, path
+const getdeps = {b, ds, path
var deps, lnum
var f
@@ -261,10 +261,11 @@
while true
lnum++
match bio.readln(f)
- | `std.Some ln:
+ | `bio.Err: std.fatal("unable to read {}\n", path)
+ | `bio.Eof: break
+ | `bio.Ok ln:
deps = depname(deps, ln, lnum)
std.slfree(ln)
- | `std.None: break
;;
;;
bio.close(f)
@@ -315,19 +316,21 @@
f = openlib(lib, incs)
match bio.getc(f)
- | `std.Some 'U': /* nothing */
- | `std.Some _: std.fatal("library {}: corrupt or invalid usefile\n", lib)
- | `std.None: std.fatal("library {}: could not read usefile\n", lib)
+ | `bio.Ok 'U': /* nothing */
+ | `bio.Ok _: std.fatal("library {}: corrupt or invalid usefile\n", lib)
+ | `bio.Err: std.fatal("library {}: could not read usefile\n", lib)
+ | `bio.Eof: std.fatal("library {}: could not read usefile\n", lib)
;;
match bio.getbe32(f)
- | `std.Some Abiversion: /* nothing: version matches. */
- | `std.Some v:
+ | `bio.Ok Abiversion: /* nothing: version matches. */
+ | `bio.Ok v:
if v < Abiversion
std.fput(1, "library {}: warning: old abi version {}\n", lib, v)
else
std.fput(1, "library {}: usefile version {} unknown\n", lib, v)
;;
- | `std.None: std.fatal("library {}: corrupt or invalid usefile\n", lib)
+ | `bio.Eof: std.fatal("library {}: corrupt or truncated usefile\n", lib)
+ | `bio.Err: std.fatal("library {}: error reading usefile\n", lib)
;;
std.slfree(rdstr(f))
@@ -335,14 +338,15 @@
deps = [][:]
while !done
match bio.getc(f)
- | `std.Some 'L':
+ | `bio.Ok 'L':
d = rdstr(f)
deps = std.slpush(deps, d)
- | `std.Some 'X':
+ | `bio.Ok 'X':
d = rdstr(f)
dg.extlibs = std.slpush(dg.extlibs, d)
- | `std.Some _: done = true
- | `std.None: done = true
+ | `bio.Ok _: done = true
+ | `bio.Eof: done = true
+ | `bio.Err: std.fatal("io error reading {}", lib)
;;
;;
bio.close(f)
@@ -388,10 +392,11 @@
var sl
match bio.getbe32(f)
- | `std.Some l:
+ | `bio.Ok l:
len = l
sl = std.slalloc(len)
- | `std.None: std.die("string length not readable")
+ | `bio.Eof: std.die("end of file while reading string")
+ | `bio.Err: std.die("error while reading string")
;;
bio.read(f, sl)
-> sl