go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/starlark/int.go (about)

     1  // Copyright 2017 The Bazel Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package starlark
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/big"
    11  	"reflect"
    12  	"strconv"
    13  
    14  	"go.starlark.net/syntax"
    15  )
    16  
    17  // Int is the type of a Starlark int.
    18  //
    19  // The zero value is not a legal value; use MakeInt(0).
    20  type Int struct{ impl intImpl }
    21  
    22  // --- high-level accessors ---
    23  
    24  // MakeInt returns a Starlark int for the specified signed integer.
    25  func MakeInt(x int) Int { return MakeInt64(int64(x)) }
    26  
    27  // MakeInt64 returns a Starlark int for the specified int64.
    28  func MakeInt64(x int64) Int {
    29  	if math.MinInt32 <= x && x <= math.MaxInt32 {
    30  		return makeSmallInt(x)
    31  	}
    32  	return makeBigInt(big.NewInt(x))
    33  }
    34  
    35  // MakeUint returns a Starlark int for the specified unsigned integer.
    36  func MakeUint(x uint) Int { return MakeUint64(uint64(x)) }
    37  
    38  // MakeUint64 returns a Starlark int for the specified uint64.
    39  func MakeUint64(x uint64) Int {
    40  	if x <= math.MaxInt32 {
    41  		return makeSmallInt(int64(x))
    42  	}
    43  	return makeBigInt(new(big.Int).SetUint64(x))
    44  }
    45  
    46  // MakeBigInt returns a Starlark int for the specified big.Int.
    47  // The new Int value will contain a copy of x. The caller is safe to modify x.
    48  func MakeBigInt(x *big.Int) Int {
    49  	if isSmall(x) {
    50  		return makeSmallInt(x.Int64())
    51  	}
    52  	z := new(big.Int).Set(x)
    53  	return makeBigInt(z)
    54  }
    55  
    56  func isSmall(x *big.Int) bool {
    57  	n := x.BitLen()
    58  	return n < 32 || n == 32 && x.Int64() == math.MinInt32
    59  }
    60  
    61  var (
    62  	zero, one = makeSmallInt(0), makeSmallInt(1)
    63  	oneBig    = big.NewInt(1)
    64  
    65  	_ HasUnary = Int{}
    66  )
    67  
    68  // Unary implements the operations +int, -int, and ~int.
    69  func (i Int) Unary(op syntax.Token) (Value, error) {
    70  	switch op {
    71  	case syntax.MINUS:
    72  		return zero.Sub(i), nil
    73  	case syntax.PLUS:
    74  		return i, nil
    75  	case syntax.TILDE:
    76  		return i.Not(), nil
    77  	}
    78  	return nil, nil
    79  }
    80  
    81  // Int64 returns the value as an int64.
    82  // If it is not exactly representable the result is undefined and ok is false.
    83  func (i Int) Int64() (_ int64, ok bool) {
    84  	iSmall, iBig := i.get()
    85  	if iBig != nil {
    86  		x, acc := bigintToInt64(iBig)
    87  		if acc != big.Exact {
    88  			return // inexact
    89  		}
    90  		return x, true
    91  	}
    92  	return iSmall, true
    93  }
    94  
    95  // BigInt returns a new big.Int with the same value as the Int.
    96  func (i Int) BigInt() *big.Int {
    97  	iSmall, iBig := i.get()
    98  	if iBig != nil {
    99  		return new(big.Int).Set(iBig)
   100  	}
   101  	return big.NewInt(iSmall)
   102  }
   103  
   104  // bigInt returns the value as a big.Int.
   105  // It differs from BigInt in that this method returns the actual
   106  // reference and any modification will change the state of i.
   107  func (i Int) bigInt() *big.Int {
   108  	iSmall, iBig := i.get()
   109  	if iBig != nil {
   110  		return iBig
   111  	}
   112  	return big.NewInt(iSmall)
   113  }
   114  
   115  // Uint64 returns the value as a uint64.
   116  // If it is not exactly representable the result is undefined and ok is false.
   117  func (i Int) Uint64() (_ uint64, ok bool) {
   118  	iSmall, iBig := i.get()
   119  	if iBig != nil {
   120  		x, acc := bigintToUint64(iBig)
   121  		if acc != big.Exact {
   122  			return // inexact
   123  		}
   124  		return x, true
   125  	}
   126  	if iSmall < 0 {
   127  		return // inexact
   128  	}
   129  	return uint64(iSmall), true
   130  }
   131  
   132  // The math/big API should provide this function.
   133  func bigintToInt64(i *big.Int) (int64, big.Accuracy) {
   134  	sign := i.Sign()
   135  	if sign > 0 {
   136  		if i.Cmp(maxint64) > 0 {
   137  			return math.MaxInt64, big.Below
   138  		}
   139  	} else if sign < 0 {
   140  		if i.Cmp(minint64) < 0 {
   141  			return math.MinInt64, big.Above
   142  		}
   143  	}
   144  	return i.Int64(), big.Exact
   145  }
   146  
   147  // The math/big API should provide this function.
   148  func bigintToUint64(i *big.Int) (uint64, big.Accuracy) {
   149  	sign := i.Sign()
   150  	if sign > 0 {
   151  		if i.BitLen() > 64 {
   152  			return math.MaxUint64, big.Below
   153  		}
   154  	} else if sign < 0 {
   155  		return 0, big.Above
   156  	}
   157  	return i.Uint64(), big.Exact
   158  }
   159  
   160  var (
   161  	minint64 = new(big.Int).SetInt64(math.MinInt64)
   162  	maxint64 = new(big.Int).SetInt64(math.MaxInt64)
   163  )
   164  
   165  func (i Int) Format(s fmt.State, ch rune) {
   166  	iSmall, iBig := i.get()
   167  	if iBig != nil {
   168  		iBig.Format(s, ch)
   169  		return
   170  	}
   171  	big.NewInt(iSmall).Format(s, ch)
   172  }
   173  func (i Int) String() string {
   174  	iSmall, iBig := i.get()
   175  	if iBig != nil {
   176  		return iBig.Text(10)
   177  	}
   178  	return strconv.FormatInt(iSmall, 10)
   179  }
   180  func (i Int) Type() string { return "int" }
   181  func (i Int) Freeze()      {} // immutable
   182  func (i Int) Truth() Bool  { return i.Sign() != 0 }
   183  func (i Int) Hash() (uint32, error) {
   184  	iSmall, iBig := i.get()
   185  	var lo big.Word
   186  	if iBig != nil {
   187  		lo = iBig.Bits()[0]
   188  	} else {
   189  		lo = big.Word(iSmall)
   190  	}
   191  	return 12582917 * uint32(lo+3), nil
   192  }
   193  
   194  // Cmp implements comparison of two Int values.
   195  // Required by the TotallyOrdered interface.
   196  func (i Int) Cmp(v Value, depth int) (int, error) {
   197  	j := v.(Int)
   198  	iSmall, iBig := i.get()
   199  	jSmall, jBig := j.get()
   200  	if iBig != nil || jBig != nil {
   201  		return i.bigInt().Cmp(j.bigInt()), nil
   202  	}
   203  	return signum64(iSmall - jSmall), nil // safe: int32 operands
   204  }
   205  
   206  // Float returns the float value nearest i.
   207  func (i Int) Float() Float {
   208  	iSmall, iBig := i.get()
   209  	if iBig != nil {
   210  		// Fast path for hardware int-to-float conversions.
   211  		if iBig.IsUint64() {
   212  			return Float(iBig.Uint64())
   213  		} else if iBig.IsInt64() {
   214  			return Float(iBig.Int64())
   215  		} else {
   216  			// Fast path for very big ints.
   217  			const maxFiniteLen = 1023 + 1 // max exponent value + implicit mantissa bit
   218  			if iBig.BitLen() > maxFiniteLen {
   219  				return Float(math.Inf(iBig.Sign()))
   220  			}
   221  		}
   222  
   223  		f, _ := new(big.Float).SetInt(iBig).Float64()
   224  		return Float(f)
   225  	}
   226  	return Float(iSmall)
   227  }
   228  
   229  // finiteFloat returns the finite float value nearest i,
   230  // or an error if the magnitude is too large.
   231  func (i Int) finiteFloat() (Float, error) {
   232  	f := i.Float()
   233  	if math.IsInf(float64(f), 0) {
   234  		return 0, fmt.Errorf("int too large to convert to float")
   235  	}
   236  	return f, nil
   237  }
   238  
   239  func (x Int) Sign() int {
   240  	xSmall, xBig := x.get()
   241  	if xBig != nil {
   242  		return xBig.Sign()
   243  	}
   244  	return signum64(xSmall)
   245  }
   246  
   247  func (x Int) Add(y Int) Int {
   248  	xSmall, xBig := x.get()
   249  	ySmall, yBig := y.get()
   250  	if xBig != nil || yBig != nil {
   251  		return MakeBigInt(new(big.Int).Add(x.bigInt(), y.bigInt()))
   252  	}
   253  	return MakeInt64(xSmall + ySmall)
   254  }
   255  func (x Int) Sub(y Int) Int {
   256  	xSmall, xBig := x.get()
   257  	ySmall, yBig := y.get()
   258  	if xBig != nil || yBig != nil {
   259  		return MakeBigInt(new(big.Int).Sub(x.bigInt(), y.bigInt()))
   260  	}
   261  	return MakeInt64(xSmall - ySmall)
   262  }
   263  func (x Int) Mul(y Int) Int {
   264  	xSmall, xBig := x.get()
   265  	ySmall, yBig := y.get()
   266  	if xBig != nil || yBig != nil {
   267  		return MakeBigInt(new(big.Int).Mul(x.bigInt(), y.bigInt()))
   268  	}
   269  	return MakeInt64(xSmall * ySmall)
   270  }
   271  func (x Int) Or(y Int) Int {
   272  	xSmall, xBig := x.get()
   273  	ySmall, yBig := y.get()
   274  	if xBig != nil || yBig != nil {
   275  		return MakeBigInt(new(big.Int).Or(x.bigInt(), y.bigInt()))
   276  	}
   277  	return makeSmallInt(xSmall | ySmall)
   278  }
   279  func (x Int) And(y Int) Int {
   280  	xSmall, xBig := x.get()
   281  	ySmall, yBig := y.get()
   282  	if xBig != nil || yBig != nil {
   283  		return MakeBigInt(new(big.Int).And(x.bigInt(), y.bigInt()))
   284  	}
   285  	return makeSmallInt(xSmall & ySmall)
   286  }
   287  func (x Int) Xor(y Int) Int {
   288  	xSmall, xBig := x.get()
   289  	ySmall, yBig := y.get()
   290  	if xBig != nil || yBig != nil {
   291  		return MakeBigInt(new(big.Int).Xor(x.bigInt(), y.bigInt()))
   292  	}
   293  	return makeSmallInt(xSmall ^ ySmall)
   294  }
   295  func (x Int) Not() Int {
   296  	xSmall, xBig := x.get()
   297  	if xBig != nil {
   298  		return MakeBigInt(new(big.Int).Not(xBig))
   299  	}
   300  	return makeSmallInt(^xSmall)
   301  }
   302  func (x Int) Lsh(y uint) Int { return MakeBigInt(new(big.Int).Lsh(x.bigInt(), y)) }
   303  func (x Int) Rsh(y uint) Int { return MakeBigInt(new(big.Int).Rsh(x.bigInt(), y)) }
   304  
   305  // Precondition: y is nonzero.
   306  func (x Int) Div(y Int) Int {
   307  	xSmall, xBig := x.get()
   308  	ySmall, yBig := y.get()
   309  	// http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html
   310  	if xBig != nil || yBig != nil {
   311  		xb, yb := x.bigInt(), y.bigInt()
   312  
   313  		var quo, rem big.Int
   314  		quo.QuoRem(xb, yb, &rem)
   315  		if (xb.Sign() < 0) != (yb.Sign() < 0) && rem.Sign() != 0 {
   316  			quo.Sub(&quo, oneBig)
   317  		}
   318  		return MakeBigInt(&quo)
   319  	}
   320  	quo := xSmall / ySmall
   321  	rem := xSmall % ySmall
   322  	if (xSmall < 0) != (ySmall < 0) && rem != 0 {
   323  		quo -= 1
   324  	}
   325  	return MakeInt64(quo)
   326  }
   327  
   328  // Precondition: y is nonzero.
   329  func (x Int) Mod(y Int) Int {
   330  	xSmall, xBig := x.get()
   331  	ySmall, yBig := y.get()
   332  	if xBig != nil || yBig != nil {
   333  		xb, yb := x.bigInt(), y.bigInt()
   334  
   335  		var quo, rem big.Int
   336  		quo.QuoRem(xb, yb, &rem)
   337  		if (xb.Sign() < 0) != (yb.Sign() < 0) && rem.Sign() != 0 {
   338  			rem.Add(&rem, yb)
   339  		}
   340  		return MakeBigInt(&rem)
   341  	}
   342  	rem := xSmall % ySmall
   343  	if (xSmall < 0) != (ySmall < 0) && rem != 0 {
   344  		rem += ySmall
   345  	}
   346  	return makeSmallInt(rem)
   347  }
   348  
   349  func (i Int) rational() *big.Rat {
   350  	iSmall, iBig := i.get()
   351  	if iBig != nil {
   352  		return new(big.Rat).SetInt(iBig)
   353  	}
   354  	return new(big.Rat).SetInt64(iSmall)
   355  }
   356  
   357  // AsInt32 returns the value of x if is representable as an int32.
   358  func AsInt32(x Value) (int, error) {
   359  	i, ok := x.(Int)
   360  	if !ok {
   361  		return 0, fmt.Errorf("got %s, want int", x.Type())
   362  	}
   363  	iSmall, iBig := i.get()
   364  	if iBig != nil {
   365  		return 0, fmt.Errorf("%s out of range", i)
   366  	}
   367  	return int(iSmall), nil
   368  }
   369  
   370  // AsInt sets *ptr to the value of Starlark int x, if it is exactly representable,
   371  // otherwise it returns an error.
   372  // The type of ptr must be one of the pointer types *int, *int8, *int16, *int32, or *int64,
   373  // or one of their unsigned counterparts including *uintptr.
   374  func AsInt(x Value, ptr interface{}) error {
   375  	xint, ok := x.(Int)
   376  	if !ok {
   377  		return fmt.Errorf("got %s, want int", x.Type())
   378  	}
   379  
   380  	bits := reflect.TypeOf(ptr).Elem().Size() * 8
   381  	switch ptr.(type) {
   382  	case *int, *int8, *int16, *int32, *int64:
   383  		i, ok := xint.Int64()
   384  		if !ok || bits < 64 && !(-1<<(bits-1) <= i && i < 1<<(bits-1)) {
   385  			return fmt.Errorf("%s out of range (want value in signed %d-bit range)", xint, bits)
   386  		}
   387  		switch ptr := ptr.(type) {
   388  		case *int:
   389  			*ptr = int(i)
   390  		case *int8:
   391  			*ptr = int8(i)
   392  		case *int16:
   393  			*ptr = int16(i)
   394  		case *int32:
   395  			*ptr = int32(i)
   396  		case *int64:
   397  			*ptr = int64(i)
   398  		}
   399  
   400  	case *uint, *uint8, *uint16, *uint32, *uint64, *uintptr:
   401  		i, ok := xint.Uint64()
   402  		if !ok || bits < 64 && i >= 1<<bits {
   403  			return fmt.Errorf("%s out of range (want value in unsigned %d-bit range)", xint, bits)
   404  		}
   405  		switch ptr := ptr.(type) {
   406  		case *uint:
   407  			*ptr = uint(i)
   408  		case *uint8:
   409  			*ptr = uint8(i)
   410  		case *uint16:
   411  			*ptr = uint16(i)
   412  		case *uint32:
   413  			*ptr = uint32(i)
   414  		case *uint64:
   415  			*ptr = uint64(i)
   416  		case *uintptr:
   417  			*ptr = uintptr(i)
   418  		}
   419  	default:
   420  		panic(fmt.Sprintf("invalid argument type: %T", ptr))
   421  	}
   422  	return nil
   423  }
   424  
   425  // NumberToInt converts a number x to an integer value.
   426  // An int is returned unchanged, a float is truncated towards zero.
   427  // NumberToInt reports an error for all other values.
   428  func NumberToInt(x Value) (Int, error) {
   429  	switch x := x.(type) {
   430  	case Int:
   431  		return x, nil
   432  	case Float:
   433  		f := float64(x)
   434  		if math.IsInf(f, 0) {
   435  			return zero, fmt.Errorf("cannot convert float infinity to integer")
   436  		} else if math.IsNaN(f) {
   437  			return zero, fmt.Errorf("cannot convert float NaN to integer")
   438  		}
   439  		return finiteFloatToInt(x), nil
   440  
   441  	}
   442  	return zero, fmt.Errorf("cannot convert %s to int", x.Type())
   443  }
   444  
   445  // finiteFloatToInt converts f to an Int, truncating towards zero.
   446  // f must be finite.
   447  func finiteFloatToInt(f Float) Int {
   448  	// We avoid '<= MaxInt64' so that both constants are exactly representable as floats.
   449  	// See https://github.com/google/starlark-go/issues/375.
   450  	if math.MinInt64 <= f && f < math.MaxInt64+1 {
   451  		// small values
   452  		return MakeInt64(int64(f))
   453  	}
   454  	rat := f.rational()
   455  	if rat == nil {
   456  		panic(f) // non-finite
   457  	}
   458  	return MakeBigInt(new(big.Int).Div(rat.Num(), rat.Denom()))
   459  }