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 }