123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738 |
- /*
- Copyright 2014 The Kubernetes Authors.
- 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 (
- "bytes"
- "errors"
- "fmt"
- "math/big"
- "strconv"
- "strings"
- inf "gopkg.in/inf.v0"
- )
- // 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 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=string
- // +protobuf.options.marshal=false
- // +protobuf.options.(gogoproto.goproto_stringer)=false
- // +k8s:deepcopy-gen=true
- // +k8s:openapi-gen=true
- type Quantity struct {
- // i is the quantity in int64 scaled form, if d.Dec == nil
- i int64Amount
- // d is the quantity in inf.Dec form if d.Dec != nil
- d infDecAmount
- // s is the generated value of this quantity to avoid recalculation
- s string
- // Change Format at will. See the comment for Canonicalize for
- // more details.
- Format
- }
- // CanonicalValue allows a quantity amount to be converted to a string.
- type CanonicalValue interface {
- // AsCanonicalBytes returns a byte array representing the string representation
- // of the value mantissa and an int32 representing its exponent in base-10. Callers may
- // pass a byte slice to the method to avoid allocations.
- AsCanonicalBytes(out []byte) ([]byte, int32)
- // AsCanonicalBase1024Bytes returns a byte array representing the string representation
- // of the value mantissa and an int32 representing its exponent in base-1024. Callers
- // may pass a byte slice to the method to avoid allocations.
- AsCanonicalBase1024Bytes(out []byte) ([]byte, int32)
- }
- // 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
- }
- 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 (
- // 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")
- )
- // parseQuantityString is a fast scanner for quantity values.
- func parseQuantityString(str string) (positive bool, value, num, denom, suffix string, err error) {
- positive = true
- pos := 0
- end := len(str)
- // handle leading sign
- if pos < end {
- switch str[0] {
- case '-':
- positive = false
- pos++
- case '+':
- pos++
- }
- }
- // strip leading zeros
- Zeroes:
- for i := pos; ; i++ {
- if i >= end {
- num = "0"
- value = num
- return
- }
- switch str[i] {
- case '0':
- pos++
- default:
- break Zeroes
- }
- }
- // extract the numerator
- Num:
- for i := pos; ; i++ {
- if i >= end {
- num = str[pos:end]
- value = str[0:end]
- return
- }
- switch str[i] {
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- default:
- num = str[pos:i]
- pos = i
- break Num
- }
- }
- // if we stripped all numerator positions, always return 0
- if len(num) == 0 {
- num = "0"
- }
- // handle a denominator
- if pos < end && str[pos] == '.' {
- pos++
- Denom:
- for i := pos; ; i++ {
- if i >= end {
- denom = str[pos:end]
- value = str[0:end]
- return
- }
- switch str[i] {
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- default:
- denom = str[pos:i]
- pos = i
- break Denom
- }
- }
- // TODO: we currently allow 1.G, but we may not want to in the future.
- // if len(denom) == 0 {
- // err = ErrFormatWrong
- // return
- // }
- }
- value = str[0:pos]
- // grab the elements of the suffix
- suffixStart := pos
- for i := pos; ; i++ {
- if i >= end {
- suffix = str[suffixStart:end]
- return
- }
- if !strings.ContainsAny(str[i:i+1], "eEinumkKMGTP") {
- pos = i
- break
- }
- }
- if pos < end {
- switch str[pos] {
- case '-', '+':
- pos++
- }
- }
- Suffix:
- for i := pos; ; i++ {
- if i >= end {
- suffix = str[suffixStart:end]
- return
- }
- switch str[i] {
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- default:
- break Suffix
- }
- }
- // we encountered a non decimal in the Suffix loop, but the last character
- // was not a valid exponent
- err = ErrFormatWrong
- return
- }
- // ParseQuantity turns str into a Quantity, or returns an error.
- func ParseQuantity(str string) (Quantity, error) {
- if len(str) == 0 {
- return Quantity{}, ErrFormatWrong
- }
- if str == "0" {
- return Quantity{Format: DecimalSI, s: str}, nil
- }
- positive, value, num, denom, suf, err := parseQuantityString(str)
- if err != nil {
- return Quantity{}, err
- }
- base, exponent, format, ok := quantitySuffixer.interpret(suffix(suf))
- if !ok {
- return Quantity{}, ErrSuffix
- }
- precision := int32(0)
- scale := int32(0)
- mantissa := int64(1)
- switch format {
- case DecimalExponent, DecimalSI:
- scale = exponent
- precision = maxInt64Factors - int32(len(num)+len(denom))
- case BinarySI:
- scale = 0
- switch {
- case exponent >= 0 && len(denom) == 0:
- // only handle positive binary numbers with the fast path
- mantissa = int64(int64(mantissa) << uint64(exponent))
- // 1Mi (2^20) has ~6 digits of decimal precision, so exponent*3/10 -1 is roughly the precision
- precision = 15 - int32(len(num)) - int32(float32(exponent)*3/10) - 1
- default:
- precision = -1
- }
- }
- if precision >= 0 {
- // if we have a denominator, shift the entire value to the left by the number of places in the
- // denominator
- scale -= int32(len(denom))
- if scale >= int32(Nano) {
- shifted := num + denom
- var value int64
- value, err := strconv.ParseInt(shifted, 10, 64)
- if err != nil {
- return Quantity{}, ErrNumeric
- }
- if result, ok := int64Multiply(value, int64(mantissa)); ok {
- if !positive {
- result = -result
- }
- // if the number is in canonical form, reuse the string
- switch format {
- case BinarySI:
- if exponent%10 == 0 && (value&0x07 != 0) {
- return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
- }
- default:
- if scale%3 == 0 && !strings.HasSuffix(shifted, "000") && shifted[0] != '0' {
- return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
- }
- }
- return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format}, nil
- }
- }
- }
- amount := new(inf.Dec)
- if _, ok := amount.SetString(value); !ok {
- return Quantity{}, ErrNumeric
- }
- // 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.
- // TODO: this prevents accumulating quantities greater than int64, for instance quota across a cluster
- if format == BinarySI && amount.Cmp(maxAllowed.Dec) > 0 {
- amount.Set(maxAllowed.Dec)
- }
- 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{d: infDecAmount{amount}, Format: format}, nil
- }
- // DeepCopy returns a deep-copy of the Quantity value. Note that the method
- // receiver is a value, so we can mutate it in-place and return it.
- func (q Quantity) DeepCopy() Quantity {
- if q.d.Dec != nil {
- tmp := &inf.Dec{}
- q.d.Dec = tmp.Set(q.d.Dec)
- }
- return q
- }
- // OpenAPISchemaType is used by the kube-openapi generator when constructing
- // the OpenAPI spec of this type.
- //
- // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
- func (_ Quantity) OpenAPISchemaType() []string { return []string{"string"} }
- // OpenAPISchemaFormat is used by the kube-openapi generator when constructing
- // the OpenAPI spec of this type.
- func (_ Quantity) OpenAPISchemaFormat() string { return "" }
- // CanonicalizeBytes 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, fractional parts of q.Amount will be
- // rounded up. (1.1i becomes 2i.)
- func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) {
- if q.IsZero() {
- return zeroBytes, nil
- }
- var rounded CanonicalValue
- format := q.Format
- switch format {
- case DecimalExponent, DecimalSI:
- case BinarySI:
- if q.CmpInt64(-1024) > 0 && q.CmpInt64(1024) < 0 {
- // This avoids rounding and hopefully confusion, too.
- format = DecimalSI
- } else {
- var exact bool
- if rounded, exact = q.AsScale(0); !exact {
- // 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:
- number, exponent := q.AsCanonicalBytes(out)
- suffix, _ := quantitySuffixer.constructBytes(10, exponent, format)
- return number, suffix
- default:
- // format must be BinarySI
- number, exponent := rounded.AsCanonicalBase1024Bytes(out)
- suffix, _ := quantitySuffixer.constructBytes(2, exponent*10, format)
- return number, suffix
- }
- }
- // AsInt64 returns a representation of the current value as an int64 if a fast conversion
- // is possible. If false is returned, callers must use the inf.Dec form of this quantity.
- func (q *Quantity) AsInt64() (int64, bool) {
- if q.d.Dec != nil {
- return 0, false
- }
- return q.i.AsInt64()
- }
- // ToDec promotes the quantity in place to use an inf.Dec representation and returns itself.
- func (q *Quantity) ToDec() *Quantity {
- if q.d.Dec == nil {
- q.d.Dec = q.i.AsDec()
- q.i = int64Amount{}
- }
- return q
- }
- // AsDec returns the quantity as represented by a scaled inf.Dec.
- func (q *Quantity) AsDec() *inf.Dec {
- if q.d.Dec != nil {
- return q.d.Dec
- }
- q.d.Dec = q.i.AsDec()
- q.i = int64Amount{}
- return q.d.Dec
- }
- // AsCanonicalBytes returns the canonical byte representation of this quantity as a mantissa
- // and base 10 exponent. The out byte slice may be passed to the method to avoid an extra
- // allocation.
- func (q *Quantity) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
- if q.d.Dec != nil {
- return q.d.AsCanonicalBytes(out)
- }
- return q.i.AsCanonicalBytes(out)
- }
- // IsZero returns true if the quantity is equal to zero.
- func (q *Quantity) IsZero() bool {
- if q.d.Dec != nil {
- return q.d.Dec.Sign() == 0
- }
- return q.i.value == 0
- }
- // Sign returns 0 if the quantity is zero, -1 if the quantity is less than zero, or 1 if the
- // quantity is greater than zero.
- func (q *Quantity) Sign() int {
- if q.d.Dec != nil {
- return q.d.Dec.Sign()
- }
- return q.i.Sign()
- }
- // AsScale returns the current value, rounded up to the provided scale, and returns
- // false if the scale resulted in a loss of precision.
- func (q *Quantity) AsScale(scale Scale) (CanonicalValue, bool) {
- if q.d.Dec != nil {
- return q.d.AsScale(scale)
- }
- return q.i.AsScale(scale)
- }
- // RoundUp updates the quantity to the provided scale, ensuring that the value is at
- // least 1. False is returned if the rounding operation resulted in a loss of precision.
- // Negative numbers are rounded away from zero (-9 scale 1 rounds to -10).
- func (q *Quantity) RoundUp(scale Scale) bool {
- if q.d.Dec != nil {
- q.s = ""
- d, exact := q.d.AsScale(scale)
- q.d = d
- return exact
- }
- // avoid clearing the string value if we have already calculated it
- if q.i.scale >= scale {
- return true
- }
- q.s = ""
- i, exact := q.i.AsScale(scale)
- q.i = i
- return exact
- }
- // Add adds the provide y quantity to the current value. If the current value is zero,
- // the format of the quantity will be updated to the format of y.
- func (q *Quantity) Add(y Quantity) {
- q.s = ""
- if q.d.Dec == nil && y.d.Dec == nil {
- if q.i.value == 0 {
- q.Format = y.Format
- }
- if q.i.Add(y.i) {
- return
- }
- } else if q.IsZero() {
- q.Format = y.Format
- }
- q.ToDec().d.Dec.Add(q.d.Dec, y.AsDec())
- }
- // Sub subtracts the provided quantity from the current value in place. If the current
- // value is zero, the format of the quantity will be updated to the format of y.
- func (q *Quantity) Sub(y Quantity) {
- q.s = ""
- if q.IsZero() {
- q.Format = y.Format
- }
- if q.d.Dec == nil && y.d.Dec == nil && q.i.Sub(y.i) {
- return
- }
- q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec())
- }
- // Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
- // quantity is greater than y.
- func (q *Quantity) Cmp(y Quantity) int {
- if q.d.Dec == nil && y.d.Dec == nil {
- return q.i.Cmp(y.i)
- }
- return q.AsDec().Cmp(y.AsDec())
- }
- // CmpInt64 returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
- // quantity is greater than y.
- func (q *Quantity) CmpInt64(y int64) int {
- if q.d.Dec != nil {
- return q.d.Dec.Cmp(inf.NewDec(y, inf.Scale(0)))
- }
- return q.i.Cmp(int64Amount{value: y})
- }
- // Neg sets quantity to be the negative value of itself.
- func (q *Quantity) Neg() {
- q.s = ""
- if q.d.Dec == nil {
- q.i.value = -q.i.value
- return
- }
- q.d.Dec.Neg(q.d.Dec)
- }
- // int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation
- // of most Quantity values.
- const int64QuantityExpectedBytes = 18
- // String formats the Quantity as a string, caching the result if not calculated.
- // String is an expensive operation and caching this result significantly reduces the cost of
- // normal parse / marshal operations on Quantity.
- func (q *Quantity) String() string {
- if len(q.s) == 0 {
- result := make([]byte, 0, int64QuantityExpectedBytes)
- number, suffix := q.CanonicalizeBytes(result)
- number = append(number, suffix...)
- q.s = string(number)
- }
- return q.s
- }
- // MarshalJSON implements the json.Marshaller interface.
- func (q Quantity) MarshalJSON() ([]byte, error) {
- if len(q.s) > 0 {
- out := make([]byte, len(q.s)+2)
- out[0], out[len(out)-1] = '"', '"'
- copy(out[1:], q.s)
- return out, nil
- }
- result := make([]byte, int64QuantityExpectedBytes, int64QuantityExpectedBytes)
- result[0] = '"'
- number, suffix := q.CanonicalizeBytes(result[1:1])
- // if the same slice was returned to us that we passed in, avoid another allocation by copying number into
- // the source slice and returning that
- if len(number) > 0 && &number[0] == &result[1] && (len(number)+len(suffix)+2) <= int64QuantityExpectedBytes {
- number = append(number, suffix...)
- number = append(number, '"')
- return result[:1+len(number)], nil
- }
- // if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use
- // append
- result = result[:1]
- result = append(result, number...)
- result = append(result, suffix...)
- result = append(result, '"')
- return result, nil
- }
- // UnmarshalJSON implements the json.Unmarshaller interface.
- // TODO: Remove support for leading/trailing whitespace
- func (q *Quantity) UnmarshalJSON(value []byte) error {
- l := len(value)
- if l == 4 && bytes.Equal(value, []byte("null")) {
- q.d.Dec = nil
- q.i = int64Amount{}
- return nil
- }
- if l >= 2 && value[0] == '"' && value[l-1] == '"' {
- value = value[1 : l-1]
- }
- parsed, err := ParseQuantity(strings.TrimSpace(string(value)))
- 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{
- i: int64Amount{value: value},
- 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{
- i: int64Amount{value: value, scale: -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{
- i: int64Amount{value: value, scale: scale},
- 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.d.Dec == nil {
- i, _ := q.i.AsScaledInt64(scale)
- return i
- }
- dec := q.d.Dec
- return scaledValue(dec.UnscaledBig(), int(dec.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) {
- q.s = ""
- q.d.Dec = nil
- q.i = int64Amount{value: value, scale: scale}
- }
- // 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.d.Dec == nil {
- return &Quantity{
- s: q.s,
- i: q.i,
- Format: q.Format,
- }
- }
- tmp := &inf.Dec{}
- return &Quantity{
- s: q.s,
- d: infDecAmount{tmp.Set(q.d.Dec)},
- Format: q.Format,
- }
- }
|