DYN-166 add jsonfilter to json library
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 56s
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 56s
This commit is contained in:
parent
b5cd116219
commit
0f52b860ea
@ -156,7 +156,6 @@ import (
|
|||||||
// an error.
|
// an error.
|
||||||
func Marshal(v any) ([]byte, error) {
|
func Marshal(v any) ([]byte, error) {
|
||||||
e := newEncodeState()
|
e := newEncodeState()
|
||||||
defer encodeStatePool.Put(e)
|
|
||||||
|
|
||||||
err := e.marshal(v, encOpts{escapeHTML: true})
|
err := e.marshal(v, encOpts{escapeHTML: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -164,6 +163,8 @@ func Marshal(v any) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
buf := append([]byte(nil), e.Bytes()...)
|
buf := append([]byte(nil), e.Bytes()...)
|
||||||
|
|
||||||
|
encodeStatePool.Put(e)
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,10 @@ func equalFoldRight(s, t []byte) bool {
|
|||||||
t = t[size:]
|
t = t[size:]
|
||||||
|
|
||||||
}
|
}
|
||||||
return len(t) == 0
|
if len(t) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// asciiEqualFold is a specialization of bytes.EqualFold for use when
|
// asciiEqualFold is a specialization of bytes.EqualFold for use when
|
||||||
|
@ -52,7 +52,9 @@ func TestFold(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFoldAgainstUnicode(t *testing.T) {
|
func TestFoldAgainstUnicode(t *testing.T) {
|
||||||
var buf1, buf2 []byte
|
const bufSize = 5
|
||||||
|
buf1 := make([]byte, 0, bufSize)
|
||||||
|
buf2 := make([]byte, 0, bufSize)
|
||||||
var runes []rune
|
var runes []rune
|
||||||
for i := 0x20; i <= 0x7f; i++ {
|
for i := 0x20; i <= 0x7f; i++ {
|
||||||
runes = append(runes, rune(i))
|
runes = append(runes, rune(i))
|
||||||
@ -94,8 +96,12 @@ func TestFoldAgainstUnicode(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, r2 := range runes {
|
for _, r2 := range runes {
|
||||||
buf1 = append(utf8.AppendRune(append(buf1[:0], 'x'), r), 'x')
|
buf1 := append(buf1[:0], 'x')
|
||||||
buf2 = append(utf8.AppendRune(append(buf2[:0], 'x'), r2), 'x')
|
buf2 := append(buf2[:0], 'x')
|
||||||
|
buf1 = buf1[:1+utf8.EncodeRune(buf1[1:bufSize], r)]
|
||||||
|
buf2 = buf2[:1+utf8.EncodeRune(buf2[1:bufSize], r2)]
|
||||||
|
buf1 = append(buf1, 'x')
|
||||||
|
buf2 = append(buf2, 'x')
|
||||||
want := bytes.EqualFold(buf1, buf2)
|
want := bytes.EqualFold(buf1, buf2)
|
||||||
if got := ff.fold(buf1, buf2); got != want {
|
if got := ff.fold(buf1, buf2); got != want {
|
||||||
t.Errorf("%s(%q, %q) = %v; want %v", ff.name, buf1, buf2, got, want)
|
t.Errorf("%s(%q, %q) = %v; want %v", ff.name, buf1, buf2, got, want)
|
||||||
|
@ -116,3 +116,18 @@ func TestNumberIsValid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkNumberIsValid(b *testing.B) {
|
||||||
|
s := "-61657.61667E+61673"
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
isValidNumber(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNumberIsValidRegexp(b *testing.B) {
|
||||||
|
var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`)
|
||||||
|
s := "-61657.61667E+61673"
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
jsonNumberRegexp.MatchString(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -594,7 +594,7 @@ func (s *scanner) error(c byte, context string) int {
|
|||||||
return scanError
|
return scanError
|
||||||
}
|
}
|
||||||
|
|
||||||
// quoteChar formats c as a quoted character literal.
|
// quoteChar formats c as a quoted character literal
|
||||||
func quoteChar(c byte) string {
|
func quoteChar(c byte) string {
|
||||||
// special cases - different from quoted strings
|
// special cases - different from quoted strings
|
||||||
if c == '\'' {
|
if c == '\'' {
|
||||||
|
@ -179,11 +179,9 @@ func nonSpace(b []byte) bool {
|
|||||||
|
|
||||||
// An Encoder writes JSON values to an output stream.
|
// An Encoder writes JSON values to an output stream.
|
||||||
type Encoder struct {
|
type Encoder struct {
|
||||||
w io.Writer
|
w io.Writer
|
||||||
err error
|
err error
|
||||||
escapeHTML bool
|
escapeHTML bool
|
||||||
nilSafeSlices bool
|
|
||||||
nilSafeMaps bool
|
|
||||||
|
|
||||||
indentBuf *bytes.Buffer
|
indentBuf *bytes.Buffer
|
||||||
indentPrefix string
|
indentPrefix string
|
||||||
@ -204,11 +202,8 @@ func (enc *Encoder) Encode(v any) error {
|
|||||||
if enc.err != nil {
|
if enc.err != nil {
|
||||||
return enc.err
|
return enc.err
|
||||||
}
|
}
|
||||||
|
|
||||||
e := newEncodeState()
|
e := newEncodeState()
|
||||||
defer encodeStatePool.Put(e)
|
err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML})
|
||||||
|
|
||||||
err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML, nilSafeMaps: enc.nilSafeMaps, nilSafeSlices: enc.nilSafeSlices})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -236,6 +231,7 @@ func (enc *Encoder) Encode(v any) error {
|
|||||||
if _, err = enc.w.Write(b); err != nil {
|
if _, err = enc.w.Write(b); err != nil {
|
||||||
enc.err = err
|
enc.err = err
|
||||||
}
|
}
|
||||||
|
encodeStatePool.Put(e)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,13 +243,6 @@ func (enc *Encoder) SetIndent(prefix, indent string) {
|
|||||||
enc.indentValue = indent
|
enc.indentValue = indent
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNilSafeCollection specifies whether to represent nil slices and maps as
|
|
||||||
// '[]' or '{}' respectfully (flag on) instead of 'null' (default) when marshaling json.
|
|
||||||
func (enc *Encoder) SetNilSafeCollection(nilSafeSlices bool, nilSafeMaps bool) {
|
|
||||||
enc.nilSafeSlices = nilSafeSlices
|
|
||||||
enc.nilSafeMaps = nilSafeMaps
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEscapeHTML specifies whether problematic HTML characters
|
// SetEscapeHTML specifies whether problematic HTML characters
|
||||||
// should be escaped inside JSON quoted strings.
|
// should be escaped inside JSON quoted strings.
|
||||||
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
|
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -42,7 +41,7 @@ false
|
|||||||
|
|
||||||
func TestEncoder(t *testing.T) {
|
func TestEncoder(t *testing.T) {
|
||||||
for i := 0; i <= len(streamTest); i++ {
|
for i := 0; i <= len(streamTest); i++ {
|
||||||
var buf strings.Builder
|
var buf bytes.Buffer
|
||||||
enc := NewEncoder(&buf)
|
enc := NewEncoder(&buf)
|
||||||
// Check that enc.SetIndent("", "") turns off indentation.
|
// Check that enc.SetIndent("", "") turns off indentation.
|
||||||
enc.SetIndent(">", ".")
|
enc.SetIndent(">", ".")
|
||||||
@ -60,43 +59,6 @@ func TestEncoder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncoderErrorAndReuseEncodeState(t *testing.T) {
|
|
||||||
// Disable the GC temporarily to prevent encodeState's in Pool being cleaned away during the test.
|
|
||||||
percent := debug.SetGCPercent(-1)
|
|
||||||
defer debug.SetGCPercent(percent)
|
|
||||||
|
|
||||||
// Trigger an error in Marshal with cyclic data.
|
|
||||||
type Dummy struct {
|
|
||||||
Name string
|
|
||||||
Next *Dummy
|
|
||||||
}
|
|
||||||
dummy := Dummy{Name: "Dummy"}
|
|
||||||
dummy.Next = &dummy
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
enc := NewEncoder(&buf)
|
|
||||||
if err := enc.Encode(dummy); err == nil {
|
|
||||||
t.Errorf("Encode(dummy) == nil; want error")
|
|
||||||
}
|
|
||||||
|
|
||||||
type Data struct {
|
|
||||||
A string
|
|
||||||
I int
|
|
||||||
}
|
|
||||||
data := Data{A: "a", I: 1}
|
|
||||||
if err := enc.Encode(data); err != nil {
|
|
||||||
t.Errorf("Marshal(%v) = %v", data, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var data2 Data
|
|
||||||
if err := Unmarshal(buf.Bytes(), &data2); err != nil {
|
|
||||||
t.Errorf("Unmarshal(%v) = %v", data2, err)
|
|
||||||
}
|
|
||||||
if data2 != data {
|
|
||||||
t.Errorf("expect: %v, but get: %v", data, data2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var streamEncodedIndent = `0.1
|
var streamEncodedIndent = `0.1
|
||||||
"hello"
|
"hello"
|
||||||
null
|
null
|
||||||
@ -115,7 +77,7 @@ false
|
|||||||
`
|
`
|
||||||
|
|
||||||
func TestEncoderIndent(t *testing.T) {
|
func TestEncoderIndent(t *testing.T) {
|
||||||
var buf strings.Builder
|
var buf bytes.Buffer
|
||||||
enc := NewEncoder(&buf)
|
enc := NewEncoder(&buf)
|
||||||
enc.SetIndent(">", ".")
|
enc.SetIndent(">", ".")
|
||||||
for _, v := range streamTest {
|
for _, v := range streamTest {
|
||||||
@ -185,7 +147,7 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
|
|||||||
`{"bar":"\"<html>foobar</html>\""}`,
|
`{"bar":"\"<html>foobar</html>\""}`,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
var buf strings.Builder
|
var buf bytes.Buffer
|
||||||
enc := NewEncoder(&buf)
|
enc := NewEncoder(&buf)
|
||||||
if err := enc.Encode(tt.v); err != nil {
|
if err := enc.Encode(tt.v); err != nil {
|
||||||
t.Errorf("Encode(%s): %s", tt.name, err)
|
t.Errorf("Encode(%s): %s", tt.name, err)
|
||||||
@ -347,6 +309,21 @@ func TestBlocking(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncoderEncode(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
type T struct {
|
||||||
|
X, Y string
|
||||||
|
}
|
||||||
|
v := &T{"foo", "bar"}
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
if err := NewEncoder(io.Discard).Encode(v); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type tokenStreamCase struct {
|
type tokenStreamCase struct {
|
||||||
json string
|
json string
|
||||||
expTokens []any
|
expTokens []any
|
||||||
@ -495,45 +472,3 @@ func TestHTTPDecoding(t *testing.T) {
|
|||||||
t.Errorf("err = %v; want io.EOF", err)
|
t.Errorf("err = %v; want io.EOF", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncoderSetNilSafeCollection(t *testing.T) {
|
|
||||||
var (
|
|
||||||
nilSlice []interface{}
|
|
||||||
pNilSlice *[]interface{}
|
|
||||||
nilMap map[string]interface{}
|
|
||||||
pNilMap *map[string]interface{}
|
|
||||||
)
|
|
||||||
for _, tt := range []struct {
|
|
||||||
name string
|
|
||||||
v interface{}
|
|
||||||
want string
|
|
||||||
rescuedWant string
|
|
||||||
}{
|
|
||||||
{"nilSlice", nilSlice, "null", "[]"},
|
|
||||||
{"nonNilSlice", []interface{}{}, "[]", "[]"},
|
|
||||||
{"sliceWithValues", []interface{}{1, 2, 3}, "[1,2,3]", "[1,2,3]"},
|
|
||||||
{"pNilSlice", pNilSlice, "null", "null"},
|
|
||||||
{"nilMap", nilMap, "null", "{}"},
|
|
||||||
{"nonNilMap", map[string]interface{}{}, "{}", "{}"},
|
|
||||||
{"mapWithValues", map[string]interface{}{"1": 1, "2": 2, "3": 3}, "{\"1\":1,\"2\":2,\"3\":3}", "{\"1\":1,\"2\":2,\"3\":3}"},
|
|
||||||
{"pNilMap", pNilMap, "null", "null"},
|
|
||||||
} {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
enc := NewEncoder(&buf)
|
|
||||||
if err := enc.Encode(tt.v); err != nil {
|
|
||||||
t.Fatalf("Encode(%s): %s", tt.name, err)
|
|
||||||
}
|
|
||||||
if got := strings.TrimSpace(buf.String()); got != tt.want {
|
|
||||||
t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.want)
|
|
||||||
}
|
|
||||||
buf.Reset()
|
|
||||||
enc.SetNilSafeCollection(true, true)
|
|
||||||
if err := enc.Encode(tt.v); err != nil {
|
|
||||||
t.Fatalf("SetNilSafeCollection(true) Encode(%s): %s", tt.name, err)
|
|
||||||
}
|
|
||||||
if got := strings.TrimSpace(buf.String()); got != tt.rescuedWant {
|
|
||||||
t.Errorf("SetNilSafeCollection(true) Encode(%s) = %#q, want %#q",
|
|
||||||
tt.name, got, tt.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user