Skip to content

fix: pass Typst source across the FFI boundary with an explicit length - #34

Merged
evolvedlight merged 1 commit into
evolvedlight:developfrom
msallin:fix/source-nul-truncation
Aug 31, 2026
Merged

fix: pass Typst source across the FFI boundary with an explicit length#34
evolvedlight merged 1 commit into
evolvedlight:developfrom
msallin:fix/source-nul-truncation

Conversation

@msallin

@msallin msallin commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Follows on from #33, which moved the diagnostics onto (pointer, length) but left the inbound source as a C string.

create_compiler read input_source with CStr::from_ptr, so a document containing a NUL byte was cut off at the first one. Nothing reported an error: the truncated document compiled cleanly and the caller got back a result that silently omitted everything after the NUL. Marshal.StringToCoTaskMemUTF8 writes the full bytes, interior NUL included, so the loss happened entirely on the native side.

The source now crosses as a (pointer, length) pair. A null pointer still means "no in-memory source", which stays distinct from a zero length meaning an empty document. fixed over an empty array yields null, so the managed side routes an empty source through a one-byte placeholder to keep those two apart.

Paths, sys_inputs and the format strings stay C strings on purpose. A path cannot contain a NUL on either Windows or POSIX, and JSON carries a NUL as a six-character escape rather than the byte itself, so that transport never sees it. Encoding the difference between "a path" and "arbitrary user text" in the signature seemed worth keeping.

Tests

  • source_after_a_nul_byte_is_still_compiled puts a syntax error after the NUL and asserts it is reported. With the truncation restored it fails with "invalid document compiled without an error", which is the bug.
  • SourceAfterNullByteIsNotTruncated asserts a heading after the NUL is present in the rendered PDF.

The source was handed to the native library as a NUL-terminated C string, so
a document containing a NUL byte was cut off at the first one. Nothing
reported an error: the truncated document compiled cleanly and the caller got
back a result that silently omitted everything after the NUL.

Pass the source as a (pointer, length) pair instead. A null pointer keeps its
meaning of "no in-memory source", which stays distinct from a zero length,
meaning an empty document.

Related to evolvedlight#27.
@evolvedlight
evolvedlight merged commit 3551018 into evolvedlight:develop Aug 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants