Skip to content

Commit

Permalink
feat(num): rework
Browse files Browse the repository at this point in the history
  • Loading branch information
ernado committed Nov 5, 2021
1 parent f22bec7 commit 941fb49
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 164 deletions.
22 changes: 4 additions & 18 deletions dec_num.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,34 @@ func (d *Decoder) Num() (Num, error) {

// NumTo decodes number into Num.
func (d *Decoder) NumTo(v Num) (Num, error) {
var str bool
switch d.Next() {
case String:
// Consume start of the string.
d.head++
str = true
case Number: // float or integer
default:
return v, errors.Errorf("unexpected %s", d.Next())
}
if d.reader == nil {
// Can use underlying buffer directly.
v.Value = d.number()
v = d.number()
} else {
buf, err := d.numberAppend(v.Value[:0])
buf, err := d.numberAppend(v[:0])
if err != nil {
return v, errors.Wrap(err, "decode")
}
v.Value = buf
v = buf
}

var dot bool
for _, c := range v.Value {
for _, c := range v {
if c != '.' {
continue
}
if dot {
return v, errors.New("multiple dots in number")
}
dot = true
break
}
if dot {
v.Format = NumFormatFloat
if str {
v.Format = NumFormatFloatStr
}
} else {
v.Format = NumFormatInt
if str {
v.Format = NumFormatIntStr
}
}

// TODO(ernado): Additional validity checks
Expand Down
10 changes: 2 additions & 8 deletions enc_num.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@ package jx

// Num encodes number.
func (e *Encoder) Num(v Num) {
if v.Format.Invalid() {
if len(v) == 0 {
e.Null()
return
}
if v.Format.Str() {
e.byte('"')
}
e.RawBytes(v.Value)
if v.Format.Str() {
e.byte('"')
}
e.RawBytes(v)
}
113 changes: 26 additions & 87 deletions num.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,85 +2,31 @@ package jx

import (
"bytes"
"strings"

"github.com/ogen-go/errors"
)

// NumFormat is format of Num.Value.
type NumFormat uint8

// Possible formats of Num.
const (
NumFormatInvalid NumFormat = iota // invalid or blank
NumFormatInt // 1234
NumFormatFloat // 1.234
NumFormatIntStr // "1234"
NumFormatFloatStr // "1.234"
)

// Float reports whether format is float.
func (f NumFormat) Float() bool {
return f == NumFormatFloat || f == NumFormatFloatStr
}

// Invalid reports whether format is invalid.
func (f NumFormat) Invalid() bool {
return f == NumFormatInvalid || f > NumFormatFloatStr
}

// Int reports whether format is integer.
func (f NumFormat) Int() bool {
return f == NumFormatInt || f == NumFormatIntStr
}

func (f NumFormat) String() string {
switch f {
case NumFormatInt:
return "integer"
case NumFormatFloat:
return "float"
case NumFormatIntStr:
return "integer string"
case NumFormatFloatStr:
return "float string"
default:
return "invalid"
}
}

// Str reports whether format is string integer or float.
func (f NumFormat) Str() bool {
return f == NumFormatIntStr || f == NumFormatFloatStr
}

// Num represents number, which can be raw json number or string of number.
//
// Zero value is invalid.
type Num struct {
// Format is number format for Value.
Format NumFormat
// Value is raw json of number, only digits or float characters.
//
// If Num is string number, Value does not contain quotes.
Value []byte
}
// Same as Raw, but with number invariants.
type Num []byte

func (n Num) dec() Decoder {
return Decoder{
buf: n.Value,
tail: len(n.Value),
buf: n,
tail: len(n),
}
}

func (n Num) floatAsInt() error {
if n.Format.Int() {
return nil
}
// 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.Value {
for _, c := range n {
if c == '.' {
dot = true
continue
Expand Down Expand Up @@ -123,35 +69,31 @@ func (n Num) Float64() (float64, error) {

// Equal reports whether numbers are strictly equal, including their formats.
func (n Num) Equal(v Num) bool {
if n.Format != v.Format {
return false
}
return bytes.Equal(n.Value, v.Value)
return bytes.Equal(n, v)
}

func (n Num) String() string {
if n.Format.Invalid() {
if len(n) == 0 {
return "<invalid>"
}
var b strings.Builder
if n.Format.Str() {
b.WriteByte('"')
}
_, _ = b.Write(n.Value)
if n.Format.Str() {
b.WriteByte('"')
}
return b.String()
return string(n)
}

// Sign reports sign of number.
//
// 0 is zero, 1 is positive, -1 is negative.
func (n Num) Sign() int {
if n.Format.Invalid() || len(n.Value) == 0 {
if len(n) == 0 {
return 0
}
switch n.Value[0] {
c := n[0]
if c == '"' {
if len(n) < 2 {
return 0
}
c = n[1]
}
switch c {
case '-':
return -1
case '0':
Expand All @@ -169,16 +111,13 @@ func (n Num) Negative() bool { return n.Sign() < 0 }

// Zero reports whether number is zero.
func (n Num) Zero() bool {
if n.Format.Invalid() || len(n.Value) == 0 {
if len(n) == 0 {
return false
}
if len(n.Value) == 1 {
return n.Value[0] == '0'
}
if n.Format.Int() {
return false
if len(n) == 1 {
return n[0] == '0'
}
for _, c := range n.Value {
for _, c := range n {
switch c {
case '.', '0':
continue
Expand Down
Loading

0 comments on commit 941fb49

Please sign in to comment.