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  }