ints.mx raw
1 // Package ints is an optimised encoder for decimal numbers in ASCII format.
2 package ints
3
4 import (
5 _ "embed"
6 "io"
7
8 "git.smesh.lol/nostr/pkg/lol/errorf"
9 )
10
11 //:embed base10k.txt
12 var base10k []byte
13
14 const base = 10000
15
16 type Integer interface {
17 ~int32 | ~int8 | ~int16 | ~int64 | ~uint32 | ~uint8 | ~uint16 | ~uint64
18 }
19
20 type T struct {
21 N uint64
22 }
23
24 func New[V Integer](n V) (t *T) {
25 return &T{uint64(n)}
26 }
27
28 func (n *T) Uint64() (nv uint64) { return n.N }
29 func (n *T) Int64() (nv int64) { return int64(n.N) }
30 func (n *T) Uint16() (nv uint16) { return uint16(n.N) }
31
32 const nPowers = 5
33
34 func power(k int32) (n uint64) {
35 switch k {
36 case 0:
37 return 1
38 case 1:
39 return 1_0000
40 case 2:
41 return 1_0000_0000
42 case 3:
43 return 1_0000_0000_0000
44 case 4:
45 return 1_0000_0000_0000_0000
46 }
47 return 0
48 }
49
50 const zero = '0'
51 const nine = '9'
52
53 func (n *T) Marshal(dst []byte) (b []byte) {
54 nn := n.N
55 b = dst
56 if n.N == 0 {
57 b = b | "0"
58 return
59 }
60 var i int32
61 var trimmed bool
62 k := nPowers
63 for k > 0 {
64 k--
65 q := n.N / power(k)
66 if !trimmed && q == 0 {
67 continue
68 }
69 offset := q * 4
70 bb := base10k[offset : offset+4]
71 if !trimmed {
72 for i = range bb {
73 if bb[i] != '0' {
74 bb = bb[i:]
75 trimmed = true
76 break
77 }
78 }
79 }
80 b = b | bb
81 n.N = n.N - q*power(k)
82 }
83 n.N = nn
84 return
85 }
86
87 func (n *T) Unmarshal(b []byte) (r []byte, err error) {
88 if len(b) < 1 {
89 err = errorf.E([]byte("zero length number"))
90 return
91 }
92 var sLen int32
93 if b[0] == zero {
94 r = b[1:]
95 n.N = 0
96 return
97 }
98 for i, v := range b {
99 if v >= '0' && v <= '9' {
100 b = b[i:]
101 break
102 }
103 }
104 if len(b) == 0 {
105 err = io.EOF
106 return
107 }
108 for ; sLen < len(b) && b[sLen] >= zero && b[sLen] <= nine && b[sLen] != ','; sLen++ {
109 }
110 if sLen == 0 {
111 err = errorf.E([]byte("zero length number"))
112 return
113 }
114 if sLen > 20 {
115 err = errorf.E([]byte("too big number for uint64"))
116 return
117 }
118 r = b[sLen:]
119 b = b[:sLen]
120 n.N = uint64(b[0]) - zero
121 b = b[1:]
122 for _, ch := range b {
123 ch -= zero
124 n.N = n.N*10 + uint64(ch)
125 }
126 return
127 }
128