Skip to content

Commit

Permalink
Use int64 for object or array length (#27)
Browse files Browse the repository at this point in the history
WARNING: This commit includes breaking changes.

When processing JSON as a stream, it is conceivable that
the total number of elements in a JSON object or array
exceeds a 32-bit integer.

Switch from int to int64 for a similar reason as why io.Copy
returns int64 instead of int.

Note that the depth still uses an int because the stack must
be representable in memory, which is limited to the native
integer width of the platform. This is similar to how
io.Reader.Read returns an int because the length of a []byte
cannot possibly be larger than the largest int.
  • Loading branch information
dsnet authored Apr 12, 2024
1 parent a256f16 commit 2f02d56
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 10 deletions.
10 changes: 8 additions & 2 deletions arshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3666,7 +3666,10 @@ func TestMarshal(t *testing.T) {
want: `[null,{},null,{},null,null,{},{},null,{},null,null,{},"LAST"]`,
opts: []Options{
WithMarshalers(func() *Marshalers {
type P [2]int
type P struct {
D int
N int64
}
type PV struct {
P P
V any
Expand Down Expand Up @@ -7714,7 +7717,10 @@ func TestUnmarshal(t *testing.T) {
}),
opts: []Options{
WithUnmarshalers(func() *Unmarshalers {
type P [2]int
type P struct {
D int
N int64
}
type PV struct {
P P
V any
Expand Down
2 changes: 1 addition & 1 deletion jsontext/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ func (d *Decoder) StackDepth() int {
// Each name and value in a JSON object is counted separately,
// so the effective number of members would be half the length.
// A complete JSON object must have an even length.
func (d *Decoder) StackIndex(i int) (Kind, int) {
func (d *Decoder) StackIndex(i int) (Kind, int64) {
// NOTE: Keep in sync with Encoder.StackIndex.
switch s := d.s.Tokens.index(i); {
case i > 0 && s.isObject():
Expand Down
2 changes: 1 addition & 1 deletion jsontext/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ func (e *Encoder) StackDepth() int {
// Each name and value in a JSON object is counted separately,
// so the effective number of members would be half the length.
// A complete JSON object must have an even length.
func (e *Encoder) StackIndex(i int) (Kind, int) {
func (e *Encoder) StackIndex(i int) (Kind, int64) {
// NOTE: Keep in sync with Decoder.StackIndex.
switch s := e.s.Tokens.index(i); {
case i > 0 && s.isObject():
Expand Down
6 changes: 3 additions & 3 deletions jsontext/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (m *stateMachine) index(i int) *stateEntry {

// DepthLength reports the current nested depth and
// the length of the last JSON object or array.
func (m stateMachine) DepthLength() (int, int) {
func (m stateMachine) DepthLength() (int, int64) {
return m.Depth(), m.Last.Length()
}

Expand Down Expand Up @@ -342,8 +342,8 @@ const (

// Length reports the number of elements in the JSON object or array.
// Each name and value in an object entry is treated as a separate element.
func (e stateEntry) Length() int {
return int(e & stateCountMask)
func (e stateEntry) Length() int64 {
return int64(e & stateCountMask)
}

// isObject reports whether this is a JSON object.
Expand Down
6 changes: 3 additions & 3 deletions jsontext/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestStateMachine(t *testing.T) {
type operation any
type (
// stackLengths checks the results of stateEntry.length accessors.
stackLengths []int
stackLengths []int64

// appendTokens is sequence of token kinds to append where
// none of them are expected to fail.
Expand Down Expand Up @@ -156,12 +156,12 @@ func TestStateMachine(t *testing.T) {
for _, op := range ops {
switch op := op.(type) {
case stackLengths:
var got []int
var got []int64
for i := 0; i < state.Depth(); i++ {
e := state.index(i)
got = append(got, e.Length())
}
want := []int(op)
want := []int64(op)
if !reflect.DeepEqual(got, want) {
t.Fatalf("%s: stack lengths mismatch:\ngot %v\nwant %v", sequence, got, want)
}
Expand Down

0 comments on commit 2f02d56

Please sign in to comment.