Search Apps Documentation Source Content File Folder Download Copy Actions Download

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