Hoehrmann DFA from ref/hare/encoding/utf8/decodetable.ha flattened
to 1D [2048]i8 (task #20: 2D-array jagged cgen still pending);
encoderune takes a caller buffer matching lib/encoding/hex.encode;
done/more/invalid all spelled as plain void aliases per lib/io's
eof precedent. Surface ports decoder + decode + next + encoderune
+ runesz + utf8sz + validate from ref/hare/encoding/utf8/{types,
decode,encode,rune}.ha. next() polarity rewritten from Hare's
`(state-1):uint >> 31` to an explicit `if state == 0` branch
because ww's uint is 64-bit (cmd/wcc/type.c:58); same effect, no
hidden 32-bit assumption.
Deferred (no in-tree callers): prev, slice, position, remaining,
appendrune, strencode, strdecode. String iteration (chars/
newchars/nextchar in the session-4 draft) dropped per Hare
discipline — belongs in lib/strings::iterator, not encoding/utf8.
Tests:
- 968_utf8_run drives lib/encoding/utf8/utf8test.ww via ww run.
21 @test fns: boundaries (ASCII, 2-byte, 3-byte, 4-byte
encode/decode), surrogate/overlong/out-of-range/bad-continuation
reject, max-in-range (U+10FFFF) accept, truncated→more, done@EOI,
validate empty/mixed/malformed, encode/decode roundtrip. Two
rows ported from ref/hare/encoding/utf8/decode.ha @test that
were missing in the session-4 draft: bad-continuation
[0xC2,0xFF]→invalid and max-in-range [0xF4,0x8F,0xBF,0xBF]→
U+10FFFF.
- 9xx stdlib runtime slot range extended from 970-989 to 960-989
to accommodate utf8 at 968 (970-989 block was full).
90/90 ok. 995_self_rebuild stays green (ww2==ww3==ww4 byte-id).
Two cgen/check bugs surfaced by new lib modules, plus the modules
themselves (crc64, siphash, random, base64, base32).
1. `(big_u64): u32` (and `: u16`, `: u8`, `: bool`) didn't truncate.
N_CAST emitted nothing for int↔int; the value stayed in AX with
its upper bits intact and downstream CMPQ/DIVQ misread the slot.
The TK_TILDE path already had clamp logic for the same reason —
N_CAST was the missing case. Both stages now MOVL r,r for u32 and
ANDQ $mask for u8/u16/bool. Signed-narrow (i8/i16/i32) stays
no-op until w6a grows reg-reg MOVSBQ/MOVSWQ/MOVSXD. selfhost
cgcast walks alias chains via aliaslookup before checking
primsize/typenameisunsigned so `(u: random)` where
`type random = u64` still bypasses the clamp.
See cmd/w6c/cgen.c N_CAST and selfhost/cmd/wcc/cgenexpr.ww cgcast.
2. `mod.mod` type refs (`random.random` when the imported module
declares `export type random = u64;`) failed with "unknown type".
The driver concatenates imports into one flat scope, so SK_USE
`random` collided with SK_TYPE `random` and scope_define silently
dropped the use. resolve_typename's leaf lookup required
`kind == SK_USE` and gave up. Adds a `use_alias` flag to Sym; the
pass-1 decl scan now marks colliding syms in both directions
(use-after-type and type-after-use). resolve_typename and the
N_DOT cexpr branch treat `use_alias` like SK_USE for qualified
lookup. selfhost check.ww was already lenient on this path so no
ww-side change was needed; bootstrap fixed point (990-995) holds.
See cmd/wcc/check.c installdecl pass + N_DOT/resolve_typename and
cmd/wcc/ww.h Sym.use_alias.
New modules under lib/, each with @test vectors in *_test.ww and wired
into test/wcc/900_stdlib.c (26 modules → all compile):
- lib/hash/crc64 ECMA, ISO (mirror of crc32 shape)
- lib/hash/siphash SipHash-2-4, buffer-based sum/sum24
- lib/math/random SplitMix64 (init, next, u32n, u64n)
- lib/encoding/base64 RFC 4648 std + url-safe encode/decode + sizes
- lib/encoding/base32 RFC 4648 std + base32hex encode/decode + sizes
Both used the -1 sentinel return; both had no external callers, so
the graduation is purely the API-shape change. utf8.runesz uses void
for "rune outside legal range"; bufio.readbyte uses void for EOF
(empty buffer). The full Hare shapes ((size | invalid) and
(u8 | EOF | io::error)) are still richer than this — those richer
returns arrive when utf8 grows an explicit invalid type and bufio
wires through io::stream's error path.