github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/resource/amount.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     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  	"math/big"
    21  	"strconv"
    22  
    23  	inf "gopkg.in/inf.v0"
    24  )
    25  
    26  // Scale is used for getting and setting the base-10 scaled value.
    27  // Base-2 scales are omitted for mathematical simplicity.
    28  // See Quantity.ScaledValue for more details.
    29  type Scale int32
    30  
    31  // infScale adapts a Scale value to an inf.Scale value.
    32  func (s Scale) infScale() inf.Scale {
    33  	return inf.Scale(-s) // inf.Scale is upside-down
    34  }
    35  
    36  const (
    37  	Nano  Scale = -9
    38  	Micro Scale = -6
    39  	Milli Scale = -3
    40  	Kilo  Scale = 3
    41  	Mega  Scale = 6
    42  	Giga  Scale = 9
    43  	Tera  Scale = 12
    44  	Peta  Scale = 15
    45  	Exa   Scale = 18
    46  )
    47  
    48  var (
    49  	Zero = int64Amount{}
    50  
    51  	// Used by quantity strings - treat as read only
    52  	zeroBytes = []byte("0")
    53  )
    54  
    55  // int64Amount represents a fixed precision numerator and arbitrary scale exponent. It is faster
    56  // than operations on inf.Dec for values that can be represented as int64.
    57  // +k8s:openapi-gen=true
    58  type int64Amount struct {
    59  	value int64
    60  	scale Scale
    61  }
    62  
    63  // Sign returns 0 if the value is zero, -1 if it is less than 0, or 1 if it is greater than 0.
    64  func (a int64Amount) Sign() int {
    65  	switch {
    66  	case a.value == 0:
    67  		return 0
    68  	case a.value > 0:
    69  		return 1
    70  	default:
    71  		return -1
    72  	}
    73  }
    74  
    75  // AsInt64 returns the current amount as an int64 at scale 0, or false if the value cannot be
    76  // represented in an int64 OR would result in a loss of precision. This method is intended as
    77  // an optimization to avoid calling AsDec.
    78  func (a int64Amount) AsInt64() (int64, bool) {
    79  	if a.scale == 0 {
    80  		return a.value, true
    81  	}
    82  	if a.scale < 0 {
    83  		// TODO: attempt to reduce factors, although it is assumed that factors are reduced prior
    84  		// to the int64Amount being created.
    85  		return 0, false
    86  	}
    87  	return positiveScaleInt64(a.value, a.scale)
    88  }
    89  
    90  // AsScaledInt64 returns an int64 representing the value of this amount at the specified scale,
    91  // rounding up, or false if that would result in overflow. (1e20).AsScaledInt64(1) would result
    92  // in overflow because 1e19 is not representable as an int64. Note that setting a scale larger
    93  // than the current value may result in loss of precision - i.e. (1e-6).AsScaledInt64(0) would
    94  // return 1, because 0.000001 is rounded up to 1.
    95  func (a int64Amount) AsScaledInt64(scale Scale) (result int64, ok bool) {
    96  	if a.scale < scale {
    97  		result, _ = negativeScaleInt64(a.value, scale-a.scale)
    98  		return result, true
    99  	}
   100  	return positiveScaleInt64(a.value, a.scale-scale)
   101  }
   102  
   103  // AsDec returns an inf.Dec representation of this value.
   104  func (a int64Amount) AsDec() *inf.Dec {
   105  	var base inf.Dec
   106  	base.SetUnscaled(a.value)
   107  	base.SetScale(inf.Scale(-a.scale))
   108  	return &base
   109  }
   110  
   111  // Cmp returns 0 if a and b are equal, 1 if a is greater than b, or -1 if a is less than b.
   112  func (a int64Amount) Cmp(b int64Amount) int {
   113  	switch {
   114  	case a.scale == b.scale:
   115  		// compare only the unscaled portion
   116  	case a.scale > b.scale:
   117  		result, remainder, exact := divideByScaleInt64(b.value, a.scale-b.scale)
   118  		if !exact {
   119  			return a.AsDec().Cmp(b.AsDec())
   120  		}
   121  		if result == a.value {
   122  			switch {
   123  			case remainder == 0:
   124  				return 0
   125  			case remainder > 0:
   126  				return -1
   127  			default:
   128  				return 1
   129  			}
   130  		}
   131  		b.value = result
   132  	default:
   133  		result, remainder, exact := divideByScaleInt64(a.value, b.scale-a.scale)
   134  		if !exact {
   135  			return a.AsDec().Cmp(b.AsDec())
   136  		}
   137  		if result == b.value {
   138  			switch {
   139  			case remainder == 0:
   140  				return 0
   141  			case remainder > 0:
   142  				return 1
   143  			default:
   144  				return -1
   145  			}
   146  		}
   147  		a.value = result
   148  	}
   149  
   150  	switch {
   151  	case a.value == b.value:
   152  		return 0
   153  	case a.value < b.value:
   154  		return -1
   155  	default:
   156  		return 1
   157  	}
   158  }
   159  
   160  // Add adds two int64Amounts together, matching scales. It will return false and not mutate
   161  // a if overflow or underflow would result.
   162  func (a *int64Amount) Add(b int64Amount) bool {
   163  	switch {
   164  	case b.value == 0:
   165  		return true
   166  	case a.value == 0:
   167  		a.value = b.value
   168  		a.scale = b.scale
   169  		return true
   170  	case a.scale == b.scale:
   171  		c, ok := int64Add(a.value, b.value)
   172  		if !ok {
   173  			return false
   174  		}
   175  		a.value = c
   176  	case a.scale > b.scale:
   177  		c, ok := positiveScaleInt64(a.value, a.scale-b.scale)
   178  		if !ok {
   179  			return false
   180  		}
   181  		c, ok = int64Add(c, b.value)
   182  		if !ok {
   183  			return false
   184  		}
   185  		a.scale = b.scale
   186  		a.value = c
   187  	default:
   188  		c, ok := positiveScaleInt64(b.value, b.scale-a.scale)
   189  		if !ok {
   190  			return false
   191  		}
   192  		c, ok = int64Add(a.value, c)
   193  		if !ok {
   194  			return false
   195  		}
   196  		a.value = c
   197  	}
   198  	return true
   199  }
   200  
   201  // Sub removes the value of b from the current amount, or returns false if underflow would result.
   202  func (a *int64Amount) Sub(b int64Amount) bool {
   203  	return a.Add(int64Amount{value: -b.value, scale: b.scale})
   204  }
   205  
   206  // AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
   207  // was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
   208  func (a int64Amount) AsScale(scale Scale) (int64Amount, bool) {
   209  	if a.scale >= scale {
   210  		return a, true
   211  	}
   212  	result, exact := negativeScaleInt64(a.value, scale-a.scale)
   213  	return int64Amount{value: result, scale: scale}, exact
   214  }
   215  
   216  // AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
   217  // either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
   218  // until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
   219  func (a int64Amount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
   220  	mantissa := a.value
   221  	exponent = int32(a.scale)
   222  
   223  	amount, times := removeInt64Factors(mantissa, 10)
   224  	exponent += int32(times)
   225  
   226  	// make sure exponent is a multiple of 3
   227  	var ok bool
   228  	switch exponent % 3 {
   229  	case 1, -2:
   230  		amount, ok = int64MultiplyScale10(amount)
   231  		if !ok {
   232  			return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
   233  		}
   234  		exponent = exponent - 1
   235  	case 2, -1:
   236  		amount, ok = int64MultiplyScale100(amount)
   237  		if !ok {
   238  			return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
   239  		}
   240  		exponent = exponent - 2
   241  	}
   242  	return strconv.AppendInt(out, amount, 10), exponent
   243  }
   244  
   245  // AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
   246  // either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
   247  // return []byte("2048"), 1.
   248  func (a int64Amount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
   249  	value, ok := a.AsScaledInt64(0)
   250  	if !ok {
   251  		return infDecAmount{a.AsDec()}.AsCanonicalBase1024Bytes(out)
   252  	}
   253  	amount, exponent := removeInt64Factors(value, 1024)
   254  	return strconv.AppendInt(out, amount, 10), exponent
   255  }
   256  
   257  // infDecAmount implements common operations over an inf.Dec that are specific to the quantity
   258  // representation.
   259  type infDecAmount struct {
   260  	*inf.Dec
   261  }
   262  
   263  // AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
   264  // was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
   265  func (a infDecAmount) AsScale(scale Scale) (infDecAmount, bool) {
   266  	tmp := &inf.Dec{}
   267  	tmp.Round(a.Dec, scale.infScale(), inf.RoundUp)
   268  	return infDecAmount{tmp}, tmp.Cmp(a.Dec) == 0
   269  }
   270  
   271  // AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
   272  // either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
   273  // until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
   274  func (a infDecAmount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
   275  	mantissa := a.Dec.UnscaledBig()
   276  	exponent = int32(-a.Dec.Scale())
   277  	amount := big.NewInt(0).Set(mantissa)
   278  	// move all factors of 10 into the exponent for easy reasoning
   279  	amount, times := removeBigIntFactors(amount, bigTen)
   280  	exponent += times
   281  
   282  	// make sure exponent is a multiple of 3
   283  	for exponent%3 != 0 {
   284  		amount.Mul(amount, bigTen)
   285  		exponent--
   286  	}
   287  
   288  	return append(out, amount.String()...), exponent
   289  }
   290  
   291  // AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
   292  // either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
   293  // return []byte("2048"), 1.
   294  func (a infDecAmount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
   295  	tmp := &inf.Dec{}
   296  	tmp.Round(a.Dec, 0, inf.RoundUp)
   297  	amount, exponent := removeBigIntFactors(tmp.UnscaledBig(), big1024)
   298  	return append(out, amount.String()...), exponent
   299  }