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.
191 lines
3.3 KiB
191 lines
3.3 KiB
3 years ago
|
package jx
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"math/big"
|
||
|
|
||
|
"github.com/go-faster/errors"
|
||
|
)
|
||
|
|
||
|
// Num represents number, which can be raw json number or number string.
|
||
|
//
|
||
|
// Same as Raw, but with number invariants.
|
||
|
//
|
||
|
// Examples:
|
||
|
// 123.45 // Str: false, IsInt: false
|
||
|
// "123.45" // Str: true, IsInt: false
|
||
|
// "12345" // Str: true, IsInt: true
|
||
|
// 12345 // Str: false, IsInt: true
|
||
|
type Num []byte
|
||
|
|
||
|
func (n Num) dec() Decoder {
|
||
|
head := 0
|
||
|
if n.Str() {
|
||
|
head = 1
|
||
|
}
|
||
|
return Decoder{
|
||
|
buf: n,
|
||
|
tail: len(n),
|
||
|
head: head,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Str reports whether Num is string number.
|
||
|
func (n Num) Str() bool {
|
||
|
return len(n) > 0 && n[0] == '"'
|
||
|
}
|
||
|
|
||
|
func (n Num) floatAsInt() error {
|
||
|
// Allow decoding floats with zero fractional, like 1.0 as 1.
|
||
|
var dot bool
|
||
|
for _, c := range n {
|
||
|
if c == '.' {
|
||
|
dot = true
|
||
|
continue
|
||
|
}
|
||
|
if !dot {
|
||
|
continue
|
||
|
}
|
||
|
switch c {
|
||
|
case '0', '"': // ok
|
||
|
default:
|
||
|
return errors.Wrap(badToken(c), "non-zero fractional part")
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Int64 decodes number as a signed 64-bit integer.
|
||
|
// Works on floats with zero fractional part.
|
||
|
func (n Num) Int64() (int64, error) {
|
||
|
if err := n.floatAsInt(); err != nil {
|
||
|
return 0, errors.Wrap(err, "float as int")
|
||
|
}
|
||
|
d := n.dec()
|
||
|
return d.Int64()
|
||
|
}
|
||
|
|
||
|
// IsInt reports whether number is integer.
|
||
|
func (n Num) IsInt() bool {
|
||
|
if len(n) == 0 {
|
||
|
return false
|
||
|
}
|
||
|
b := n
|
||
|
if b[0] == '"' {
|
||
|
b = b[1 : len(b)-1]
|
||
|
}
|
||
|
if b[0] == '-' {
|
||
|
b = b[1:]
|
||
|
}
|
||
|
for _, c := range b {
|
||
|
switch c {
|
||
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': // ok
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Uint64 decodes number as an unsigned 64-bit integer.
|
||
|
// Works on floats with zero fractional part.
|
||
|
func (n Num) Uint64() (uint64, error) {
|
||
|
if err := n.floatAsInt(); err != nil {
|
||
|
return 0, errors.Wrap(err, "float as int")
|
||
|
}
|
||
|
d := n.dec()
|
||
|
return d.UInt64()
|
||
|
}
|
||
|
|
||
|
// Float64 decodes number as 64-bit floating point.
|
||
|
func (n Num) Float64() (float64, error) {
|
||
|
d := n.dec()
|
||
|
return d.Float64()
|
||
|
}
|
||
|
|
||
|
// Equal reports whether numbers are strictly equal, including their formats.
|
||
|
func (n Num) Equal(v Num) bool {
|
||
|
return bytes.Equal(n, v)
|
||
|
}
|
||
|
|
||
|
func (n Num) String() string {
|
||
|
if len(n) == 0 {
|
||
|
return "<invalid>"
|
||
|
}
|
||
|
return string(n)
|
||
|
}
|
||
|
|
||
|
// Format implements fmt.Formatter.
|
||
|
func (n Num) Format(f fmt.State, verb rune) {
|
||
|
switch verb {
|
||
|
case 's', 'v':
|
||
|
_, _ = f.Write(n)
|
||
|
case 'd':
|
||
|
d, err := n.Int64()
|
||
|
if err != nil {
|
||
|
fmt.Fprintf(f, "%%!invalid(Num=%s)", n.String())
|
||
|
return
|
||
|
}
|
||
|
v := big.NewInt(d)
|
||
|
v.Format(f, verb)
|
||
|
case 'f':
|
||
|
d, err := n.Float64()
|
||
|
if err != nil {
|
||
|
fmt.Fprintf(f, "%%!invalid(Num=%s)", n.String())
|
||
|
return
|
||
|
}
|
||
|
v := big.NewFloat(d)
|
||
|
v.Format(f, verb)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sign reports sign of number.
|
||
|
//
|
||
|
// 0 is zero, 1 is positive, -1 is negative.
|
||
|
func (n Num) Sign() int {
|
||
|
if len(n) == 0 {
|
||
|
return 0
|
||
|
}
|
||
|
c := n[0]
|
||
|
if c == '"' {
|
||
|
if len(n) < 2 {
|
||
|
return 0
|
||
|
}
|
||
|
c = n[1]
|
||
|
}
|
||
|
switch c {
|
||
|
case '-':
|
||
|
return -1
|
||
|
case '0':
|
||
|
return 0
|
||
|
default:
|
||
|
return 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Positive reports whether number is positive.
|
||
|
func (n Num) Positive() bool { return n.Sign() > 0 }
|
||
|
|
||
|
// Negative reports whether number is negative.
|
||
|
func (n Num) Negative() bool { return n.Sign() < 0 }
|
||
|
|
||
|
// Zero reports whether number is zero.
|
||
|
func (n Num) Zero() bool {
|
||
|
if len(n) == 0 {
|
||
|
return false
|
||
|
}
|
||
|
if len(n) == 1 {
|
||
|
return n[0] == '0'
|
||
|
}
|
||
|
for _, c := range n {
|
||
|
switch c {
|
||
|
case '.', '0', '-':
|
||
|
continue
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|