| 1 | import { ByteWriter, concat, fromUtf8, utf8 } from "./bytes.js"; |
| 2 | |
| 3 | export const FLUSH = utf8("0000"); |
| 4 | export const DELIM = utf8("0001"); |
| 5 | export const RESPONSE_END = utf8("0002"); |
| 6 | |
| 7 | export function pkt(data: string | Uint8Array): Uint8Array { |
| 8 | const body = typeof data === "string" ? utf8(data) : data; |
| 9 | if (body.length > 65516) throw new Error("pkt-line too long"); |
| 10 | return concat([utf8((body.length + 4).toString(16).padStart(4, "0")), body]); |
| 11 | } |
| 12 | |
| 13 | export type Pkt = { type: "data"; data: Uint8Array } | { type: "flush" } | { type: "delim" } | { type: "end" }; |
| 14 | |
| 15 | /** Decode a buffer of pkt-lines. Returns the packets and the offset where parsing stopped. */ |
| 16 | export function readPkts(buf: Uint8Array, start = 0, stopAtFlush = false): { pkts: Pkt[]; offset: number } { |
| 17 | const pkts: Pkt[] = []; |
| 18 | let i = start; |
| 19 | while (i + 4 <= buf.length) { |
| 20 | const len = parseInt(fromUtf8(buf.subarray(i, i + 4)), 16); |
| 21 | if (Number.isNaN(len)) throw new Error(`bad pkt-line length at ${i}`); |
| 22 | if (len === 0) { |
| 23 | pkts.push({ type: "flush" }); |
| 24 | i += 4; |
| 25 | if (stopAtFlush) break; |
| 26 | continue; |
| 27 | } |
| 28 | if (len === 1) { |
| 29 | pkts.push({ type: "delim" }); |
| 30 | i += 4; |
| 31 | continue; |
| 32 | } |
| 33 | if (len === 2) { |
| 34 | pkts.push({ type: "end" }); |
| 35 | i += 4; |
| 36 | continue; |
| 37 | } |
| 38 | if (len < 4 || i + len > buf.length) throw new Error(`truncated pkt-line at ${i}`); |
| 39 | pkts.push({ type: "data", data: buf.subarray(i + 4, i + len) }); |
| 40 | i += len; |
| 41 | } |
| 42 | return { pkts, offset: i }; |
| 43 | } |
| 44 | |
| 45 | /** Text of a data pkt without its trailing newline. */ |
| 46 | export const pktText = (p: Pkt): string => (p.type === "data" ? fromUtf8(p.data).replace(/\n$/, "") : ""); |
| 47 | |
| 48 | /** Wrap pack bytes in side-band-64k packets on the given band. */ |
| 49 | export function sideband(band: 1 | 2 | 3, data: Uint8Array, w: ByteWriter): void { |
| 50 | const max = 65515; |
| 51 | for (let i = 0; i < data.length; i += max) { |
| 52 | const chunk = data.subarray(i, Math.min(data.length, i + max)); |
| 53 | w.write(pkt(concat([new Uint8Array([band]), chunk]))); |
| 54 | } |
| 55 | } |