github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/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 resource
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"math/big"
    23  	"regexp"
    24  	"strings"
    25  
    26  	flag "github.com/spf13/pflag"
    27  	"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  	if v, ok := amount.Unscaled(); v != int64(0) || !ok {
   194  		amount.Round(amount, 3, inf.RoundUp)
   195  	}
   196  
   197  	// The max is just a simple cap.
   198  	if amount.Cmp(maxAllowed) > 0 {
   199  		amount.Set(maxAllowed)
   200  	}
   201  	if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 {
   202  		// This avoids rounding and hopefully confusion, too.
   203  		format = DecimalSI
   204  	}
   205  	if sign == -1 {
   206  		amount.Neg(amount)
   207  	}
   208  
   209  	return &Quantity{amount, format}, nil
   210  }
   211  
   212  // removeFactors divides in a loop; the return values have the property that
   213  // d == result * factor ^ times
   214  // d may be modified in place.
   215  // If d == 0, then the return values will be (0, 0)
   216  func removeFactors(d, factor *big.Int) (result *big.Int, times int) {
   217  	q := big.NewInt(0)
   218  	m := big.NewInt(0)
   219  	for d.Cmp(bigZero) != 0 {
   220  		q.DivMod(d, factor, m)
   221  		if m.Cmp(bigZero) != 0 {
   222  			break
   223  		}
   224  		times++
   225  		d, q = q, d
   226  	}
   227  	return d, times
   228  }
   229  
   230  // Canonicalize returns the canonical form of q and its suffix (see comment on Quantity).
   231  //
   232  // Note about BinarySI:
   233  // * If q.Format is set to BinarySI and q.Amount represents a non-zero value between
   234  //   -1 and +1, it will be emitted as if q.Format were DecimalSI.
   235  // * Otherwise, if q.Format is set to BinarySI, frational parts of q.Amount will be
   236  //   rounded up. (1.1i becomes 2i.)
   237  func (q *Quantity) Canonicalize() (string, suffix) {
   238  	if q.Amount == nil {
   239  		return "0", ""
   240  	}
   241  
   242  	// zero is zero always
   243  	if q.Amount.Cmp(&inf.Dec{}) == 0 {
   244  		return "0", ""
   245  	}
   246  
   247  	format := q.Format
   248  	switch format {
   249  	case DecimalExponent, DecimalSI:
   250  	case BinarySI:
   251  		if q.Amount.Cmp(decMinus1024) > 0 && q.Amount.Cmp(dec1024) < 0 {
   252  			// This avoids rounding and hopefully confusion, too.
   253  			format = DecimalSI
   254  		} else {
   255  			tmp := &inf.Dec{}
   256  			tmp.Round(q.Amount, 0, inf.RoundUp)
   257  			if tmp.Cmp(q.Amount) != 0 {
   258  				// Don't lose precision-- show as DecimalSI
   259  				format = DecimalSI
   260  			}
   261  		}
   262  	default:
   263  		format = DecimalExponent
   264  	}
   265  
   266  	// TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
   267  	// one of the other formats.
   268  	switch format {
   269  	case DecimalExponent, DecimalSI:
   270  		mantissa := q.Amount.UnscaledBig()
   271  		exponent := int(-q.Amount.Scale())
   272  		amount := big.NewInt(0).Set(mantissa)
   273  		// move all factors of 10 into the exponent for easy reasoning
   274  		amount, times := removeFactors(amount, bigTen)
   275  		exponent += times
   276  
   277  		// make sure exponent is a multiple of 3
   278  		for exponent%3 != 0 {
   279  			amount.Mul(amount, bigTen)
   280  			exponent--
   281  		}
   282  
   283  		suffix, _ := quantitySuffixer.construct(10, exponent, format)
   284  		number := amount.String()
   285  		return number, suffix
   286  	case BinarySI:
   287  		tmp := &inf.Dec{}
   288  		tmp.Round(q.Amount, 0, inf.RoundUp)
   289  
   290  		amount, exponent := removeFactors(tmp.UnscaledBig(), big1024)
   291  		suffix, _ := quantitySuffixer.construct(2, exponent*10, format)
   292  		number := amount.String()
   293  		return number, suffix
   294  	}
   295  	return "0", ""
   296  }
   297  
   298  // String formats the Quantity as a string.
   299  func (q *Quantity) String() string {
   300  	number, suffix := q.Canonicalize()
   301  	return number + string(suffix)
   302  }
   303  
   304  // Cmp compares q and y and returns:
   305  //
   306  //   -1 if q <  y
   307  //    0 if q == y
   308  //   +1 if q >  y
   309  //
   310  func (q *Quantity) Cmp(y Quantity) int {
   311  	num1 := q.Value()
   312  	num2 := y.Value()
   313  	if num1 < MaxMilliValue && num2 < MaxMilliValue {
   314  		num1 = q.MilliValue()
   315  		num2 = y.MilliValue()
   316  	}
   317  	if num1 < num2 {
   318  		return -1
   319  	} else if num1 > num2 {
   320  		return 1
   321  	}
   322  	return 0
   323  }
   324  
   325  func (q *Quantity) Add(y Quantity) error {
   326  	q.Amount.Add(q.Amount, y.Amount)
   327  	return nil
   328  }
   329  
   330  func (q *Quantity) Sub(y Quantity) error {
   331  	if q.Format != y.Format {
   332  		return fmt.Errorf("format mismatch: %v vs. %v", q.Format, y.Format)
   333  	}
   334  	q.Amount.Sub(q.Amount, y.Amount)
   335  	return nil
   336  }
   337  
   338  // MarshalJSON implements the json.Marshaller interface.
   339  func (q Quantity) MarshalJSON() ([]byte, error) {
   340  	return []byte(`"` + q.String() + `"`), nil
   341  }
   342  
   343  // UnmarshalJSON implements the json.Unmarshaller interface.
   344  func (q *Quantity) UnmarshalJSON(value []byte) error {
   345  	str := string(value)
   346  	parsed, err := ParseQuantity(strings.Trim(str, `"`))
   347  	if err != nil {
   348  		return err
   349  	}
   350  	// This copy is safe because parsed will not be referred to again.
   351  	*q = *parsed
   352  	return nil
   353  }
   354  
   355  // NewQuantity returns a new Quantity representing the given
   356  // value in the given format.
   357  func NewQuantity(value int64, format Format) *Quantity {
   358  	return &Quantity{
   359  		Amount: inf.NewDec(value, 0),
   360  		Format: format,
   361  	}
   362  }
   363  
   364  // NewMilliQuantity returns a new Quantity representing the given
   365  // value * 1/1000 in the given format. Note that BinarySI formatting
   366  // will round fractional values, and will be changed to DecimalSI for
   367  // values x where (-1 < x < 1) && (x != 0).
   368  func NewMilliQuantity(value int64, format Format) *Quantity {
   369  	return &Quantity{
   370  		Amount: inf.NewDec(value, 3),
   371  		Format: format,
   372  	}
   373  }
   374  
   375  // Value returns the value of q; any fractional part will be lost.
   376  func (q *Quantity) Value() int64 {
   377  	if q.Amount == nil {
   378  		return 0
   379  	}
   380  	tmp := &inf.Dec{}
   381  	return tmp.Round(q.Amount, 0, inf.RoundUp).UnscaledBig().Int64()
   382  }
   383  
   384  // MilliValue returns the value of q * 1000; this could overflow an int64;
   385  // if that's a concern, call Value() first to verify the number is small enough.
   386  func (q *Quantity) MilliValue() int64 {
   387  	if q.Amount == nil {
   388  		return 0
   389  	}
   390  	tmp := &inf.Dec{}
   391  	return tmp.Round(tmp.Mul(q.Amount, decThousand), 0, inf.RoundUp).UnscaledBig().Int64()
   392  }
   393  
   394  // Set sets q's value to be value.
   395  func (q *Quantity) Set(value int64) {
   396  	if q.Amount == nil {
   397  		q.Amount = &inf.Dec{}
   398  	}
   399  	q.Amount.SetUnscaled(value)
   400  	q.Amount.SetScale(0)
   401  }
   402  
   403  // SetMilli sets q's value to be value * 1/1000.
   404  func (q *Quantity) SetMilli(value int64) {
   405  	if q.Amount == nil {
   406  		q.Amount = &inf.Dec{}
   407  	}
   408  	q.Amount.SetUnscaled(value)
   409  	q.Amount.SetScale(3)
   410  }
   411  
   412  // Copy is a convenience function that makes a deep copy for you. Non-deep
   413  // copies of quantities share pointers and you will regret that.
   414  func (q *Quantity) Copy() *Quantity {
   415  	if q.Amount == nil {
   416  		return NewQuantity(0, q.Format)
   417  	}
   418  	tmp := &inf.Dec{}
   419  	return &Quantity{
   420  		Amount: tmp.Set(q.Amount),
   421  		Format: q.Format,
   422  	}
   423  }
   424  
   425  // qFlag is a helper type for the Flag function
   426  type qFlag struct {
   427  	dest *Quantity
   428  }
   429  
   430  // Sets the value of the internal Quantity. (used by flag & pflag)
   431  func (qf qFlag) Set(val string) error {
   432  	q, err := ParseQuantity(val)
   433  	if err != nil {
   434  		return err
   435  	}
   436  	// This copy is OK because q will not be referenced again.
   437  	*qf.dest = *q
   438  	return nil
   439  }
   440  
   441  // Converts the value of the internal Quantity to a string. (used by flag & pflag)
   442  func (qf qFlag) String() string {
   443  	return qf.dest.String()
   444  }
   445  
   446  // States the type of flag this is (Quantity). (used by pflag)
   447  func (qf qFlag) Type() string {
   448  	return "quantity"
   449  }
   450  
   451  // QuantityFlag is a helper that makes a quantity flag (using standard flag package).
   452  // Will panic if defaultValue is not a valid quantity.
   453  func QuantityFlag(flagName, defaultValue, description string) *Quantity {
   454  	q := MustParse(defaultValue)
   455  	flag.Var(NewQuantityFlagValue(&q), flagName, description)
   456  	return &q
   457  }
   458  
   459  // NewQuantityFlagValue returns an object that can be used to back a flag,
   460  // pointing at the given Quantity variable.
   461  func NewQuantityFlagValue(q *Quantity) flag.Value {
   462  	return qFlag{q}
   463  }