diff options
author | Ori Bernstein <ori@eigenstate.org> | 2015-10-04 10:59:36 -0700 |
---|---|---|
committer | Ori Bernstein <ori@eigenstate.org> | 2015-10-04 20:35:52 -0700 |
commit | 8728342de349e7029312ddbd33c2710d2d03fff2 (patch) | |
tree | 392b0b93b70b3129f9fb790bc04cac9571eb05e5 | |
parent | 35bde60621ecf5cbcf07b743cafd9724f87faccb (diff) | |
download | mc-8728342de349e7029312ddbd33c2710d2d03fff2.tar.gz |
Make the interface for libbio expose errors.
Filling in all the error conditions correctly is only lightly
tested, but it seems to work.
-rw-r--r-- | lib/bio/bio.myr | 260 | ||||
-rw-r--r-- | lib/bio/geti.myr | 50 | ||||
-rw-r--r-- | lib/bio/puti.myr | 64 | ||||
-rw-r--r-- | lib/regex/redump.myr | 7 | ||||
-rw-r--r-- | mbld/deps.myr | 41 |
5 files changed, 266 insertions, 156 deletions
diff --git a/lib/bio/bio.myr b/lib/bio/bio.myr index a546152..8e8e9fe 100644 --- a/lib/bio/bio.myr +++ b/lib/bio/bio.myr @@ -10,6 +10,7 @@ pkg bio = /* backing fd */ fd : std.fd mode : mode + haderr : bool /* read buffer */ rbuf : byte[:] @@ -21,6 +22,12 @@ pkg bio = 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,35 +37,36 @@ pkg bio = 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 */ 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 @@ const write = {f, src 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 @@ const read = {f, dst 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 @@ const read = {f, dst 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 @@ const flush = {f 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 @@ const seek = {f, off /* 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,53 +272,63 @@ const putc = {f, c 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 */ 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 - ;; - 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 */ + 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) ;; - -> ensureread(f, len) } /* @@ -319,19 +357,21 @@ generic putbe = {f, v : @a::(numeric,integral) /* 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,20 +391,23 @@ const readto = {f, delim /* 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 ;; } 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 @@ const readln = {f 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 @@ const readln = {f 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,20 +451,30 @@ const readln = {f std.die("unreachable") } +const unwrapc = {cc, v + match cc + | `Ok c: -> c + | _: -> v + ;; +} + const readdelim = {f, delim, drop var ret 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 @@ const readdelim = {f, delim, drop ret = readinto(f, ret, i - f.rstart) ;; f.rstart += delim.len - -> `std.Some ret + -> `Ok ret ;; :nextiterread ;; @@ -476,9 +532,15 @@ const readinto = {f, buf, n 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 @@ const ensureread = {f, n 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 @@ const writebuf = {fd, src 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 @@ const fill = {f, min 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 + ;; } diff --git a/lib/bio/geti.myr b/lib/bio/geti.myr index 48de2bc..ce20219 100644 --- a/lib/bio/geti.myr +++ b/lib/bio/geti.myr @@ -4,51 +4,55 @@ use "bio.use" 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))) ;; /* 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)) } /* 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 - ;; - for i = 0; i < n; i++ - v <<= 8 - v |= (f.rbuf[f.rstart++] castto(uint64)) + 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)) ;; - -> `std.Some v castto(@a::(numeric,integral)) } generic getbe8 = {f; -> getbe(f, 1)} diff --git a/lib/bio/puti.myr b/lib/bio/puti.myr index 16f014a..035a1ba 100644 --- a/lib/bio/puti.myr +++ b/lib/bio/puti.myr @@ -4,16 +4,16 @@ use "bio.use" 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 @@ generic putle64 = {f, v; -> putle(f, v castto(uint64), 8)} 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:]) } diff --git a/lib/regex/redump.myr b/lib/regex/redump.myr index 920b257..5c1e7bc 100644 --- a/lib/regex/redump.myr +++ b/lib/regex/redump.myr @@ -57,10 +57,13 @@ const runall = {re, files 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 ;; ;; diff --git a/mbld/deps.myr b/mbld/deps.myr index 66aeb6a..296ea60 100644 --- a/mbld/deps.myr +++ b/mbld/deps.myr @@ -215,11 +215,11 @@ const scrapecflags = {b, ds, path 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 @@ const getcflags = {ln, cflags, libs -> (cflags, libs) } -const getdeps = {b : build#, ds : depscan#, path +const getdeps = {b, ds, path var deps, lnum var f @@ -261,10 +261,11 @@ const getdeps = {b : build#, ds : depscan#, path 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 @@ const scrapelibs = {dg, lib, incs 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 @@ const scrapelibs = {dg, lib, incs 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 @@ const rdstr = {f 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 |