You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
360 lines
7.3 KiB
360 lines
7.3 KiB
package jx |
|
|
|
import ( |
|
"bytes" |
|
"io" |
|
"math/big" |
|
"strconv" |
|
|
|
"github.com/go-faster/errors" |
|
) |
|
|
|
var pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000} |
|
|
|
var floatDigits []int8 |
|
|
|
const invalidCharForNumber = int8(-1) |
|
const endOfNumber = int8(-2) |
|
const dotInNumber = int8(-3) |
|
const maxFloat64 = 1<<63 - 1 |
|
|
|
func init() { |
|
floatDigits = make([]int8, 256) |
|
for i := 0; i < len(floatDigits); i++ { |
|
floatDigits[i] = invalidCharForNumber |
|
} |
|
for i := int8('0'); i <= int8('9'); i++ { |
|
floatDigits[i] = i - int8('0') |
|
} |
|
floatDigits[','] = endOfNumber |
|
floatDigits[']'] = endOfNumber |
|
floatDigits['}'] = endOfNumber |
|
floatDigits[' '] = endOfNumber |
|
floatDigits['\t'] = endOfNumber |
|
floatDigits['\n'] = endOfNumber |
|
floatDigits['.'] = dotInNumber |
|
} |
|
|
|
// BigFloat read big.Float |
|
func (d *Decoder) BigFloat() (*big.Float, error) { |
|
str, err := d.numberAppend(nil) |
|
if err != nil { |
|
return nil, errors.Wrap(err, "number") |
|
} |
|
prec := 64 |
|
if len(str) > prec { |
|
prec = len(str) |
|
} |
|
val, _, err := big.ParseFloat(string(str), 10, uint(prec), big.ToZero) |
|
if err != nil { |
|
return nil, errors.Wrap(err, "float") |
|
} |
|
return val, nil |
|
} |
|
|
|
// BigInt read big.Int |
|
func (d *Decoder) BigInt() (*big.Int, error) { |
|
str, err := d.numberAppend(nil) |
|
if err != nil { |
|
return nil, errors.Wrap(err, "number") |
|
} |
|
v := big.NewInt(0) |
|
var ok bool |
|
if v, ok = v.SetString(string(str), 10); !ok { |
|
return nil, errors.New("invalid") |
|
} |
|
return v, nil |
|
} |
|
|
|
// Float32 reads float32 value. |
|
func (d *Decoder) Float32() (float32, error) { |
|
c, err := d.more() |
|
if err != nil { |
|
return 0, errors.Wrap(err, "byte") |
|
} |
|
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 |
|
// First char. |
|
if i == d.tail { |
|
return d.f32Slow() |
|
} |
|
c := d.buf[i] |
|
i++ |
|
ind := floatDigits[c] |
|
switch ind { |
|
case invalidCharForNumber: |
|
return d.f32Slow() |
|
case endOfNumber: |
|
return 0, errors.New("empty") |
|
case dotInNumber: |
|
return 0, errors.New("leading dot") |
|
case 0: |
|
if i == d.tail { |
|
return d.f32Slow() |
|
} |
|
c = d.buf[i] |
|
switch c { |
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': |
|
return 0, errors.New("leading zero") |
|
} |
|
} |
|
value := uint64(ind) |
|
// Chars before dot. |
|
NonDecimalLoop: |
|
for ; i < d.tail; i++ { |
|
c = d.buf[i] |
|
ind := floatDigits[c] |
|
switch ind { |
|
case invalidCharForNumber: |
|
return d.f32Slow() |
|
case endOfNumber: |
|
d.head = i |
|
return float32(value), nil |
|
case dotInNumber: |
|
break NonDecimalLoop |
|
} |
|
if value > uint64SafeToMultiple10 { |
|
return d.f32Slow() |
|
} |
|
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; |
|
} |
|
// Chars after dot. |
|
if c == '.' { |
|
i++ |
|
decimalPlaces := 0 |
|
if i == d.tail { |
|
return d.f32Slow() |
|
} |
|
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 |
|
} |
|
// too many decimal places |
|
return d.f32Slow() |
|
case invalidCharForNumber, dotInNumber: |
|
return d.f32Slow() |
|
} |
|
decimalPlaces++ |
|
if value > uint64SafeToMultiple10 { |
|
return d.f32Slow() |
|
} |
|
value = (value << 3) + (value << 1) + uint64(ind) |
|
} |
|
} |
|
return d.f32Slow() |
|
} |
|
|
|
func (d *Decoder) number() []byte { |
|
start := d.head |
|
buf := d.buf[d.head:d.tail] |
|
for i, c := range buf { |
|
switch c { |
|
case '+', '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': |
|
continue |
|
default: |
|
// End of number. |
|
d.head += i |
|
return d.buf[start:d.head] |
|
} |
|
} |
|
// Buffer is number within head:tail. |
|
d.head = d.tail |
|
return d.buf[start:d.tail] |
|
} |
|
|
|
func (d *Decoder) numberAppend(b []byte) ([]byte, error) { |
|
for { |
|
b = append(b, d.number()...) |
|
if d.head != d.tail { |
|
return b, nil |
|
} |
|
if err := d.read(); err != nil { |
|
if err == io.EOF { |
|
return b, nil |
|
} |
|
return b, err |
|
} |
|
} |
|
} |
|
|
|
const ( |
|
size32 = 32 |
|
size64 = 64 |
|
) |
|
|
|
func (d *Decoder) f32Slow() (float32, error) { |
|
v, err := d.floatSlow(size32) |
|
if err != nil { |
|
return 0, err |
|
} |
|
return float32(v), err |
|
} |
|
|
|
// Float64 read float64 |
|
func (d *Decoder) Float64() (float64, error) { |
|
c, err := d.more() |
|
if err != nil { |
|
return 0, errors.Wrap(err, "byte") |
|
} |
|
switch c { |
|
case '-': |
|
v, err := d.positiveFloat64() |
|
if err != nil { |
|
return 0, err |
|
} |
|
return -v, err |
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': |
|
d.unread() |
|
return d.positiveFloat64() |
|
default: |
|
return 0, badToken(c) |
|
} |
|
} |
|
|
|
func (d *Decoder) positiveFloat64() (float64, error) { |
|
i := d.head |
|
// First char. |
|
if i == d.tail { |
|
return d.float64Slow() |
|
} |
|
c := d.buf[i] |
|
i++ |
|
ind := floatDigits[c] |
|
switch ind { |
|
case invalidCharForNumber: |
|
return d.float64Slow() |
|
case endOfNumber: |
|
return 0, errors.New("empty") |
|
case dotInNumber: |
|
return 0, errors.New("leading dot") |
|
case 0: |
|
if i == d.tail { |
|
return d.float64Slow() |
|
} |
|
c = d.buf[i] |
|
switch c { |
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': |
|
return 0, errors.New("leading zero") |
|
} |
|
} |
|
value := uint64(ind) |
|
// Chars before dot. |
|
NonDecimal: |
|
for ; i < d.tail; i++ { |
|
c = d.buf[i] |
|
ind := floatDigits[c] |
|
switch ind { |
|
case invalidCharForNumber: |
|
return d.float64Slow() |
|
case endOfNumber: |
|
d.head = i |
|
return float64(value), nil |
|
case dotInNumber: |
|
break NonDecimal |
|
} |
|
if value > uint64SafeToMultiple10 { |
|
return d.float64Slow() |
|
} |
|
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; |
|
} |
|
// chars after dot |
|
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 |
|
} |
|
// too many decimal places |
|
return d.float64Slow() |
|
case invalidCharForNumber, dotInNumber: |
|
return d.float64Slow() |
|
} |
|
decimalPlaces++ |
|
// Not checking for uint64SafeToMultiple10 here because |
|
// if condition is positive value multiplied by 10 is |
|
// guaranteed to be bigger than maxFloat64. |
|
value = (value << 3) + (value << 1) + uint64(ind) |
|
if value > maxFloat64 { |
|
return d.float64Slow() |
|
} |
|
} |
|
} |
|
return d.float64Slow() |
|
} |
|
|
|
func (d *Decoder) floatSlow(size int) (float64, error) { |
|
var buf [32]byte |
|
|
|
str, err := d.numberAppend(buf[:0]) |
|
if err != nil { |
|
return 0, errors.Wrap(err, "number") |
|
} |
|
if err := validateFloat(str); err != nil { |
|
return 0, errors.Wrap(err, "invalid") |
|
} |
|
|
|
val, err := strconv.ParseFloat(string(str), size) |
|
if err != nil { |
|
return 0, err |
|
} |
|
|
|
return val, nil |
|
} |
|
|
|
func (d *Decoder) float64Slow() (float64, error) { return d.floatSlow(size64) } |
|
|
|
func validateFloat(str []byte) error { |
|
// strconv.ParseFloat is not validating `1.` or `1.e1` |
|
if len(str) == 0 { |
|
return errors.New("empty") |
|
} |
|
if str[0] == '-' { |
|
return errors.New("double minus") |
|
} |
|
if len(str) >= 2 && str[0] == '0' { |
|
switch str[1] { |
|
case 'e', 'E', '.': |
|
default: |
|
return errors.New("leading zero") |
|
} |
|
} |
|
dotPos := bytes.IndexByte(str, '.') |
|
if dotPos != -1 { |
|
if dotPos == len(str)-1 { |
|
return errors.New("dot as last char") |
|
} |
|
switch str[dotPos+1] { |
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': |
|
default: |
|
return errors.New("no digit after dot") |
|
} |
|
} |
|
return nil |
|
}
|
|
|