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 }