blob: 8ffcb9f09ac5cf4cf8da8b1040ce90c3f26c0391 [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
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 "math/big"
21
22 inf "gopkg.in/inf.v0"
23)
24
25const (
26 // maxInt64Factors is the highest value that will be checked when removing factors of 10 from an int64.
27 // It is also the maximum decimal digits that can be represented with an int64.
28 maxInt64Factors = 18
29)
30
31var (
32 // Commonly needed big.Int values-- treat as read only!
33 bigTen = big.NewInt(10)
34 bigZero = big.NewInt(0)
35 bigOne = big.NewInt(1)
36 bigThousand = big.NewInt(1000)
37 big1024 = big.NewInt(1024)
38
39 // Commonly needed inf.Dec values-- treat as read only!
40 decZero = inf.NewDec(0, 0)
41 decOne = inf.NewDec(1, 0)
42
43 // Largest (in magnitude) number allowed.
44 maxAllowed = infDecAmount{inf.NewDec((1<<63)-1, 0)} // == max int64
45
46 // The maximum value we can represent milli-units for.
47 // Compare with the return value of Quantity.Value() to
48 // see if it's safe to use Quantity.MilliValue().
49 MaxMilliValue = int64(((1 << 63) - 1) / 1000)
50)
51
52const mostNegative = -(mostPositive + 1)
53const mostPositive = 1<<63 - 1
54
55// int64Add returns a+b, or false if that would overflow int64.
56func int64Add(a, b int64) (int64, bool) {
57 c := a + b
58 switch {
59 case a > 0 && b > 0:
60 if c < 0 {
61 return 0, false
62 }
63 case a < 0 && b < 0:
64 if c > 0 {
65 return 0, false
66 }
67 if a == mostNegative && b == mostNegative {
68 return 0, false
69 }
70 }
71 return c, true
72}
73
74// int64Multiply returns a*b, or false if that would overflow or underflow int64.
75func int64Multiply(a, b int64) (int64, bool) {
76 if a == 0 || b == 0 || a == 1 || b == 1 {
77 return a * b, true
78 }
79 if a == mostNegative || b == mostNegative {
80 return 0, false
81 }
82 c := a * b
83 return c, c/b == a
84}
85
86// int64MultiplyScale returns a*b, assuming b is greater than one, or false if that would overflow or underflow int64.
87// Use when b is known to be greater than one.
88func int64MultiplyScale(a int64, b int64) (int64, bool) {
89 if a == 0 || a == 1 {
90 return a * b, true
91 }
92 if a == mostNegative && b != 1 {
93 return 0, false
94 }
95 c := a * b
96 return c, c/b == a
97}
98
99// int64MultiplyScale10 multiplies a by 10, or returns false if that would overflow. This method is faster than
100// int64Multiply(a, 10) because the compiler can optimize constant factor multiplication.
101func int64MultiplyScale10(a int64) (int64, bool) {
102 if a == 0 || a == 1 {
103 return a * 10, true
104 }
105 if a == mostNegative {
106 return 0, false
107 }
108 c := a * 10
109 return c, c/10 == a
110}
111
112// int64MultiplyScale100 multiplies a by 100, or returns false if that would overflow. This method is faster than
113// int64Multiply(a, 100) because the compiler can optimize constant factor multiplication.
114func int64MultiplyScale100(a int64) (int64, bool) {
115 if a == 0 || a == 1 {
116 return a * 100, true
117 }
118 if a == mostNegative {
119 return 0, false
120 }
121 c := a * 100
122 return c, c/100 == a
123}
124
125// int64MultiplyScale1000 multiplies a by 1000, or returns false if that would overflow. This method is faster than
126// int64Multiply(a, 1000) because the compiler can optimize constant factor multiplication.
127func int64MultiplyScale1000(a int64) (int64, bool) {
128 if a == 0 || a == 1 {
129 return a * 1000, true
130 }
131 if a == mostNegative {
132 return 0, false
133 }
134 c := a * 1000
135 return c, c/1000 == a
136}
137
138// positiveScaleInt64 multiplies base by 10^scale, returning false if the
139// value overflows. Passing a negative scale is undefined.
140func positiveScaleInt64(base int64, scale Scale) (int64, bool) {
141 switch scale {
142 case 0:
143 return base, true
144 case 1:
145 return int64MultiplyScale10(base)
146 case 2:
147 return int64MultiplyScale100(base)
148 case 3:
149 return int64MultiplyScale1000(base)
150 case 6:
151 return int64MultiplyScale(base, 1000000)
152 case 9:
153 return int64MultiplyScale(base, 1000000000)
154 default:
155 value := base
156 var ok bool
157 for i := Scale(0); i < scale; i++ {
158 if value, ok = int64MultiplyScale(value, 10); !ok {
159 return 0, false
160 }
161 }
162 return value, true
163 }
164}
165
166// negativeScaleInt64 reduces base by the provided scale, rounding up, until the
167// value is zero or the scale is reached. Passing a negative scale is undefined.
168// The value returned, if not exact, is rounded away from zero.
169func negativeScaleInt64(base int64, scale Scale) (result int64, exact bool) {
170 if scale == 0 {
171 return base, true
172 }
173
174 value := base
175 var fraction bool
176 for i := Scale(0); i < scale; i++ {
177 if !fraction && value%10 != 0 {
178 fraction = true
179 }
180 value = value / 10
181 if value == 0 {
182 if fraction {
183 if base > 0 {
184 return 1, false
185 }
186 return -1, false
187 }
188 return 0, true
189 }
190 }
191 if fraction {
192 if base > 0 {
193 value++
194 } else {
195 value--
196 }
197 }
198 return value, !fraction
199}
200
201func pow10Int64(b int64) int64 {
202 switch b {
203 case 0:
204 return 1
205 case 1:
206 return 10
207 case 2:
208 return 100
209 case 3:
210 return 1000
211 case 4:
212 return 10000
213 case 5:
214 return 100000
215 case 6:
216 return 1000000
217 case 7:
218 return 10000000
219 case 8:
220 return 100000000
221 case 9:
222 return 1000000000
223 case 10:
224 return 10000000000
225 case 11:
226 return 100000000000
227 case 12:
228 return 1000000000000
229 case 13:
230 return 10000000000000
231 case 14:
232 return 100000000000000
233 case 15:
234 return 1000000000000000
235 case 16:
236 return 10000000000000000
237 case 17:
238 return 100000000000000000
239 case 18:
240 return 1000000000000000000
241 default:
242 return 0
243 }
244}
245
246// negativeScaleInt64 returns the result of dividing base by scale * 10 and the remainder, or
247// false if no such division is possible. Dividing by negative scales is undefined.
248func divideByScaleInt64(base int64, scale Scale) (result, remainder int64, exact bool) {
249 if scale == 0 {
250 return base, 0, true
251 }
252 // the max scale representable in base 10 in an int64 is 18 decimal places
253 if scale >= 18 {
254 return 0, base, false
255 }
256 divisor := pow10Int64(int64(scale))
257 return base / divisor, base % divisor, true
258}
259
260// removeInt64Factors divides in a loop; the return values have the property that
261// value == result * base ^ scale
262func removeInt64Factors(value int64, base int64) (result int64, times int32) {
263 times = 0
264 result = value
265 negative := result < 0
266 if negative {
267 result = -result
268 }
269 switch base {
270 // allow the compiler to optimize the common cases
271 case 10:
272 for result >= 10 && result%10 == 0 {
273 times++
274 result = result / 10
275 }
276 // allow the compiler to optimize the common cases
277 case 1024:
278 for result >= 1024 && result%1024 == 0 {
279 times++
280 result = result / 1024
281 }
282 default:
283 for result >= base && result%base == 0 {
284 times++
285 result = result / base
286 }
287 }
288 if negative {
289 result = -result
290 }
291 return result, times
292}
293
294// removeBigIntFactors divides in a loop; the return values have the property that
295// d == result * factor ^ times
296// d may be modified in place.
297// If d == 0, then the return values will be (0, 0)
298func removeBigIntFactors(d, factor *big.Int) (result *big.Int, times int32) {
299 q := big.NewInt(0)
300 m := big.NewInt(0)
301 for d.Cmp(bigZero) != 0 {
302 q.DivMod(d, factor, m)
303 if m.Cmp(bigZero) != 0 {
304 break
305 }
306 times++
307 d, q = q, d
308 }
309 return d, times
310}