mirror of https://github.com/goccy/go-json.git
Fix Compact
This commit is contained in:
parent
901128a986
commit
cff09d2448
|
@ -34,7 +34,7 @@ func trueBytes(s *stream) error {
|
||||||
|
|
||||||
func falseBytes(s *stream) error {
|
func falseBytes(s *stream) error {
|
||||||
if s.cursor+4 >= s.length {
|
if s.cursor+4 >= s.length {
|
||||||
if s.read() {
|
if !s.read() {
|
||||||
return errInvalidCharacter(s.char(), "bool(false)", s.totalOffset())
|
return errInvalidCharacter(s.char(), "bool(false)", s.totalOffset())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,21 +67,25 @@ func (s *stream) read() bool {
|
||||||
if n < readChunkSize || err == io.EOF {
|
if n < readChunkSize || err == io.EOF {
|
||||||
s.allRead = true
|
s.allRead = true
|
||||||
}
|
}
|
||||||
totalSize := s.length + int64(n) + 1
|
// extend buffer (2) is protect ( s.cursor++ x2 )
|
||||||
|
// e.g.) line 85 in decode_interface.go
|
||||||
|
const extendBufLength = int64(2)
|
||||||
|
|
||||||
|
totalSize := s.length + int64(n) + extendBufLength
|
||||||
if totalSize > readChunkSize {
|
if totalSize > readChunkSize {
|
||||||
newBuf := make([]byte, totalSize)
|
newBuf := make([]byte, totalSize)
|
||||||
copy(newBuf, s.buf)
|
copy(newBuf, s.buf)
|
||||||
copy(newBuf[s.length:], buf)
|
copy(newBuf[s.length:], buf)
|
||||||
s.buf = newBuf
|
s.buf = newBuf
|
||||||
s.length = totalSize - 1
|
s.length = totalSize - extendBufLength
|
||||||
} else if s.length > 0 {
|
} else if s.length > 0 {
|
||||||
copy(buf[s.length:], buf)
|
copy(buf[s.length:], buf)
|
||||||
copy(buf, s.buf[:s.length])
|
copy(buf, s.buf[:s.length])
|
||||||
s.buf = buf
|
s.buf = buf
|
||||||
s.length = totalSize - 1
|
s.length = totalSize - extendBufLength
|
||||||
} else {
|
} else {
|
||||||
s.buf = buf
|
s.buf = buf
|
||||||
s.length = totalSize - 1
|
s.length = totalSize - extendBufLength
|
||||||
}
|
}
|
||||||
s.offset += s.cursor
|
s.offset += s.cursor
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
|
|
12
encode_vm.go
12
encode_vm.go
|
@ -84,10 +84,20 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
typ = typ.Elem()
|
typ = typ.Elem()
|
||||||
}
|
}
|
||||||
e.indent = ifaceCode.indent
|
e.indent = ifaceCode.indent
|
||||||
c, err := e.compile(typ, ifaceCode.root, e.enabledIndent)
|
var c *opcode
|
||||||
|
if typ.Kind() == reflect.Map {
|
||||||
|
code, err := e.compileMap(typ, false, ifaceCode.root, e.enabledIndent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
c = code
|
||||||
|
} else {
|
||||||
|
code, err := e.compile(typ, ifaceCode.root, e.enabledIndent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c = code
|
||||||
|
}
|
||||||
c.ptr = uintptr(header.ptr)
|
c.ptr = uintptr(header.ptr)
|
||||||
c.beforeLastCode().next = code.next
|
c.beforeLastCode().next = code.next
|
||||||
code = c
|
code = c
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package json
|
||||||
|
|
||||||
|
func NewSyntaxError(msg string, offset int64) *SyntaxError {
|
||||||
|
return &SyntaxError{
|
||||||
|
msg: msg,
|
||||||
|
Offset: offset,
|
||||||
|
}
|
||||||
|
}
|
181
json_test.go
181
json_test.go
|
@ -2,6 +2,9 @@ package json_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
|
@ -116,3 +119,181 @@ func TestIndent(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests of a large random structure.
|
||||||
|
|
||||||
|
func TestCompactBig(t *testing.T) {
|
||||||
|
initBig()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := json.Compact(&buf, jsonBig); err != nil {
|
||||||
|
t.Fatalf("Compact: %v", err)
|
||||||
|
}
|
||||||
|
b := buf.Bytes()
|
||||||
|
if !bytes.Equal(b, jsonBig) {
|
||||||
|
t.Error("Compact(jsonBig) != jsonBig")
|
||||||
|
diff(t, b, jsonBig)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndentBig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
initBig()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := json.Indent(&buf, jsonBig, "", "\t"); err != nil {
|
||||||
|
t.Fatalf("Indent1: %v", err)
|
||||||
|
}
|
||||||
|
b := buf.Bytes()
|
||||||
|
if len(b) == len(jsonBig) {
|
||||||
|
// jsonBig is compact (no unnecessary spaces);
|
||||||
|
// indenting should make it bigger
|
||||||
|
t.Fatalf("Indent(jsonBig) did not get bigger")
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be idempotent
|
||||||
|
var buf1 bytes.Buffer
|
||||||
|
if err := json.Indent(&buf1, b, "", "\t"); err != nil {
|
||||||
|
t.Fatalf("Indent2: %v", err)
|
||||||
|
}
|
||||||
|
b1 := buf1.Bytes()
|
||||||
|
if !bytes.Equal(b1, b) {
|
||||||
|
t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)")
|
||||||
|
diff(t, b1, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// should get back to original
|
||||||
|
buf1.Reset()
|
||||||
|
if err := json.Compact(&buf1, b); err != nil {
|
||||||
|
t.Fatalf("Compact: %v", err)
|
||||||
|
}
|
||||||
|
b1 = buf1.Bytes()
|
||||||
|
if !bytes.Equal(b1, jsonBig) {
|
||||||
|
t.Error("Compact(Indent(jsonBig)) != jsonBig")
|
||||||
|
diff(t, b1, jsonBig)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type indentErrorTest struct {
|
||||||
|
in string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var indentErrorTests = []indentErrorTest{
|
||||||
|
{`{"X": "foo", "Y"}`, json.NewSyntaxError("invalid character '}' after object key", 17)},
|
||||||
|
{`{"X": "foo" "Y": "bar"}`, json.NewSyntaxError("invalid character '\"' after object key:value pair", 13)},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndentErrors(t *testing.T) {
|
||||||
|
for i, tt := range indentErrorTests {
|
||||||
|
slice := make([]uint8, 0)
|
||||||
|
buf := bytes.NewBuffer(slice)
|
||||||
|
if err := json.Indent(buf, []uint8(tt.in), "", ""); err != nil {
|
||||||
|
if !reflect.DeepEqual(err, tt.err) {
|
||||||
|
t.Errorf("#%d: Indent: %#v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func diff(t *testing.T, a, b []byte) {
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if i >= len(a) || i >= len(b) || a[i] != b[i] {
|
||||||
|
j := i - 10
|
||||||
|
if j < 0 {
|
||||||
|
j = 0
|
||||||
|
}
|
||||||
|
t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func trim(b []byte) []byte {
|
||||||
|
if len(b) > 20 {
|
||||||
|
return b[0:20]
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random JSON object.
|
||||||
|
|
||||||
|
var jsonBig []byte
|
||||||
|
|
||||||
|
func initBig() {
|
||||||
|
n := 10000
|
||||||
|
if testing.Short() {
|
||||||
|
n = 100
|
||||||
|
}
|
||||||
|
v := genValue(n)
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
jsonBig = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func genValue(n int) interface{} {
|
||||||
|
if n > 1 {
|
||||||
|
switch rand.Intn(2) {
|
||||||
|
case 0:
|
||||||
|
return genArray(n)
|
||||||
|
case 1:
|
||||||
|
return genMap(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch rand.Intn(3) {
|
||||||
|
case 0:
|
||||||
|
return rand.Intn(2) == 0
|
||||||
|
case 1:
|
||||||
|
return rand.NormFloat64()
|
||||||
|
case 2:
|
||||||
|
return genString(30)
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func genString(stddev float64) string {
|
||||||
|
n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2))
|
||||||
|
c := make([]rune, n)
|
||||||
|
for i := range c {
|
||||||
|
f := math.Abs(rand.NormFloat64()*64 + 32)
|
||||||
|
if f > 0x10ffff {
|
||||||
|
f = 0x10ffff
|
||||||
|
}
|
||||||
|
c[i] = rune(f)
|
||||||
|
}
|
||||||
|
return string(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func genArray(n int) []interface{} {
|
||||||
|
f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
|
||||||
|
if f > n {
|
||||||
|
f = n
|
||||||
|
}
|
||||||
|
if f < 1 {
|
||||||
|
f = 1
|
||||||
|
}
|
||||||
|
x := make([]interface{}, f)
|
||||||
|
for i := range x {
|
||||||
|
x[i] = genValue(((i+1)*n)/f - (i*n)/f)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMap(n int) map[string]interface{} {
|
||||||
|
f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
|
||||||
|
if f > n {
|
||||||
|
f = n
|
||||||
|
}
|
||||||
|
if n > 0 && f == 0 {
|
||||||
|
f = 1
|
||||||
|
}
|
||||||
|
x := make(map[string]interface{})
|
||||||
|
for i := 0; i < f; i++ {
|
||||||
|
x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue