mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-01 00:23:56 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			300 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			300 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| 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 (
 | |
| 	"math/big"
 | |
| 	"strconv"
 | |
| 
 | |
| 	inf "gopkg.in/inf.v0"
 | |
| )
 | |
| 
 | |
| // 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 int32
 | |
| 
 | |
| // 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
 | |
| }
 | |
| 
 | |
| 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
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	Zero = int64Amount{}
 | |
| 
 | |
| 	// Used by quantity strings - treat as read only
 | |
| 	zeroBytes = []byte("0")
 | |
| )
 | |
| 
 | |
| // int64Amount represents a fixed precision numerator and arbitrary scale exponent. It is faster
 | |
| // than operations on inf.Dec for values that can be represented as int64.
 | |
| // +k8s:openapi-gen=true
 | |
| type int64Amount struct {
 | |
| 	value int64
 | |
| 	scale Scale
 | |
| }
 | |
| 
 | |
| // Sign returns 0 if the value is zero, -1 if it is less than 0, or 1 if it is greater than 0.
 | |
| func (a int64Amount) Sign() int {
 | |
| 	switch {
 | |
| 	case a.value == 0:
 | |
| 		return 0
 | |
| 	case a.value > 0:
 | |
| 		return 1
 | |
| 	default:
 | |
| 		return -1
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // AsInt64 returns the current amount as an int64 at scale 0, or false if the value cannot be
 | |
| // represented in an int64 OR would result in a loss of precision. This method is intended as
 | |
| // an optimization to avoid calling AsDec.
 | |
| func (a int64Amount) AsInt64() (int64, bool) {
 | |
| 	if a.scale == 0 {
 | |
| 		return a.value, true
 | |
| 	}
 | |
| 	if a.scale < 0 {
 | |
| 		// TODO: attempt to reduce factors, although it is assumed that factors are reduced prior
 | |
| 		// to the int64Amount being created.
 | |
| 		return 0, false
 | |
| 	}
 | |
| 	return positiveScaleInt64(a.value, a.scale)
 | |
| }
 | |
| 
 | |
| // AsScaledInt64 returns an int64 representing the value of this amount at the specified scale,
 | |
| // rounding up, or false if that would result in overflow. (1e20).AsScaledInt64(1) would result
 | |
| // in overflow because 1e19 is not representable as an int64. Note that setting a scale larger
 | |
| // than the current value may result in loss of precision - i.e. (1e-6).AsScaledInt64(0) would
 | |
| // return 1, because 0.000001 is rounded up to 1.
 | |
| func (a int64Amount) AsScaledInt64(scale Scale) (result int64, ok bool) {
 | |
| 	if a.scale < scale {
 | |
| 		result, _ = negativeScaleInt64(a.value, scale-a.scale)
 | |
| 		return result, true
 | |
| 	}
 | |
| 	return positiveScaleInt64(a.value, a.scale-scale)
 | |
| }
 | |
| 
 | |
| // AsDec returns an inf.Dec representation of this value.
 | |
| func (a int64Amount) AsDec() *inf.Dec {
 | |
| 	var base inf.Dec
 | |
| 	base.SetUnscaled(a.value)
 | |
| 	base.SetScale(inf.Scale(-a.scale))
 | |
| 	return &base
 | |
| }
 | |
| 
 | |
| // Cmp returns 0 if a and b are equal, 1 if a is greater than b, or -1 if a is less than b.
 | |
| func (a int64Amount) Cmp(b int64Amount) int {
 | |
| 	switch {
 | |
| 	case a.scale == b.scale:
 | |
| 		// compare only the unscaled portion
 | |
| 	case a.scale > b.scale:
 | |
| 		result, remainder, exact := divideByScaleInt64(b.value, a.scale-b.scale)
 | |
| 		if !exact {
 | |
| 			return a.AsDec().Cmp(b.AsDec())
 | |
| 		}
 | |
| 		if result == a.value {
 | |
| 			switch {
 | |
| 			case remainder == 0:
 | |
| 				return 0
 | |
| 			case remainder > 0:
 | |
| 				return -1
 | |
| 			default:
 | |
| 				return 1
 | |
| 			}
 | |
| 		}
 | |
| 		b.value = result
 | |
| 	default:
 | |
| 		result, remainder, exact := divideByScaleInt64(a.value, b.scale-a.scale)
 | |
| 		if !exact {
 | |
| 			return a.AsDec().Cmp(b.AsDec())
 | |
| 		}
 | |
| 		if result == b.value {
 | |
| 			switch {
 | |
| 			case remainder == 0:
 | |
| 				return 0
 | |
| 			case remainder > 0:
 | |
| 				return 1
 | |
| 			default:
 | |
| 				return -1
 | |
| 			}
 | |
| 		}
 | |
| 		a.value = result
 | |
| 	}
 | |
| 
 | |
| 	switch {
 | |
| 	case a.value == b.value:
 | |
| 		return 0
 | |
| 	case a.value < b.value:
 | |
| 		return -1
 | |
| 	default:
 | |
| 		return 1
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Add adds two int64Amounts together, matching scales. It will return false and not mutate
 | |
| // a if overflow or underflow would result.
 | |
| func (a *int64Amount) Add(b int64Amount) bool {
 | |
| 	switch {
 | |
| 	case b.value == 0:
 | |
| 		return true
 | |
| 	case a.value == 0:
 | |
| 		a.value = b.value
 | |
| 		a.scale = b.scale
 | |
| 		return true
 | |
| 	case a.scale == b.scale:
 | |
| 		c, ok := int64Add(a.value, b.value)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		a.value = c
 | |
| 	case a.scale > b.scale:
 | |
| 		c, ok := positiveScaleInt64(a.value, a.scale-b.scale)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		c, ok = int64Add(c, b.value)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		a.scale = b.scale
 | |
| 		a.value = c
 | |
| 	default:
 | |
| 		c, ok := positiveScaleInt64(b.value, b.scale-a.scale)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		c, ok = int64Add(a.value, c)
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 		a.value = c
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Sub removes the value of b from the current amount, or returns false if underflow would result.
 | |
| func (a *int64Amount) Sub(b int64Amount) bool {
 | |
| 	return a.Add(int64Amount{value: -b.value, scale: b.scale})
 | |
| }
 | |
| 
 | |
| // AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
 | |
| // was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
 | |
| func (a int64Amount) AsScale(scale Scale) (int64Amount, bool) {
 | |
| 	if a.scale >= scale {
 | |
| 		return a, true
 | |
| 	}
 | |
| 	result, exact := negativeScaleInt64(a.value, scale-a.scale)
 | |
| 	return int64Amount{value: result, scale: scale}, exact
 | |
| }
 | |
| 
 | |
| // AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
 | |
| // either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
 | |
| // until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
 | |
| func (a int64Amount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
 | |
| 	mantissa := a.value
 | |
| 	exponent = int32(a.scale)
 | |
| 
 | |
| 	amount, times := removeInt64Factors(mantissa, 10)
 | |
| 	exponent += int32(times)
 | |
| 
 | |
| 	// make sure exponent is a multiple of 3
 | |
| 	var ok bool
 | |
| 	switch exponent % 3 {
 | |
| 	case 1, -2:
 | |
| 		amount, ok = int64MultiplyScale10(amount)
 | |
| 		if !ok {
 | |
| 			return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
 | |
| 		}
 | |
| 		exponent = exponent - 1
 | |
| 	case 2, -1:
 | |
| 		amount, ok = int64MultiplyScale100(amount)
 | |
| 		if !ok {
 | |
| 			return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
 | |
| 		}
 | |
| 		exponent = exponent - 2
 | |
| 	}
 | |
| 	return strconv.AppendInt(out, amount, 10), exponent
 | |
| }
 | |
| 
 | |
| // AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
 | |
| // either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
 | |
| // return []byte("2048"), 1.
 | |
| func (a int64Amount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
 | |
| 	value, ok := a.AsScaledInt64(0)
 | |
| 	if !ok {
 | |
| 		return infDecAmount{a.AsDec()}.AsCanonicalBase1024Bytes(out)
 | |
| 	}
 | |
| 	amount, exponent := removeInt64Factors(value, 1024)
 | |
| 	return strconv.AppendInt(out, amount, 10), exponent
 | |
| }
 | |
| 
 | |
| // infDecAmount implements common operations over an inf.Dec that are specific to the quantity
 | |
| // representation.
 | |
| type infDecAmount struct {
 | |
| 	*inf.Dec
 | |
| }
 | |
| 
 | |
| // AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
 | |
| // was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
 | |
| func (a infDecAmount) AsScale(scale Scale) (infDecAmount, bool) {
 | |
| 	tmp := &inf.Dec{}
 | |
| 	tmp.Round(a.Dec, scale.infScale(), inf.RoundUp)
 | |
| 	return infDecAmount{tmp}, tmp.Cmp(a.Dec) == 0
 | |
| }
 | |
| 
 | |
| // AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
 | |
| // either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
 | |
| // until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
 | |
| func (a infDecAmount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
 | |
| 	mantissa := a.Dec.UnscaledBig()
 | |
| 	exponent = int32(-a.Dec.Scale())
 | |
| 	amount := big.NewInt(0).Set(mantissa)
 | |
| 	// move all factors of 10 into the exponent for easy reasoning
 | |
| 	amount, times := removeBigIntFactors(amount, bigTen)
 | |
| 	exponent += times
 | |
| 
 | |
| 	// make sure exponent is a multiple of 3
 | |
| 	for exponent%3 != 0 {
 | |
| 		amount.Mul(amount, bigTen)
 | |
| 		exponent--
 | |
| 	}
 | |
| 
 | |
| 	return append(out, amount.String()...), exponent
 | |
| }
 | |
| 
 | |
| // AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
 | |
| // either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
 | |
| // return []byte("2048"), 1.
 | |
| func (a infDecAmount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
 | |
| 	tmp := &inf.Dec{}
 | |
| 	tmp.Round(a.Dec, 0, inf.RoundUp)
 | |
| 	amount, exponent := removeBigIntFactors(tmp.UnscaledBig(), big1024)
 | |
| 	return append(out, amount.String()...), exponent
 | |
| }
 | 
