2 Copyright 2014 The Kubernetes Authors.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
30 // Quantity is a fixed-point representation of a number.
31 // It provides convenient marshaling/unmarshaling in JSON and YAML,
32 // in addition to String() and Int64() accessors.
34 // The serialization format is:
36 // <quantity> ::= <signedNumber><suffix>
37 // (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
38 // <digit> ::= 0 | 1 | ... | 9
39 // <digits> ::= <digit> | <digit><digits>
40 // <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
41 // <sign> ::= "+" | "-"
42 // <signedNumber> ::= <number> | <sign><number>
43 // <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI>
44 // <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei
45 // (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
46 // <decimalSI> ::= m | "" | k | M | G | T | P | E
47 // (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
48 // <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
50 // No matter which of the three exponent forms is used, no quantity may represent
51 // a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal
52 // places. Numbers larger or more precise will be capped or rounded up.
53 // (E.g.: 0.1m will rounded up to 1m.)
54 // This may be extended in the future if we require larger or smaller quantities.
56 // When a Quantity is parsed from a string, it will remember the type of suffix
57 // it had, and will use the same type again when it is serialized.
59 // Before serializing, Quantity will be put in "canonical form".
60 // This means that Exponent/suffix will be adjusted up or down (with a
61 // corresponding increase or decrease in Mantissa) such that:
62 // a. No precision is lost
63 // b. No fractional digits will be emitted
64 // c. The exponent (or suffix) is as large as possible.
65 // The sign will be omitted unless the number is negative.
68 // 1.5 will be serialized as "1500m"
69 // 1.5Gi will be serialized as "1536Mi"
71 // Note that the quantity will NEVER be internally represented by a
72 // floating point number. That is the whole point of this exercise.
74 // Non-canonical values will still parse as long as they are well formed,
75 // but will be re-emitted in their canonical form. (So always use canonical
76 // form, or don't diff.)
78 // This format is intended to make it difficult to use these numbers without
79 // writing some sort of special handling code in the hopes that that will
80 // cause implementors to also use a fixed point implementation.
83 // +protobuf.embed=string
84 // +protobuf.options.marshal=false
85 // +protobuf.options.(gogoproto.goproto_stringer)=false
86 // +k8s:deepcopy-gen=true
87 // +k8s:openapi-gen=true
88 type Quantity struct {
89 // i is the quantity in int64 scaled form, if d.Dec == nil
91 // d is the quantity in inf.Dec form if d.Dec != nil
93 // s is the generated value of this quantity to avoid recalculation
96 // Change Format at will. See the comment for Canonicalize for
101 // CanonicalValue allows a quantity amount to be converted to a string.
102 type CanonicalValue interface {
103 // AsCanonicalBytes returns a byte array representing the string representation
104 // of the value mantissa and an int32 representing its exponent in base-10. Callers may
105 // pass a byte slice to the method to avoid allocations.
106 AsCanonicalBytes(out []byte) ([]byte, int32)
107 // AsCanonicalBase1024Bytes returns a byte array representing the string representation
108 // of the value mantissa and an int32 representing its exponent in base-1024. Callers
109 // may pass a byte slice to the method to avoid allocations.
110 AsCanonicalBase1024Bytes(out []byte) ([]byte, int32)
113 // Format lists the three possible formattings of a quantity.
117 DecimalExponent = Format("DecimalExponent") // e.g., 12e6
118 BinarySI = Format("BinarySI") // e.g., 12Mi (12 * 2^20)
119 DecimalSI = Format("DecimalSI") // e.g., 12M (12 * 10^6)
122 // MustParse turns the given string into a quantity or panics; for tests
123 // or others cases where you know the string is valid.
124 func MustParse(str string) Quantity {
125 q, err := ParseQuantity(str)
127 panic(fmt.Errorf("cannot parse '%v': %v", str, err))
133 // splitREString is used to separate a number from its suffix; as such,
134 // this is overly permissive, but that's OK-- it will be checked later.
135 splitREString = "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"
139 // Errors that could happen while parsing a string.
140 ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'")
141 ErrNumeric = errors.New("unable to parse numeric part of quantity")
142 ErrSuffix = errors.New("unable to parse quantity's suffix")
145 // parseQuantityString is a fast scanner for quantity values.
146 func parseQuantityString(str string) (positive bool, value, num, denom, suffix string, err error) {
151 // handle leading sign
162 // strip leading zeros
164 for i := pos; ; i++ {
178 // extract the numerator
180 for i := pos; ; i++ {
187 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
195 // if we stripped all numerator positions, always return 0
200 // handle a denominator
201 if pos < end && str[pos] == '.' {
204 for i := pos; ; i++ {
211 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
218 // TODO: we currently allow 1.G, but we may not want to in the future.
219 // if len(denom) == 0 {
220 // err = ErrFormatWrong
226 // grab the elements of the suffix
228 for i := pos; ; i++ {
230 suffix = str[suffixStart:end]
233 if !strings.ContainsAny(str[i:i+1], "eEinumkKMGTP") {
245 for i := pos; ; i++ {
247 suffix = str[suffixStart:end]
251 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
256 // we encountered a non decimal in the Suffix loop, but the last character
257 // was not a valid exponent
262 // ParseQuantity turns str into a Quantity, or returns an error.
263 func ParseQuantity(str string) (Quantity, error) {
265 return Quantity{}, ErrFormatWrong
268 return Quantity{Format: DecimalSI, s: str}, nil
271 positive, value, num, denom, suf, err := parseQuantityString(str)
273 return Quantity{}, err
276 base, exponent, format, ok := quantitySuffixer.interpret(suffix(suf))
278 return Quantity{}, ErrSuffix
281 precision := int32(0)
285 case DecimalExponent, DecimalSI:
287 precision = maxInt64Factors - int32(len(num)+len(denom))
291 case exponent >= 0 && len(denom) == 0:
292 // only handle positive binary numbers with the fast path
293 mantissa = int64(int64(mantissa) << uint64(exponent))
294 // 1Mi (2^20) has ~6 digits of decimal precision, so exponent*3/10 -1 is roughly the precision
295 precision = 15 - int32(len(num)) - int32(float32(exponent)*3/10) - 1
302 // if we have a denominator, shift the entire value to the left by the number of places in the
304 scale -= int32(len(denom))
305 if scale >= int32(Nano) {
306 shifted := num + denom
309 value, err := strconv.ParseInt(shifted, 10, 64)
311 return Quantity{}, ErrNumeric
313 if result, ok := int64Multiply(value, int64(mantissa)); ok {
317 // if the number is in canonical form, reuse the string
320 if exponent%10 == 0 && (value&0x07 != 0) {
321 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
324 if scale%3 == 0 && !strings.HasSuffix(shifted, "000") && shifted[0] != '0' {
325 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
328 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format}, nil
333 amount := new(inf.Dec)
334 if _, ok := amount.SetString(value); !ok {
335 return Quantity{}, ErrNumeric
338 // So that no one but us has to think about suffixes, remove it.
340 amount.SetScale(amount.Scale() + Scale(exponent).infScale())
341 } else if base == 2 {
342 // numericSuffix = 2 ** exponent
343 numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent))
344 ub := amount.UnscaledBig()
345 amount.SetUnscaledBig(ub.Mul(ub, numericSuffix))
348 // Cap at min/max bounds.
349 sign := amount.Sign()
354 // This rounds non-zero values up to the minimum representable value, under the theory that
355 // if you want some resources, you should get some resources, even if you asked for way too small
356 // of an amount. Arguably, this should be inf.RoundHalfUp (normal rounding), but that would have
357 // the side effect of rounding values < .5n to zero.
358 if v, ok := amount.Unscaled(); v != int64(0) || !ok {
359 amount.Round(amount, Nano.infScale(), inf.RoundUp)
362 // The max is just a simple cap.
363 // TODO: this prevents accumulating quantities greater than int64, for instance quota across a cluster
364 if format == BinarySI && amount.Cmp(maxAllowed.Dec) > 0 {
365 amount.Set(maxAllowed.Dec)
368 if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 {
369 // This avoids rounding and hopefully confusion, too.
376 return Quantity{d: infDecAmount{amount}, Format: format}, nil
379 // DeepCopy returns a deep-copy of the Quantity value. Note that the method
380 // receiver is a value, so we can mutate it in-place and return it.
381 func (q Quantity) DeepCopy() Quantity {
384 q.d.Dec = tmp.Set(q.d.Dec)
389 // OpenAPISchemaType is used by the kube-openapi generator when constructing
390 // the OpenAPI spec of this type.
392 // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
393 func (_ Quantity) OpenAPISchemaType() []string { return []string{"string"} }
395 // OpenAPISchemaFormat is used by the kube-openapi generator when constructing
396 // the OpenAPI spec of this type.
397 func (_ Quantity) OpenAPISchemaFormat() string { return "" }
399 // CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity).
401 // Note about BinarySI:
402 // * If q.Format is set to BinarySI and q.Amount represents a non-zero value between
403 // -1 and +1, it will be emitted as if q.Format were DecimalSI.
404 // * Otherwise, if q.Format is set to BinarySI, fractional parts of q.Amount will be
405 // rounded up. (1.1i becomes 2i.)
406 func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) {
408 return zeroBytes, nil
411 var rounded CanonicalValue
414 case DecimalExponent, DecimalSI:
416 if q.CmpInt64(-1024) > 0 && q.CmpInt64(1024) < 0 {
417 // This avoids rounding and hopefully confusion, too.
421 if rounded, exact = q.AsScale(0); !exact {
422 // Don't lose precision-- show as DecimalSI
427 format = DecimalExponent
430 // TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
431 // one of the other formats.
433 case DecimalExponent, DecimalSI:
434 number, exponent := q.AsCanonicalBytes(out)
435 suffix, _ := quantitySuffixer.constructBytes(10, exponent, format)
436 return number, suffix
438 // format must be BinarySI
439 number, exponent := rounded.AsCanonicalBase1024Bytes(out)
440 suffix, _ := quantitySuffixer.constructBytes(2, exponent*10, format)
441 return number, suffix
445 // AsInt64 returns a representation of the current value as an int64 if a fast conversion
446 // is possible. If false is returned, callers must use the inf.Dec form of this quantity.
447 func (q *Quantity) AsInt64() (int64, bool) {
454 // ToDec promotes the quantity in place to use an inf.Dec representation and returns itself.
455 func (q *Quantity) ToDec() *Quantity {
457 q.d.Dec = q.i.AsDec()
463 // AsDec returns the quantity as represented by a scaled inf.Dec.
464 func (q *Quantity) AsDec() *inf.Dec {
468 q.d.Dec = q.i.AsDec()
473 // AsCanonicalBytes returns the canonical byte representation of this quantity as a mantissa
474 // and base 10 exponent. The out byte slice may be passed to the method to avoid an extra
476 func (q *Quantity) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
478 return q.d.AsCanonicalBytes(out)
480 return q.i.AsCanonicalBytes(out)
483 // IsZero returns true if the quantity is equal to zero.
484 func (q *Quantity) IsZero() bool {
486 return q.d.Dec.Sign() == 0
488 return q.i.value == 0
491 // Sign returns 0 if the quantity is zero, -1 if the quantity is less than zero, or 1 if the
492 // quantity is greater than zero.
493 func (q *Quantity) Sign() int {
495 return q.d.Dec.Sign()
500 // AsScale returns the current value, rounded up to the provided scale, and returns
501 // false if the scale resulted in a loss of precision.
502 func (q *Quantity) AsScale(scale Scale) (CanonicalValue, bool) {
504 return q.d.AsScale(scale)
506 return q.i.AsScale(scale)
509 // RoundUp updates the quantity to the provided scale, ensuring that the value is at
510 // least 1. False is returned if the rounding operation resulted in a loss of precision.
511 // Negative numbers are rounded away from zero (-9 scale 1 rounds to -10).
512 func (q *Quantity) RoundUp(scale Scale) bool {
515 d, exact := q.d.AsScale(scale)
519 // avoid clearing the string value if we have already calculated it
520 if q.i.scale >= scale {
524 i, exact := q.i.AsScale(scale)
529 // Add adds the provide y quantity to the current value. If the current value is zero,
530 // the format of the quantity will be updated to the format of y.
531 func (q *Quantity) Add(y Quantity) {
533 if q.d.Dec == nil && y.d.Dec == nil {
540 } else if q.IsZero() {
543 q.ToDec().d.Dec.Add(q.d.Dec, y.AsDec())
546 // Sub subtracts the provided quantity from the current value in place. If the current
547 // value is zero, the format of the quantity will be updated to the format of y.
548 func (q *Quantity) Sub(y Quantity) {
553 if q.d.Dec == nil && y.d.Dec == nil && q.i.Sub(y.i) {
556 q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec())
559 // Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
560 // quantity is greater than y.
561 func (q *Quantity) Cmp(y Quantity) int {
562 if q.d.Dec == nil && y.d.Dec == nil {
565 return q.AsDec().Cmp(y.AsDec())
568 // CmpInt64 returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
569 // quantity is greater than y.
570 func (q *Quantity) CmpInt64(y int64) int {
572 return q.d.Dec.Cmp(inf.NewDec(y, inf.Scale(0)))
574 return q.i.Cmp(int64Amount{value: y})
577 // Neg sets quantity to be the negative value of itself.
578 func (q *Quantity) Neg() {
581 q.i.value = -q.i.value
587 // int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation
588 // of most Quantity values.
589 const int64QuantityExpectedBytes = 18
591 // String formats the Quantity as a string, caching the result if not calculated.
592 // String is an expensive operation and caching this result significantly reduces the cost of
593 // normal parse / marshal operations on Quantity.
594 func (q *Quantity) String() string {
596 result := make([]byte, 0, int64QuantityExpectedBytes)
597 number, suffix := q.CanonicalizeBytes(result)
598 number = append(number, suffix...)
604 // MarshalJSON implements the json.Marshaller interface.
605 func (q Quantity) MarshalJSON() ([]byte, error) {
607 out := make([]byte, len(q.s)+2)
608 out[0], out[len(out)-1] = '"', '"'
612 result := make([]byte, int64QuantityExpectedBytes, int64QuantityExpectedBytes)
614 number, suffix := q.CanonicalizeBytes(result[1:1])
615 // if the same slice was returned to us that we passed in, avoid another allocation by copying number into
616 // the source slice and returning that
617 if len(number) > 0 && &number[0] == &result[1] && (len(number)+len(suffix)+2) <= int64QuantityExpectedBytes {
618 number = append(number, suffix...)
619 number = append(number, '"')
620 return result[:1+len(number)], nil
622 // if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use
625 result = append(result, number...)
626 result = append(result, suffix...)
627 result = append(result, '"')
631 // UnmarshalJSON implements the json.Unmarshaller interface.
632 // TODO: Remove support for leading/trailing whitespace
633 func (q *Quantity) UnmarshalJSON(value []byte) error {
635 if l == 4 && bytes.Equal(value, []byte("null")) {
640 if l >= 2 && value[0] == '"' && value[l-1] == '"' {
641 value = value[1 : l-1]
644 parsed, err := ParseQuantity(strings.TrimSpace(string(value)))
649 // This copy is safe because parsed will not be referred to again.
654 // NewQuantity returns a new Quantity representing the given
655 // value in the given format.
656 func NewQuantity(value int64, format Format) *Quantity {
658 i: int64Amount{value: value},
663 // NewMilliQuantity returns a new Quantity representing the given
664 // value * 1/1000 in the given format. Note that BinarySI formatting
665 // will round fractional values, and will be changed to DecimalSI for
666 // values x where (-1 < x < 1) && (x != 0).
667 func NewMilliQuantity(value int64, format Format) *Quantity {
669 i: int64Amount{value: value, scale: -3},
674 // NewScaledQuantity returns a new Quantity representing the given
675 // value * 10^scale in DecimalSI format.
676 func NewScaledQuantity(value int64, scale Scale) *Quantity {
678 i: int64Amount{value: value, scale: scale},
683 // Value returns the value of q; any fractional part will be lost.
684 func (q *Quantity) Value() int64 {
685 return q.ScaledValue(0)
688 // MilliValue returns the value of ceil(q * 1000); this could overflow an int64;
689 // if that's a concern, call Value() first to verify the number is small enough.
690 func (q *Quantity) MilliValue() int64 {
691 return q.ScaledValue(Milli)
694 // ScaledValue returns the value of ceil(q * 10^scale); this could overflow an int64.
695 // To detect overflow, call Value() first and verify the expected magnitude.
696 func (q *Quantity) ScaledValue(scale Scale) int64 {
698 i, _ := q.i.AsScaledInt64(scale)
702 return scaledValue(dec.UnscaledBig(), int(dec.Scale()), int(scale.infScale()))
705 // Set sets q's value to be value.
706 func (q *Quantity) Set(value int64) {
707 q.SetScaled(value, 0)
710 // SetMilli sets q's value to be value * 1/1000.
711 func (q *Quantity) SetMilli(value int64) {
712 q.SetScaled(value, Milli)
715 // SetScaled sets q's value to be value * 10^scale
716 func (q *Quantity) SetScaled(value int64, scale Scale) {
719 q.i = int64Amount{value: value, scale: scale}
722 // Copy is a convenience function that makes a deep copy for you. Non-deep
723 // copies of quantities share pointers and you will regret that.
724 func (q *Quantity) Copy() *Quantity {
735 d: infDecAmount{tmp.Set(q.d.Dec)},