golang.org/x/build@v0.0.0-20240506185731-218518f32b70/kubernetes/api/quantity.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors All rights reserved.
     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 api
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"math/big"
    23  	"regexp"
    24  	"strings"
    25  
    26  	"gopkg.in/inf.v0"
    27  )
    28  
    29  // Quantity is a fixed-point representation of a number.
    30  // It provides convenient marshaling/unmarshaling in JSON and YAML,
    31  // in addition to String() and Int64() accessors.
    32  //
    33  // The serialization format is:
    34  //
    35  //	<quantity>        ::= <signedNumber><suffix>
    36  //	  (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
    37  //	<digit>           ::= 0 | 1 | ... | 9
    38  //	<digits>          ::= <digit> | <digit><digits>
    39  //	<number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
    40  //	<sign>            ::= "+" | "-"
    41  //	<signedNumber>    ::= <number> | <sign><number>
    42  //	<suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI>
    43  //	<binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
    44  //	  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
    45  //	<decimalSI>       ::= m | "" | k | M | G | T | P | E
    46  //	  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
    47  //	<decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
    48  //
    49  // No matter which of the three exponent forms is used, no quantity may represent
    50  // a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal
    51  // places. Numbers larger or more precise will be capped or rounded up.
    52  // (E.g.: 0.1m will rounded up to 1m.)
    53  // This may be extended in the future if we require larger or smaller quantities.
    54  //
    55  // When a Quantity is parsed from a string, it will remember the type of suffix
    56  // it had, and will use the same type again when it is serialized.
    57  //
    58  // Before serializing, Quantity will be put in "canonical form".
    59  // This means that Exponent/suffix will be adjusted up or down (with a
    60  // corresponding increase or decrease in Mantissa) such that:
    61  //
    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  //
    66  // The sign will be omitted unless the number is negative.
    67  //
    68  // Examples:
    69  //
    70  //	1.5 will be serialized as "1500m"
    71  //	1.5Gi will be serialized as "1536Mi"
    72  //
    73  // NOTE: We reserve the right to amend this canonical format, perhaps to
    74  // allow 1.5 to be canonical.
    75  // TODO: Remove above disclaimer after all bikeshedding about format is over,
    76  // or after March 2015.
    77  //
    78  // Note that the quantity will NEVER be internally represented by a
    79  // floating point number. That is the whole point of this exercise.
    80  //
    81  // Non-canonical values will still parse as long as they are well formed,
    82  // but will be re-emitted in their canonical form. (So always use canonical
    83  // form, or don't diff.)
    84  //
    85  // This format is intended to make it difficult to use these numbers without
    86  // writing some sort of special handling code in the hopes that that will
    87  // cause implementors to also use a fixed point implementation.
    88  type Quantity struct {
    89  	// Amount is public, so you can manipulate it if the accessor
    90  	// functions are not sufficient.
    91  	Amount *inf.Dec
    92  
    93  	// Change Format at will. See the comment for Canonicalize for
    94  	// more details.
    95  	Format
    96  }
    97  
    98  // Format lists the three possible formattings of a quantity.
    99  type Format string
   100  
   101  const (
   102  	DecimalExponent = Format("DecimalExponent") // e.g., 12e6
   103  	BinarySI        = Format("BinarySI")        // e.g., 12Mi (12 * 2^20)
   104  	DecimalSI       = Format("DecimalSI")       // e.g., 12M  (12 * 10^6)
   105  )
   106  
   107  // MustParse turns the given string into a quantity or panics; for tests
   108  // or others cases where you know the string is valid.
   109  func MustParse(str string) Quantity {
   110  	q, err := ParseQuantity(str)
   111  	if err != nil {
   112  		panic(fmt.Errorf("cannot parse '%v': %v", str, err))
   113  	}
   114  	return *q
   115  }
   116  
   117  const (
   118  	// splitREString is used to separate a number from its suffix; as such,
   119  	// this is overly permissive, but that's OK-- it will be checked later.
   120  	splitREString = "^([+-]?[0-9.]+)([eEimkKMGTP]*[-+]?[0-9]*)$"
   121  )
   122  
   123  var (
   124  	// splitRE is used to get the various parts of a number.
   125  	splitRE = regexp.MustCompile(splitREString)
   126  
   127  	// Errors that could happen while parsing a string.
   128  	ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'")
   129  	ErrNumeric     = errors.New("unable to parse numeric part of quantity")
   130  	ErrSuffix      = errors.New("unable to parse quantity's suffix")
   131  
   132  	// Commonly needed big.Int values-- treat as read only!
   133  	bigTen      = big.NewInt(10)
   134  	bigZero     = big.NewInt(0)
   135  	bigOne      = big.NewInt(1)
   136  	bigThousand = big.NewInt(1000)
   137  	big1024     = big.NewInt(1024)
   138  
   139  	// Commonly needed inf.Dec values-- treat as read only!
   140  	decZero      = inf.NewDec(0, 0)
   141  	decOne       = inf.NewDec(1, 0)
   142  	decMinusOne  = inf.NewDec(-1, 0)
   143  	decThousand  = inf.NewDec(1000, 0)
   144  	dec1024      = inf.NewDec(1024, 0)
   145  	decMinus1024 = inf.NewDec(-1024, 0)
   146  
   147  	// Largest (in magnitude) number allowed.
   148  	maxAllowed = inf.NewDec((1<<63)-1, 0) // == max int64
   149  
   150  	// The maximum value we can represent milli-units for.
   151  	// Compare with the return value of Quantity.Value() to
   152  	// see if it's safe to use Quantity.MilliValue().
   153  	MaxMilliValue = int64(((1 << 63) - 1) / 1000)
   154  )
   155  
   156  // ParseQuantity turns str into a Quantity, or returns an error.
   157  func ParseQuantity(str string) (*Quantity, error) {
   158  	parts := splitRE.FindStringSubmatch(strings.TrimSpace(str))
   159  	// regexp returns are entire match, followed by an entry for each () section.
   160  	if len(parts) != 3 {
   161  		return nil, ErrFormatWrong
   162  	}
   163  
   164  	amount := new(inf.Dec)
   165  	if _, ok := amount.SetString(parts[1]); !ok {
   166  		return nil, ErrNumeric
   167  	}
   168  
   169  	base, exponent, format, ok := quantitySuffixer.interpret(suffix(parts[2]))
   170  	if !ok {
   171  		return nil, ErrSuffix
   172  	}
   173  
   174  	// So that no one but us has to think about suffixes, remove it.
   175  	if base == 10 {
   176  		amount.SetScale(amount.Scale() + inf.Scale(-exponent))
   177  	} else if base == 2 {
   178  		// numericSuffix = 2 ** exponent
   179  		numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent))
   180  		ub := amount.UnscaledBig()
   181  		amount.SetUnscaledBig(ub.Mul(ub, numericSuffix))
   182  	}
   183  
   184  	// Cap at min/max bounds.
   185  	sign := amount.Sign()
   186  	if sign == -1 {
   187  		amount.Neg(amount)
   188  	}
   189  	// This rounds non-zero values up to the minimum representable
   190  	// value, under the theory that if you want some resources, you
   191  	// should get some resources, even if you asked for way too small
   192  	// of an amount.
   193  	// Arguably, this should be inf.RoundHalfUp (normal rounding), but
   194  	// that would have the side effect of rounding values < .5m to zero.
   195  	if v, ok := amount.Unscaled(); v != int64(0) || !ok {
   196  		amount.Round(amount, 3, inf.RoundUp)
   197  	}
   198  
   199  	// The max is just a simple cap.
   200  	if amount.Cmp(maxAllowed) > 0 {
   201  		amount.Set(maxAllowed)
   202  	}
   203  	if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 {
   204  		// This avoids rounding and hopefully confusion, too.
   205  		format = DecimalSI
   206  	}
   207  	if sign == -1 {
   208  		amount.Neg(amount)
   209  	}
   210  
   211  	return &Quantity{amount, format}, nil
   212  }
   213  
   214  // removeFactors divides in a loop; the return values have the property that
   215  // d == result * factor ^ times
   216  // d may be modified in place.
   217  // If d == 0, then the return values will be (0, 0)
   218  func removeFactors(d, factor *big.Int) (result *big.Int, times int) {
   219  	q := big.NewInt(0)
   220  	m := big.NewInt(0)
   221  	for d.Cmp(bigZero) != 0 {
   222  		q.DivMod(d, factor, m)
   223  		if m.Cmp(bigZero) != 0 {
   224  			break
   225  		}
   226  		times++
   227  		d, q = q, d
   228  	}
   229  	return d, times
   230  }
   231  
   232  // Canonicalize returns the canonical form of q and its suffix (see comment on Quantity).
   233  //
   234  // Note about BinarySI:
   235  //   - If q.Format is set to BinarySI and q.Amount represents a non-zero value between
   236  //     -1 and +1, it will be emitted as if q.Format were DecimalSI.
   237  //   - Otherwise, if q.Format is set to BinarySI, frational parts of q.Amount will be
   238  //     rounded up. (1.1i becomes 2i.)
   239  func (q *Quantity) Canonicalize() (string, suffix) {
   240  	if q.Amount == nil {
   241  		return "0", ""
   242  	}
   243  
   244  	// zero is zero always
   245  	if q.Amount.Cmp(&inf.Dec{}) == 0 {
   246  		return "0", ""
   247  	}
   248  
   249  	format := q.Format
   250  	switch format {
   251  	case DecimalExponent, DecimalSI:
   252  	case BinarySI:
   253  		if q.Amount.Cmp(decMinus1024) > 0 && q.Amount.Cmp(dec1024) < 0 {
   254  			// This avoids rounding and hopefully confusion, too.
   255  			format = DecimalSI
   256  		} else {
   257  			tmp := &inf.Dec{}
   258  			tmp.Round(q.Amount, 0, inf.RoundUp)
   259  			if tmp.Cmp(q.Amount) != 0 {
   260  				// Don't lose precision-- show as DecimalSI
   261  				format = DecimalSI
   262  			}
   263  		}
   264  	default:
   265  		format = DecimalExponent
   266  	}
   267  
   268  	// TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
   269  	// one of the other formats.
   270  	switch format {
   271  	case DecimalExponent, DecimalSI:
   272  		mantissa := q.Amount.UnscaledBig()
   273  		exponent := int(-q.Amount.Scale())
   274  		amount := big.NewInt(0).Set(mantissa)
   275  		// move all factors of 10 into the exponent for easy reasoning
   276  		amount, times := removeFactors(amount, bigTen)
   277  		exponent += times
   278  
   279  		// make sure exponent is a multiple of 3
   280  		for exponent%3 != 0 {
   281  			amount.Mul(amount, bigTen)
   282  			exponent--
   283  		}
   284  
   285  		suffix, _ := quantitySuffixer.construct(10, exponent, format)
   286  		number := amount.String()
   287  		return number, suffix
   288  	case BinarySI:
   289  		tmp := &inf.Dec{}
   290  		tmp.Round(q.Amount, 0, inf.RoundUp)
   291  
   292  		amount, exponent := removeFactors(tmp.UnscaledBig(), big1024)
   293  		suffix, _ := quantitySuffixer.construct(2, exponent*10, format)
   294  		number := amount.String()
   295  		return number, suffix
   296  	}
   297  	return "0", ""
   298  }
   299  
   300  // String formats the Quantity as a string.
   301  func (q *Quantity) String() string {
   302  	number, suffix := q.Canonicalize()
   303  	return number + string(suffix)
   304  }
   305  
   306  func (q *Quantity) Add(y Quantity) error {
   307  	if q.Format != y.Format {
   308  		return fmt.Errorf("format mismatch: %v vs. %v", q.Format, y.Format)
   309  	}
   310  	q.Amount.Add(q.Amount, y.Amount)
   311  	return nil
   312  }
   313  
   314  func (q *Quantity) Sub(y Quantity) error {
   315  	if q.Format != y.Format {
   316  		return fmt.Errorf("format mismatch: %v vs. %v", q.Format, y.Format)
   317  	}
   318  	q.Amount.Sub(q.Amount, y.Amount)
   319  	return nil
   320  }
   321  
   322  // MarshalJSON implements the json.Marshaller interface.
   323  func (q Quantity) MarshalJSON() ([]byte, error) {
   324  	return []byte(`"` + q.String() + `"`), nil
   325  }
   326  
   327  // UnmarshalJSON implements the json.Unmarshaller interface.
   328  func (q *Quantity) UnmarshalJSON(value []byte) error {
   329  	str := string(value)
   330  	parsed, err := ParseQuantity(strings.Trim(str, `"`))
   331  	if err != nil {
   332  		return err
   333  	}
   334  	// This copy is safe because parsed will not be referred to again.
   335  	*q = *parsed
   336  	return nil
   337  }
   338  
   339  // NewQuantity returns a new Quantity representing the given
   340  // value in the given format.
   341  func NewQuantity(value int64, format Format) *Quantity {
   342  	return &Quantity{
   343  		Amount: inf.NewDec(value, 0),
   344  		Format: format,
   345  	}
   346  }
   347  
   348  // NewMilliQuantity returns a new Quantity representing the given
   349  // value * 1/1000 in the given format. Note that BinarySI formatting
   350  // will round fractional values, and will be changed to DecimalSI for
   351  // values x where (-1 < x < 1) && (x != 0).
   352  func NewMilliQuantity(value int64, format Format) *Quantity {
   353  	return &Quantity{
   354  		Amount: inf.NewDec(value, 3),
   355  		Format: format,
   356  	}
   357  }
   358  
   359  // Value returns the value of q; any fractional part will be lost.
   360  func (q *Quantity) Value() int64 {
   361  	if q.Amount == nil {
   362  		return 0
   363  	}
   364  	tmp := &inf.Dec{}
   365  	return tmp.Round(q.Amount, 0, inf.RoundUp).UnscaledBig().Int64()
   366  }
   367  
   368  // MilliValue returns the value of q * 1000; this could overflow an int64;
   369  // if that's a concern, call Value() first to verify the number is small enough.
   370  func (q *Quantity) MilliValue() int64 {
   371  	if q.Amount == nil {
   372  		return 0
   373  	}
   374  	tmp := &inf.Dec{}
   375  	return tmp.Round(tmp.Mul(q.Amount, decThousand), 0, inf.RoundUp).UnscaledBig().Int64()
   376  }
   377  
   378  // Set sets q's value to be value.
   379  func (q *Quantity) Set(value int64) {
   380  	if q.Amount == nil {
   381  		q.Amount = &inf.Dec{}
   382  	}
   383  	q.Amount.SetUnscaled(value)
   384  	q.Amount.SetScale(0)
   385  }
   386  
   387  // SetMilli sets q's value to be value * 1/1000.
   388  func (q *Quantity) SetMilli(value int64) {
   389  	if q.Amount == nil {
   390  		q.Amount = &inf.Dec{}
   391  	}
   392  	q.Amount.SetUnscaled(value)
   393  	q.Amount.SetScale(3)
   394  }
   395  
   396  // Copy is a convenience function that makes a deep copy for you. Non-deep
   397  // copies of quantities share pointers and you will regret that.
   398  func (q *Quantity) Copy() *Quantity {
   399  	if q.Amount == nil {
   400  		return NewQuantity(0, q.Format)
   401  	}
   402  	tmp := &inf.Dec{}
   403  	return &Quantity{
   404  		Amount: tmp.Set(q.Amount),
   405  		Format: q.Format,
   406  	}
   407  }