1 // A variable declared from an untyped constant takes that constant's default
2 // type: `acc := 0` is an int32 variable, not a value that is still untyped.
3 //
4 // Stage4 left it untyped, which made every later use "context-dependent": the
5 // `byte(...)` in `out[j] = byte((acc >> bits) & 0x1f)` retyped the whole shift
6 // chain to uint8, the emitter declared an i8 shift, and the shift ran on acc's
7 // low byte sign-extended. The five-bit packing below answered 1e 00 02 where
8 // the correct groups are 02 04 1a - bech32 payloads came out wrong, and the
9 // web app's npub/nsec encoding with them. Legacy (go/types) types the variable
10 // int32 and was right, so stage4's declaration typing is what had to change.
11 package main
12 13 // conv packs eight-bit groups into five-bit ones, the conversion bech32 is
14 // built on.
15 func conv(data []byte, out []byte) (n int32) {
16 j := 0
17 acc := 0 // untyped literal: the variable is int32
18 bits := 0
19 for _, b := range data {
20 acc = (acc << 8) | int32(b)
21 bits += 8
22 for bits >= 5 {
23 bits -= 5
24 out[j] = byte((acc >> bits) & 0x1f)
25 j++
26 }
27 acc &= (1 << uint32(bits)) - 1
28 }
29 if bits > 0 {
30 out[j] = byte((acc << (5 - bits)) & 0x1f)
31 j++
32 }
33 return int32(j)
34 }
35 36 func hexs(b []byte, n int32) (s string) {
37 for i := int32(0); i < n; i++ {
38 s = s | nd(b[i]>>4) | nd(b[i]&0xf)
39 }
40 return s
41 }
42 43 func nd(v byte) (s string) {
44 const d = "0123456789abcdef"
45 return string([]byte{d[v]})
46 }
47 48 func main() {
49 out := []byte{:8}
50 n := conv([]byte{0x90, 0xa8, 0x22, 0x0b, 0x53}, out)
51 println("groups " | hexs(out, n))
52 }
53