io-v2-protocol.js (3215B)
1 export function canonicalNat(reader, handle, bits) { 2 if (!Number.isInteger(bits) || bits < 0 || bits > 64) 3 throw new Error('Invalid natural bit bound'); 4 let rest = handle, value = 0n, count = 0, last = 0n; 5 for (;;) { 6 const node = reader.observe(rest).node; 7 if (node.kind === 'leaf') 8 return count === 0 || last === 1n ? value : undefined; 9 if (count === bits || node.kind !== 'fork') 10 return undefined; 11 const bit = reader.observe(node.left).node; 12 if (bit.kind === 'leaf') 13 last = 0n; 14 else if (bit.kind === 'stem' && reader.observe(bit.child).node.kind === 'leaf') 15 last = 1n; 16 else 17 return undefined; 18 value |= last << BigInt(count++); 19 rest = node.right; 20 } 21 } 22 function name(reader, handle) { 23 let rest = handle, value = ''; 24 for (;;) { 25 const node = reader.observe(rest).node; 26 if (node.kind === 'leaf') 27 return value || undefined; 28 // Observe only the excess spine cell, never its head or tail. 29 if (value.length === 64 || node.kind !== 'fork') 30 return undefined; 31 const code = canonicalNat(reader, node.left, 7); 32 if (code === undefined || !(code >= 97n && code <= 122n || value.length > 0 && (code >= 48n && code <= 57n || code === 45n))) 33 return undefined; 34 value += String.fromCharCode(Number(code)); 35 rest = node.right; 36 } 37 } 38 export class ProtocolError extends Error { 39 } 40 export function decodeAction(reader, handle) { 41 const action = reader.observe(handle).node; 42 if (action.kind !== 'fork') 43 throw new ProtocolError('invalid computation shape'); 44 const code = canonicalNat(reader, action.left, 8); 45 if (code !== undefined && code <= 9n) 46 return { kind: 'core', code: Number(code), payload: action.right }; 47 if (code !== 10n) 48 throw new ProtocolError('unknown computation constructor'); 49 const group = reader.observe(action.right).node; 50 if (group.kind !== 'fork') 51 throw new ProtocolError('invalid facility header'); 52 const revision = reader.observe(group.right).node; 53 if (revision.kind !== 'fork') 54 throw new ProtocolError('invalid facility header'); 55 const operation = reader.observe(revision.right).node; 56 if (operation.kind !== 'fork') 57 throw new ProtocolError('invalid facility header'); 58 const g = name(reader, group.left); 59 if (g === undefined) 60 throw new ProtocolError('invalid facility header'); 61 const v = canonicalNat(reader, revision.left, 16); 62 if (v === undefined || v === 0n) 63 throw new ProtocolError('invalid facility header'); 64 const op = name(reader, operation.left); 65 if (op === undefined) 66 throw new ProtocolError('invalid facility header'); 67 return { kind: 'request', group: g, revision: Number(v), operation: op, arguments: operation.right }; 68 } 69 export function header(reader, handle) { 70 const action = decodeAction(reader, handle); 71 return action.kind === 'core' ? { kind: action.kind, code: action.code } 72 : { kind: action.kind, group: action.group, revision: action.revision, operation: action.operation }; 73 }