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 }