Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

conversion.gno

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