github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity.go (about)

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