conversion.gno
4.74 Kb · 225 lines
1package int256
2
3import (
4 "errors"
5 "math/bits"
6 "strconv"
7
8 u256 "gno.land/p/gnoswap/uint256"
9)
10
11const (
12 maxAbsI256Dec = "57896044618658097711785492504343953926634992332820282019728792003956564819968"
13 maxWords = 256 / bits.UintSize
14)
15
16// MUST BE IMMUTABLE, DO NOT MODIFY
17// multipliers is a table of multipliers for the decimal conversion.
18var _multipliers = [5]*Int{
19 nil,
20 {0x8ac7230489e80000, 0, 0, 0},
21 {0x98a224000000000, 0x4b3b4ca85a86c47a, 0, 0},
22 {0x4a00000000000000, 0xebfdcb54864ada83, 0x28c87cb5c89a2571, 0},
23 {0, 0x7775a5f171951000, 0x764b4abe8652979, 0x161bcca7119915b5},
24}
25
26func FromDecimal(decimal string) (*Int, error) {
27 z, err := new(Int).SetString(decimal)
28 if err != nil {
29 return nil, err
30 }
31
32 return z, nil
33}
34
35func MustFromDecimal(decimal string) *Int {
36 z, err := FromDecimal(decimal)
37 if err != nil {
38 panic(err)
39 }
40 return z
41}
42
43func (z *Int) ToString() string {
44 s := z.Sign()
45 if s == 0 {
46 return "0"
47 }
48 if z.IsInt64() {
49 return strconv.FormatInt(z.Int64(), 10)
50 }
51 y := new(Int)
52 if s > 0 {
53 y.Set(z)
54 } else {
55 y.Neg(z)
56 }
57 var (
58 out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
59 divisor = new(Int).SetUint64(10000000000000000000)
60 pos = len(out)
61 buf = make([]byte, 0, 19)
62 )
63
64 for {
65 var quot Int
66 rem := udivrem(quot[:], y[:], divisor)
67 y.Set(")
68 buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10)
69 copy(out[pos-len(buf):], buf)
70 if y.IsZero() {
71 break
72 }
73 pos -= 19
74 }
75
76 var res string
77 if s < 0 {
78 res = "-"
79 }
80 res += string(out[pos-len(buf):])
81 return res
82}
83
84func (z *Int) SetString(s string) (*Int, error) {
85 if len(s) == 0 {
86 return nil, errors.New("int256: empty string")
87 }
88
89 isNeg := false
90 switch s[0] {
91 case '+':
92 s = s[1:]
93 case '-':
94 isNeg = true
95 s = s[1:]
96 }
97
98 if len(s) == 0 {
99 return nil, errors.New("int256: empty string")
100 }
101
102 // Parallel comparison technique for validation
103 // Process in 8-byte chunks for optimal performance
104 sLen := len(s)
105 i := 0
106
107 // Process 8 bytes at a time
108 for i+7 < sLen {
109 // Access up to s[i+7] is safe, then we can reduce the number of bounds checks
110 _ = s[i+7]
111
112 // Convert 8 bytes into a single uint64
113 // This method processes bytes directly, so no endianness issues
114 chunk := uint64(s[i]) | uint64(s[i+1])<<8
115 chunk |= uint64(s[i+2])<<16 | uint64(s[i+3])<<24
116 chunk |= uint64(s[i+4])<<32 | uint64(s[i+5])<<40
117 chunk |= uint64(s[i+6])<<48 | uint64(s[i+7])<<56
118
119 // Check for '+' (0x2B) using SWAR technique
120 // Subtracting 0x2B from each byte makes '+' bytes become 0
121 // Subtracting 0x01 makes bytes in ASCII range (0-127) have 0 in their highest bit
122 // Therefore, AND with 0x80 to check for zero bytes
123 plusTest := ((chunk ^ 0x2B2B2B2B2B2B2B2B) - 0x0101010101010101) & 0x8080808080808080
124
125 // Check for '-' (0x2D) using SWAR technique
126 minusTest := ((chunk ^ 0x2D2D2D2D2D2D2D2D) - 0x0101010101010101) & 0x8080808080808080
127
128 // If either test is non-zero, a sign character exists
129 if (plusTest | minusTest) != 0 {
130 return nil, errors.New("int256: invalid sign in middle of number")
131 }
132
133 i += 8
134 }
135
136 // Process remaining bytes
137 for ; i < sLen; i++ {
138 if s[i] == '+' || s[i] == '-' {
139 return nil, errors.New("int256: invalid sign in middle of number")
140 }
141 }
142
143 // Strip leading zeros
144 if len(s) > 0 && s[0] == '0' {
145 idx := 0
146 for idx < len(s) && s[idx] == '0' {
147 idx++
148 }
149 s = s[idx:]
150 // If all characters were zeros, set to "0"
151 if len(s) == 0 {
152 s = "0"
153 }
154 }
155
156 // Check for overflow
157 if len(s) > len(maxAbsI256Dec) ||
158 (len(s) == len(maxAbsI256Dec) && s > maxAbsI256Dec) ||
159 (s == maxAbsI256Dec && !isNeg) {
160 return nil, errors.New("int256: overflow")
161 }
162
163 if err := z.fromDecimal(s); err != nil {
164 return nil, err
165 }
166
167 if isNeg {
168 z.Neg(z)
169 }
170
171 return z, nil
172}
173
174func (z *Int) fromDecimal(bs string) error {
175 z.Clear()
176 var (
177 num uint64
178 err error
179 remaining = len(bs)
180 )
181
182 if remaining == 0 {
183 return errors.New("EOF")
184 }
185
186 for i, mult := range _multipliers {
187 if remaining <= 0 {
188 return nil
189 }
190 if remaining > 19 {
191 num, err = strconv.ParseUint(bs[remaining-19:remaining], 10, 64)
192 } else {
193 num, err = strconv.ParseUint(bs, 10, 64)
194 }
195 if err != nil {
196 return err
197 }
198 if i == 0 {
199 z.SetUint64(num)
200 } else {
201 base := new(Int).SetUint64(num)
202 z.Add(z, base.Mul(base, mult))
203 }
204 if remaining > 19 {
205 bs = bs[0 : remaining-19]
206 }
207 remaining -= 19
208 }
209 return nil
210}
211
212// FromUint256 converts a uint256 to int256.
213// Panics if the uint256 value is greater than MaxInt256 (2^255 - 1).
214func FromUint256(x *u256.Uint) *Int {
215 // Check overflow: if MSB of x[3] is set, value > MaxInt256
216 if x[3] > 0x7fffffffffffffff {
217 panic("int256: overflow - uint256 value exceeds MaxInt256")
218 }
219 z := &Int{}
220 z[0] = x[0]
221 z[1] = x[1]
222 z[2] = x[2]
223 z[3] = x[3]
224 return z
225}