Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}