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

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(&quot)
 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}