Skip to content

fix(chr): byte-writing inverse of ord (1–255, loud errors); utf8_encode lands (#435)#450

Merged
InauguralPhysicist merged 1 commit into
mainfrom
chr-byte-writer-435
Jul 5, 2026
Merged

fix(chr): byte-writing inverse of ord (1–255, loud errors); utf8_encode lands (#435)#450
InauguralPhysicist merged 1 commit into
mainfrom
chr-byte-writer-435

Conversation

@InauguralPhysicist

Copy link
Copy Markdown
Collaborator

Correcting the record first: the premise was a misdiagnosis. High bytes have been constructible since #248str_from_bytes of [195, 169] yields the two raw bytes of é today, on v0.26.0, unchanged. utf8_encode was never actually blocked; it was omitted on a mistaken belief. (The confusion had help: the str_from_bytes C comment and BUILTINS.md both described chr as a codepoint→UTF-8 encoder, which it has never been.)

What WAS real, and is now fixed:

  1. chr silently returned "" for anything outside 0–127 and silently truncated fractions (chr of 65.5"A") — the silent-tolerance class. chr is now the byte-writing inverse of ord: any integer 1–255 emits that raw byte (chr of n == str_from_bytes of [n]); everything else raises loudly. chr of 0 raises too — strings are NUL-terminated and cannot hold a NUL, so returning a 0-length string was a lie; the >255 error message points at utf8_encode.
  2. utf8_encode + utf8_from_codepoints landed in lib/utf8.eigs, built on str_from_bytes, tested against the published vectors (U+00E9 → C3 A9, U+20AC → 3B, U+4E2D → 3B, U+10348 → 4B) plus every width boundary (0x7F/0x80, 0x7FF/0x800, 0xFFFF/0x10000, 0x10FFFF ceiling) and a full utf8_from_codepoints ∘ utf8_codepoints round-trip.
  3. The false doc claims are corrected (BUILTINS.md ×2, STDLIB.md, the str_from_bytes C comment, the lib/utf8.eigs header).

Consumer note for the next bump train: DMG's serial path (src/memory.eigs:340) calls chr of ch unguarded — a ROM writing byte 0x00 to serial would now raise instead of silently appending nothing. Blargg ROMs write ASCII so the twin gates are unaffected, but DMG should guard ch != 0 when it takes the pin.

Neither option in the issue was taken as written: option 1 said "0..255", but 0 is unrepresentable in a NUL-terminated string and silently returning "" for it is the same tolerance bug this issue is about. 1–255 loud is the honest contract.

Closes #435

🤖 Generated with Claude Code

…eigs gains utf8_encode (#435)

chr silently returned "" for anything outside 0-127 and silently
truncated fractions — the silent-tolerance class. It is now the
byte-writing partner of ord under the bytes-forever position (#416):
any integer 1-255 emits that raw byte (chr of n == str_from_bytes of
[n]); 0 raises (strings are NUL-terminated), as do negatives,
fractions, >255 (message points at utf8_encode), and non-numbers.

#435's premise was a misdiagnosis: high bytes were already
constructible via str_from_bytes (#248). utf8_encode +
utf8_from_codepoints land in lib/utf8.eigs on top of it, tested
against the published vectors, every width boundary, and a
from_codepoints∘codepoints round-trip. The false doc claims (chr
'emits the UTF-8 of a codepoint' in BUILTINS.md and the
str_from_bytes C comment; 'decode-only' in STDLIB.md and the module
header) are corrected.

Both suites green: release + ASan/UBSan detect_leaks=1, 2519/2519,
leak tally 0.

Closes #435

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@InauguralPhysicist InauguralPhysicist merged commit 6de6f49 into main Jul 5, 2026
17 checks passed
@InauguralPhysicist InauguralPhysicist deleted the chr-byte-writer-435 branch July 5, 2026 21:40
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.

No way to construct a byte >= 128: chr caps at 127, blocking utf8_encode and general byte-building

1 participant