bytes.bend source
bytes.bend on the hub · documented module
# Bytes: a List<&2, U32> of values below 256. Big-endian numbers (the# network's order) and the little-endian ones RTMP has here and there,# cuts of a buffer, and bytes as text. The loops are tail calls: a# nested recursion costs a frame per byte in Bend, and a video frame is# tens of thousands of bytes.import Base# Numbers# -------# The big-endian number in the first n bytes (missing bytes count as 0).def Bytes.be(n: Nat, bs: List<&2, U32>, +acc: U32) -> U32: match n bs: case 1n+p Con{b, t}: Bytes.be(p, t, U32.or(U32.shln(acc, 8n), U32.and(b, 255))) case 1n+p Nil{}: Bytes.be(p, Nil{}, U32.shln(acc, 8n)) case _ _: accdef Bytes.u8(bs: List<&2, U32>) -> U32: Bytes.be(1n, bs, 0)def Bytes.u16(bs: List<&2, U32>) -> U32: Bytes.be(2n, bs, 0)def Bytes.u24(bs: List<&2, U32>) -> U32: Bytes.be(3n, bs, 0)def Bytes.u32(bs: List<&2, U32>) -> U32: Bytes.be(4n, bs, 0)# The little-endian U32 in the first 4 bytes.def Bytes.u32le(bs: List<&2, U32>) -> U32: match bs: case Con{a, Con{b, Con{c, Con{d, _}}}}: U32.or(U32.or(a, U32.shln(b, 8n)), U32.or(U32.shln(c, 16n), U32.shln(d, 24n))) case _: 0def Bytes.b(+x: U32, n: Nat) -> U32: U32.and(U32.shrn(x, n), 255)# x as 2, 3 and 4 big-endian bytes, in front of rest.def Bytes.put16(+x: U32, rest: List<&2, U32>) -> List<&2, U32>: Bytes.b(x, 8n) <> Bytes.b(x, 0n) <> restdef Bytes.put24(+x: U32, rest: List<&2, U32>) -> List<&2, U32>: Bytes.b(x, 16n) <> Bytes.b(x, 8n) <> Bytes.b(x, 0n) <> restdef Bytes.put32(+x: U32, rest: List<&2, U32>) -> List<&2, U32>: Bytes.b(x, 24n) <> Bytes.b(x, 16n) <> Bytes.b(x, 8n) <> Bytes.b(x, 0n) <> rest# x as 4 little-endian bytes, in front of rest.def Bytes.put32le(+x: U32, rest: List<&2, U32>) -> List<&2, U32>: Bytes.b(x, 0n) <> Bytes.b(x, 8n) <> Bytes.b(x, 16n) <> Bytes.b(x, 24n) <> rest# Cuts# ----# acc reversed in front of tail.def Bytes.onto(acc: List<&2, U32>, tail: List<&2, U32>) -> List<&2, U32>: match acc: case Nil{}: tail case Con{b, t}: Bytes.onto(t, b <> tail)def Bytes.rev(bs: List<&2, U32>) -> List<&2, U32>: Bytes.onto(bs, Nil{})# a then b, without a frame per byte of a.def Bytes.cat(a: List<&2, U32>, b: List<&2, U32>) -> List<&2, U32>: Bytes.onto(Bytes.rev(a), b)def Bytes.len.go(bs: List<&2, U32>, +n: U32) -> U32: match bs: case Nil{}: n case Con{_, t}: Bytes.len.go(t, U32.add(n, 1))def Bytes.len(bs: List<&2, U32>) -> U32: Bytes.len.go(bs, 0)# A buffer cut after its first n bytes: both parts, or Short when it# has fewer (then nothing is taken).type Cut is Data: Short{} Cut{head: List<&2, U32>, rest: List<&2, U32>}def Bytes.cut.go(n: Nat, bs: List<&2, U32>, acc: List<&2, U32>) -> Cut: match n bs: case 0n rest: Cut{Bytes.rev(acc), rest} case 1n+p Con{b, t}: Bytes.cut.go(p, t, b <> acc) case 1n+p Nil{}: Short{}def Bytes.cut(n: U32, bs: List<&2, U32>) -> Cut: Bytes.cut.go(U32.to_nat(n), bs, Nil{})def Bytes.drop(n: Nat, bs: List<&2, U32>) -> List<&2, U32>: match n bs: case 1n+p Con{_, t}: Bytes.drop(p, t) case _ rest: restdef Bytes.take.go(n: Nat, bs: List<&2, U32>, acc: List<&2, U32>) -> List<&2, U32>: match n bs: case 1n+p Con{b, t}: Bytes.take.go(p, t, b <> acc) case _ _: Bytes.rev(acc)# The first n bytes (fewer if there are fewer).def Bytes.take(n: Nat, bs: List<&2, U32>) -> List<&2, U32>: Bytes.take.go(n, bs, Nil{})# Text# ----def Bytes.text.go(bs: List<&2, U32>, acc: String) -> String: match bs: case Nil{}: String.reverse(acc) case Con{b, t}: Bytes.text.go(t, SCon{Chr{b}, acc})# Each byte as the char of that code (ASCII; Latin-1 above it).def Bytes.text(bs: List<&2, U32>) -> String: Bytes.text.go(bs, SNil{})def Bytes.of.go(s: String, acc: List<&2, U32>) -> List<&2, U32>: match s: case SNil{}: Bytes.rev(acc) case SCon{Chr{c}, t}: Bytes.of.go(t, U32.and(c, 255) <> acc)# Each char as one byte (for text known to be ASCII).def Bytes.of(s: String) -> List<&2, U32>: Bytes.of.go(s, Nil{})# Parts kept last-first, joined in order in front of acc.def Bytes.join(parts: List<&2, List<&2, U32>>, acc: List<&2, U32>) -> List<&2, U32>: match parts: case Nil{}: acc case Con{p, t}: Bytes.join(t, Bytes.cat(p, acc))def Bytes.split.go(n: Nat, bs: List<&2, U32>, acc: List<&2, U32>) -> Cut: match n bs: case 1n+p Con{b, t}: Bytes.split.go(p, t, b <> acc) case _ rest: Cut{Bytes.rev(acc), rest}# The first n bytes and the rest; all of it and nothing when it has# fewer.def Bytes.split(n: Nat, bs: List<&2, U32>) -> Cut: Bytes.split.go(n, bs, Nil{})