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 }