package jx
import (
"bytes"
"strconv"
"github.com/go-faster/errors"
)
var (
pow10 = [...]uint64 {1 , 10 , 100 , 1000 , 10000 , 100000 , 1000000 }
floatDigits = [256 ]int8 {}
)
const (
dotInNumber int8 = -iota - 1
expInNumber
plusInNumber
minusInNumber
endOfNumber
invalidCharForNumber
maxFloat64 = 1 <<63 - 1
)
func init () {
for i := 0 ; i < len (floatDigits ); i ++ {
floatDigits [i ] = invalidCharForNumber
}
floatDigits [',' ] = endOfNumber
floatDigits [']' ] = endOfNumber
floatDigits ['}' ] = endOfNumber
for ch , isSpace := range spaceSet {
if isSpace == 1 {
floatDigits [ch ] = endOfNumber
}
}
for i := int8 ('0' ); i <= int8 ('9' ); i ++ {
floatDigits [i ] = i - int8 ('0' )
}
floatDigits ['.' ] = dotInNumber
floatDigits ['e' ] = expInNumber
floatDigits ['E' ] = expInNumber
floatDigits ['+' ] = plusInNumber
floatDigits ['-' ] = minusInNumber
}
func (d *Decoder ) Float32 () (float32 , error ) {
c , err := d .more ()
if err != nil {
return 0 , err
}
if c != '-' {
d .unread ()
}
v , err := d .positiveFloat32 ()
if err != nil {
return 0 , err
}
if c == '-' {
v *= -1
}
return v , nil
}
func (d *Decoder ) positiveFloat32 () (float32 , error ) {
i := d .head
if i == d .tail {
return d .float32Slow ()
}
c := d .buf [i ]
i ++
ind := floatDigits [c ]
switch ind {
case invalidCharForNumber , endOfNumber :
return 0 , badToken (c , d .offset ())
case dotInNumber , plusInNumber , expInNumber :
err := badToken (c , d .offset ())
return 0 , errors .Wrapf (err , "leading %q" , c )
case minusInNumber :
err := badToken (c , d .offset ())
return 0 , errors .Wrap (err , "double minus" )
case 0 :
if i == d .tail {
return d .float32Slow ()
}
c = d .buf [i ]
if floatDigits [c ] >= 0 {
err := badToken (c , d .offset ()+1 )
return 0 , errors .Wrap (err , "leading zero" )
}
}
value := uint64 (ind )
NonDecimalLoop :
for ; i < d .tail ; i ++ {
c = d .buf [i ]
ind := floatDigits [c ]
switch ind {
case invalidCharForNumber :
return 0 , badToken (c , d .offset ()+i )
case endOfNumber :
d .head = i
return float32 (value ), nil
case dotInNumber , expInNumber :
break NonDecimalLoop
}
if value > uint64SafeToMultiple10 {
return d .float32Slow ()
}
value = (value << 3 ) + (value << 1 ) + uint64 (ind )
}
if c == '.' {
i ++
decimalPlaces := 0
if i == d .tail {
return d .float32Slow ()
}
for ; i < d .tail ; i ++ {
c = d .buf [i ]
ind := floatDigits [c ]
switch ind {
case endOfNumber :
if decimalPlaces > 0 && decimalPlaces < len (pow10 ) {
d .head = i
return float32 (float64 (value ) / float64 (pow10 [decimalPlaces ])), nil
}
return d .float32Slow ()
case dotInNumber , expInNumber , plusInNumber , minusInNumber :
return d .float32Slow ()
case invalidCharForNumber :
return 0 , badToken (c , d .offset ()+i )
}
decimalPlaces ++
if value > uint64SafeToMultiple10 {
return d .float32Slow ()
}
value = (value << 3 ) + (value << 1 ) + uint64 (ind )
}
}
return d .float32Slow ()
}
func (d *Decoder ) Float64 () (float64 , error ) {
c , err := d .more ()
if err != nil {
return 0 , err
}
if c != '-' {
d .unread ()
}
v , err := d .positiveFloat64 ()
if err != nil {
return 0 , err
}
if c == '-' {
v *= -1
}
return v , nil
}
func (d *Decoder ) positiveFloat64 () (float64 , error ) {
i := d .head
if i == d .tail {
return d .float64Slow ()
}
c := d .buf [i ]
i ++
ind := floatDigits [c ]
switch ind {
case invalidCharForNumber , endOfNumber :
return 0 , badToken (c , d .offset ())
case dotInNumber , plusInNumber , expInNumber :
err := badToken (c , d .offset ())
return 0 , errors .Wrapf (err , "leading %q" , c )
case minusInNumber :
err := badToken (c , d .offset ())
return 0 , errors .Wrap (err , "double minus" )
case 0 :
if i == d .tail {
return d .float64Slow ()
}
c = d .buf [i ]
if floatDigits [c ] >= 0 {
err := badToken (c , d .offset ()+1 )
return 0 , errors .Wrap (err , "leading zero" )
}
}
value := uint64 (ind )
NonDecimal :
for ; i < d .tail ; i ++ {
c = d .buf [i ]
ind := floatDigits [c ]
switch ind {
case invalidCharForNumber :
return 0 , badToken (c , d .offset ()+i )
case endOfNumber :
d .head = i
return float64 (value ), nil
case dotInNumber , expInNumber :
break NonDecimal
}
if value > uint64SafeToMultiple10 {
return d .float64Slow ()
}
value = (value << 3 ) + (value << 1 ) + uint64 (ind )
}
if c == '.' {
i ++
decimalPlaces := 0
if i == d .tail {
return d .float64Slow ()
}
for ; i < d .tail ; i ++ {
c = d .buf [i ]
ind := floatDigits [c ]
switch ind {
case endOfNumber :
if decimalPlaces > 0 && decimalPlaces < len (pow10 ) {
d .head = i
return float64 (value ) / float64 (pow10 [decimalPlaces ]), nil
}
return d .float64Slow ()
case dotInNumber , expInNumber , plusInNumber , minusInNumber :
return d .float64Slow ()
case invalidCharForNumber :
return 0 , badToken (c , d .offset ()+i )
}
decimalPlaces ++
value = (value << 3 ) + (value << 1 ) + uint64 (ind )
if value > maxFloat64 {
return d .float64Slow ()
}
}
}
return d .float64Slow ()
}
func (d *Decoder ) float32Slow () (float32 , error ) {
v , err := d .floatSlow (32 )
if err != nil {
return 0 , err
}
return float32 (v ), err
}
func (d *Decoder ) float64Slow () (float64 , error ) { return d .floatSlow (64 ) }
func (d *Decoder ) floatSlow (size int ) (float64 , error ) {
var (
buf [32 ]byte
offset = d .offset ()
)
str , err := d .numberAppend (buf [:0 ])
if err != nil {
return 0 , errors .Wrap (err , "number" )
}
if err := validateFloat (str , offset ); err != nil {
return 0 , err
}
val , err := strconv .ParseFloat (string (str ), size )
if err != nil {
return 0 , err
}
return val , nil
}
func validateFloat (str []byte , offset int ) error {
if len (str ) == 0 {
return errors .New ("empty" )
}
switch c := str [0 ]; floatDigits [c ] {
case dotInNumber , plusInNumber , expInNumber :
err := badToken (c , offset )
return errors .Wrapf (err , "leading %q" , c )
case minusInNumber :
err := badToken (c , offset )
return errors .Wrap (err , "double minus" )
case 0 :
if len (str ) >= 2 {
switch str [1 ] {
case 'e' , 'E' , '.' :
default :
err := badToken (str [1 ], offset +1 )
return errors .Wrap (err , "leading zero" )
}
}
}
dotPos := bytes .IndexByte (str , '.' )
if dotPos != -1 {
if dotPos == len (str )-1 {
return errors .New ("dot as last char" )
}
switch c := str [dotPos +1 ]; c {
case '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' :
default :
err := badToken (c , offset +dotPos +1 )
return errors .Wrap (err , "no digit after dot" )
}
}
return nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .