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