mirror of https://github.com/tidwall/sjson.git
Added ReplaceInPlace Option
Allows for replacing the input byte slice instead of allocating new memory.
This commit is contained in:
parent
e6094ca119
commit
a47c1e938e
117
sjson.go
117
sjson.go
|
@ -23,6 +23,13 @@ type Options struct {
|
||||||
// Optimistic is a hint that the value likely exists which
|
// Optimistic is a hint that the value likely exists which
|
||||||
// allows for the sjson to perform a fast-track search and replace.
|
// allows for the sjson to perform a fast-track search and replace.
|
||||||
Optimistic bool
|
Optimistic bool
|
||||||
|
// ReplaceInPlace is a hint to replace the input json rather than
|
||||||
|
// allocate a new json byte slice. When this field is specified
|
||||||
|
// the input json will not longer be valid and it should not be used
|
||||||
|
// There is no guarentees that the memory will be replaced in-place.
|
||||||
|
// The Optimistic flag must be set to true and the input must be a
|
||||||
|
// byte slice in order to use this field.
|
||||||
|
ReplaceInPlace bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type pathResult struct {
|
type pathResult struct {
|
||||||
|
@ -71,9 +78,11 @@ func parsePath(path string) (pathResult, error) {
|
||||||
r.more = true
|
r.more = true
|
||||||
return r, nil
|
return r, nil
|
||||||
} else if path[i] == '*' || path[i] == '?' {
|
} else if path[i] == '*' || path[i] == '?' {
|
||||||
return r, &errorType{"wildcard characters not allowed in path"}
|
return r, &errorType{
|
||||||
|
"wildcard characters not allowed in path"}
|
||||||
} else if path[i] == '#' {
|
} else if path[i] == '#' {
|
||||||
return r, &errorType{"array access character not allowed in path"}
|
return r, &errorType{
|
||||||
|
"array access character not allowed in path"}
|
||||||
}
|
}
|
||||||
epart = append(epart, path[i])
|
epart = append(epart, path[i])
|
||||||
}
|
}
|
||||||
|
@ -87,14 +96,21 @@ func parsePath(path string) (pathResult, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendStringify makes a json string and appends to buf.
|
func mustMarshalString(s string) bool {
|
||||||
func appendStringify(buf []byte, s string) []byte {
|
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
if s[i] < ' ' || s[i] > 0x7f || s[i] == '"' {
|
if s[i] < ' ' || s[i] > 0x7f || s[i] == '"' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendStringify makes a json string and appends to buf.
|
||||||
|
func appendStringify(buf []byte, s string) []byte {
|
||||||
|
if mustMarshalString(s) {
|
||||||
b, _ := jsongo.Marshal(s)
|
b, _ := jsongo.Marshal(s)
|
||||||
return append(buf, b...)
|
return append(buf, b...)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
buf = append(buf, '"')
|
buf = append(buf, '"')
|
||||||
buf = append(buf, s...)
|
buf = append(buf, s...)
|
||||||
buf = append(buf, '"')
|
buf = append(buf, '"')
|
||||||
|
@ -102,7 +118,8 @@ func appendStringify(buf []byte, s string) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendBuild builds a json block from a json path.
|
// appendBuild builds a json block from a json path.
|
||||||
func appendBuild(buf []byte, array bool, paths []pathResult, raw string, stringify bool) []byte {
|
func appendBuild(buf []byte, array bool, paths []pathResult, raw string,
|
||||||
|
stringify bool) []byte {
|
||||||
if !array {
|
if !array {
|
||||||
buf = appendStringify(buf, paths[0].part)
|
buf = appendStringify(buf, paths[0].part)
|
||||||
buf = append(buf, ':')
|
buf = append(buf, ':')
|
||||||
|
@ -215,7 +232,8 @@ loop:
|
||||||
|
|
||||||
var errNoChange = &errorType{"no change"}
|
var errNoChange = &errorType{"no change"}
|
||||||
|
|
||||||
func appendRawPaths(buf []byte, jstr string, paths []pathResult, raw string, stringify, del bool) ([]byte, error) {
|
func appendRawPaths(buf []byte, jstr string, paths []pathResult, raw string,
|
||||||
|
stringify, del bool) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
var res gjson.Result
|
var res gjson.Result
|
||||||
var found bool
|
var found bool
|
||||||
|
@ -234,7 +252,8 @@ func appendRawPaths(buf []byte, jstr string, paths []pathResult, raw string, str
|
||||||
if res.Index > 0 {
|
if res.Index > 0 {
|
||||||
if len(paths) > 1 {
|
if len(paths) > 1 {
|
||||||
buf = append(buf, jstr[:res.Index]...)
|
buf = append(buf, jstr[:res.Index]...)
|
||||||
buf, err = appendRawPaths(buf, res.Raw, paths[1:], raw, stringify, del)
|
buf, err = appendRawPaths(buf, res.Raw, paths[1:], raw,
|
||||||
|
stringify, del)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -247,7 +266,8 @@ func appendRawPaths(buf []byte, jstr string, paths []pathResult, raw string, str
|
||||||
var delNextComma bool
|
var delNextComma bool
|
||||||
buf, delNextComma = deleteTailItem(buf)
|
buf, delNextComma = deleteTailItem(buf)
|
||||||
if delNextComma {
|
if delNextComma {
|
||||||
for i, j := res.Index+len(res.Raw), 0; i < len(jstr); i, j = i+1, j+1 {
|
i, j := res.Index+len(res.Raw), 0
|
||||||
|
for ; i < len(jstr); i, j = i+1, j+1 {
|
||||||
if jstr[i] <= ' ' {
|
if jstr[i] <= ' ' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -322,7 +342,9 @@ func appendRawPaths(buf []byte, jstr string, paths []pathResult, raw string, str
|
||||||
if paths[0].part == "-1" && !paths[0].force {
|
if paths[0].part == "-1" && !paths[0].force {
|
||||||
appendit = true
|
appendit = true
|
||||||
} else {
|
} else {
|
||||||
return nil, &errorType{"cannot set array element for non-numeric key '" + paths[0].part + "'"}
|
return nil, &errorType{
|
||||||
|
"cannot set array element for non-numeric key '" +
|
||||||
|
paths[0].part + "'"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if appendit {
|
if appendit {
|
||||||
|
@ -376,7 +398,8 @@ func isOptimisticPath(path string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(jstr, path, raw string, stringify, del, optimistic bool) ([]byte, error) {
|
func set(jstr, path, raw string,
|
||||||
|
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return nil, &errorType{"path cannot be empty"}
|
return nil, &errorType{"path cannot be empty"}
|
||||||
}
|
}
|
||||||
|
@ -387,6 +410,27 @@ func set(jstr, path, raw string, stringify, del, optimistic bool) ([]byte, error
|
||||||
if stringify {
|
if stringify {
|
||||||
sz += 2
|
sz += 2
|
||||||
}
|
}
|
||||||
|
if inplace && sz <= len(jstr) {
|
||||||
|
if !stringify || !mustMarshalString(raw) {
|
||||||
|
jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&jstr))
|
||||||
|
jsonbh := reflect.SliceHeader{
|
||||||
|
Data: jsonh.Data, Len: jsonh.Len, Cap: jsonh.Len}
|
||||||
|
jbytes := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||||
|
if stringify {
|
||||||
|
jbytes[res.Index] = '"'
|
||||||
|
copy(jbytes[res.Index+1:], []byte(raw))
|
||||||
|
jbytes[res.Index+1+len(raw)] = '"'
|
||||||
|
copy(jbytes[res.Index+1+len(raw)+1:],
|
||||||
|
jbytes[res.Index+len(res.Raw):])
|
||||||
|
} else {
|
||||||
|
copy(jbytes[res.Index:], []byte(raw))
|
||||||
|
copy(jbytes[res.Index+len(raw):],
|
||||||
|
jbytes[res.Index+len(res.Raw):])
|
||||||
|
}
|
||||||
|
return jbytes[:sz], nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
buf := make([]byte, 0, sz)
|
buf := make([]byte, 0, sz)
|
||||||
buf = append(buf, jstr[:res.Index]...)
|
buf = append(buf, jstr[:res.Index]...)
|
||||||
if stringify {
|
if stringify {
|
||||||
|
@ -452,6 +496,15 @@ func Set(json, path string, value interface{}) (string, error) {
|
||||||
// An error is returned if the path is not valid.
|
// An error is returned if the path is not valid.
|
||||||
func SetOptions(json, path string, value interface{},
|
func SetOptions(json, path string, value interface{},
|
||||||
opts *Options) (string, error) {
|
opts *Options) (string, error) {
|
||||||
|
if opts != nil {
|
||||||
|
if opts.ReplaceInPlace {
|
||||||
|
// it's not safe to replace bytes in-place for strings
|
||||||
|
// copy the Options and set options.ReplaceInPlace to false.
|
||||||
|
nopts := *opts
|
||||||
|
opts = &nopts
|
||||||
|
opts.ReplaceInPlace = false
|
||||||
|
}
|
||||||
|
}
|
||||||
jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&json))
|
jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&json))
|
||||||
jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
|
jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
|
||||||
jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||||
|
@ -471,9 +524,10 @@ func SetBytes(json []byte, path string, value interface{}) ([]byte, error) {
|
||||||
// SetOptions(string(data), path, value)
|
// SetOptions(string(data), path, value)
|
||||||
func SetBytesOptions(json []byte, path string, value interface{},
|
func SetBytesOptions(json []byte, path string, value interface{},
|
||||||
opts *Options) ([]byte, error) {
|
opts *Options) ([]byte, error) {
|
||||||
var optimistic bool
|
var optimistic, inplace bool
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
optimistic = opts.Optimistic
|
optimistic = opts.Optimistic
|
||||||
|
inplace = opts.ReplaceInPlace
|
||||||
}
|
}
|
||||||
jstr := *(*string)(unsafe.Pointer(&json))
|
jstr := *(*string)(unsafe.Pointer(&json))
|
||||||
var res []byte
|
var res []byte
|
||||||
|
@ -485,50 +539,50 @@ func SetBytesOptions(json []byte, path string, value interface{},
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
raw := *(*string)(unsafe.Pointer(&b))
|
raw := *(*string)(unsafe.Pointer(&b))
|
||||||
res, err = set(jstr, path, raw, false, false, optimistic)
|
res, err = set(jstr, path, raw, false, false, optimistic, inplace)
|
||||||
case dtype:
|
case dtype:
|
||||||
res, err = set(jstr, path, "", false, true, optimistic)
|
res, err = set(jstr, path, "", false, true, optimistic, inplace)
|
||||||
case string:
|
case string:
|
||||||
res, err = set(jstr, path, v, true, false, optimistic)
|
res, err = set(jstr, path, v, true, false, optimistic, inplace)
|
||||||
case []byte:
|
case []byte:
|
||||||
raw := *(*string)(unsafe.Pointer(&v))
|
raw := *(*string)(unsafe.Pointer(&v))
|
||||||
res, err = set(jstr, path, raw, true, false, optimistic)
|
res, err = set(jstr, path, raw, true, false, optimistic, inplace)
|
||||||
case bool:
|
case bool:
|
||||||
if v {
|
if v {
|
||||||
res, err = set(jstr, path, "true", false, false, optimistic)
|
res, err = set(jstr, path, "true", false, false, optimistic, inplace)
|
||||||
} else {
|
} else {
|
||||||
res, err = set(jstr, path, "false", false, false, optimistic)
|
res, err = set(jstr, path, "false", false, false, optimistic, inplace)
|
||||||
}
|
}
|
||||||
case int8:
|
case int8:
|
||||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case int16:
|
case int16:
|
||||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case int32:
|
case int32:
|
||||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case int64:
|
case int64:
|
||||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case uint8:
|
case uint8:
|
||||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case uint16:
|
case uint16:
|
||||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case uint32:
|
case uint32:
|
||||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case uint64:
|
case uint64:
|
||||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case float32:
|
case float32:
|
||||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
case float64:
|
case float64:
|
||||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||||
false, false, optimistic)
|
false, false, optimistic, inplace)
|
||||||
}
|
}
|
||||||
if err == errNoChange {
|
if err == errNoChange {
|
||||||
return json, nil
|
return json, nil
|
||||||
|
@ -551,7 +605,7 @@ func SetRawOptions(json, path, value string, opts *Options) (string, error) {
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
optimistic = opts.Optimistic
|
optimistic = opts.Optimistic
|
||||||
}
|
}
|
||||||
res, err := set(json, path, value, false, false, optimistic)
|
res, err := set(json, path, value, false, false, optimistic, false)
|
||||||
if err == errNoChange {
|
if err == errNoChange {
|
||||||
return json, nil
|
return json, nil
|
||||||
}
|
}
|
||||||
|
@ -572,11 +626,12 @@ func SetRawBytesOptions(json []byte, path string, value []byte,
|
||||||
opts *Options) ([]byte, error) {
|
opts *Options) ([]byte, error) {
|
||||||
jstr := *(*string)(unsafe.Pointer(&json))
|
jstr := *(*string)(unsafe.Pointer(&json))
|
||||||
vstr := *(*string)(unsafe.Pointer(&value))
|
vstr := *(*string)(unsafe.Pointer(&value))
|
||||||
var optimistic bool
|
var optimistic, inplace bool
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
optimistic = opts.Optimistic
|
optimistic = opts.Optimistic
|
||||||
|
inplace = opts.ReplaceInPlace
|
||||||
}
|
}
|
||||||
res, err := set(jstr, path, vstr, false, false, optimistic)
|
res, err := set(jstr, path, vstr, false, false, optimistic, inplace)
|
||||||
if err == errNoChange {
|
if err == errNoChange {
|
||||||
return json, nil
|
return json, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,7 +180,7 @@ var json = `
|
||||||
},
|
},
|
||||||
"committer": {
|
"committer": {
|
||||||
"name": "Tom Tom Anderson",
|
"name": "Tom Tom Anderson",
|
||||||
"email": "jeff@anderson.edu",
|
"email": "jeffditto@anderson.edu",
|
||||||
"date": "2013-06-22T16:30:59Z"
|
"date": "2013-06-22T16:30:59Z"
|
||||||
},
|
},
|
||||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161",
|
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161",
|
||||||
|
@ -197,10 +197,12 @@ var path = "commit.committer.email"
|
||||||
var value = "tomtom@anderson.com"
|
var value = "tomtom@anderson.com"
|
||||||
var rawValue = `"tomtom@anderson.com"`
|
var rawValue = `"tomtom@anderson.com"`
|
||||||
var rawValueBytes = []byte(rawValue)
|
var rawValueBytes = []byte(rawValue)
|
||||||
var expect = strings.Replace(json, "jeff@anderson.edu", "tomtom@anderson.com", 1)
|
var expect = strings.Replace(json, "jeffditto@anderson.edu", "tomtom@anderson.com", 1)
|
||||||
var jsonBytes = []byte(json)
|
var jsonBytes = []byte(json)
|
||||||
|
var jsonBytes2 = []byte(json)
|
||||||
var expectBytes = []byte(expect)
|
var expectBytes = []byte(expect)
|
||||||
var opts = &Options{Optimistic: true}
|
var opts = &Options{Optimistic: true}
|
||||||
|
var optsInPlace = &Options{Optimistic: true, ReplaceInPlace: true}
|
||||||
|
|
||||||
func BenchmarkSet(t *testing.B) {
|
func BenchmarkSet(t *testing.B) {
|
||||||
t.ReportAllocs()
|
t.ReportAllocs()
|
||||||
|
@ -253,6 +255,7 @@ func BenchmarkSetRawBytes(t *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkSetOptimistic(t *testing.B) {
|
func BenchmarkSetOptimistic(t *testing.B) {
|
||||||
t.ReportAllocs()
|
t.ReportAllocs()
|
||||||
for i := 0; i < t.N; i++ {
|
for i := 0; i < t.N; i++ {
|
||||||
|
@ -266,6 +269,19 @@ func BenchmarkSetOptimistic(t *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkSetInPlace(t *testing.B) {
|
||||||
|
t.ReportAllocs()
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
res, err := SetOptions(json, path, value, optsInPlace)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if res != expect {
|
||||||
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkSetRawOptimistic(t *testing.B) {
|
func BenchmarkSetRawOptimistic(t *testing.B) {
|
||||||
t.ReportAllocs()
|
t.ReportAllocs()
|
||||||
for i := 0; i < t.N; i++ {
|
for i := 0; i < t.N; i++ {
|
||||||
|
@ -278,6 +294,20 @@ func BenchmarkSetRawOptimistic(t *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkSetRawInPlace(t *testing.B) {
|
||||||
|
t.ReportAllocs()
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
res, err := SetRawOptions(json, path, rawValue, optsInPlace)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if res != expect {
|
||||||
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkSetBytesOptimistic(t *testing.B) {
|
func BenchmarkSetBytesOptimistic(t *testing.B) {
|
||||||
t.ReportAllocs()
|
t.ReportAllocs()
|
||||||
for i := 0; i < t.N; i++ {
|
for i := 0; i < t.N; i++ {
|
||||||
|
@ -291,6 +321,20 @@ func BenchmarkSetBytesOptimistic(t *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkSetBytesInPlace(t *testing.B) {
|
||||||
|
t.ReportAllocs()
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
copy(jsonBytes2, jsonBytes)
|
||||||
|
res, err := SetBytesOptions(jsonBytes2, path, value, optsInPlace)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if bytes.Compare(res, expectBytes) != 0 {
|
||||||
|
t.Fatal("expected '%v', got '%v'", string(expectBytes), string(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkSetRawBytesOptimistic(t *testing.B) {
|
func BenchmarkSetRawBytesOptimistic(t *testing.B) {
|
||||||
t.ReportAllocs()
|
t.ReportAllocs()
|
||||||
for i := 0; i < t.N; i++ {
|
for i := 0; i < t.N; i++ {
|
||||||
|
@ -303,3 +347,17 @@ func BenchmarkSetRawBytesOptimistic(t *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkSetRawBytesInPlace(t *testing.B) {
|
||||||
|
t.ReportAllocs()
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
copy(jsonBytes2, jsonBytes)
|
||||||
|
res, err := SetRawBytesOptions(jsonBytes2, path, rawValueBytes, optsInPlace)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if bytes.Compare(res, expectBytes) != 0 {
|
||||||
|
t.Fatal("expected '%v', got '%v'", string(expectBytes), string(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue