// encoding/hex — RFC 4648 base16 (hexadecimal) encode/decode, // buffer-based. // // Mirrors Hare's encoding::hex surface, modulo Hare's stream-based // encoder/decoder and memio-backed allocator helpers. ww ships the // in-memory subset only — same shape as lib/encoding/base32: caller // provides dst, fn returns the byte count or invalid. // // Output is always lowercase per ref/hare/encoding/hex/README:13; // decode accepts both upper- and lower-case per the same line. // invalid — input wasn't a valid hex sequence. Mirrors the // `errors::invalid` (!void) that ref/hare/encoding/hex/hex.ha:175 // returns from decodestr. lib/encoding/base32's local !i32 spelling // is a pre-existing divergence; this module follows Hare. package hex; export type invalid = !void; // encodedsize — bytes required to encode `n` source bytes. // ref/hare/encoding/hex/hex.ha:91 writes 2 chars per input byte // unconditionally. export fn encodedsize(n: i32) i32 = { return n * 2; }; // decodedsize — bytes produced when decoding `n` encoded bytes. // `n` must be even or decode returns invalid. export fn decodedsize(n: i32) i32 = { return n / 2; }; // 4-bit value to lowercase hex char. '0'..'9' is +48; 'a'..'f' is // +87 (= 'a' - 10). fn nibble(v: u8) u8 = { if (v < 10u8) { return v + 48u8; }; return v + 87u8; }; // Hex char to 4-bit value, accepting upper or lower case. Returns // 255 on invalid char (sentinel; '=' isn't legal in hex). fn denibble(c: u8) u8 = { if (c >= 48u8) { if (c <= 57u8) { return c - 48u8; }; }; // '0'..'9' if (c >= 65u8) { if (c <= 70u8) { return c - 55u8; }; }; // 'A'..'F' if (c >= 97u8) { if (c <= 102u8) { return c - 87u8; }; }; // 'a'..'f' return 255u8; }; // encode — encode `src` into `dst` as lowercase hex pairs. Returns // bytes written. `dst` must hold at least encodedsize(src.len) // bytes. Mirrors ref/hare/encoding/hex/hex.ha:91 `encode(out, in)` // (sans the io::handle wrapper). export fn encode(dst: []u8, src: []u8) i32 = { let i: i32 = 0; let j: i32 = 0; for (i < src.len) { let b: u8 = src[i]; dst[j] = nibble(b >> 4u8); dst[j + 1] = nibble(b & 15u8); i += 1; j += 2; }; return j; }; // decode — decode hex pairs from `src` into `dst`. Returns count // of bytes written or invalid on odd-length input or non-hex char. // Mirrors ref/hare/encoding/hex/hex.ha:175 `decodestr(s)` (sans // the allocator return). export fn decode(dst: []u8, src: []u8) (i32 | invalid) = { if ((src.len & 1) != 0) { let e: invalid; return e; }; let i: i32 = 0; let j: i32 = 0; for (i < src.len) { let hi: u8 = denibble(src[i]); let lo: u8 = denibble(src[i + 1]); if (hi == 255u8) { let e: invalid; return e; }; if (lo == 255u8) { let e: invalid; return e; }; dst[j] = (hi << 4u8) | lo; i += 2; j += 1; }; return j; };