3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-12 11:48:29 +01:00

Answer stability round 1: the escape flags flow through return values, and V8's failed typed-array allocation is refused as its memory running short

This commit is contained in:
2026-10-13 12:02:31 +02:00
parent ae4aa55e30
commit e6fa3db1f5
4 changed files with 9 additions and 8 deletions
+3 -3
View File
@@ -410,9 +410,9 @@ typed on `ConvertError` takes both.
A call reports the first refusal in document order and stops, so a document with several surfaces
them one per call. Every refusal is deterministic — there is no I/O anywhere — so a retry returns
the identical error: fix the input, or set the document aside. On JavaScriptCore (Bun, Safari),
retry once any call that returns `unsupported-node-shape` at the empty path: that engine reports a
heap running short the same way as a string too long to build.
the identical error: fix the input, or set the document aside. The exception is memory running
short, which an engine may report as `unsupported-node-shape` at the empty path, and JavaScriptCore
(Bun, Safari) reports the same way as a string too long to build: retry such a call once.
### Parsing
+3 -3
View File
@@ -95,8 +95,7 @@ function escape(segments: readonly InlineSegment[], container: LineContainer, cl
const code = codes[index]
return code === undefined ? undefined : escapingCodes[code]
}
const escaped = escapeClaims(scan, escapings, container, claims)
escapeClosedRuns(scan, escapings, escaped)
const escaped = escapeClosedRuns(scan, escapings, escapeClaims(scan, escapings, container, claims))
const { output, placements } = backslashed(segments, scan, escaped)
if (container === 'paragraph' && opensLinkDefinition(output)) {
if (segments[0]?.nodes !== undefined) return { line: output, openingLinkAsDirective: true, unspellableRuns: [] }
@@ -227,7 +226,7 @@ function claimsCharacter({ container, escapings, headingCloser, linkClose, scan
// CommonMark reads no escape inside a code span, so a backtick string left beside an escape still closes a span that an earlier bare run of its length opens.
// The walk runs backwards and escapes only the run it just read, so every index it reads still holds the claims alone.
function escapeClosedRuns(scan: string, escapings: Escapings, escaped: Uint8Array): void {
function escapeClosedRuns(scan: string, escapings: Escapings, escaped: Uint8Array): Uint8Array {
// The lengths of the backtick strings escapes leave: a bare run of one of these lengths would open a span one of them closes, so it is escaped too.
const formed = new Set<number>()
let end = scan.length - 1
@@ -252,6 +251,7 @@ function escapeClosedRuns(scan: string, escapings: Escapings, escaped: Uint8Arra
}
end = start - 1
}
return escaped
}
// One emphasis run, the innermost, or every highlight run the line cannot spell.
+1
View File
@@ -31,6 +31,7 @@ test('refuses a string past the bound as too long on every engine, and names any
ok: false,
})
assert.equal(withinEngineLimits(thrown('RangeError', 'Invalid array length')).ok, false)
assert.equal(withinEngineLimits(thrown('RangeError', 'Array buffer allocation failed')).ok, false)
assert.deepEqual(
withinEngineLimits(() => success('x')),
{ ok: true, value: 'x' },
+2 -2
View File
@@ -3,9 +3,9 @@ import { failure, success, type Result } from '../result.ts'
// V8's longest string (docs/decisions.md §Markdown past V8's longest string is refused on every engine).
export const longestMarkdown = 2 ** 29 - 24
// Measured on Firefox 155, Node 24 and Bun 1.4. SpiderMonkey's overflow and V8's string overflow both mean a string past longestMarkdown; the other two name an engine's own limit, and JavaScriptCore's also a heap running short.
// Measured on Firefox 155, Node 24 and Bun 1.4. SpiderMonkey's overflow and V8's string overflow both mean a string past longestMarkdown; the others name an engine's own limit or its memory running short.
const pastLongestString = ['InternalError: allocation size overflow', 'RangeError: Invalid string length']
const pastEngineLimit = ['RangeError: Invalid array length', 'RangeError: Out of memory']
const pastEngineLimit = ['RangeError: Array buffer allocation failed', 'RangeError: Invalid array length', 'RangeError: Out of memory']
export function tooLong<T>(): Result<T> {
return failure('unsupported-node-shape', `the markdown for this document, or for a directive's plain CommonMark, would be longer than ${longestMarkdown} characters, the longest this library writes`, [])