Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR to #21 #23

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func main() {
]
}`

err := jscan.Scan(j, func(i *jscan.Iterator[string]) (err bool) {
err := jscan.Scan(j, func(i *jscan.Iterator[string]) (exit bool) {
fmt.Printf("%q:\n", i.Pointer())
fmt.Printf("├─ valueType: %s\n", i.ValueType().String())
if k := i.Key(); k != "" {
Expand Down
2 changes: 1 addition & 1 deletion bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type Stats struct {
func MustCalcStatsJscan(p *jscan.Parser[[]byte], str []byte) (s Stats) {
if err := p.Scan(
str,
func(i *jscan.Iterator[[]byte]) (err bool) {
func(i *jscan.Iterator[[]byte]) (exit bool) {
if i.KeyIndex() != -1 {
// Calculate key length excluding the quotes
l := i.KeyIndexEnd() - i.KeyIndex() - 2
Expand Down
6 changes: 3 additions & 3 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func ExampleScan() {
]
}`

err := jscan.Scan(j, func(i *jscan.Iterator[string]) (err bool) {
err := jscan.Scan(j, func(i *jscan.Iterator[string]) (exit bool) {
fmt.Printf("%q:\n", i.Pointer())
fmt.Printf("├─ valueType: %s\n", i.ValueType().String())
if k := i.Key(); k != "" {
Expand Down Expand Up @@ -157,7 +157,7 @@ func ExampleScan() {
func ExampleScan_error_handling() {
j := `"something...`

err := jscan.Scan(j, func(i *jscan.Iterator[string]) (err bool) {
err := jscan.Scan(j, func(i *jscan.Iterator[string]) (exit bool) {
fmt.Println("This shall never be executed")
return false // No Error, resume scanning
})
Expand Down Expand Up @@ -203,7 +203,7 @@ func ExampleScan_decode2DIntArray() {

s := [][]int{}
currentIndex := 0
err := jscan.Scan(j, func(i *jscan.Iterator[string]) (err bool) {
err := jscan.Scan(j, func(i *jscan.Iterator[string]) (exit bool) {
switch i.Level() {
case 0: // Root array
return i.ValueType() != jscan.ValueTypeArray
Expand Down
16 changes: 8 additions & 8 deletions internal/jsonnum/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func BenchmarkValid(b *testing.B) {
var err bool
var exit bool
for _, bb := range []string{
"0,",
"1e10,",
Expand All @@ -21,7 +21,7 @@ func BenchmarkValid(b *testing.B) {
b.Run("", func(b *testing.B) {
b.Run("string", func(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err = jsonnum.ReadNumber(bb); err {
if _, exit = jsonnum.ReadNumber(bb); exit {
b.Fatal("unexpected error")
}
}
Expand All @@ -31,7 +31,7 @@ func BenchmarkValid(b *testing.B) {
bb := []byte(bb)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err = jsonnum.ReadNumber(bb); err {
if _, exit = jsonnum.ReadNumber(bb); exit {
b.Fatal("unexpected error")
}
}
Expand All @@ -55,28 +55,28 @@ func BenchmarkInvalid(b *testing.B) {
"0.1234567890e",
} {
b.Run("", func(b *testing.B) {
// This err will not be checked since "01" is not technically wrong
// This exit value will not be checked since "01" is not technically wrong
// according to jsonnum.ReadNumber, it would return ("1", false) instead.
// All inputs are already tested in TestReadNumberErr and TestReadNumberZero.
var err bool
var exit bool
var remainderString string
var remainderBytes []byte

b.Run("string", func(b *testing.B) {
for i := 0; i < b.N; i++ {
remainderString, err = jsonnum.ReadNumber(bb)
remainderString, exit = jsonnum.ReadNumber(bb)
}
})

b.Run("bytes", func(b *testing.B) {
bb := []byte(bb)
b.ResetTimer()
for i := 0; i < b.N; i++ {
remainderBytes, err = jsonnum.ReadNumber(bb)
remainderBytes, exit = jsonnum.ReadNumber(bb)
}
})

runtime.KeepAlive(err)
runtime.KeepAlive(exit)
runtime.KeepAlive(remainderString)
runtime.KeepAlive(remainderBytes)
})
Expand Down
2 changes: 1 addition & 1 deletion internal/jsonnum/jsonnum.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package jsonnum

// ReadNumber returns the index of the end of the number value
// and err=true if a syntax error was encountered.
func ReadNumber[S ~string | ~[]byte](s S) (trailing S, err bool) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This boolean actually indicates whether there was an error, please don't change it in internal/jsonnum

func ReadNumber[S ~string | ~[]byte](s S) (trailing S, exit bool) {
var i int

if s[0] == '-' {
Expand Down
34 changes: 17 additions & 17 deletions jscan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ func testStrictOK[S ~string | ~[]byte](t *testing.T, input S) {
t.Run("Scan", func(t *testing.T) {
err := jscan.Scan(
string(input),
func(i *jscan.Iterator[string]) (err bool) { return false },
func(i *jscan.Iterator[string]) (exit bool) { return false },
)
require.False(t, err.IsErr())
})
t.Run("ScanOne", func(t *testing.T) {
_, err := jscan.ScanOne(
string(input),
func(i *jscan.Iterator[string]) (err bool) { return false },
func(i *jscan.Iterator[string]) (exit bool) { return false },
)
require.False(t, err.IsErr())
})
Expand Down Expand Up @@ -117,13 +117,13 @@ func testStrictErr[S ~string | ~[]byte](t *testing.T, input S) {
t.Run(testDataType(input), func(t *testing.T) {
t.Run("ParserScan", func(t *testing.T) {
err := jscan.NewParser[S](1024).Scan(
input, func(i *jscan.Iterator[S]) (err bool) { return false },
input, func(i *jscan.Iterator[S]) (exit bool) { return false },
)
require.True(t, err.IsErr())
})
t.Run("Scan", func(t *testing.T) {
err := jscan.Scan(
input, func(i *jscan.Iterator[S]) (err bool) { return false },
input, func(i *jscan.Iterator[S]) (exit bool) { return false },
)
require.True(t, err.IsErr())
})
Expand Down Expand Up @@ -1028,7 +1028,7 @@ func testError[S ~string | ~[]byte](t *testing.T, td ErrorTest) {
t.Run("Scan", func(t *testing.T) {
err := jscan.Scan[S](
S(td.input),
func(i *jscan.Iterator[S]) (err bool) { return false },
func(i *jscan.Iterator[S]) (exit bool) { return false },
)
require.Equal(t, td.expect, err.Error())
require.True(t, err.IsErr())
Expand All @@ -1037,7 +1037,7 @@ func testError[S ~string | ~[]byte](t *testing.T, td ErrorTest) {
p := jscan.NewParser[S](64)
err := p.Scan(
S(td.input),
func(i *jscan.Iterator[S]) (err bool) { return false },
func(i *jscan.Iterator[S]) (exit bool) { return false },
)
require.Equal(t, td.expect, err.Error())
require.True(t, err.IsErr())
Expand Down Expand Up @@ -1228,7 +1228,7 @@ func testControlCharacters[S ~string | ~[]byte](t *testing.T, input S, expectErr
p := jscan.NewParser[S](64)
_, err := p.ScanOne(
S(input),
func(i *jscan.Iterator[S]) (err bool) { return false },
func(i *jscan.Iterator[S]) (exit bool) { return false },
)
require.Equal(t, expectErr, err.Error())
require.True(t, err.IsErr())
Expand All @@ -1238,7 +1238,7 @@ func testControlCharacters[S ~string | ~[]byte](t *testing.T, input S, expectErr
p := jscan.NewParser[S](64)
err := p.Scan(
S(input),
func(i *jscan.Iterator[S]) (err bool) { return false },
func(i *jscan.Iterator[S]) (exit bool) { return false },
)
require.Equal(t, expectErr, err.Error())
require.True(t, err.IsErr())
Expand All @@ -1247,7 +1247,7 @@ func testControlCharacters[S ~string | ~[]byte](t *testing.T, input S, expectErr
t.Run("ScanOne", func(t *testing.T) {
_, err := jscan.ScanOne[S](
S(input),
func(i *jscan.Iterator[S]) (err bool) { return false },
func(i *jscan.Iterator[S]) (exit bool) { return false },
)
require.Equal(t, expectErr, err.Error())
require.True(t, err.IsErr())
Expand All @@ -1256,7 +1256,7 @@ func testControlCharacters[S ~string | ~[]byte](t *testing.T, input S, expectErr
t.Run("Scan", func(t *testing.T) {
err := jscan.Scan[S](
S(input),
func(i *jscan.Iterator[S]) (err bool) { return false },
func(i *jscan.Iterator[S]) (exit bool) { return false },
)
require.Equal(t, expectErr, err.Error())
require.True(t, err.IsErr())
Expand All @@ -1276,7 +1276,7 @@ func testReturnErrorTrue[S ~string | ~[]byte](t *testing.T, input S) {
j := 0
err := jscan.Scan(
input,
func(i *jscan.Iterator[S]) (err bool) {
func(i *jscan.Iterator[S]) (exit bool) {
require.Equal(t, jscan.ValueTypeObject, i.ValueType())
j++
return true // Expect immediate return
Expand Down Expand Up @@ -1330,7 +1330,7 @@ func testScanOne[S ~string | ~[]byte](
for i, c, s := 0, 1, s; len(s) > 0; c++ {
trailing, err := jscan.ScanOne(
s,
func(itr *jscan.Iterator[S]) (err bool) {
func(itr *jscan.Iterator[S]) (exit bool) {
require.Equal(t, expect[i], TypeValuePair{
Type: itr.ValueType(),
Value: string(itr.Value()),
Expand Down Expand Up @@ -1434,7 +1434,7 @@ func testStrings[S ~string | ~[]byte](t *testing.T, input S) {
t.Run("ParserScan", func(t *testing.T) {
p := jscan.NewParser[S](64)
c := 0
err := p.Scan(inputObject, func(i *jscan.Iterator[S]) (err bool) {
err := p.Scan(inputObject, func(i *jscan.Iterator[S]) (exit bool) {
if c < 1 {
c++
return false
Expand All @@ -1451,7 +1451,7 @@ func testStrings[S ~string | ~[]byte](t *testing.T, input S) {
c := 0
trailing, err := p.ScanOne(
inputObject,
func(i *jscan.Iterator[S]) (err bool) {
func(i *jscan.Iterator[S]) (exit bool) {
if c < 1 {
c++
return false
Expand All @@ -1469,7 +1469,7 @@ func testStrings[S ~string | ~[]byte](t *testing.T, input S) {
c := 0
err := jscan.Scan[S](
inputObject,
func(i *jscan.Iterator[S]) (err bool) {
func(i *jscan.Iterator[S]) (exit bool) {
if c < 1 {
c++
return false
Expand All @@ -1486,7 +1486,7 @@ func testStrings[S ~string | ~[]byte](t *testing.T, input S) {
c := 0
trailing, err := jscan.ScanOne[S](
inputObject,
func(i *jscan.Iterator[S]) (err bool) {
func(i *jscan.Iterator[S]) (exit bool) {
if c < 1 {
c++
return false
Expand Down Expand Up @@ -1523,7 +1523,7 @@ func testDataType[S ~string | ~[]byte](input S) string {

func TestIndexEnd(t *testing.T) {
c := 0
err := jscan.Scan(`{"x":["y"]}`, func(i *jscan.Iterator[string]) (err bool) {
err := jscan.Scan(`{"x":["y"]}`, func(i *jscan.Iterator[string]) (exit bool) {
switch c {
case 0:
require.Equal(t, jscan.ValueTypeObject, i.ValueType())
Expand Down
22 changes: 13 additions & 9 deletions scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (

// ScanOne calls fn for every encountered value including objects and arrays.
// When an object or array is encountered fn will also be called for each of its
// member and element values.
// member and element values. If fn returns true, ScanOne immediately stops and
// returns ErrorCodeCallback.
//
// Unlike Scan, ScanOne doesn't return ErrorCodeUnexpectedToken when
// it encounters anything other than EOF after reading a valid JSON value.
Expand All @@ -21,7 +22,7 @@ import (
//
// WARNING: Don't use or alias *Iterator[S] after fn returns!
func ScanOne[S ~string | ~[]byte](
s S, fn func(*Iterator[S]) (err bool),
s S, fn func(*Iterator[S]) (exit bool),
) (trailing S, err Error[S]) {
var i *Iterator[S]
switch any(s).(type) {
Expand All @@ -41,15 +42,16 @@ func ScanOne[S ~string | ~[]byte](

// Scan calls fn for every encountered value including objects and arrays.
// When an object or array is encountered fn will also be called for each of its
// member and element values.
// member and element values. If fn returns true, ScanOne immediately stops and
flrdv marked this conversation as resolved.
Show resolved Hide resolved
// returns ErrorCodeCallback.
//
// Unlike (*Parser).Scan this function will take an iterator instance
// from a global iterator pool and can therefore be less efficient.
// Consider reusing a Parser instance instead.
//
// WARNING: Don't use or alias *Iterator[S] after fn returns!
func Scan[S ~string | ~[]byte](
s S, fn func(*Iterator[S]) (err bool),
s S, fn func(*Iterator[S]) (exit bool),
) (err Error[S]) {
var i *Iterator[S]
switch any(s).(type) {
Expand Down Expand Up @@ -98,7 +100,8 @@ func NewParser[S ~string | ~[]byte](preallocStackFrames int) *Parser[S] {

// ScanOne calls fn for every encountered value including objects and arrays.
// When an object or array is encountered fn will also be called for each of its
// member and element values.
// member and element values. If fn returns true, ScanOne immediately stops and
// returns ErrorCodeCallback.
//
// Unlike Scan, ScanOne doesn't return ErrorCodeUnexpectedToken when
// it encounters anything other than EOF after reading a valid JSON value.
Expand All @@ -108,7 +111,7 @@ func NewParser[S ~string | ~[]byte](preallocStackFrames int) *Parser[S] {
//
// WARNING: Don't use or alias *Iterator[S] after fn returns!
func (p *Parser[S]) ScanOne(
s S, fn func(*Iterator[S]) (err bool),
s S, fn func(*Iterator[S]) (exit bool),
) (trailing S, err Error[S]) {
reset(p.i)
p.i.src = s
Expand All @@ -117,11 +120,12 @@ func (p *Parser[S]) ScanOne(

// Scan calls fn for every encountered value including objects and arrays.
// When an object or array is encountered fn will also be called for each of its
// member and element values.
// member and element values. If fn returns true, ScanOne immediately stops and
flrdv marked this conversation as resolved.
Show resolved Hide resolved
// returns ErrorCodeCallback.
//
// WARNING: Don't use or alias *Iterator[S] after fn returns!
func (p *Parser[S]) Scan(
s S, fn func(*Iterator[S]) (err bool),
s S, fn func(*Iterator[S]) (exit bool),
) Error[S] {
reset(p.i)
p.i.src = s
Expand All @@ -144,7 +148,7 @@ func (p *Parser[S]) Scan(
// scan calls fn for every value encountered.
// Returns the remainder of i.src and an error if any is encountered.
func scan[S ~string | ~[]byte](
i *Iterator[S], fn func(*Iterator[S]) (err bool),
i *Iterator[S], fn func(*Iterator[S]) (exit bool),
) (S, Error[S]) {
var (
rollback S // Used as fallback for error report
Expand Down