mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
Add examples & clarify comments further
This commit is contained in:
parent
b7b2ee34de
commit
2d2df5593c
@ -33,18 +33,19 @@ import (
|
|||||||
//
|
//
|
||||||
// The serialization format is:
|
// The serialization format is:
|
||||||
//
|
//
|
||||||
// <serialized> ::= <sign><numeric> | <numeric>
|
// <quantity> ::= <signedNumber><suffix>
|
||||||
// <numeric> ::= <digits><suffix> | <digits>.<digits><suffix>
|
// (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
|
||||||
// (Note that <suffix> may be ""!)
|
|
||||||
// <sign> ::= "+" | "-"
|
|
||||||
// <digits> ::= <digit> | <digit><digits>
|
|
||||||
// <signedDigits> ::= <digits> | <sign><digits>
|
|
||||||
// <digit> ::= 0 | 1 | ... | 9
|
// <digit> ::= 0 | 1 | ... | 9
|
||||||
// <suffix> ::= <binarySuffix> | <decimalExponent> | <decimalSuffix>
|
// <digits> ::= <digit> | <digit><digits>
|
||||||
// <binarySuffix> ::= i | Ki | Mi | Gi | Ti | Pi | Ei
|
// <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
|
||||||
// <decimalSuffix> ::= m | "" | k | M | G | T | P | E
|
// <sign> ::= "+" | "-"
|
||||||
// <decimalExponent> ::= "e" <signedDigits> | "E" <signedDigits>
|
// <signedNumber> ::= <number> | <sign><number>
|
||||||
|
// <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI>
|
||||||
|
// <binarySI> ::= i | Ki | Mi | Gi | Ti | Pi | Ei
|
||||||
|
// (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
|
||||||
|
// <decimalSI> ::= m | "" | k | M | G | T | P | E
|
||||||
// (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
|
// (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
|
||||||
|
// <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
|
||||||
//
|
//
|
||||||
// No matter which of the three exponent forms is used, no quantity may represent
|
// No matter which of the three exponent forms is used, no quantity may represent
|
||||||
// a number greater than 2^63-1 in magnitude, nor may it have more than 3 digits
|
// a number greater than 2^63-1 in magnitude, nor may it have more than 3 digits
|
||||||
@ -54,7 +55,7 @@ import (
|
|||||||
//
|
//
|
||||||
// When a Quantity is parsed from a string, it will remember the type of suffix
|
// When a Quantity is parsed from a string, it will remember the type of suffix
|
||||||
// it had, and will use the same type again when it is serialized.
|
// it had, and will use the same type again when it is serialized.
|
||||||
// One exception: numbers with a Binary SI suffix less than one will be changed
|
// One exception: numbers with a Binary SI suffix less than bigOne will be changed
|
||||||
// to Decimal SI suffix. E.g., .5i becomes 500m. [NOT 512m!]
|
// to Decimal SI suffix. E.g., .5i becomes 500m. [NOT 512m!]
|
||||||
//
|
//
|
||||||
// Before serializing, Quantity will be put in "canonical form".
|
// Before serializing, Quantity will be put in "canonical form".
|
||||||
@ -67,7 +68,12 @@ import (
|
|||||||
//
|
//
|
||||||
// Examples:
|
// Examples:
|
||||||
// 1.5 will be serialized as "1500m"
|
// 1.5 will be serialized as "1500m"
|
||||||
// 1.5Gi will be serialized as "1576Mi"
|
// 1.5Gi will be serialized as "1536Mi"
|
||||||
|
//
|
||||||
|
// NOTE: We reserve the right to amend this canonical format, perhaps to
|
||||||
|
// allow 1.5 to be canonical.
|
||||||
|
// TODO: Remove above disclaimer after all bikeshedding about format is over,
|
||||||
|
// or after March 2015.
|
||||||
//
|
//
|
||||||
// Note that the quantity will NEVER be internally represented by a
|
// Note that the quantity will NEVER be internally represented by a
|
||||||
// floating point number. That is the whole point of this exercise.
|
// floating point number. That is the whole point of this exercise.
|
||||||
@ -93,16 +99,25 @@ type Quantity struct {
|
|||||||
type Format string
|
type Format string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DecimalExponent = Format("DecExponent")
|
DecimalExponent = Format("DecimalExponent") // e.g., 12e6
|
||||||
// SI = International System of units.
|
BinarySI = Format("BinarySI") // e.g., 12Mi (12 * 2^20)
|
||||||
BinarySI = Format("BinSI")
|
DecimalSI = Format("DecimalSI") // e.g., 12M (12 * 10^6)
|
||||||
DecimalSI = Format("DecSI")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ParseOrDie turns the given string into a quantity or panics; for tests
|
||||||
|
// or others cases where you know the string is valid.
|
||||||
|
func ParseOrDie(str string) *Quantity {
|
||||||
|
q, err := ParseQuantity(str)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("cannot parse '%v': %v", str, err))
|
||||||
|
}
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// splitREString is used to separate a number from its suffix; as such,
|
// splitREString is used to separate a number from its suffix; as such,
|
||||||
// this is overly permissive, but that's OK-- it will be checked later.
|
// this is overly permissive, but that's OK-- it will be checked later.
|
||||||
splitREString = "^([+-]?[0123456789.]+)([eEimkKMGTP]*[-+]?[0123456789]*)$"
|
splitREString = "^([+-]?[0-9.]+)([eEimkKMGTP]*[-+]?[0-9]*)$"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -115,11 +130,11 @@ var (
|
|||||||
ErrSuffix = errors.New("unable to parse quantity's suffix")
|
ErrSuffix = errors.New("unable to parse quantity's suffix")
|
||||||
|
|
||||||
// Commonly needed big.Int values-- treat as read only!
|
// Commonly needed big.Int values-- treat as read only!
|
||||||
ten = big.NewInt(10)
|
bigTen = big.NewInt(10)
|
||||||
zero = big.NewInt(0)
|
bigZero = big.NewInt(0)
|
||||||
one = big.NewInt(1)
|
bigOne = big.NewInt(1)
|
||||||
thousand = big.NewInt(1000)
|
bigThousand = big.NewInt(1000)
|
||||||
ten24 = big.NewInt(1024)
|
big1024 = big.NewInt(1024)
|
||||||
|
|
||||||
// Commonly needed inf.Dec values-- treat as read only!
|
// Commonly needed inf.Dec values-- treat as read only!
|
||||||
decZero = inf.NewDec(0, 0)
|
decZero = inf.NewDec(0, 0)
|
||||||
@ -127,8 +142,7 @@ var (
|
|||||||
decMinusOne = inf.NewDec(-1, 0)
|
decMinusOne = inf.NewDec(-1, 0)
|
||||||
decThousand = inf.NewDec(1000, 0)
|
decThousand = inf.NewDec(1000, 0)
|
||||||
|
|
||||||
// Smallest and largest (in magnitude) numbers allowed.
|
// Largest (in magnitude) number allowed.
|
||||||
minAllowed = inf.NewDec(1, 3) // == 1/1000
|
|
||||||
maxAllowed = inf.NewDec((1<<63)-1, 0) // == max int64
|
maxAllowed = inf.NewDec((1<<63)-1, 0) // == max int64
|
||||||
|
|
||||||
// The maximum value we can represent milli-units for.
|
// The maximum value we can represent milli-units for.
|
||||||
@ -140,6 +154,7 @@ var (
|
|||||||
// ParseQuantity turns str into a Quantity, or returns an error.
|
// ParseQuantity turns str into a Quantity, or returns an error.
|
||||||
func ParseQuantity(str string) (*Quantity, error) {
|
func ParseQuantity(str string) (*Quantity, error) {
|
||||||
parts := splitRE.FindStringSubmatch(strings.TrimSpace(str))
|
parts := splitRE.FindStringSubmatch(strings.TrimSpace(str))
|
||||||
|
// regexp returns are entire match, followed by an entry for each () section.
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
return nil, ErrFormatWrong
|
return nil, ErrFormatWrong
|
||||||
}
|
}
|
||||||
@ -154,13 +169,14 @@ func ParseQuantity(str string) (*Quantity, error) {
|
|||||||
return nil, ErrSuffix
|
return nil, ErrSuffix
|
||||||
}
|
}
|
||||||
|
|
||||||
// So that no one but us has to think about suffixes, remove it.
|
// So that no bigOne but us has to think about suffixes, remove it.
|
||||||
if base == 10 {
|
if base == 10 {
|
||||||
amount.SetScale(amount.Scale() + inf.Scale(-exponent))
|
amount.SetScale(amount.Scale() + inf.Scale(-exponent))
|
||||||
} else if base == 2 {
|
} else if base == 2 {
|
||||||
// numericSuffix = 2 ** exponent
|
// numericSuffix = 2 ** exponent
|
||||||
numericSuffix := big.NewInt(1).Lsh(one, uint(exponent))
|
numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent))
|
||||||
amount.UnscaledBig().Mul(amount.UnscaledBig(), numericSuffix)
|
ub := amount.UnscaledBig()
|
||||||
|
amount.SetUnscaledBig(ub.Mul(ub, numericSuffix))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cap at min/max bounds.
|
// Cap at min/max bounds.
|
||||||
@ -168,12 +184,12 @@ func ParseQuantity(str string) (*Quantity, error) {
|
|||||||
if sign == -1 {
|
if sign == -1 {
|
||||||
amount.Neg(amount)
|
amount.Neg(amount)
|
||||||
}
|
}
|
||||||
// This rounds non-zero values up to the minimum representable
|
// This rounds non-bigZero values up to the minimum representable
|
||||||
// value, under the theory that if you want some resources, you
|
// value, under the theory that if you want some resources, you
|
||||||
// should get some resources, even if you asked for way too small
|
// should get some resources, even if you asked for way too small
|
||||||
// of an amount.
|
// of an amount.
|
||||||
// Arguably, this should be inf.RoundHalfUp (normal rounding), but
|
// Arguably, this should be inf.RoundHalfUp (normal rounding), but
|
||||||
// that would have the side effect of rounding values < .5m to zero.
|
// that would have the side effect of rounding values < .5m to bigZero.
|
||||||
amount.Round(amount, 3, inf.RoundUp)
|
amount.Round(amount, 3, inf.RoundUp)
|
||||||
|
|
||||||
// The max is just a simple cap.
|
// The max is just a simple cap.
|
||||||
@ -198,9 +214,9 @@ func ParseQuantity(str string) (*Quantity, error) {
|
|||||||
func removeFactors(d, factor *big.Int) (result *big.Int, times int) {
|
func removeFactors(d, factor *big.Int) (result *big.Int, times int) {
|
||||||
q := big.NewInt(0)
|
q := big.NewInt(0)
|
||||||
m := big.NewInt(0)
|
m := big.NewInt(0)
|
||||||
for d.Cmp(zero) != 0 {
|
for d.Cmp(bigZero) != 0 {
|
||||||
q.DivMod(d, factor, m)
|
q.DivMod(d, factor, m)
|
||||||
if m.Cmp(zero) != 0 {
|
if m.Cmp(bigZero) != 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
times++
|
times++
|
||||||
@ -212,7 +228,7 @@ func removeFactors(d, factor *big.Int) (result *big.Int, times int) {
|
|||||||
// Canonicalize returns the canonical form of q and its suffix (see comment on Quantity).
|
// Canonicalize returns the canonical form of q and its suffix (see comment on Quantity).
|
||||||
//
|
//
|
||||||
// Note about BinarySI:
|
// Note about BinarySI:
|
||||||
// * If q.Format is set to BinarySI and q.Amount represents a non-zero value between
|
// * If q.Format is set to BinarySI and q.Amount represents a non-bigZero value between
|
||||||
// -1 and +1, it will be emitted as if q.Format were DecimalSI.
|
// -1 and +1, it will be emitted as if q.Format were DecimalSI.
|
||||||
// * Otherwise, if q.Format is set to BinarySI, frational parts of q.Amount will be
|
// * Otherwise, if q.Format is set to BinarySI, frational parts of q.Amount will be
|
||||||
// rounded up. (1.1i becomes 2i.)
|
// rounded up. (1.1i becomes 2i.)
|
||||||
@ -243,19 +259,19 @@ func (q *Quantity) Canonicalize() (string, suffix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
|
// TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
|
||||||
// one of the other formats.
|
// bigOne of the other formats.
|
||||||
switch format {
|
switch format {
|
||||||
case DecimalExponent, DecimalSI:
|
case DecimalExponent, DecimalSI:
|
||||||
mantissa := q.Amount.UnscaledBig()
|
mantissa := q.Amount.UnscaledBig()
|
||||||
exponent := int(-q.Amount.Scale())
|
exponent := int(-q.Amount.Scale())
|
||||||
amount := big.NewInt(0).Set(mantissa)
|
amount := big.NewInt(0).Set(mantissa)
|
||||||
// move all factors of 10 into the exponent for easy reasoning
|
// move all factors of 10 into the exponent for easy reasoning
|
||||||
amount, times := removeFactors(amount, ten)
|
amount, times := removeFactors(amount, bigTen)
|
||||||
exponent += times
|
exponent += times
|
||||||
|
|
||||||
// make sure exponent is a multiple of 3
|
// make sure exponent is a multiple of 3
|
||||||
for exponent%3 != 0 {
|
for exponent%3 != 0 {
|
||||||
amount.Mul(amount, ten)
|
amount.Mul(amount, bigTen)
|
||||||
exponent--
|
exponent--
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,18 +280,17 @@ func (q *Quantity) Canonicalize() (string, suffix) {
|
|||||||
return number, suffix
|
return number, suffix
|
||||||
case BinarySI:
|
case BinarySI:
|
||||||
tmp := &inf.Dec{}
|
tmp := &inf.Dec{}
|
||||||
//tmp.Set(q.Amount)
|
|
||||||
tmp.Round(q.Amount, 0, inf.RoundUp)
|
tmp.Round(q.Amount, 0, inf.RoundUp)
|
||||||
amount := tmp.UnscaledBig()
|
amount := tmp.UnscaledBig()
|
||||||
exponent := int(-q.Amount.Scale())
|
exponent := int(-q.Amount.Scale())
|
||||||
// Apply the (base-10) shift. This will lose any fractional
|
// Apply the (base-10) shift. This will lose any fractional
|
||||||
// part, which is intentional.
|
// part, which is intentional.
|
||||||
for exponent > 0 {
|
for exponent > 0 {
|
||||||
amount.Mul(amount, ten)
|
amount.Mul(amount, bigTen)
|
||||||
exponent--
|
exponent--
|
||||||
}
|
}
|
||||||
|
|
||||||
amount, exponent = removeFactors(amount, ten24)
|
amount, exponent = removeFactors(amount, big1024)
|
||||||
suffix, _ := quantitySuffixer.construct(2, exponent*10, format)
|
suffix, _ := quantitySuffixer.construct(2, exponent*10, format)
|
||||||
number := amount.String()
|
number := amount.String()
|
||||||
return number, suffix
|
return number, suffix
|
||||||
@ -317,7 +332,8 @@ func NewQuantity(value int64, format Format) *Quantity {
|
|||||||
|
|
||||||
// NewMilliQuantity returns a new Quantity representing the given
|
// NewMilliQuantity returns a new Quantity representing the given
|
||||||
// value * 1/1000 in the given format. Note that BinarySI formatting
|
// value * 1/1000 in the given format. Note that BinarySI formatting
|
||||||
// will cause rounding for fractional values.
|
// will round fractional values, and will be changed to DecimalSI for
|
||||||
|
// values x where (-1 < x < 1) && (x != 0).
|
||||||
func NewMilliQuantity(value int64, format Format) *Quantity {
|
func NewMilliQuantity(value int64, format Format) *Quantity {
|
||||||
return &Quantity{
|
return &Quantity{
|
||||||
Amount: inf.NewDec(value, 3),
|
Amount: inf.NewDec(value, 3),
|
||||||
|
59
pkg/api/resource/quantity_example_test.go
Normal file
59
pkg/api/resource/quantity_example_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package resource_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleFormat() {
|
||||||
|
memorySize := resource.NewQuantity(5*1024*1024*1024, resource.BinarySI)
|
||||||
|
fmt.Printf("memorySize = %v\n", memorySize)
|
||||||
|
|
||||||
|
diskSize := resource.NewQuantity(5*1000*1000*1000, resource.DecimalSI)
|
||||||
|
fmt.Printf("diskSize = %v\n", diskSize)
|
||||||
|
|
||||||
|
cores := resource.NewMilliQuantity(5300, resource.DecimalSI)
|
||||||
|
fmt.Printf("cores = %v\n", cores)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// memorySize = 5Gi
|
||||||
|
// diskSize = 5G
|
||||||
|
// cores = 5300m
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleParseOrDie() {
|
||||||
|
memorySize := resource.ParseOrDie("5Gi")
|
||||||
|
fmt.Printf("memorySize = %v (%v)\n", memorySize.Value(), memorySize.Format)
|
||||||
|
|
||||||
|
diskSize := resource.ParseOrDie("5G")
|
||||||
|
fmt.Printf("diskSize = %v (%v)\n", diskSize.Value(), diskSize.Format)
|
||||||
|
|
||||||
|
cores := resource.ParseOrDie("5300m")
|
||||||
|
fmt.Printf("milliCores = %v (%v)\n", cores.MilliValue(), cores.Format)
|
||||||
|
|
||||||
|
cores2 := resource.ParseOrDie("5.4")
|
||||||
|
fmt.Printf("milliCores = %v (%v)\n", cores2.MilliValue(), cores2.Format)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// memorySize = 5368709120 (BinarySI)
|
||||||
|
// diskSize = 5000000000 (DecimalSI)
|
||||||
|
// milliCores = 5300 (DecimalSI)
|
||||||
|
// milliCores = 5400 (DecimalSI)
|
||||||
|
}
|
@ -162,6 +162,10 @@ func TestQuantityParse(t *testing.T) {
|
|||||||
// Even if it were, 500 * 1/1024 = .48828125, NOT .512
|
// Even if it were, 500 * 1/1024 = .48828125, NOT .512
|
||||||
// I cannot recommend using this feature, it is confusing.
|
// I cannot recommend using this feature, it is confusing.
|
||||||
{"0.5i", Quantity{dec(500, -3), DecimalSI}},
|
{"0.5i", Quantity{dec(500, -3), DecimalSI}},
|
||||||
|
|
||||||
|
// Things written by trolls
|
||||||
|
{".001", Quantity{dec(1, -3), DecimalSI}},
|
||||||
|
{"1.", Quantity{dec(1, 0), DecimalSI}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range table {
|
for _, item := range table {
|
||||||
|
Loading…
Reference in New Issue
Block a user