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 {
|
||||
if s.cursor+4 >= s.length {
|
||||
if s.read() {
|
||||
if !s.read() {
|
||||
return errInvalidCharacter(s.char(), "bool(false)", s.totalOffset())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,21 +67,25 @@ func (s *stream) read() bool {
|
|||
if n < readChunkSize || err == io.EOF {
|
||||
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 {
|
||||
newBuf := make([]byte, totalSize)
|
||||
copy(newBuf, s.buf)
|
||||
copy(newBuf[s.length:], buf)
|
||||
s.buf = newBuf
|
||||
s.length = totalSize - 1
|
||||
s.length = totalSize - extendBufLength
|
||||
} else if s.length > 0 {
|
||||
copy(buf[s.length:], buf)
|
||||
copy(buf, s.buf[:s.length])
|
||||
s.buf = buf
|
||||
s.length = totalSize - 1
|
||||
s.length = totalSize - extendBufLength
|
||||
} else {
|
||||
s.buf = buf
|
||||
s.length = totalSize - 1
|
||||
s.length = totalSize - extendBufLength
|
||||
}
|
||||
s.offset += s.cursor
|
||||
if n == 0 {
|
||||
|
|
16
encode_vm.go
16
encode_vm.go
|
@ -84,9 +84,19 @@ func (e *Encoder) run(code *opcode) error {
|
|||
typ = typ.Elem()
|
||||
}
|
||||
e.indent = ifaceCode.indent
|
||||
c, err := e.compile(typ, ifaceCode.root, e.enabledIndent)
|
||||
if err != nil {
|
||||
return err
|
||||
var c *opcode
|
||||
if typ.Kind() == reflect.Map {
|
||||
code, err := e.compileMap(typ, false, ifaceCode.root, e.enabledIndent)
|
||||
if err != nil {
|
||||
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.beforeLastCode().next = code.next
|
||||
|
|
|
@ -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 (
|
||||
"bytes"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"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