bend-kit-journal Releases (5): 0.3.1.0 0xaffc49d02c2d4450c3672c0c80f393fd indexed https://jend.leeeet.dev/files.txt?path=bend-kit-journal@0.3.1.0 0.3.0.1 0x5ea9815b576b1e930ae6bea0244356b4 unindexed https://jend.leeeet.dev/files.txt?path=bend-kit-journal@0.3.0.1 0.3.0.0 0xc2feaba1ac738108fed1e76f01f1fb0d unindexed https://jend.leeeet.dev/files.txt?path=bend-kit-journal@0.3.0.0 0.2.0.0 0x228909d5e58383594fb8ed54c2ddc150 unindexed https://jend.leeeet.dev/files.txt?path=bend-kit-journal@0.2.0.0 0.1.0.0 0x801060290904d418c14ffa544e867442 unindexed https://jend.leeeet.dev/files.txt?path=bend-kit-journal@0.1.0.0 Definitions (65): Journal.Tail (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=Tail) # Clean: the input ends on a frame boundary. # Torn: the input ends inside a frame. A crash cut the last append short. # Corrupt: a whole header or a whole frame fails its check. Recovery does not go past it. type Tail is Data: Clean{} Torn{} Corrupt{why: String} Journal.Step (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=Step) # One decoding step. Short, Bad, and End give the input back untouched, at the frame start. type Step<-P: Type, -S: Type> is Type: End{s: S} Short{s: S} Bad{why: String, s: S} Got{p: P, s: S} Journal.Recovery (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=Recovery) # The committed records in order, how the input ends, and the input from the first frame not taken. type Recovery is Type: Recovery{records: List, tail: Tail, rest: S} Journal.Replay (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=Replay) # The state after every committed record, how the input ends, and the input from the first frame not taken. type Replay<-A: Type, -S: Type> is Type: Replay{acc: A, tail: Tail, rest: S} Journal.next.verdict (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.verdict) def next.verdict(~P: Type, ~S: Type, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ok: Bool, +len: U32, s: S, p: P) -> Step: Journal.next.summed (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.summed) def next.summed(~P: Type, ~S: Type, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, +want: U32, +len: U32, s: S, r: P & U32) -> Step: Journal.next.cut (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.cut) def next.cut(~P: Type, ~S: Type, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, +want: U32, +len: U32, r: S & P) -> Step: Journal.next.want (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.want) def next.want(~P: Type, ~S: Type, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, +len: U32, r: S & U32) -> Step: Journal.next.body (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.body) def next.body(~P: Type, ~S: Type, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, +len: U32, r: S & Bool) -> Step: Journal.next.size (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.size) def next.size(~P: Type, ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, over: Bool, +len: U32, s: S) -> Step: Journal.next.head (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.head) def next.head(~P: Type, ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, ok: Bool, +max: U32, +len: U32, s: S) -> Step: Journal.next.lenx (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.lenx) def next.lenx(~P: Type, ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, +max: U32, +len: U32, r: S & U32) -> Step: Journal.next.len (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.len) def next.len(~P: Type, ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, +max: U32, r: S & U32) -> Step: Journal.next.start (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.start) # The two length fields are checked before the payload is waited for, so a damaged length is # Bad and never a Torn tail that hides the frames after it. def next.start(~P: Type, ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, +max: U32, r: S & Bool) -> Step: Journal.next.any (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next.any) def next.any(~P: Type, ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, +max: U32, r: S & Bool) -> Step: Journal.next (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=next) # One frame from s. max caps a payload's length. def next(~P: Type, ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, +max: U32, s: S) -> Step: Journal.recover.con (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=recover.con) def recover.con(~a: Quant, ~P: Kind(a), ~S: Type, p: P, r: Recovery) -> Recovery: Journal.recover.go (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=recover.go) # Every frame from st on. Once n frames are taken, the next one is the last: the tail is Corrupt, # and rest starts after it. Callers give n at least the frame count, so this never happens. def recover.go(~a: Quant, ~P: Kind(a), ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, n: Nat, +max: U32, st: Step) -> Recovery: Journal.recover (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=recover) # Every committed record of s, in order. def recover(~a: Quant, ~P: Kind(a), ~S: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, n: Nat, +max: U32, s: S) -> Recovery: Journal.replay.go (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=replay.go) # step folded over every frame from st on, without a list of the records. n as in recover.go. def replay.go(~P: Type, ~S: Type, ~A: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, ~step: A -> P -> A, n: Nat, +max: U32, st: Step, acc: A) -> Replay: Journal.replay (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=replay) def replay(~P: Type, ~S: Type, ~A: Type, ~fits: S -> @+at:U32 -> @+n:U32 -> S & Bool, ~u32: S -> @+at:U32 -> S & U32, ~cut: S -> @+hs:U32 -> @+len:U32 -> S & P, ~skip: S -> @+hs:U32 -> @+len:U32 -> S, ~sum: P -> P & U32, ~step: A -> P -> A, n: Nat, +max: U32, s: S, acc: A) -> Replay: Journal.list.fits (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.fits) def list.fits(xs: List<&2, U32>, +at: U32, +n: U32) -> List<&2, U32> & Bool: Journal.list.field (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.field) def list.field(xs: List<&2, U32>) -> U32: Journal.list.u32 (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.u32) def list.u32(xs: List<&2, U32>, +at: U32) -> List<&2, U32> & U32: Journal.list.cut (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.cut) def list.cut(xs: List<&2, U32>, +hs: U32, +len: U32) -> List<&2, U32> & List<&2, U32>: Journal.list.skip (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.skip) def list.skip(xs: List<&2, U32>, +hs: U32, +len: U32) -> List<&2, U32>: Journal.crc.bit (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=crc.bit) def crc.bit(n: Nat, +c: U32) -> U32: Journal.crc.go (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=crc.go) def crc.go(xs: List<&2, U32>, +c: U32) -> U32: Journal.list.crc (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.crc) # CRC-32 (ISO-HDLC, as zlib and Hash.crc32) of a list of octets, bit by bit. def list.crc(xs: List<&2, U32>) -> U32: Journal.list.sum (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.sum) def list.sum(xs: List<&2, U32>) -> List<&2, U32> & U32: Journal.list.next (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.next) def list.next(+max: U32, xs: List<&2, U32>) -> Step, List<&2, U32>>: Journal.list.recover (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.recover) def list.recover(n: Nat, +max: U32, xs: List<&2, U32>) -> Recovery<&2, List<&2, U32>, List<&2, U32>>: Journal.list.replay (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.replay) def list.replay(~A: Type, ~step: A -> List<&2, U32> -> A, n: Nat, +max: U32, xs: List<&2, U32>, acc: A) -> Replay>: Journal.list.head (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.head) def list.head(+len: U32, +sum: U32) -> List<&2, U32>: Journal.list.frame (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.frame) def list.frame(+p: List<&2, U32>) -> List<&2, U32>: Journal.list.log (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=list.log) # The frames of rs, one after another: what appending each record writes. def list.log(rs: List<&2, List<&2, U32>>) -> List<&2, U32>: Journal.Cur (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=Cur) # The Bytes instance. A cursor reads b from pos; pos only moves forward, past whole frames. type Cur is Type: Cur{b: Bytes.Bytes, pos: U32} Journal.cur.fits (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=cur.fits) def cur.fits(c: Cur, +at: U32, +n: U32) -> Cur & Bool: Journal.cur.field (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=cur.field) def cur.field(m: Maybe<&2, U32>) -> U32: Journal.cur.u32.fin (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=cur.u32.fin) def cur.u32.fin(+pos: U32, r: Bytes.Bytes & Maybe<&2, U32>) -> Cur & U32: Journal.cur.u32 (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=cur.u32) def cur.u32(c: Cur, +at: U32) -> Cur & U32: Journal.cur.cut.fin (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=cur.cut.fin) def cur.cut.fin(+pos: U32, r: Bytes.Bytes & Bytes.Bytes) -> Cur & Bytes.Bytes: Journal.cur.cut (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=cur.cut) def cur.cut(c: Cur, +hs: U32, +len: U32) -> Cur & Bytes.Bytes: Journal.cur.skip (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=cur.skip) def cur.skip(c: Cur, +hs: U32, +len: U32) -> Cur: Journal.cur.sum (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=cur.sum) # ponytail: Hash.crc32 builds its 256-entry table per record; share one table if recovery gets hot. def cur.sum(p: Bytes.Bytes) -> Bytes.Bytes & U32: Journal.fuel (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=fuel) # Every frame of b holds at least 12 octets, so len / 12 + 1 steps reach the end. def fuel(+len: U32) -> Nat: Journal.replay.bytes (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=replay.bytes) # step folded over every committed record of b, in order. rest.pos is where the committed prefix ends. def replay.bytes(~A: Type, ~step: A -> Bytes.Bytes -> A, +max: U32, b: Bytes.Bytes, acc: A) -> Replay: Journal.frame.of (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=frame.of) def frame.of(+len: U32, r: Bytes.Bytes & U32) -> Bytes.Bytes: Journal.frame (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=frame) # The frame that appending p writes. def frame(p: Bytes.Bytes) -> Bytes.Bytes: Journal.open.words (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=open.words) # Opens path for reading and appending, creating it, and fsyncs its directory. Answers the file and # its whole contents. Fails with EFBIG when the file holds more than limit octets. def open.words(path: String, limit: U32) -> IO(Result<&1, &1, U32 & String, File & U32 & Array>): Journal.append.words (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=append.words) # Writes len octets of words at the end of file, then fsyncs it. def append.words(file: File, len: U32, words: Array) -> IO(File & Result<&1, &1, U32 & String, Unit>): Journal.truncate (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=truncate) # Cuts file to len octets, then fsyncs it. def truncate(file: File, len: U32) -> IO(File & Result<&1, &1, U32 & String, Unit>): Journal.Journal (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=Journal) # An open journal. max caps a record's length. end is the file's length. ok turns False when an # append fails: what reached the disk is then unknown, so appends stop until the journal is reopened. type Journal is Type: Journal{file: File, max: U32, end: U32, ok: Bool} Journal.open.cut (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=open.cut) def open.cut(r: File & Result<&1, &1, U32 & String, Unit>, +max: U32, +end: U32, -A: Type, acc: A) -> IO(Result<&1, &1, U32 & String, Journal & A>): Journal.open.tail (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=open.tail) def open.tail(t: Tail, file: File, +max: U32, +pos: U32, -A: Type, acc: A) -> IO(Result<&1, &1, U32 & String, Journal & A>): Journal.open.replayed (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=open.replayed) def open.replayed(file: File, +max: U32, -A: Type, r: Replay) -> IO(Result<&1, &1, U32 & String, Journal & A>): Journal.open.read (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=open.read) def open.read(~A: Type, ~step: A -> Bytes.Bytes -> A, r: Result<&1, &1, U32 & String, File & U32 & Array>, +max: U32, acc: A) -> IO(Result<&1, &1, U32 & String, Journal & A>): Journal.open (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=open) # Opens the journal at path and folds step over its committed records, from acc. # A torn tail is cut off and fsynced before open answers. A corrupt frame fails with EINVAL and # its offset; the file is left as it is. max caps a record's length; limit caps the file's size. def open(~A: Type, ~step: A -> Bytes.Bytes -> A, path: String, +max: U32, limit: U32, acc: A) -> IO(Result<&1, &1, U32 & String, Journal & A>): Journal.append.back (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=append.back) def append.back(+max: U32, +end: U32, +len: U32, r: File & Result<&1, &1, U32 & String, Unit>) -> Journal & Result<&1, &1, U32 & String, Unit>: Journal.append.write (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=append.write) def append.write(file: File, +max: U32, +end: U32, f: Bytes.Bytes) -> IO(Journal & Result<&1, &1, U32 & String, Unit>): Journal.append.refuse (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=append.refuse) def append.refuse(file: File, +max: U32, +end: U32, +ok: Bool, e: U32 & String) -> IO(Journal & Result<&1, &1, U32 & String, Unit>): Journal.append.size (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=append.size) # The file stays under 4 GiB, so every offset and the next open's read fit a U32. def append.size(over: Bool, big: Bool, file: File, +max: U32, +end: U32, p: Bytes.Bytes) -> IO(Journal & Result<&1, &1, U32 & String, Unit>): Journal.append.ok (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=append.ok) def append.ok(ok: Bool, file: File, +max: U32, +end: U32, p: Bytes.Bytes) -> IO(Journal & Result<&1, &1, U32 & String, Unit>): Journal.append (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=append) # Appends p as one frame and answers once it is fsynced: Done means durable, under the trust # assumptions in README.md. After a Fail the record may or may not survive a reopen. def append(j: Journal, p: Bytes.Bytes) -> IO(Journal & Result<&1, &1, U32 & String, Unit>): Journal.close (https://jend.leeeet.dev/definition.txt?path=bend-kit-journal@0.3.1.0/journal.bend&name=close) def close(j: Journal) -> IO(Unit):