mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-30 21:30:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			538 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			538 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2014 The Kubernetes Authors 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
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"math/big"
 | |
| 	"regexp"
 | |
| 	"strings"
 | |
| 
 | |
| 	flag "github.com/spf13/pflag"
 | |
| 	"speter.net/go/exp/math/dec/inf"
 | |
| )
 | |
| 
 | |
| // Quantity is a fixed-point representation of a number.
 | |
| // It provides convenient marshaling/unmarshaling in JSON and YAML,
 | |
| // in addition to String() and Int64() accessors.
 | |
| //
 | |
| // The serialization format is:
 | |
| //
 | |
| // <quantity>        ::= <signedNumber><suffix>
 | |
| //   (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
 | |
| // <digit>           ::= 0 | 1 | ... | 9
 | |
| // <digits>          ::= <digit> | <digit><digits>
 | |
| // <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
 | |
| // <sign>            ::= "+" | "-"
 | |
| // <signedNumber>    ::= <number> | <sign><number>
 | |
| // <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI>
 | |
| // <binarySI>        ::= 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.)
 | |
| // <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
 | |
| //
 | |
| // 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 decimal
 | |
| // places. Numbers larger or more precise will be capped or rounded up.
 | |
| // (E.g.: 0.1m will rounded up to 1m.)
 | |
| // This may be extended in the future if we require larger or smaller quantities.
 | |
| //
 | |
| // 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.
 | |
| //
 | |
| // Before serializing, Quantity will be put in "canonical form".
 | |
| // This means that Exponent/suffix will be adjusted up or down (with a
 | |
| // corresponding increase or decrease in Mantissa) such that:
 | |
| //   a. No precision is lost
 | |
| //   b. No fractional digits will be emitted
 | |
| //   c. The exponent (or suffix) is as large as possible.
 | |
| // The sign will be omitted unless the number is negative.
 | |
| //
 | |
| // Examples:
 | |
| //   1.5 will be serialized as "1500m"
 | |
| //   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
 | |
| // floating point number. That is the whole point of this exercise.
 | |
| //
 | |
| // Non-canonical values will still parse as long as they are well formed,
 | |
| // but will be re-emitted in their canonical form. (So always use canonical
 | |
| // form, or don't diff.)
 | |
| //
 | |
| // This format is intended to make it difficult to use these numbers without
 | |
| // writing some sort of special handling code in the hopes that that will
 | |
| // cause implementors to also use a fixed point implementation.
 | |
| //
 | |
| // +protobuf=true
 | |
| // +protobuf.embed=QuantityProto
 | |
| // +protobuf.options.marshal=false
 | |
| // +protobuf.options.(gogoproto.goproto_stringer)=false
 | |
| type Quantity struct {
 | |
| 	// Amount is public, so you can manipulate it if the accessor
 | |
| 	// functions are not sufficient.
 | |
| 	Amount *inf.Dec
 | |
| 
 | |
| 	// Change Format at will. See the comment for Canonicalize for
 | |
| 	// more details.
 | |
| 	Format
 | |
| }
 | |
| 
 | |
| // Format lists the three possible formattings of a quantity.
 | |
| type Format string
 | |
| 
 | |
| const (
 | |
| 	DecimalExponent = Format("DecimalExponent") // e.g., 12e6
 | |
| 	BinarySI        = Format("BinarySI")        // e.g., 12Mi (12 * 2^20)
 | |
| 	DecimalSI       = Format("DecimalSI")       // e.g., 12M  (12 * 10^6)
 | |
| )
 | |
| 
 | |
| // MustParse turns the given string into a quantity or panics; for tests
 | |
| // or others cases where you know the string is valid.
 | |
| func MustParse(str string) Quantity {
 | |
| 	q, err := ParseQuantity(str)
 | |
| 	if err != nil {
 | |
| 		panic(fmt.Errorf("cannot parse '%v': %v", str, err))
 | |
| 	}
 | |
| 	return *q
 | |
| }
 | |
| 
 | |
| // Scale is used for getting and setting the base-10 scaled value.
 | |
| // Base-2 scales are omitted for mathematical simplicity.
 | |
| // See Quantity.ScaledValue for more details.
 | |
| type Scale int
 | |
| 
 | |
| const (
 | |
| 	Nano  Scale = -9
 | |
| 	Micro Scale = -6
 | |
| 	Milli Scale = -3
 | |
| 	Kilo  Scale = 3
 | |
| 	Mega  Scale = 6
 | |
| 	Giga  Scale = 9
 | |
| 	Tera  Scale = 12
 | |
| 	Peta  Scale = 15
 | |
| 	Exa   Scale = 18
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	// 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.
 | |
| 	splitREString = "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	// splitRE is used to get the various parts of a number.
 | |
| 	splitRE = regexp.MustCompile(splitREString)
 | |
| 
 | |
| 	// Errors that could happen while parsing a string.
 | |
| 	ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'")
 | |
| 	ErrNumeric     = errors.New("unable to parse numeric part of quantity")
 | |
| 	ErrSuffix      = errors.New("unable to parse quantity's suffix")
 | |
| 
 | |
| 	// Commonly needed big.Int values-- treat as read only!
 | |
| 	bigTen      = big.NewInt(10)
 | |
| 	bigZero     = big.NewInt(0)
 | |
| 	bigOne      = big.NewInt(1)
 | |
| 	bigThousand = big.NewInt(1000)
 | |
| 	big1024     = big.NewInt(1024)
 | |
| 
 | |
| 	// Commonly needed inf.Dec values-- treat as read only!
 | |
| 	decZero      = inf.NewDec(0, 0)
 | |
| 	decOne       = inf.NewDec(1, 0)
 | |
| 	decMinusOne  = inf.NewDec(-1, 0)
 | |
| 	decThousand  = inf.NewDec(1000, 0)
 | |
| 	dec1024      = inf.NewDec(1024, 0)
 | |
| 	decMinus1024 = inf.NewDec(-1024, 0)
 | |
| 
 | |
| 	// Largest (in magnitude) number allowed.
 | |
| 	maxAllowed = inf.NewDec((1<<63)-1, 0) // == max int64
 | |
| 
 | |
| 	// The maximum value we can represent milli-units for.
 | |
| 	// Compare with the return value of Quantity.Value() to
 | |
| 	// see if it's safe to use Quantity.MilliValue().
 | |
| 	MaxMilliValue = int64(((1 << 63) - 1) / 1000)
 | |
| )
 | |
| 
 | |
| // ParseQuantity turns str into a Quantity, or returns an error.
 | |
| func ParseQuantity(str string) (*Quantity, error) {
 | |
| 	parts := splitRE.FindStringSubmatch(strings.TrimSpace(str))
 | |
| 	// regexp returns are entire match, followed by an entry for each () section.
 | |
| 	if len(parts) != 3 {
 | |
| 		return nil, ErrFormatWrong
 | |
| 	}
 | |
| 
 | |
| 	amount := new(inf.Dec)
 | |
| 	if _, ok := amount.SetString(parts[1]); !ok {
 | |
| 		return nil, ErrNumeric
 | |
| 	}
 | |
| 
 | |
| 	base, exponent, format, ok := quantitySuffixer.interpret(suffix(parts[2]))
 | |
| 	if !ok {
 | |
| 		return nil, ErrSuffix
 | |
| 	}
 | |
| 
 | |
| 	// So that no one but us has to think about suffixes, remove it.
 | |
| 	if base == 10 {
 | |
| 		amount.SetScale(amount.Scale() + Scale(exponent).infScale())
 | |
| 	} else if base == 2 {
 | |
| 		// numericSuffix = 2 ** exponent
 | |
| 		numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent))
 | |
| 		ub := amount.UnscaledBig()
 | |
| 		amount.SetUnscaledBig(ub.Mul(ub, numericSuffix))
 | |
| 	}
 | |
| 
 | |
| 	// Cap at min/max bounds.
 | |
| 	sign := amount.Sign()
 | |
| 	if sign == -1 {
 | |
| 		amount.Neg(amount)
 | |
| 	}
 | |
| 
 | |
| 	// This rounds non-zero values up to the minimum representable value, under the theory that
 | |
| 	// if you want some resources, you should get some resources, even if you asked for way too small
 | |
| 	// of an amount.  Arguably, this should be inf.RoundHalfUp (normal rounding), but that would have
 | |
| 	// the side effect of rounding values < .5n to zero.
 | |
| 	if v, ok := amount.Unscaled(); v != int64(0) || !ok {
 | |
| 		amount.Round(amount, Nano.infScale(), inf.RoundUp)
 | |
| 	}
 | |
| 
 | |
| 	// The max is just a simple cap.
 | |
| 	if amount.Cmp(maxAllowed) > 0 {
 | |
| 		amount.Set(maxAllowed)
 | |
| 	}
 | |
| 	if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 {
 | |
| 		// This avoids rounding and hopefully confusion, too.
 | |
| 		format = DecimalSI
 | |
| 	}
 | |
| 	if sign == -1 {
 | |
| 		amount.Neg(amount)
 | |
| 	}
 | |
| 
 | |
| 	return &Quantity{amount, format}, nil
 | |
| }
 | |
| 
 | |
| // removeFactors divides in a loop; the return values have the property that
 | |
| // d == result * factor ^ times
 | |
| // d may be modified in place.
 | |
| // If d == 0, then the return values will be (0, 0)
 | |
| func removeFactors(d, factor *big.Int) (result *big.Int, times int) {
 | |
| 	q := big.NewInt(0)
 | |
| 	m := big.NewInt(0)
 | |
| 	for d.Cmp(bigZero) != 0 {
 | |
| 		q.DivMod(d, factor, m)
 | |
| 		if m.Cmp(bigZero) != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		times++
 | |
| 		d, q = q, d
 | |
| 	}
 | |
| 	return d, times
 | |
| }
 | |
| 
 | |
| // Canonicalize returns the canonical form of q and its suffix (see comment on Quantity).
 | |
| //
 | |
| // Note about BinarySI:
 | |
| // * If q.Format is set to BinarySI and q.Amount represents a non-zero value between
 | |
| //   -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
 | |
| //   rounded up. (1.1i becomes 2i.)
 | |
| func (q *Quantity) Canonicalize() (string, suffix) {
 | |
| 	if q.Amount == nil {
 | |
| 		return "0", ""
 | |
| 	}
 | |
| 
 | |
| 	// zero is zero always
 | |
| 	if q.Amount.Cmp(&inf.Dec{}) == 0 {
 | |
| 		return "0", ""
 | |
| 	}
 | |
| 
 | |
| 	format := q.Format
 | |
| 	switch format {
 | |
| 	case DecimalExponent, DecimalSI:
 | |
| 	case BinarySI:
 | |
| 		if q.Amount.Cmp(decMinus1024) > 0 && q.Amount.Cmp(dec1024) < 0 {
 | |
| 			// This avoids rounding and hopefully confusion, too.
 | |
| 			format = DecimalSI
 | |
| 		} else {
 | |
| 			tmp := &inf.Dec{}
 | |
| 			tmp.Round(q.Amount, 0, inf.RoundUp)
 | |
| 			if tmp.Cmp(q.Amount) != 0 {
 | |
| 				// Don't lose precision-- show as DecimalSI
 | |
| 				format = DecimalSI
 | |
| 			}
 | |
| 		}
 | |
| 	default:
 | |
| 		format = DecimalExponent
 | |
| 	}
 | |
| 
 | |
| 	// TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
 | |
| 	// one of the other formats.
 | |
| 	switch format {
 | |
| 	case DecimalExponent, DecimalSI:
 | |
| 		mantissa := q.Amount.UnscaledBig()
 | |
| 		exponent := int(-q.Amount.Scale())
 | |
| 		amount := big.NewInt(0).Set(mantissa)
 | |
| 		// move all factors of 10 into the exponent for easy reasoning
 | |
| 		amount, times := removeFactors(amount, bigTen)
 | |
| 		exponent += times
 | |
| 
 | |
| 		// make sure exponent is a multiple of 3
 | |
| 		for exponent%3 != 0 {
 | |
| 			amount.Mul(amount, bigTen)
 | |
| 			exponent--
 | |
| 		}
 | |
| 
 | |
| 		suffix, _ := quantitySuffixer.construct(10, exponent, format)
 | |
| 		number := amount.String()
 | |
| 		return number, suffix
 | |
| 	case BinarySI:
 | |
| 		tmp := &inf.Dec{}
 | |
| 		tmp.Round(q.Amount, 0, inf.RoundUp)
 | |
| 
 | |
| 		amount, exponent := removeFactors(tmp.UnscaledBig(), big1024)
 | |
| 		suffix, _ := quantitySuffixer.construct(2, exponent*10, format)
 | |
| 		number := amount.String()
 | |
| 		return number, suffix
 | |
| 	}
 | |
| 	return "0", ""
 | |
| }
 | |
| 
 | |
| // String formats the Quantity as a string.
 | |
| func (q *Quantity) String() string {
 | |
| 	number, suffix := q.Canonicalize()
 | |
| 	return number + string(suffix)
 | |
| }
 | |
| 
 | |
| // Cmp compares q and y and returns:
 | |
| //
 | |
| //   -1 if q <  y
 | |
| //    0 if q == y
 | |
| //   +1 if q >  y
 | |
| //
 | |
| func (q *Quantity) Cmp(y Quantity) int {
 | |
| 	if q.Amount == nil {
 | |
| 		if y.Amount == nil {
 | |
| 			return 0
 | |
| 		}
 | |
| 		return -y.Amount.Sign()
 | |
| 	}
 | |
| 	if y.Amount == nil {
 | |
| 		return q.Amount.Sign()
 | |
| 	}
 | |
| 	return q.Amount.Cmp(y.Amount)
 | |
| }
 | |
| 
 | |
| func (q *Quantity) Add(y Quantity) error {
 | |
| 	switch {
 | |
| 	case y.Amount == nil:
 | |
| 		// Adding 0: do nothing.
 | |
| 	case q.Amount == nil:
 | |
| 		q.Amount = &inf.Dec{}
 | |
| 		return q.Add(y)
 | |
| 	default:
 | |
| 		// we want to preserve the format of the non-zero value
 | |
| 		zero := &inf.Dec{}
 | |
| 		if q.Amount.Cmp(zero) == 0 && y.Amount.Cmp(zero) != 0 {
 | |
| 			q.Format = y.Format
 | |
| 		}
 | |
| 		q.Amount.Add(q.Amount, y.Amount)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (q *Quantity) Sub(y Quantity) error {
 | |
| 	switch {
 | |
| 	case y.Amount == nil:
 | |
| 		// Subtracting 0: do nothing.
 | |
| 	case q.Amount == nil:
 | |
| 		q.Amount = &inf.Dec{}
 | |
| 		return q.Sub(y)
 | |
| 	default:
 | |
| 		// we want to preserve the format of the non-zero value
 | |
| 		zero := &inf.Dec{}
 | |
| 		if q.Amount.Cmp(zero) == 0 && y.Amount.Cmp(zero) != 0 {
 | |
| 			q.Format = y.Format
 | |
| 		}
 | |
| 		q.Amount.Sub(q.Amount, y.Amount)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Neg sets q to the negative value of y.
 | |
| // It updates the format of q to match y.
 | |
| func (q *Quantity) Neg(y Quantity) error {
 | |
| 	switch {
 | |
| 	case y.Amount == nil:
 | |
| 		*q = y
 | |
| 	case q.Amount == nil:
 | |
| 		q.Amount = &inf.Dec{}
 | |
| 		fallthrough
 | |
| 	default:
 | |
| 		q.Amount.Neg(y.Amount)
 | |
| 		q.Format = y.Format
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // MarshalJSON implements the json.Marshaller interface.
 | |
| func (q Quantity) MarshalJSON() ([]byte, error) {
 | |
| 	return []byte(`"` + q.String() + `"`), nil
 | |
| }
 | |
| 
 | |
| // UnmarshalJSON implements the json.Unmarshaller interface.
 | |
| func (q *Quantity) UnmarshalJSON(value []byte) error {
 | |
| 	str := string(value)
 | |
| 	parsed, err := ParseQuantity(strings.Trim(str, `"`))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	// This copy is safe because parsed will not be referred to again.
 | |
| 	*q = *parsed
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // NewQuantity returns a new Quantity representing the given
 | |
| // value in the given format.
 | |
| func NewQuantity(value int64, format Format) *Quantity {
 | |
| 	return &Quantity{
 | |
| 		Amount: inf.NewDec(value, 0),
 | |
| 		Format: format,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewMilliQuantity returns a new Quantity representing the given
 | |
| // value * 1/1000 in the given format. Note that BinarySI formatting
 | |
| // 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 {
 | |
| 	return &Quantity{
 | |
| 		Amount: inf.NewDec(value, 3),
 | |
| 		Format: format,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewScaledQuantity returns a new Quantity representing the given
 | |
| // value * 10^scale in DecimalSI format.
 | |
| func NewScaledQuantity(value int64, scale Scale) *Quantity {
 | |
| 	return &Quantity{
 | |
| 		Amount: inf.NewDec(value, scale.infScale()),
 | |
| 		Format: DecimalSI,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Value returns the value of q; any fractional part will be lost.
 | |
| func (q *Quantity) Value() int64 {
 | |
| 	return q.ScaledValue(0)
 | |
| }
 | |
| 
 | |
| // MilliValue returns the value of ceil(q * 1000); this could overflow an int64;
 | |
| // if that's a concern, call Value() first to verify the number is small enough.
 | |
| func (q *Quantity) MilliValue() int64 {
 | |
| 	return q.ScaledValue(Milli)
 | |
| }
 | |
| 
 | |
| // ScaledValue returns the value of ceil(q * 10^scale); this could overflow an int64.
 | |
| // To detect overflow, call Value() first and verify the expected magnitude.
 | |
| func (q *Quantity) ScaledValue(scale Scale) int64 {
 | |
| 	if q.Amount == nil {
 | |
| 		return 0
 | |
| 	}
 | |
| 	return scaledValue(q.Amount.UnscaledBig(), int(q.Amount.Scale()), int(scale.infScale()))
 | |
| }
 | |
| 
 | |
| // Set sets q's value to be value.
 | |
| func (q *Quantity) Set(value int64) {
 | |
| 	q.SetScaled(value, 0)
 | |
| }
 | |
| 
 | |
| // SetMilli sets q's value to be value * 1/1000.
 | |
| func (q *Quantity) SetMilli(value int64) {
 | |
| 	q.SetScaled(value, Milli)
 | |
| }
 | |
| 
 | |
| // SetScaled sets q's value to be value * 10^scale
 | |
| func (q *Quantity) SetScaled(value int64, scale Scale) {
 | |
| 	if q.Amount == nil {
 | |
| 		q.Amount = &inf.Dec{}
 | |
| 	}
 | |
| 	q.Amount.SetUnscaled(value)
 | |
| 	q.Amount.SetScale(scale.infScale())
 | |
| }
 | |
| 
 | |
| // Copy is a convenience function that makes a deep copy for you. Non-deep
 | |
| // copies of quantities share pointers and you will regret that.
 | |
| func (q *Quantity) Copy() *Quantity {
 | |
| 	if q.Amount == nil {
 | |
| 		return NewQuantity(0, q.Format)
 | |
| 	}
 | |
| 	tmp := &inf.Dec{}
 | |
| 	return &Quantity{
 | |
| 		Amount: tmp.Set(q.Amount),
 | |
| 		Format: q.Format,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // qFlag is a helper type for the Flag function
 | |
| type qFlag struct {
 | |
| 	dest *Quantity
 | |
| }
 | |
| 
 | |
| // Sets the value of the internal Quantity. (used by flag & pflag)
 | |
| func (qf qFlag) Set(val string) error {
 | |
| 	q, err := ParseQuantity(val)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	// This copy is OK because q will not be referenced again.
 | |
| 	*qf.dest = *q
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Converts the value of the internal Quantity to a string. (used by flag & pflag)
 | |
| func (qf qFlag) String() string {
 | |
| 	return qf.dest.String()
 | |
| }
 | |
| 
 | |
| // States the type of flag this is (Quantity). (used by pflag)
 | |
| func (qf qFlag) Type() string {
 | |
| 	return "quantity"
 | |
| }
 | |
| 
 | |
| // QuantityFlag is a helper that makes a quantity flag (using standard flag package).
 | |
| // Will panic if defaultValue is not a valid quantity.
 | |
| func QuantityFlag(flagName, defaultValue, description string) *Quantity {
 | |
| 	q := MustParse(defaultValue)
 | |
| 	flag.Var(NewQuantityFlagValue(&q), flagName, description)
 | |
| 	return &q
 | |
| }
 | |
| 
 | |
| // NewQuantityFlagValue returns an object that can be used to back a flag,
 | |
| // pointing at the given Quantity variable.
 | |
| func NewQuantityFlagValue(q *Quantity) flag.Value {
 | |
| 	return qFlag{q}
 | |
| }
 | |
| 
 | |
| // infScale adapts a Scale value to an inf.Scale value.
 | |
| func (s Scale) infScale() inf.Scale {
 | |
| 	return inf.Scale(-s) // inf.Scale is upside-down
 | |
| }
 |