github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/resource/quantity.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package resource 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "math" 24 "math/big" 25 "strconv" 26 "strings" 27 28 inf "gopkg.in/inf.v0" 29 ) 30 31 // Quantity is a fixed-point representation of a number. 32 // It provides convenient marshaling/unmarshaling in JSON and YAML, 33 // in addition to String() and AsInt64() accessors. 34 // 35 // The serialization format is: 36 // 37 // ``` 38 // <quantity> ::= <signedNumber><suffix> 39 // 40 // (Note that <suffix> may be empty, from the "" case in <decimalSI>.) 41 // 42 // <digit> ::= 0 | 1 | ... | 9 43 // <digits> ::= <digit> | <digit><digits> 44 // <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits> 45 // <sign> ::= "+" | "-" 46 // <signedNumber> ::= <number> | <sign><number> 47 // <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI> 48 // <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei 49 // 50 // (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) 51 // 52 // <decimalSI> ::= m | "" | k | M | G | T | P | E 53 // 54 // (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) 55 // 56 // <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber> 57 // ``` 58 // 59 // No matter which of the three exponent forms is used, no quantity may represent 60 // a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal 61 // places. Numbers larger or more precise will be capped or rounded up. 62 // (E.g.: 0.1m will rounded up to 1m.) 63 // This may be extended in the future if we require larger or smaller quantities. 64 // 65 // When a Quantity is parsed from a string, it will remember the type of suffix 66 // it had, and will use the same type again when it is serialized. 67 // 68 // Before serializing, Quantity will be put in "canonical form". 69 // This means that Exponent/suffix will be adjusted up or down (with a 70 // corresponding increase or decrease in Mantissa) such that: 71 // 72 // - No precision is lost 73 // - No fractional digits will be emitted 74 // - The exponent (or suffix) is as large as possible. 75 // 76 // The sign will be omitted unless the number is negative. 77 // 78 // Examples: 79 // 80 // - 1.5 will be serialized as "1500m" 81 // - 1.5Gi will be serialized as "1536Mi" 82 // 83 // Note that the quantity will NEVER be internally represented by a 84 // floating point number. That is the whole point of this exercise. 85 // 86 // Non-canonical values will still parse as long as they are well formed, 87 // but will be re-emitted in their canonical form. (So always use canonical 88 // form, or don't diff.) 89 // 90 // This format is intended to make it difficult to use these numbers without 91 // writing some sort of special handling code in the hopes that that will 92 // cause implementors to also use a fixed point implementation. 93 // 94 // +protobuf=true 95 // +protobuf.embed=string 96 // +protobuf.options.marshal=false 97 // +protobuf.options.(gogoproto.goproto_stringer)=false 98 // +k8s:deepcopy-gen=true 99 // +k8s:openapi-gen=true 100 type Quantity struct { 101 // i is the quantity in int64 scaled form, if d.Dec == nil 102 i int64Amount 103 // d is the quantity in inf.Dec form if d.Dec != nil 104 d infDecAmount 105 // s is the generated value of this quantity to avoid recalculation 106 s string 107 108 // Change Format at will. See the comment for Canonicalize for 109 // more details. 110 Format 111 } 112 113 // CanonicalValue allows a quantity amount to be converted to a string. 114 type CanonicalValue interface { 115 // AsCanonicalBytes returns a byte array representing the string representation 116 // of the value mantissa and an int32 representing its exponent in base-10. Callers may 117 // pass a byte slice to the method to avoid allocations. 118 AsCanonicalBytes(out []byte) ([]byte, int32) 119 // AsCanonicalBase1024Bytes returns a byte array representing the string representation 120 // of the value mantissa and an int32 representing its exponent in base-1024. Callers 121 // may pass a byte slice to the method to avoid allocations. 122 AsCanonicalBase1024Bytes(out []byte) ([]byte, int32) 123 } 124 125 // Format lists the three possible formattings of a quantity. 126 type Format string 127 128 const ( 129 DecimalExponent = Format("DecimalExponent") // e.g., 12e6 130 BinarySI = Format("BinarySI") // e.g., 12Mi (12 * 2^20) 131 DecimalSI = Format("DecimalSI") // e.g., 12M (12 * 10^6) 132 ) 133 134 // MustParse turns the given string into a quantity or panics; for tests 135 // or other cases where you know the string is valid. 136 func MustParse(str string) Quantity { 137 q, err := ParseQuantity(str) 138 if err != nil { 139 panic(fmt.Errorf("cannot parse '%v': %v", str, err)) 140 } 141 return q 142 } 143 144 const ( 145 // splitREString is used to separate a number from its suffix; as such, 146 // this is overly permissive, but that's OK-- it will be checked later. 147 splitREString = "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$" 148 ) 149 150 var ( 151 // Errors that could happen while parsing a string. 152 ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'") 153 ErrNumeric = errors.New("unable to parse numeric part of quantity") 154 ErrSuffix = errors.New("unable to parse quantity's suffix") 155 ) 156 157 // parseQuantityString is a fast scanner for quantity values. 158 func parseQuantityString(str string) (positive bool, value, num, denom, suffix string, err error) { 159 positive = true 160 pos := 0 161 end := len(str) 162 163 // handle leading sign 164 if pos < end { 165 switch str[0] { 166 case '-': 167 positive = false 168 pos++ 169 case '+': 170 pos++ 171 } 172 } 173 174 // strip leading zeros 175 Zeroes: 176 for i := pos; ; i++ { 177 if i >= end { 178 num = "0" 179 value = num 180 return 181 } 182 switch str[i] { 183 case '0': 184 pos++ 185 default: 186 break Zeroes 187 } 188 } 189 190 // extract the numerator 191 Num: 192 for i := pos; ; i++ { 193 if i >= end { 194 num = str[pos:end] 195 value = str[0:end] 196 return 197 } 198 switch str[i] { 199 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 200 default: 201 num = str[pos:i] 202 pos = i 203 break Num 204 } 205 } 206 207 // if we stripped all numerator positions, always return 0 208 if len(num) == 0 { 209 num = "0" 210 } 211 212 // handle a denominator 213 if pos < end && str[pos] == '.' { 214 pos++ 215 Denom: 216 for i := pos; ; i++ { 217 if i >= end { 218 denom = str[pos:end] 219 value = str[0:end] 220 return 221 } 222 switch str[i] { 223 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 224 default: 225 denom = str[pos:i] 226 pos = i 227 break Denom 228 } 229 } 230 // TODO: we currently allow 1.G, but we may not want to in the future. 231 // if len(denom) == 0 { 232 // err = ErrFormatWrong 233 // return 234 // } 235 } 236 value = str[0:pos] 237 238 // grab the elements of the suffix 239 suffixStart := pos 240 for i := pos; ; i++ { 241 if i >= end { 242 suffix = str[suffixStart:end] 243 return 244 } 245 if !strings.ContainsAny(str[i:i+1], "eEinumkKMGTP") { 246 pos = i 247 break 248 } 249 } 250 if pos < end { 251 switch str[pos] { 252 case '-', '+': 253 pos++ 254 } 255 } 256 Suffix: 257 for i := pos; ; i++ { 258 if i >= end { 259 suffix = str[suffixStart:end] 260 return 261 } 262 switch str[i] { 263 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 264 default: 265 break Suffix 266 } 267 } 268 // we encountered a non decimal in the Suffix loop, but the last character 269 // was not a valid exponent 270 err = ErrFormatWrong 271 return 272 } 273 274 // ParseQuantity turns str into a Quantity, or returns an error. 275 func ParseQuantity(str string) (Quantity, error) { 276 if len(str) == 0 { 277 return Quantity{}, ErrFormatWrong 278 } 279 if str == "0" { 280 return Quantity{Format: DecimalSI, s: str}, nil 281 } 282 283 positive, value, num, denom, suf, err := parseQuantityString(str) 284 if err != nil { 285 return Quantity{}, err 286 } 287 288 base, exponent, format, ok := quantitySuffixer.interpret(suffix(suf)) 289 if !ok { 290 return Quantity{}, ErrSuffix 291 } 292 293 precision := int32(0) 294 scale := int32(0) 295 mantissa := int64(1) 296 switch format { 297 case DecimalExponent, DecimalSI: 298 scale = exponent 299 precision = maxInt64Factors - int32(len(num)+len(denom)) 300 case BinarySI: 301 scale = 0 302 switch { 303 case exponent >= 0 && len(denom) == 0: 304 // only handle positive binary numbers with the fast path 305 mantissa = int64(int64(mantissa) << uint64(exponent)) 306 // 1Mi (2^20) has ~6 digits of decimal precision, so exponent*3/10 -1 is roughly the precision 307 precision = 15 - int32(len(num)) - int32(float32(exponent)*3/10) - 1 308 default: 309 precision = -1 310 } 311 } 312 313 if precision >= 0 { 314 // if we have a denominator, shift the entire value to the left by the number of places in the 315 // denominator 316 scale -= int32(len(denom)) 317 if scale >= int32(Nano) { 318 shifted := num + denom 319 320 var value int64 321 value, err := strconv.ParseInt(shifted, 10, 64) 322 if err != nil { 323 return Quantity{}, ErrNumeric 324 } 325 if result, ok := int64Multiply(value, int64(mantissa)); ok { 326 if !positive { 327 result = -result 328 } 329 // if the number is in canonical form, reuse the string 330 switch format { 331 case BinarySI: 332 if exponent%10 == 0 && (value&0x07 != 0) { 333 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil 334 } 335 default: 336 if scale%3 == 0 && !strings.HasSuffix(shifted, "000") && shifted[0] != '0' { 337 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil 338 } 339 } 340 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format}, nil 341 } 342 } 343 } 344 345 amount := new(inf.Dec) 346 if _, ok := amount.SetString(value); !ok { 347 return Quantity{}, ErrNumeric 348 } 349 350 // So that no one but us has to think about suffixes, remove it. 351 if base == 10 { 352 amount.SetScale(amount.Scale() + Scale(exponent).infScale()) 353 } else if base == 2 { 354 // numericSuffix = 2 ** exponent 355 numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent)) 356 ub := amount.UnscaledBig() 357 amount.SetUnscaledBig(ub.Mul(ub, numericSuffix)) 358 } 359 360 // Cap at min/max bounds. 361 sign := amount.Sign() 362 if sign == -1 { 363 amount.Neg(amount) 364 } 365 366 // This rounds non-zero values up to the minimum representable value, under the theory that 367 // if you want some resources, you should get some resources, even if you asked for way too small 368 // of an amount. Arguably, this should be inf.RoundHalfUp (normal rounding), but that would have 369 // the side effect of rounding values < .5n to zero. 370 if v, ok := amount.Unscaled(); v != int64(0) || !ok { 371 amount.Round(amount, Nano.infScale(), inf.RoundUp) 372 } 373 374 // The max is just a simple cap. 375 // TODO: this prevents accumulating quantities greater than int64, for instance quota across a cluster 376 if format == BinarySI && amount.Cmp(maxAllowed.Dec) > 0 { 377 amount.Set(maxAllowed.Dec) 378 } 379 380 if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 { 381 // This avoids rounding and hopefully confusion, too. 382 format = DecimalSI 383 } 384 if sign == -1 { 385 amount.Neg(amount) 386 } 387 388 return Quantity{d: infDecAmount{amount}, Format: format}, nil 389 } 390 391 // DeepCopy returns a deep-copy of the Quantity value. Note that the method 392 // receiver is a value, so we can mutate it in-place and return it. 393 func (q Quantity) DeepCopy() Quantity { 394 if q.d.Dec != nil { 395 tmp := &inf.Dec{} 396 q.d.Dec = tmp.Set(q.d.Dec) 397 } 398 return q 399 } 400 401 // OpenAPISchemaType is used by the kube-openapi generator when constructing 402 // the OpenAPI spec of this type. 403 // 404 // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators 405 func (_ Quantity) OpenAPISchemaType() []string { return []string{"string"} } 406 407 // OpenAPISchemaFormat is used by the kube-openapi generator when constructing 408 // the OpenAPI spec of this type. 409 func (_ Quantity) OpenAPISchemaFormat() string { return "" } 410 411 // OpenAPIV3OneOfTypes is used by the kube-openapi generator when constructing 412 // the OpenAPI v3 spec of this type. 413 func (Quantity) OpenAPIV3OneOfTypes() []string { return []string{"string", "number"} } 414 415 // CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity). 416 // 417 // Note about BinarySI: 418 // - If q.Format is set to BinarySI and q.Amount represents a non-zero value between 419 // -1 and +1, it will be emitted as if q.Format were DecimalSI. 420 // - Otherwise, if q.Format is set to BinarySI, fractional parts of q.Amount will be 421 // rounded up. (1.1i becomes 2i.) 422 func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) { 423 if q.IsZero() { 424 return zeroBytes, nil 425 } 426 427 var rounded CanonicalValue 428 format := q.Format 429 switch format { 430 case DecimalExponent, DecimalSI: 431 case BinarySI: 432 if q.CmpInt64(-1024) > 0 && q.CmpInt64(1024) < 0 { 433 // This avoids rounding and hopefully confusion, too. 434 format = DecimalSI 435 } else { 436 var exact bool 437 if rounded, exact = q.AsScale(0); !exact { 438 // Don't lose precision-- show as DecimalSI 439 format = DecimalSI 440 } 441 } 442 default: 443 format = DecimalExponent 444 } 445 446 // TODO: If BinarySI formatting is requested but would cause rounding, upgrade to 447 // one of the other formats. 448 switch format { 449 case DecimalExponent, DecimalSI: 450 number, exponent := q.AsCanonicalBytes(out) 451 suffix, _ := quantitySuffixer.constructBytes(10, exponent, format) 452 return number, suffix 453 default: 454 // format must be BinarySI 455 number, exponent := rounded.AsCanonicalBase1024Bytes(out) 456 suffix, _ := quantitySuffixer.constructBytes(2, exponent*10, format) 457 return number, suffix 458 } 459 } 460 461 // AsApproximateFloat64 returns a float64 representation of the quantity which may 462 // lose precision. If the value of the quantity is outside the range of a float64 463 // +Inf/-Inf will be returned. 464 func (q *Quantity) AsApproximateFloat64() float64 { 465 var base float64 466 var exponent int 467 if q.d.Dec != nil { 468 base, _ = big.NewFloat(0).SetInt(q.d.Dec.UnscaledBig()).Float64() 469 exponent = int(-q.d.Dec.Scale()) 470 } else { 471 base = float64(q.i.value) 472 exponent = int(q.i.scale) 473 } 474 if exponent == 0 { 475 return base 476 } 477 478 return base * math.Pow10(exponent) 479 } 480 481 // AsInt64 returns a representation of the current value as an int64 if a fast conversion 482 // is possible. If false is returned, callers must use the inf.Dec form of this quantity. 483 func (q *Quantity) AsInt64() (int64, bool) { 484 if q.d.Dec != nil { 485 return 0, false 486 } 487 return q.i.AsInt64() 488 } 489 490 // ToDec promotes the quantity in place to use an inf.Dec representation and returns itself. 491 func (q *Quantity) ToDec() *Quantity { 492 if q.d.Dec == nil { 493 q.d.Dec = q.i.AsDec() 494 q.i = int64Amount{} 495 } 496 return q 497 } 498 499 // AsDec returns the quantity as represented by a scaled inf.Dec. 500 func (q *Quantity) AsDec() *inf.Dec { 501 if q.d.Dec != nil { 502 return q.d.Dec 503 } 504 q.d.Dec = q.i.AsDec() 505 q.i = int64Amount{} 506 return q.d.Dec 507 } 508 509 // AsCanonicalBytes returns the canonical byte representation of this quantity as a mantissa 510 // and base 10 exponent. The out byte slice may be passed to the method to avoid an extra 511 // allocation. 512 func (q *Quantity) AsCanonicalBytes(out []byte) (result []byte, exponent int32) { 513 if q.d.Dec != nil { 514 return q.d.AsCanonicalBytes(out) 515 } 516 return q.i.AsCanonicalBytes(out) 517 } 518 519 // IsZero returns true if the quantity is equal to zero. 520 func (q *Quantity) IsZero() bool { 521 if q.d.Dec != nil { 522 return q.d.Dec.Sign() == 0 523 } 524 return q.i.value == 0 525 } 526 527 // Sign returns 0 if the quantity is zero, -1 if the quantity is less than zero, or 1 if the 528 // quantity is greater than zero. 529 func (q *Quantity) Sign() int { 530 if q.d.Dec != nil { 531 return q.d.Dec.Sign() 532 } 533 return q.i.Sign() 534 } 535 536 // AsScale returns the current value, rounded up to the provided scale, and returns 537 // false if the scale resulted in a loss of precision. 538 func (q *Quantity) AsScale(scale Scale) (CanonicalValue, bool) { 539 if q.d.Dec != nil { 540 return q.d.AsScale(scale) 541 } 542 return q.i.AsScale(scale) 543 } 544 545 // RoundUp updates the quantity to the provided scale, ensuring that the value is at 546 // least 1. False is returned if the rounding operation resulted in a loss of precision. 547 // Negative numbers are rounded away from zero (-9 scale 1 rounds to -10). 548 func (q *Quantity) RoundUp(scale Scale) bool { 549 if q.d.Dec != nil { 550 q.s = "" 551 d, exact := q.d.AsScale(scale) 552 q.d = d 553 return exact 554 } 555 // avoid clearing the string value if we have already calculated it 556 if q.i.scale >= scale { 557 return true 558 } 559 q.s = "" 560 i, exact := q.i.AsScale(scale) 561 q.i = i 562 return exact 563 } 564 565 // Add adds the provide y quantity to the current value. If the current value is zero, 566 // the format of the quantity will be updated to the format of y. 567 func (q *Quantity) Add(y Quantity) { 568 q.s = "" 569 if q.d.Dec == nil && y.d.Dec == nil { 570 if q.i.value == 0 { 571 q.Format = y.Format 572 } 573 if q.i.Add(y.i) { 574 return 575 } 576 } else if q.IsZero() { 577 q.Format = y.Format 578 } 579 q.ToDec().d.Dec.Add(q.d.Dec, y.AsDec()) 580 } 581 582 // Sub subtracts the provided quantity from the current value in place. If the current 583 // value is zero, the format of the quantity will be updated to the format of y. 584 func (q *Quantity) Sub(y Quantity) { 585 q.s = "" 586 if q.IsZero() { 587 q.Format = y.Format 588 } 589 if q.d.Dec == nil && y.d.Dec == nil && q.i.Sub(y.i) { 590 return 591 } 592 q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec()) 593 } 594 595 // Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the 596 // quantity is greater than y. 597 func (q *Quantity) Cmp(y Quantity) int { 598 if q.d.Dec == nil && y.d.Dec == nil { 599 return q.i.Cmp(y.i) 600 } 601 return q.AsDec().Cmp(y.AsDec()) 602 } 603 604 // CmpInt64 returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the 605 // quantity is greater than y. 606 func (q *Quantity) CmpInt64(y int64) int { 607 if q.d.Dec != nil { 608 return q.d.Dec.Cmp(inf.NewDec(y, inf.Scale(0))) 609 } 610 return q.i.Cmp(int64Amount{value: y}) 611 } 612 613 // Neg sets quantity to be the negative value of itself. 614 func (q *Quantity) Neg() { 615 q.s = "" 616 if q.d.Dec == nil { 617 q.i.value = -q.i.value 618 return 619 } 620 q.d.Dec.Neg(q.d.Dec) 621 } 622 623 // Equal checks equality of two Quantities. This is useful for testing with 624 // cmp.Equal. 625 func (q Quantity) Equal(v Quantity) bool { 626 return q.Cmp(v) == 0 627 } 628 629 // int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation 630 // of most Quantity values. 631 const int64QuantityExpectedBytes = 18 632 633 // String formats the Quantity as a string, caching the result if not calculated. 634 // String is an expensive operation and caching this result significantly reduces the cost of 635 // normal parse / marshal operations on Quantity. 636 func (q *Quantity) String() string { 637 if q == nil { 638 return "<nil>" 639 } 640 if len(q.s) == 0 { 641 result := make([]byte, 0, int64QuantityExpectedBytes) 642 number, suffix := q.CanonicalizeBytes(result) 643 number = append(number, suffix...) 644 q.s = string(number) 645 } 646 return q.s 647 } 648 649 // MarshalJSON implements the json.Marshaller interface. 650 func (q Quantity) MarshalJSON() ([]byte, error) { 651 if len(q.s) > 0 { 652 out := make([]byte, len(q.s)+2) 653 out[0], out[len(out)-1] = '"', '"' 654 copy(out[1:], q.s) 655 return out, nil 656 } 657 result := make([]byte, int64QuantityExpectedBytes) 658 result[0] = '"' 659 number, suffix := q.CanonicalizeBytes(result[1:1]) 660 // if the same slice was returned to us that we passed in, avoid another allocation by copying number into 661 // the source slice and returning that 662 if len(number) > 0 && &number[0] == &result[1] && (len(number)+len(suffix)+2) <= int64QuantityExpectedBytes { 663 number = append(number, suffix...) 664 number = append(number, '"') 665 return result[:1+len(number)], nil 666 } 667 // if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use 668 // append 669 result = result[:1] 670 result = append(result, number...) 671 result = append(result, suffix...) 672 result = append(result, '"') 673 return result, nil 674 } 675 676 // ToUnstructured implements the value.UnstructuredConverter interface. 677 func (q Quantity) ToUnstructured() interface{} { 678 return q.String() 679 } 680 681 // UnmarshalJSON implements the json.Unmarshaller interface. 682 // TODO: Remove support for leading/trailing whitespace 683 func (q *Quantity) UnmarshalJSON(value []byte) error { 684 l := len(value) 685 if l == 4 && bytes.Equal(value, []byte("null")) { 686 q.d.Dec = nil 687 q.i = int64Amount{} 688 return nil 689 } 690 if l >= 2 && value[0] == '"' && value[l-1] == '"' { 691 value = value[1 : l-1] 692 } 693 694 parsed, err := ParseQuantity(strings.TrimSpace(string(value))) 695 if err != nil { 696 return err 697 } 698 699 // This copy is safe because parsed will not be referred to again. 700 *q = parsed 701 return nil 702 } 703 704 // NewDecimalQuantity returns a new Quantity representing the given 705 // value in the given format. 706 func NewDecimalQuantity(b inf.Dec, format Format) *Quantity { 707 return &Quantity{ 708 d: infDecAmount{&b}, 709 Format: format, 710 } 711 } 712 713 // NewQuantity returns a new Quantity representing the given 714 // value in the given format. 715 func NewQuantity(value int64, format Format) *Quantity { 716 return &Quantity{ 717 i: int64Amount{value: value}, 718 Format: format, 719 } 720 } 721 722 // NewMilliQuantity returns a new Quantity representing the given 723 // value * 1/1000 in the given format. Note that BinarySI formatting 724 // will round fractional values, and will be changed to DecimalSI for 725 // values x where (-1 < x < 1) && (x != 0). 726 func NewMilliQuantity(value int64, format Format) *Quantity { 727 return &Quantity{ 728 i: int64Amount{value: value, scale: -3}, 729 Format: format, 730 } 731 } 732 733 // NewScaledQuantity returns a new Quantity representing the given 734 // value * 10^scale in DecimalSI format. 735 func NewScaledQuantity(value int64, scale Scale) *Quantity { 736 return &Quantity{ 737 i: int64Amount{value: value, scale: scale}, 738 Format: DecimalSI, 739 } 740 } 741 742 // Value returns the unscaled value of q rounded up to the nearest integer away from 0. 743 func (q *Quantity) Value() int64 { 744 return q.ScaledValue(0) 745 } 746 747 // MilliValue returns the value of ceil(q * 1000); this could overflow an int64; 748 // if that's a concern, call Value() first to verify the number is small enough. 749 func (q *Quantity) MilliValue() int64 { 750 return q.ScaledValue(Milli) 751 } 752 753 // ScaledValue returns the value of ceil(q / 10^scale). 754 // For example, NewQuantity(1, DecimalSI).ScaledValue(Milli) returns 1000. 755 // This could overflow an int64. 756 // To detect overflow, call Value() first and verify the expected magnitude. 757 func (q *Quantity) ScaledValue(scale Scale) int64 { 758 if q.d.Dec == nil { 759 i, _ := q.i.AsScaledInt64(scale) 760 return i 761 } 762 dec := q.d.Dec 763 return scaledValue(dec.UnscaledBig(), int(dec.Scale()), int(scale.infScale())) 764 } 765 766 // Set sets q's value to be value. 767 func (q *Quantity) Set(value int64) { 768 q.SetScaled(value, 0) 769 } 770 771 // SetMilli sets q's value to be value * 1/1000. 772 func (q *Quantity) SetMilli(value int64) { 773 q.SetScaled(value, Milli) 774 } 775 776 // SetScaled sets q's value to be value * 10^scale 777 func (q *Quantity) SetScaled(value int64, scale Scale) { 778 q.s = "" 779 q.d.Dec = nil 780 q.i = int64Amount{value: value, scale: scale} 781 } 782 783 // QuantityValue makes it possible to use a Quantity as value for a command 784 // line parameter. 785 // 786 // +protobuf=true 787 // +protobuf.embed=string 788 // +protobuf.options.marshal=false 789 // +protobuf.options.(gogoproto.goproto_stringer)=false 790 // +k8s:deepcopy-gen=true 791 type QuantityValue struct { 792 Quantity 793 } 794 795 // Set implements pflag.Value.Set and Go flag.Value.Set. 796 func (q *QuantityValue) Set(s string) error { 797 quantity, err := ParseQuantity(s) 798 if err != nil { 799 return err 800 } 801 q.Quantity = quantity 802 return nil 803 } 804 805 // Type implements pflag.Value.Type. 806 func (q QuantityValue) Type() string { 807 return "quantity" 808 }