59 lines
2.3 KiB
JavaScript
59 lines
2.3 KiB
JavaScript
/**
|
|
* Little-endian primitives for reading the walk/snapshot wire buffers. Shared by
|
|
* the hand-written decoders and the generated layout decoder so both interpret
|
|
* the bytes identically.
|
|
*/
|
|
const textDecoder = new TextDecoder("utf-8");
|
|
/** Read a u16 (LE) at `off`. */
|
|
export function ru16(view, off) {
|
|
return view.getUint16(off, true);
|
|
}
|
|
/** Read a u32 (LE) at `off`. */
|
|
export function ru32(view, off) {
|
|
return view.getUint32(off, true);
|
|
}
|
|
/** Read a UTF-8 string of `len` bytes at `off`. Very short ASCII runs decode
|
|
* with a charCode loop — TextDecoder's per-call overhead dominates there. The
|
|
* threshold is lower than the encode-side `INLINE_STR_MAX`: decoding builds a
|
|
* JS string by concatenation, which costs more per char than storing bytes.
|
|
* Measured crossover ~8-12 bytes (Node 24: 8B 30 vs 57 ns, 16B 64 vs 57). */
|
|
export function rstr(buf, off, len) {
|
|
if (len === 0)
|
|
return "";
|
|
if (len <= 8) {
|
|
let ascii = true;
|
|
for (let i = 0; i < len; i++) {
|
|
if (buf[off + i] > 127) {
|
|
ascii = false;
|
|
break;
|
|
}
|
|
}
|
|
if (ascii) {
|
|
let s = "";
|
|
for (let i = 0; i < len; i++)
|
|
s += String.fromCharCode(buf[off + i]);
|
|
return s;
|
|
}
|
|
}
|
|
return textDecoder.decode(buf.subarray(off, off + len));
|
|
}
|
|
/**
|
|
* Decode the 24-byte position block ([startOffset, endOffset, startLine,
|
|
* startColumn, endLine, endColumn], u32 LE each) shared by the walk prefixes
|
|
* and the snapshot node structs. A zero start line is the sentinel for
|
|
* synthesized nodes with no source range (e.g. GFM autolink-literal nodes, or a
|
|
* plugin-built replacement spliced by the rebuild): unist lines are 1-based, so
|
|
* line 0 always means "no source position" — even when the rebuild has rebased
|
|
* the (otherwise zero) offset by the spliced subtree's source base. Surfaced as
|
|
* `undefined` so a node reads the same however it's reached.
|
|
*/
|
|
export function readPosition(view, off) {
|
|
const startLine = ru32(view, off + 8);
|
|
if (startLine === 0)
|
|
return undefined;
|
|
const startOffset = ru32(view, off);
|
|
return {
|
|
start: { offset: startOffset, line: startLine, column: ru32(view, off + 12) },
|
|
end: { offset: ru32(view, off + 4), line: ru32(view, off + 16), column: ru32(view, off + 20) },
|
|
};
|
|
}
|