11package udecimal
22
33import (
4+ "bytes"
45 "fmt"
56 "math/big"
67 "strings"
78)
89
10+ const (
11+ // maxDigitU64 is the maximum digits of a number
12+ // that can be safely stored in a uint64.
13+ maxDigitU64 = 19
14+ )
15+
916var (
1017 bigZero = big .NewInt (0 )
1118 bigOne = big .NewInt (1 )
@@ -171,7 +178,7 @@ func parseBint(s []byte) (bool, bint, uint8, error) {
171178 return false , bint {}, 0 , errInvalidFormat (s )
172179 }
173180
174- // nolint: gosec
181+ //nolint:gosec
175182 return neg , bintFromBigInt (dValue ), uint8 (prec ), nil
176183}
177184
@@ -211,7 +218,7 @@ func parseBintFromU128(s []byte) (bool, bint, uint8, error) {
211218 prec uint8
212219 )
213220
214- if len (s [pos :]) <= 19 {
221+ if len (s [pos :]) <= maxDigitU64 {
215222 coef , prec , err = parseSmallToU128 (s [pos :])
216223 } else {
217224 coef , prec , err = parseLargeToU128 (s [pos :])
@@ -237,7 +244,7 @@ func parseSmallToU128(s []byte) (u128, uint8, error) {
237244 return u128 {}, 0 , ErrInvalidFormat
238245 }
239246
240- // nolint: gosec
247+ //nolint:gosec
241248 prec = uint8 (len (s ) - i - 1 )
242249
243250 // prevent "123." or "-123."
@@ -269,27 +276,12 @@ func parseSmallToU128(s []byte) (u128, uint8, error) {
269276func parseLargeToU128 (s []byte ) (u128 , uint8 , error ) {
270277 // find '.' position
271278 l := len (s )
272- pos := - 1
273-
274- for i := 0 ; i < len (s ); i ++ {
275- if s [i ] == '.' {
276- pos = i
277- }
278- }
279-
279+ pos := bytes .IndexByte (s , '.' )
280280 if pos == 0 || pos == l - 1 {
281+ // prevent ".123" or "123."
281282 return u128 {}, 0 , ErrInvalidFormat
282283 }
283284
284- var prec uint8
285- if pos != - 1 {
286- // nolint: gosec
287- prec = uint8 (l - pos - 1 )
288- if prec > defaultPrec {
289- return u128 {}, 0 , ErrPrecOutOfRange
290- }
291- }
292-
293285 if pos == - 1 {
294286 // no decimal point
295287 coef , err := digitToU128 (s )
@@ -300,14 +292,21 @@ func parseLargeToU128(s []byte) (u128, uint8, error) {
300292 return coef , 0 , nil
301293 }
302294
295+ // now 0 < pos < l-1
296+ //nolint:gosec
297+ prec := uint8 (l - pos - 1 )
298+ if prec > defaultPrec {
299+ return u128 {}, 0 , ErrPrecOutOfRange
300+ }
301+
303302 // number has a decimal point, split into 2 parts: integer and fraction
304303 intPart , err := digitToU128 (s [:pos ])
305304 if err != nil {
306305 return u128 {}, 0 , err
307306 }
308307
309308 // because max prec is 19,
310- // factionPart can't be larger than 10%20 -1 and will fit into uint64 (fractionPart.hi == 0)
309+ // factionPart can't be larger than 10^19 -1 and will fit into uint64 (fractionPart.hi == 0)
311310 fractionPart , err := digitToU128 (s [pos + 1 :])
312311 if err != nil {
313312 return u128 {}, 0 , err
@@ -328,7 +327,7 @@ func parseLargeToU128(s []byte) (u128, uint8, error) {
328327}
329328
330329func digitToU128 (s []byte ) (u128 , error ) {
331- if len (s ) <= 19 {
330+ if len (s ) <= maxDigitU64 {
332331 var u uint64
333332 for i := 0 ; i < len (s ); i ++ {
334333 if s [i ] < '0' || s [i ] > '9' {
0 commit comments