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.
361 lines
7.3 KiB
361 lines
7.3 KiB
3 years ago
|
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
|
||
|
}
|