oficina / slides Public

Clone
git clone https://amadan.net/oficina/slides

Plain git — no account needed to clone.

Download

Download this file

1// fixtures/frames.mjs — the updates wire's framing (spec plan, Global
2// Constraints: "each update framed as a 4-byte big-endian length then
3// the bytes, in seq order"). live.go's updatesGet writes it; this reads
4// it back into the individual Yjs updates Y.applyUpdate takes one at a
5// time.
6//
7// Docs' endpoint concatenates updates with no framing and its client
8// loops Y.readUpdate over one decoder; Slides frames them so the
9// client never has to know how a Yjs update finds its own end, and so
10// a snapshot (one large update) and a tail (many small ones) travel the
11// same way. Pure bytes in, bytes out, so it is tested under plain Node.
12
13/** The frames a body carries, in order. Throws on a body that does not divide cleanly. */
14export function parseFrames (bytes) {
15 const out = []
16 let at = 0
17 while (at < bytes.length) {
18 if (bytes.length - at < 4) throw new Error(`frame: ${bytes.length - at} trailing bytes with no length prefix`)
19 const n = ((bytes[at] << 24) >>> 0) + (bytes[at + 1] << 16) + (bytes[at + 2] << 8) + bytes[at + 3]
20 at += 4
21 if (bytes.length - at < n) throw new Error(`frame: a frame claims ${n} bytes and ${bytes.length - at} remain`)
22 out.push(bytes.subarray(at, at + n))
23 at += n
24 }
25 return out
26}
27
28/** One frame: the length prefix then the bytes (what a test or a beacon builds). */
29export function frame (update) {
30 const out = new Uint8Array(4 + update.length)
31 out[0] = (update.length >>> 24) & 255
32 out[1] = (update.length >>> 16) & 255
33 out[2] = (update.length >>> 8) & 255
34 out[3] = update.length & 255
35 out.set(update, 4)
36 return out
37}
38