44 "encoding/json"
55 "fmt"
66 "io"
7+ "math"
78 "math/bits"
89
910 "github.com/ethereum/go-ethereum/common/hexutil"
@@ -67,12 +68,10 @@ func NewMultiGas(kind ResourceKind, amount uint64) MultiGas {
6768func MultiGasFromPairs (pairs ... Pair ) MultiGas {
6869 var mg MultiGas
6970 for _ , p := range pairs {
70- newTotal , c := bits .Add64 (mg .total , p .Amount , 0 )
71- if c != 0 {
72- panic ("multigas overflow" )
73- }
7471 mg .gas [p .Kind ] = p .Amount
75- mg .total = newTotal
72+ }
73+ if mg .recomputeTotal () {
74+ panic ("multigas overflow" )
7675 }
7776 return mg
7877}
@@ -120,13 +119,14 @@ func (z MultiGas) Get(kind ResourceKind) uint64 {
120119// With returns a copy of z with the given resource kind set to amount.
121120// The total is adjusted accordingly. It returns the updated value and true if an overflow occurred.
122121func (z MultiGas ) With (kind ResourceKind , amount uint64 ) (MultiGas , bool ) {
123- res := z
124- newTotal , c := bits .Add64 (z .total - z .gas [kind ], amount , 0 )
125- if c != 0 {
122+ res , overflow := z , false
123+
124+ res .total , overflow = saturatingScalarAdd (z .total - z .gas [kind ], amount )
125+ if overflow {
126126 return z , true
127127 }
128+
128129 res .gas [kind ] = amount
129- res .total = newTotal
130130 return res , false
131131}
132132
@@ -146,27 +146,23 @@ func (z MultiGas) WithRefund(amount uint64) MultiGas {
146146// added to the values from x. It returns the updated value and true if
147147// an overflow occurred.
148148func (z MultiGas ) SafeAdd (x MultiGas ) (MultiGas , bool ) {
149- res := z
149+ res , overflow := z , false
150150
151151 for i := 0 ; i < int (NumResourceKind ); i ++ {
152- v , c := bits . Add64 (res .gas [i ], x .gas [i ], 0 )
153- if c != 0 {
152+ res . gas [ i ], overflow = saturatingScalarAdd (res .gas [i ], x .gas [i ])
153+ if overflow {
154154 return z , true
155155 }
156- res .gas [i ] = v
157156 }
158157
159- t , c := bits . Add64 (res .total , x .total , 0 )
160- if c != 0 {
158+ res . total , overflow = saturatingScalarAdd (res .total , x .total )
159+ if overflow {
161160 return z , true
162161 }
163- res .total = t
164-
165- r , c := bits .Add64 (res .refund , x .refund , 0 )
166- if c != 0 {
162+ res .refund , overflow = saturatingScalarAdd (res .refund , x .refund )
163+ if overflow {
167164 return z , true
168165 }
169- res .refund = r
170166
171167 return res , false
172168}
@@ -178,25 +174,11 @@ func (z MultiGas) SaturatingAdd(x MultiGas) MultiGas {
178174 res := z
179175
180176 for i := 0 ; i < int (NumResourceKind ); i ++ {
181- if v , c := bits .Add64 (res .gas [i ], x .gas [i ], 0 ); c != 0 {
182- res .gas [i ] = ^ uint64 (0 ) // clamp
183- } else {
184- res .gas [i ] = v
185- }
186- }
187-
188- if t , c := bits .Add64 (res .total , x .total , 0 ); c != 0 {
189- res .total = ^ uint64 (0 ) // clamp
190- } else {
191- res .total = t
192- }
193-
194- if r , c := bits .Add64 (res .refund , x .refund , 0 ); c != 0 {
195- res .refund = ^ uint64 (0 ) // clamp
196- } else {
197- res .refund = r
177+ res .gas [i ], _ = saturatingScalarAdd (res .gas [i ], x .gas [i ])
198178 }
199179
180+ res .total , _ = saturatingScalarAdd (res .total , x .total )
181+ res .refund , _ = saturatingScalarAdd (res .refund , x .refund )
200182 return res
201183}
202184
@@ -205,49 +187,31 @@ func (z MultiGas) SaturatingAdd(x MultiGas) MultiGas {
205187// This is a hot-path helper; the public immutable API remains preferred elsewhere.
206188func (z * MultiGas ) SaturatingAddInto (x MultiGas ) {
207189 for i := 0 ; i < int (NumResourceKind ); i ++ {
208- if v , c := bits .Add64 (z .gas [i ], x .gas [i ], 0 ); c != 0 {
209- z .gas [i ] = ^ uint64 (0 ) // clamp
210- } else {
211- z .gas [i ] = v
212- }
213- }
214- if t , c := bits .Add64 (z .total , x .total , 0 ); c != 0 {
215- z .total = ^ uint64 (0 ) // clamp
216- } else {
217- z .total = t
218- }
219- if r , c := bits .Add64 (z .refund , x .refund , 0 ); c != 0 {
220- z .refund = ^ uint64 (0 ) // clamp
221- } else {
222- z .refund = r
190+ z .gas [i ], _ = saturatingScalarAdd (z .gas [i ], x .gas [i ])
223191 }
192+ z .total , _ = saturatingScalarAdd (z .total , x .total )
193+ z .refund , _ = saturatingScalarAdd (z .refund , x .refund )
224194}
225195
226196// SafeSub returns a copy of z with the per-kind, total, and refund gas
227197// subtracted by the values from x. It returns the updated value and true if
228198// a underflow occurred.
229199func (z MultiGas ) SafeSub (x MultiGas ) (MultiGas , bool ) {
230- res := z
200+ res , underflow := z , false
231201
232202 for i := 0 ; i < int (NumResourceKind ); i ++ {
233- v , b := bits . Sub64 (res .gas [i ], x .gas [i ], 0 )
234- if b != 0 {
203+ res . gas [ i ], underflow = saturatingScalarSub (res .gas [i ], x .gas [i ])
204+ if underflow {
235205 return z , true
236206 }
237- res .gas [i ] = v
238207 }
239208
240- t , b := bits . Sub64 (res .total , x .total , 0 )
241- if b != 0 {
209+ res . refund , underflow = saturatingScalarSub (res .refund , x .refund )
210+ if underflow {
242211 return z , true
243212 }
244- res .total = t
245213
246- r , b := bits .Sub64 (res .refund , x .refund , 0 )
247- if b != 0 {
248- return z , true
249- }
250- res .refund = r
214+ res .recomputeTotal ()
251215
252216 return res , false
253217}
@@ -257,68 +221,39 @@ func (z MultiGas) SafeSub(x MultiGas) (MultiGas, bool) {
257221// clamped to zero.
258222func (z MultiGas ) SaturatingSub (x MultiGas ) MultiGas {
259223 res := z
260-
261224 for i := 0 ; i < int (NumResourceKind ); i ++ {
262- if v , c := bits .Sub64 (res .gas [i ], x .gas [i ], 0 ); c != 0 {
263- res .gas [i ] = uint64 (0 ) // clamp
264- } else {
265- res .gas [i ] = v
266- }
225+ res .gas [i ], _ = saturatingScalarSub (res .gas [i ], x .gas [i ])
267226 }
268-
269- if t , c := bits .Sub64 (res .total , x .total , 0 ); c != 0 {
270- res .total = uint64 (0 ) // clamp
271- } else {
272- res .total = t
273- }
274-
275- if r , c := bits .Sub64 (res .refund , x .refund , 0 ); c != 0 {
276- res .refund = uint64 (0 ) // clamp
277- } else {
278- res .refund = r
279- }
280-
227+ res .refund , _ = saturatingScalarSub (res .refund , x .refund )
228+ res .recomputeTotal ()
281229 return res
282230}
283231
284232// SafeIncrement returns a copy of z with the given resource kind
285233// and the total incremented by gas. It returns the updated value and true if
286234// an overflow occurred.
287235func (z MultiGas ) SafeIncrement (kind ResourceKind , gas uint64 ) (MultiGas , bool ) {
288- res := z
236+ res , overflow := z , false
289237
290- newValue , c := bits . Add64 (z .gas [kind ], gas , 0 )
291- if c != 0 {
292- return res , true
238+ res . gas [ kind ], overflow = saturatingScalarAdd (z .gas [kind ], gas )
239+ if overflow {
240+ return z , true
293241 }
294242
295- newTotal , c := bits . Add64 (z .total , gas , 0 )
296- if c != 0 {
297- return res , true
243+ res . total , overflow = saturatingScalarAdd (z .total , gas )
244+ if overflow {
245+ return z , true
298246 }
299247
300- res .gas [kind ] = newValue
301- res .total = newTotal
302248 return res , false
303249}
304250
305251// SaturatingIncrement returns a copy of z with the given resource kind
306252// and the total incremented by gas. On overflow, the field(s) are clamped to MaxUint64.
307253func (z MultiGas ) SaturatingIncrement (kind ResourceKind , gas uint64 ) MultiGas {
308254 res := z
309-
310- if v , c := bits .Add64 (res .gas [kind ], gas , 0 ); c != 0 {
311- res .gas [kind ] = ^ uint64 (0 ) // clamp
312- } else {
313- res .gas [kind ] = v
314- }
315-
316- if t , c := bits .Add64 (res .total , gas , 0 ); c != 0 {
317- res .total = ^ uint64 (0 ) // clamp
318- } else {
319- res .total = t
320- }
321-
255+ res .gas [kind ], _ = saturatingScalarAdd (z .gas [kind ], gas )
256+ res .total , _ = saturatingScalarAdd (z .total , gas )
322257 return res
323258}
324259
@@ -351,17 +286,8 @@ func (z MultiGas) SaturatingDecrement(kind ResourceKind, gas uint64) MultiGas {
351286// Unlike SaturatingIncrement, this method mutates the receiver directly and
352287// is intended for VM hot paths where avoiding value copies is critical.
353288func (z * MultiGas ) SaturatingIncrementInto (kind ResourceKind , gas uint64 ) {
354- if v , c := bits .Add64 (z .gas [kind ], gas , 0 ); c != 0 {
355- z .gas [kind ] = ^ uint64 (0 )
356- } else {
357- z .gas [kind ] = v
358- }
359-
360- if t , c := bits .Add64 (z .total , gas , 0 ); c != 0 {
361- z .total = ^ uint64 (0 )
362- } else {
363- z .total = t
364- }
289+ z .gas [kind ], _ = saturatingScalarAdd (z .gas [kind ], gas )
290+ z .total , _ = saturatingScalarAdd (z .total , gas )
365291}
366292
367293// SingleGas returns the single-dimensional total gas.
@@ -477,3 +403,38 @@ func (z *MultiGas) DecodeRLP(s *rlp.Stream) error {
477403 z .refund = refund
478404 return nil
479405}
406+
407+ // recomputeTotal recomputes the total gas from the per-kind amounts. Returns
408+ // true if an overflow occurred (and the total was set to MaxUint64).
409+ func (z * MultiGas ) recomputeTotal () (overflow bool ) {
410+ z .total = 0
411+ for i := 0 ; i < int (NumResourceKind ); i ++ {
412+ z .total , overflow = saturatingScalarAdd (z .total , z .gas [i ])
413+ if overflow {
414+ return
415+ }
416+ }
417+ return
418+ }
419+
420+ // saturatingScalarAdd adds two uint64 values, returning the sum and a boolean
421+ // indicating whether an overflow occurred. If an overflow occurs, the sum is
422+ // set to math.MaxUint64.
423+ func saturatingScalarAdd (a , b uint64 ) (uint64 , bool ) {
424+ sum , carry := bits .Add64 (a , b , 0 )
425+ if carry != 0 {
426+ return math .MaxUint64 , true
427+ }
428+ return sum , false
429+ }
430+
431+ // saturatingScalarSub subtracts two uint64 values, returning the difference and a boolean
432+ // indicating whether an underflow occurred. If an underflow occurs, the difference is
433+ // set to 0.
434+ func saturatingScalarSub (a , b uint64 ) (uint64 , bool ) {
435+ diff , borrow := bits .Sub64 (a , b , 0 )
436+ if borrow != 0 {
437+ return 0 , true
438+ }
439+ return diff , false
440+ }
0 commit comments