docs(calibration.js): explain why one UTF-8 repair pass suffices in JS

TextEncoder always emits well-formed UTF-8, so truncation can strand at
most a lead byte plus three continuations — one repair pass covers it.
The C++ hub needs a loop because its input is raw bytes off the wire.
Also drops a dead variable from the byte-boundary test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino Ferrari
2026-08-17 00:41:00 +02:00
co-authored by Claude Opus 4.6
parent 48d62c1f80
commit 7312dd0ca0
2 changed files with 8 additions and 6 deletions
+7
View File
@@ -53,6 +53,13 @@
// Drop continuation bytes (10xxxxxx) from the tail until the last byte // Drop continuation bytes (10xxxxxx) from the tail until the last byte
// is either an ASCII byte (< 0x80) or a lead byte whose sequence is // is either an ASCII byte (< 0x80) or a lead byte whose sequence is
// complete (i.e. all expected continuation bytes are present). // complete (i.e. all expected continuation bytes are present).
//
// A single pass suffices here, where the C++ needs a loop. The C++ input
// is a raw const char* straight off the wire and may hold arbitrary bytes;
// `bytes` here comes from TextEncoder, which always emits well-formed
// UTF-8 (unpaired surrogates become U+FFFD = EF BF BD, and no byte is ever
// >= 0xF8). Truncating well-formed UTF-8 can therefore strand at most a
// lead byte plus three continuation bytes, which one pass fully repairs.
var b = bytes.slice(0, MAX_UNIT_LEN); var b = bytes.slice(0, MAX_UNIT_LEN);
var len = b.length; var len = b.length;
// Walk back over continuation bytes (up to 3) to find the lead byte of // Walk back over continuation bytes (up to 3) to find the lead byte of
+1 -6
View File
@@ -69,12 +69,7 @@ test('normaliseCal cuts a mid-rune byte boundary back to the last complete rune'
}); });
test('normaliseCal leaves a unit that is exactly 16 bytes ending on a complete multi-byte rune untouched', () => { test('normaliseCal leaves a unit that is exactly 16 bytes ending on a complete multi-byte rune untouched', () => {
// 7 ASCII chars + 'Ω' (2 bytes) + 6 ASCII chars + 'µ' (2 bytes) - 1 = let's // 'abcdefgΩhijklµ' → 7 ASCII + 'Ω' (2 bytes) + 5 ASCII + 'µ' (2 bytes) = 16 bytes
// build exactly 16 bytes ending on a complete 2-byte rune.
// 7 × 'a' (7 bytes) + 'Ω' (2 bytes) + 5 × 'b' (5 bytes) + '°' (2 bytes) =
// 7 + 2 + 5 + 2 = 16 bytes.
const exact = 'aaaaaaаbbbbb°'; // avoid confusion: use simple construction below
// Simple: 'abcdefgΩhijklµ' → 7 + 2 + 5 + 2 = 16 bytes
const u = 'abcdefgΩhijklµ'; const u = 'abcdefgΩhijklµ';
assert.strictEqual(new TextEncoder().encode(u).length, 16); assert.strictEqual(new TextEncoder().encode(u).length, 16);
assert.strictEqual(C.normaliseCal({source: 'w', signal: 's', unit: u}).unit, u); assert.strictEqual(C.normaliseCal({source: 'w', signal: 's', unit: u}).unit, u);