blob: c3cd139607e97f09c9cd86546f070f1f10ca49ce [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package resource
18
19import (
20 "bytes"
21 "errors"
22 "fmt"
23 "math/big"
24 "regexp"
25 "strconv"
26 "strings"
27
28 inf "gopkg.in/inf.v0"
29)
30
31// Quantity is a fixed-point representation of a number.
32// It provides convenient marshaling/unmarshaling in JSON and YAML,
33// in addition to String() and Int64() accessors.
34//
35// The serialization format is:
36//
37// <quantity> ::= <signedNumber><suffix>
38// (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
39// <digit> ::= 0 | 1 | ... | 9
40// <digits> ::= <digit> | <digit><digits>
41// <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
42// <sign> ::= "+" | "-"
43// <signedNumber> ::= <number> | <sign><number>
44// <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI>
45// <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei
46// (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
47// <decimalSI> ::= m | "" | k | M | G | T | P | E
48// (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
49// <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
50//
51// No matter which of the three exponent forms is used, no quantity may represent
52// a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal
53// places. Numbers larger or more precise will be capped or rounded up.
54// (E.g.: 0.1m will rounded up to 1m.)
55// This may be extended in the future if we require larger or smaller quantities.
56//
57// When a Quantity is parsed from a string, it will remember the type of suffix
58// it had, and will use the same type again when it is serialized.
59//
60// Before serializing, Quantity will be put in "canonical form".
61// This means that Exponent/suffix will be adjusted up or down (with a
62// corresponding increase or decrease in Mantissa) such that:
63// a. No precision is lost
64// b. No fractional digits will be emitted
65// c. The exponent (or suffix) is as large as possible.
66// The sign will be omitted unless the number is negative.
67//
68// Examples:
69// 1.5 will be serialized as "1500m"
70// 1.5Gi will be serialized as "1536Mi"
71//
72// NOTE: We reserve the right to amend this canonical format, perhaps to
73// allow 1.5 to be canonical.
74// TODO: Remove above disclaimer after all bikeshedding about format is over,
75// or after March 2015.
76//
77// Note that the quantity will NEVER be internally represented by a
78// floating point number. That is the whole point of this exercise.
79//
80// Non-canonical values will still parse as long as they are well formed,
81// but will be re-emitted in their canonical form. (So always use canonical
82// form, or don't diff.)
83//
84// This format is intended to make it difficult to use these numbers without
85// writing some sort of special handling code in the hopes that that will
86// cause implementors to also use a fixed point implementation.
87//
88// +protobuf=true
89// +protobuf.embed=string
90// +protobuf.options.marshal=false
91// +protobuf.options.(gogoproto.goproto_stringer)=false
92// +k8s:deepcopy-gen=true
93// +k8s:openapi-gen=true
94type Quantity struct {
95 // i is the quantity in int64 scaled form, if d.Dec == nil
96 i int64Amount
97 // d is the quantity in inf.Dec form if d.Dec != nil
98 d infDecAmount
99 // s is the generated value of this quantity to avoid recalculation
100 s string
101
102 // Change Format at will. See the comment for Canonicalize for
103 // more details.
104 Format
105}
106
107// CanonicalValue allows a quantity amount to be converted to a string.
108type CanonicalValue interface {
109 // AsCanonicalBytes returns a byte array representing the string representation
110 // of the value mantissa and an int32 representing its exponent in base-10. Callers may
111 // pass a byte slice to the method to avoid allocations.
112 AsCanonicalBytes(out []byte) ([]byte, int32)
113 // AsCanonicalBase1024Bytes returns a byte array representing the string representation
114 // of the value mantissa and an int32 representing its exponent in base-1024. Callers
115 // may pass a byte slice to the method to avoid allocations.
116 AsCanonicalBase1024Bytes(out []byte) ([]byte, int32)
117}
118
119// Format lists the three possible formattings of a quantity.
120type Format string
121
122const (
123 DecimalExponent = Format("DecimalExponent") // e.g., 12e6
124 BinarySI = Format("BinarySI") // e.g., 12Mi (12 * 2^20)
125 DecimalSI = Format("DecimalSI") // e.g., 12M (12 * 10^6)
126)
127
128// MustParse turns the given string into a quantity or panics; for tests
129// or others cases where you know the string is valid.
130func MustParse(str string) Quantity {
131 q, err := ParseQuantity(str)
132 if err != nil {
133 panic(fmt.Errorf("cannot parse '%v': %v", str, err))
134 }
135 return q
136}
137
138const (
139 // splitREString is used to separate a number from its suffix; as such,
140 // this is overly permissive, but that's OK-- it will be checked later.
141 splitREString = "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"
142)
143
144var (
145 // splitRE is used to get the various parts of a number.
146 splitRE = regexp.MustCompile(splitREString)
147
148 // Errors that could happen while parsing a string.
149 ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'")
150 ErrNumeric = errors.New("unable to parse numeric part of quantity")
151 ErrSuffix = errors.New("unable to parse quantity's suffix")
152)
153
154// parseQuantityString is a fast scanner for quantity values.
155func parseQuantityString(str string) (positive bool, value, num, denom, suffix string, err error) {
156 positive = true
157 pos := 0
158 end := len(str)
159
160 // handle leading sign
161 if pos < end {
162 switch str[0] {
163 case '-':
164 positive = false
165 pos++
166 case '+':
167 pos++
168 }
169 }
170
171 // strip leading zeros
172Zeroes:
173 for i := pos; ; i++ {
174 if i >= end {
175 num = "0"
176 value = num
177 return
178 }
179 switch str[i] {
180 case '0':
181 pos++
182 default:
183 break Zeroes
184 }
185 }
186
187 // extract the numerator
188Num:
189 for i := pos; ; i++ {
190 if i >= end {
191 num = str[pos:end]
192 value = str[0:end]
193 return
194 }
195 switch str[i] {
196 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
197 default:
198 num = str[pos:i]
199 pos = i
200 break Num
201 }
202 }
203
204 // if we stripped all numerator positions, always return 0
205 if len(num) == 0 {
206 num = "0"
207 }
208
209 // handle a denominator
210 if pos < end && str[pos] == '.' {
211 pos++
212 Denom:
213 for i := pos; ; i++ {
214 if i >= end {
215 denom = str[pos:end]
216 value = str[0:end]
217 return
218 }
219 switch str[i] {
220 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
221 default:
222 denom = str[pos:i]
223 pos = i
224 break Denom
225 }
226 }
227 // TODO: we currently allow 1.G, but we may not want to in the future.
228 // if len(denom) == 0 {
229 // err = ErrFormatWrong
230 // return
231 // }
232 }
233 value = str[0:pos]
234
235 // grab the elements of the suffix
236 suffixStart := pos
237 for i := pos; ; i++ {
238 if i >= end {
239 suffix = str[suffixStart:end]
240 return
241 }
242 if !strings.ContainsAny(str[i:i+1], "eEinumkKMGTP") {
243 pos = i
244 break
245 }
246 }
247 if pos < end {
248 switch str[pos] {
249 case '-', '+':
250 pos++
251 }
252 }
253Suffix:
254 for i := pos; ; i++ {
255 if i >= end {
256 suffix = str[suffixStart:end]
257 return
258 }
259 switch str[i] {
260 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
261 default:
262 break Suffix
263 }
264 }
265 // we encountered a non decimal in the Suffix loop, but the last character
266 // was not a valid exponent
267 err = ErrFormatWrong
268 return
269}
270
271// ParseQuantity turns str into a Quantity, or returns an error.
272func ParseQuantity(str string) (Quantity, error) {
273 if len(str) == 0 {
274 return Quantity{}, ErrFormatWrong
275 }
276 if str == "0" {
277 return Quantity{Format: DecimalSI, s: str}, nil
278 }
279
280 positive, value, num, denom, suf, err := parseQuantityString(str)
281 if err != nil {
282 return Quantity{}, err
283 }
284
285 base, exponent, format, ok := quantitySuffixer.interpret(suffix(suf))
286 if !ok {
287 return Quantity{}, ErrSuffix
288 }
289
290 precision := int32(0)
291 scale := int32(0)
292 mantissa := int64(1)
293 switch format {
294 case DecimalExponent, DecimalSI:
295 scale = exponent
296 precision = maxInt64Factors - int32(len(num)+len(denom))
297 case BinarySI:
298 scale = 0
299 switch {
300 case exponent >= 0 && len(denom) == 0:
301 // only handle positive binary numbers with the fast path
302 mantissa = int64(int64(mantissa) << uint64(exponent))
303 // 1Mi (2^20) has ~6 digits of decimal precision, so exponent*3/10 -1 is roughly the precision
304 precision = 15 - int32(len(num)) - int32(float32(exponent)*3/10) - 1
305 default:
306 precision = -1
307 }
308 }
309
310 if precision >= 0 {
311 // if we have a denominator, shift the entire value to the left by the number of places in the
312 // denominator
313 scale -= int32(len(denom))
314 if scale >= int32(Nano) {
315 shifted := num + denom
316
317 var value int64
318 value, err := strconv.ParseInt(shifted, 10, 64)
319 if err != nil {
320 return Quantity{}, ErrNumeric
321 }
322 if result, ok := int64Multiply(value, int64(mantissa)); ok {
323 if !positive {
324 result = -result
325 }
326 // if the number is in canonical form, reuse the string
327 switch format {
328 case BinarySI:
329 if exponent%10 == 0 && (value&0x07 != 0) {
330 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
331 }
332 default:
333 if scale%3 == 0 && !strings.HasSuffix(shifted, "000") && shifted[0] != '0' {
334 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
335 }
336 }
337 return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format}, nil
338 }
339 }
340 }
341
342 amount := new(inf.Dec)
343 if _, ok := amount.SetString(value); !ok {
344 return Quantity{}, ErrNumeric
345 }
346
347 // So that no one but us has to think about suffixes, remove it.
348 if base == 10 {
349 amount.SetScale(amount.Scale() + Scale(exponent).infScale())
350 } else if base == 2 {
351 // numericSuffix = 2 ** exponent
352 numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent))
353 ub := amount.UnscaledBig()
354 amount.SetUnscaledBig(ub.Mul(ub, numericSuffix))
355 }
356
357 // Cap at min/max bounds.
358 sign := amount.Sign()
359 if sign == -1 {
360 amount.Neg(amount)
361 }
362
363 // This rounds non-zero values up to the minimum representable value, under the theory that
364 // if you want some resources, you should get some resources, even if you asked for way too small
365 // of an amount. Arguably, this should be inf.RoundHalfUp (normal rounding), but that would have
366 // the side effect of rounding values < .5n to zero.
367 if v, ok := amount.Unscaled(); v != int64(0) || !ok {
368 amount.Round(amount, Nano.infScale(), inf.RoundUp)
369 }
370
371 // The max is just a simple cap.
372 // TODO: this prevents accumulating quantities greater than int64, for instance quota across a cluster
373 if format == BinarySI && amount.Cmp(maxAllowed.Dec) > 0 {
374 amount.Set(maxAllowed.Dec)
375 }
376
377 if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 {
378 // This avoids rounding and hopefully confusion, too.
379 format = DecimalSI
380 }
381 if sign == -1 {
382 amount.Neg(amount)
383 }
384
385 return Quantity{d: infDecAmount{amount}, Format: format}, nil
386}
387
388// DeepCopy returns a deep-copy of the Quantity value. Note that the method
389// receiver is a value, so we can mutate it in-place and return it.
390func (q Quantity) DeepCopy() Quantity {
391 if q.d.Dec != nil {
392 tmp := &inf.Dec{}
393 q.d.Dec = tmp.Set(q.d.Dec)
394 }
395 return q
396}
397
398// OpenAPISchemaType is used by the kube-openapi generator when constructing
399// the OpenAPI spec of this type.
400//
401// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
402func (_ Quantity) OpenAPISchemaType() []string { return []string{"string"} }
403
404// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
405// the OpenAPI spec of this type.
406func (_ Quantity) OpenAPISchemaFormat() string { return "" }
407
408// CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity).
409//
410// Note about BinarySI:
411// * If q.Format is set to BinarySI and q.Amount represents a non-zero value between
412// -1 and +1, it will be emitted as if q.Format were DecimalSI.
413// * Otherwise, if q.Format is set to BinarySI, fractional parts of q.Amount will be
414// rounded up. (1.1i becomes 2i.)
415func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) {
416 if q.IsZero() {
417 return zeroBytes, nil
418 }
419
420 var rounded CanonicalValue
421 format := q.Format
422 switch format {
423 case DecimalExponent, DecimalSI:
424 case BinarySI:
425 if q.CmpInt64(-1024) > 0 && q.CmpInt64(1024) < 0 {
426 // This avoids rounding and hopefully confusion, too.
427 format = DecimalSI
428 } else {
429 var exact bool
430 if rounded, exact = q.AsScale(0); !exact {
431 // Don't lose precision-- show as DecimalSI
432 format = DecimalSI
433 }
434 }
435 default:
436 format = DecimalExponent
437 }
438
439 // TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
440 // one of the other formats.
441 switch format {
442 case DecimalExponent, DecimalSI:
443 number, exponent := q.AsCanonicalBytes(out)
444 suffix, _ := quantitySuffixer.constructBytes(10, exponent, format)
445 return number, suffix
446 default:
447 // format must be BinarySI
448 number, exponent := rounded.AsCanonicalBase1024Bytes(out)
449 suffix, _ := quantitySuffixer.constructBytes(2, exponent*10, format)
450 return number, suffix
451 }
452}
453
454// AsInt64 returns a representation of the current value as an int64 if a fast conversion
455// is possible. If false is returned, callers must use the inf.Dec form of this quantity.
456func (q *Quantity) AsInt64() (int64, bool) {
457 if q.d.Dec != nil {
458 return 0, false
459 }
460 return q.i.AsInt64()
461}
462
463// ToDec promotes the quantity in place to use an inf.Dec representation and returns itself.
464func (q *Quantity) ToDec() *Quantity {
465 if q.d.Dec == nil {
466 q.d.Dec = q.i.AsDec()
467 q.i = int64Amount{}
468 }
469 return q
470}
471
472// AsDec returns the quantity as represented by a scaled inf.Dec.
473func (q *Quantity) AsDec() *inf.Dec {
474 if q.d.Dec != nil {
475 return q.d.Dec
476 }
477 q.d.Dec = q.i.AsDec()
478 q.i = int64Amount{}
479 return q.d.Dec
480}
481
482// AsCanonicalBytes returns the canonical byte representation of this quantity as a mantissa
483// and base 10 exponent. The out byte slice may be passed to the method to avoid an extra
484// allocation.
485func (q *Quantity) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
486 if q.d.Dec != nil {
487 return q.d.AsCanonicalBytes(out)
488 }
489 return q.i.AsCanonicalBytes(out)
490}
491
492// IsZero returns true if the quantity is equal to zero.
493func (q *Quantity) IsZero() bool {
494 if q.d.Dec != nil {
495 return q.d.Dec.Sign() == 0
496 }
497 return q.i.value == 0
498}
499
500// Sign returns 0 if the quantity is zero, -1 if the quantity is less than zero, or 1 if the
501// quantity is greater than zero.
502func (q *Quantity) Sign() int {
503 if q.d.Dec != nil {
504 return q.d.Dec.Sign()
505 }
506 return q.i.Sign()
507}
508
509// AsScaled returns the current value, rounded up to the provided scale, and returns
510// false if the scale resulted in a loss of precision.
511func (q *Quantity) AsScale(scale Scale) (CanonicalValue, bool) {
512 if q.d.Dec != nil {
513 return q.d.AsScale(scale)
514 }
515 return q.i.AsScale(scale)
516}
517
518// RoundUp updates the quantity to the provided scale, ensuring that the value is at
519// least 1. False is returned if the rounding operation resulted in a loss of precision.
520// Negative numbers are rounded away from zero (-9 scale 1 rounds to -10).
521func (q *Quantity) RoundUp(scale Scale) bool {
522 if q.d.Dec != nil {
523 q.s = ""
524 d, exact := q.d.AsScale(scale)
525 q.d = d
526 return exact
527 }
528 // avoid clearing the string value if we have already calculated it
529 if q.i.scale >= scale {
530 return true
531 }
532 q.s = ""
533 i, exact := q.i.AsScale(scale)
534 q.i = i
535 return exact
536}
537
538// Add adds the provide y quantity to the current value. If the current value is zero,
539// the format of the quantity will be updated to the format of y.
540func (q *Quantity) Add(y Quantity) {
541 q.s = ""
542 if q.d.Dec == nil && y.d.Dec == nil {
543 if q.i.value == 0 {
544 q.Format = y.Format
545 }
546 if q.i.Add(y.i) {
547 return
548 }
549 } else if q.IsZero() {
550 q.Format = y.Format
551 }
552 q.ToDec().d.Dec.Add(q.d.Dec, y.AsDec())
553}
554
555// Sub subtracts the provided quantity from the current value in place. If the current
556// value is zero, the format of the quantity will be updated to the format of y.
557func (q *Quantity) Sub(y Quantity) {
558 q.s = ""
559 if q.IsZero() {
560 q.Format = y.Format
561 }
562 if q.d.Dec == nil && y.d.Dec == nil && q.i.Sub(y.i) {
563 return
564 }
565 q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec())
566}
567
568// Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
569// quantity is greater than y.
570func (q *Quantity) Cmp(y Quantity) int {
571 if q.d.Dec == nil && y.d.Dec == nil {
572 return q.i.Cmp(y.i)
573 }
574 return q.AsDec().Cmp(y.AsDec())
575}
576
577// CmpInt64 returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
578// quantity is greater than y.
579func (q *Quantity) CmpInt64(y int64) int {
580 if q.d.Dec != nil {
581 return q.d.Dec.Cmp(inf.NewDec(y, inf.Scale(0)))
582 }
583 return q.i.Cmp(int64Amount{value: y})
584}
585
586// Neg sets quantity to be the negative value of itself.
587func (q *Quantity) Neg() {
588 q.s = ""
589 if q.d.Dec == nil {
590 q.i.value = -q.i.value
591 return
592 }
593 q.d.Dec.Neg(q.d.Dec)
594}
595
596// int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation
597// of most Quantity values.
598const int64QuantityExpectedBytes = 18
599
600// String formats the Quantity as a string, caching the result if not calculated.
601// String is an expensive operation and caching this result significantly reduces the cost of
602// normal parse / marshal operations on Quantity.
603func (q *Quantity) String() string {
604 if len(q.s) == 0 {
605 result := make([]byte, 0, int64QuantityExpectedBytes)
606 number, suffix := q.CanonicalizeBytes(result)
607 number = append(number, suffix...)
608 q.s = string(number)
609 }
610 return q.s
611}
612
613// MarshalJSON implements the json.Marshaller interface.
614func (q Quantity) MarshalJSON() ([]byte, error) {
615 if len(q.s) > 0 {
616 out := make([]byte, len(q.s)+2)
617 out[0], out[len(out)-1] = '"', '"'
618 copy(out[1:], q.s)
619 return out, nil
620 }
621 result := make([]byte, int64QuantityExpectedBytes, int64QuantityExpectedBytes)
622 result[0] = '"'
623 number, suffix := q.CanonicalizeBytes(result[1:1])
624 // if the same slice was returned to us that we passed in, avoid another allocation by copying number into
625 // the source slice and returning that
626 if len(number) > 0 && &number[0] == &result[1] && (len(number)+len(suffix)+2) <= int64QuantityExpectedBytes {
627 number = append(number, suffix...)
628 number = append(number, '"')
629 return result[:1+len(number)], nil
630 }
631 // if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use
632 // append
633 result = result[:1]
634 result = append(result, number...)
635 result = append(result, suffix...)
636 result = append(result, '"')
637 return result, nil
638}
639
640// UnmarshalJSON implements the json.Unmarshaller interface.
641// TODO: Remove support for leading/trailing whitespace
642func (q *Quantity) UnmarshalJSON(value []byte) error {
643 l := len(value)
644 if l == 4 && bytes.Equal(value, []byte("null")) {
645 q.d.Dec = nil
646 q.i = int64Amount{}
647 return nil
648 }
649 if l >= 2 && value[0] == '"' && value[l-1] == '"' {
650 value = value[1 : l-1]
651 }
652
653 parsed, err := ParseQuantity(strings.TrimSpace(string(value)))
654 if err != nil {
655 return err
656 }
657
658 // This copy is safe because parsed will not be referred to again.
659 *q = parsed
660 return nil
661}
662
663// NewQuantity returns a new Quantity representing the given
664// value in the given format.
665func NewQuantity(value int64, format Format) *Quantity {
666 return &Quantity{
667 i: int64Amount{value: value},
668 Format: format,
669 }
670}
671
672// NewMilliQuantity returns a new Quantity representing the given
673// value * 1/1000 in the given format. Note that BinarySI formatting
674// will round fractional values, and will be changed to DecimalSI for
675// values x where (-1 < x < 1) && (x != 0).
676func NewMilliQuantity(value int64, format Format) *Quantity {
677 return &Quantity{
678 i: int64Amount{value: value, scale: -3},
679 Format: format,
680 }
681}
682
683// NewScaledQuantity returns a new Quantity representing the given
684// value * 10^scale in DecimalSI format.
685func NewScaledQuantity(value int64, scale Scale) *Quantity {
686 return &Quantity{
687 i: int64Amount{value: value, scale: scale},
688 Format: DecimalSI,
689 }
690}
691
692// Value returns the value of q; any fractional part will be lost.
693func (q *Quantity) Value() int64 {
694 return q.ScaledValue(0)
695}
696
697// MilliValue returns the value of ceil(q * 1000); this could overflow an int64;
698// if that's a concern, call Value() first to verify the number is small enough.
699func (q *Quantity) MilliValue() int64 {
700 return q.ScaledValue(Milli)
701}
702
703// ScaledValue returns the value of ceil(q * 10^scale); this could overflow an int64.
704// To detect overflow, call Value() first and verify the expected magnitude.
705func (q *Quantity) ScaledValue(scale Scale) int64 {
706 if q.d.Dec == nil {
707 i, _ := q.i.AsScaledInt64(scale)
708 return i
709 }
710 dec := q.d.Dec
711 return scaledValue(dec.UnscaledBig(), int(dec.Scale()), int(scale.infScale()))
712}
713
714// Set sets q's value to be value.
715func (q *Quantity) Set(value int64) {
716 q.SetScaled(value, 0)
717}
718
719// SetMilli sets q's value to be value * 1/1000.
720func (q *Quantity) SetMilli(value int64) {
721 q.SetScaled(value, Milli)
722}
723
724// SetScaled sets q's value to be value * 10^scale
725func (q *Quantity) SetScaled(value int64, scale Scale) {
726 q.s = ""
727 q.d.Dec = nil
728 q.i = int64Amount{value: value, scale: scale}
729}
730
731// Copy is a convenience function that makes a deep copy for you. Non-deep
732// copies of quantities share pointers and you will regret that.
733func (q *Quantity) Copy() *Quantity {
734 if q.d.Dec == nil {
735 return &Quantity{
736 s: q.s,
737 i: q.i,
738 Format: q.Format,
739 }
740 }
741 tmp := &inf.Dec{}
742 return &Quantity{
743 s: q.s,
744 d: infDecAmount{tmp.Set(q.d.Dec)},
745 Format: q.Format,
746 }
747}