mirror of
https://github.com/minio/minio.git
synced 2025-01-11 15:03:22 -05:00
Update vendored sjson (includes a bug fix) (#7317)
This commit is contained in:
parent
6b4c6f69af
commit
19c10cb4d0
8
vendor/github.com/tidwall/sjson/README.md
generated
vendored
8
vendor/github.com/tidwall/sjson/README.md
generated
vendored
@ -7,12 +7,12 @@
|
||||
<a href="https://godoc.org/github.com/tidwall/sjson"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">set a json value quickly</a></p>
|
||||
<p align="center">set a json value quickly</p>
|
||||
|
||||
SJSON is a Go package that provides a [very fast](#performance) and simple way to set a value in a json document. The purpose for this library is to provide efficient json updating for the [SummitDB](https://github.com/tidwall/summitdb) project.
|
||||
For quickly retrieving json values check out [GJSON](https://github.com/tidwall/gjson).
|
||||
|
||||
For a command line interface check out [JSONed](https://github.com/tidwall/jsoned).
|
||||
For a command line interface check out [JJ](https://github.com/tidwall/jj).
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
@ -59,7 +59,7 @@ Path syntax
|
||||
-----------
|
||||
|
||||
A path is a series of keys separated by a dot.
|
||||
The dot and colon characters can be escaped with '\'.
|
||||
The dot and colon characters can be escaped with ``\``.
|
||||
|
||||
```json
|
||||
{
|
||||
@ -268,7 +268,7 @@ widget.image.hOffset
|
||||
widget.text.onMouseUp
|
||||
```
|
||||
|
||||
*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7.*
|
||||
*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7 and can be be found [here](https://github.com/tidwall/sjson-benchmarks)*.
|
||||
|
||||
## Contact
|
||||
Josh Baker [@tidwall](http://twitter.com/tidwall)
|
||||
|
197
vendor/github.com/tidwall/sjson/sjson.go
generated
vendored
197
vendor/github.com/tidwall/sjson/sjson.go
generated
vendored
@ -3,9 +3,7 @@ package sjson
|
||||
|
||||
import (
|
||||
jsongo "encoding/json"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
@ -36,6 +34,7 @@ type Options struct {
|
||||
|
||||
type pathResult struct {
|
||||
part string // current key part
|
||||
gpart string // gjson get part
|
||||
path string // remaining path
|
||||
force bool // force a string key
|
||||
more bool // there is more path to parse
|
||||
@ -50,6 +49,7 @@ func parsePath(path string) (pathResult, error) {
|
||||
for i := 0; i < len(path); i++ {
|
||||
if path[i] == '.' {
|
||||
r.part = path[:i]
|
||||
r.gpart = path[:i]
|
||||
r.path = path[i+1:]
|
||||
r.more = true
|
||||
return r, nil
|
||||
@ -63,19 +63,24 @@ func parsePath(path string) (pathResult, error) {
|
||||
// go into escape mode. this is a slower path that
|
||||
// strips off the escape character from the part.
|
||||
epart := []byte(path[:i])
|
||||
gpart := []byte(path[:i+1])
|
||||
i++
|
||||
if i < len(path) {
|
||||
epart = append(epart, path[i])
|
||||
gpart = append(gpart, path[i])
|
||||
i++
|
||||
for ; i < len(path); i++ {
|
||||
if path[i] == '\\' {
|
||||
gpart = append(gpart, '\\')
|
||||
i++
|
||||
if i < len(path) {
|
||||
epart = append(epart, path[i])
|
||||
gpart = append(gpart, path[i])
|
||||
}
|
||||
continue
|
||||
} else if path[i] == '.' {
|
||||
r.part = string(epart)
|
||||
r.gpart = string(gpart)
|
||||
r.path = path[i+1:]
|
||||
r.more = true
|
||||
return r, nil
|
||||
@ -87,20 +92,23 @@ func parsePath(path string) (pathResult, error) {
|
||||
"array access character not allowed in path"}
|
||||
}
|
||||
epart = append(epart, path[i])
|
||||
gpart = append(gpart, path[i])
|
||||
}
|
||||
}
|
||||
// append the last part
|
||||
r.part = string(epart)
|
||||
r.gpart = string(gpart)
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
r.part = path
|
||||
r.gpart = path
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func mustMarshalString(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] < ' ' || s[i] > 0x7f || s[i] == '"' {
|
||||
if s[i] < ' ' || s[i] > 0x7f || s[i] == '"' || s[i] == '\\' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -208,7 +216,7 @@ loop:
|
||||
for ; i >= 0; i-- {
|
||||
if buf[i] == '"' {
|
||||
i--
|
||||
if i >= 0 && i == '\\' {
|
||||
if i >= 0 && buf[i] == '\\' {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
@ -249,7 +257,7 @@ func appendRawPaths(buf []byte, jstr string, paths []pathResult, raw string,
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
res = gjson.Get(jstr, paths[0].part)
|
||||
res = gjson.Get(jstr, paths[0].gpart)
|
||||
}
|
||||
if res.Index > 0 {
|
||||
if len(paths) > 1 {
|
||||
@ -400,72 +408,6 @@ func isOptimisticPath(path string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func set(jstr, path, raw string,
|
||||
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
||||
if path == "" {
|
||||
return nil, &errorType{"path cannot be empty"}
|
||||
}
|
||||
if !del && optimistic && isOptimisticPath(path) {
|
||||
res := gjson.Get(jstr, path)
|
||||
if res.Exists() && res.Index > 0 {
|
||||
sz := len(jstr) - len(res.Raw) + len(raw)
|
||||
if stringify {
|
||||
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 = append(buf, jstr[:res.Index]...)
|
||||
if stringify {
|
||||
buf = appendStringify(buf, raw)
|
||||
} else {
|
||||
buf = append(buf, raw...)
|
||||
}
|
||||
buf = append(buf, jstr[res.Index+len(res.Raw):]...)
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
// parse the path, make sure that it does not contain invalid characters
|
||||
// such as '#', '?', '*'
|
||||
paths := make([]pathResult, 0, 4)
|
||||
r, err := parsePath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, r)
|
||||
for r.more {
|
||||
if r, err = parsePath(r.path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, r)
|
||||
}
|
||||
|
||||
njson, err := appendRawPaths(nil, jstr, paths, raw, stringify, del)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return njson, nil
|
||||
}
|
||||
|
||||
// Set sets a json value for the specified path.
|
||||
// A path is in dot syntax, such as "name.last" or "age".
|
||||
// This function expects that the json is well-formed, and does not validate.
|
||||
@ -491,29 +433,6 @@ func Set(json, path string, value interface{}) (string, error) {
|
||||
return SetOptions(json, path, value, nil)
|
||||
}
|
||||
|
||||
// SetOptions sets a json value for the specified path with options.
|
||||
// A path is in dot syntax, such as "name.last" or "age".
|
||||
// This function expects that the json is well-formed, and does not validate.
|
||||
// Invalid json will not panic, but it may return back unexpected results.
|
||||
// An error is returned if the path is not valid.
|
||||
func SetOptions(json, path string, value interface{},
|
||||
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))
|
||||
jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
|
||||
jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||
res, err := SetBytesOptions(jsonb, path, value, opts)
|
||||
return string(res), err
|
||||
}
|
||||
|
||||
// SetBytes sets a json value for the specified path.
|
||||
// If working with bytes, this method preferred over
|
||||
// Set(string(data), path, value)
|
||||
@ -521,77 +440,6 @@ func SetBytes(json []byte, path string, value interface{}) ([]byte, error) {
|
||||
return SetBytesOptions(json, path, value, nil)
|
||||
}
|
||||
|
||||
// SetBytesOptions sets a json value for the specified path with options.
|
||||
// If working with bytes, this method preferred over
|
||||
// SetOptions(string(data), path, value)
|
||||
func SetBytesOptions(json []byte, path string, value interface{},
|
||||
opts *Options) ([]byte, error) {
|
||||
var optimistic, inplace bool
|
||||
if opts != nil {
|
||||
optimistic = opts.Optimistic
|
||||
inplace = opts.ReplaceInPlace
|
||||
}
|
||||
jstr := *(*string)(unsafe.Pointer(&json))
|
||||
var res []byte
|
||||
var err error
|
||||
switch v := value.(type) {
|
||||
default:
|
||||
b, err := jsongo.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := *(*string)(unsafe.Pointer(&b))
|
||||
res, err = set(jstr, path, raw, false, false, optimistic, inplace)
|
||||
case dtype:
|
||||
res, err = set(jstr, path, "", false, true, optimistic, inplace)
|
||||
case string:
|
||||
res, err = set(jstr, path, v, true, false, optimistic, inplace)
|
||||
case []byte:
|
||||
raw := *(*string)(unsafe.Pointer(&v))
|
||||
res, err = set(jstr, path, raw, true, false, optimistic, inplace)
|
||||
case bool:
|
||||
if v {
|
||||
res, err = set(jstr, path, "true", false, false, optimistic, inplace)
|
||||
} else {
|
||||
res, err = set(jstr, path, "false", false, false, optimistic, inplace)
|
||||
}
|
||||
case int8:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int16:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int32:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int64:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint8:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint16:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint32:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint64:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case float32:
|
||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||
false, false, optimistic, inplace)
|
||||
case float64:
|
||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||
false, false, optimistic, inplace)
|
||||
}
|
||||
if err == errNoChange {
|
||||
return json, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// SetRaw sets a raw json value for the specified path.
|
||||
// This function works the same as Set except that the value is set as a
|
||||
// raw block of json. This allows for setting premarshalled json objects.
|
||||
@ -621,25 +469,6 @@ func SetRawBytes(json []byte, path string, value []byte) ([]byte, error) {
|
||||
return SetRawBytesOptions(json, path, value, nil)
|
||||
}
|
||||
|
||||
// SetRawBytesOptions sets a raw json value for the specified path with options.
|
||||
// If working with bytes, this method preferred over
|
||||
// SetRawOptions(string(data), path, value, opts)
|
||||
func SetRawBytesOptions(json []byte, path string, value []byte,
|
||||
opts *Options) ([]byte, error) {
|
||||
jstr := *(*string)(unsafe.Pointer(&json))
|
||||
vstr := *(*string)(unsafe.Pointer(&value))
|
||||
var optimistic, inplace bool
|
||||
if opts != nil {
|
||||
optimistic = opts.Optimistic
|
||||
inplace = opts.ReplaceInPlace
|
||||
}
|
||||
res, err := set(jstr, path, vstr, false, false, optimistic, inplace)
|
||||
if err == errNoChange {
|
||||
return json, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
type dtype struct{}
|
||||
|
||||
// Delete deletes a value from json for the specified path.
|
||||
|
196
vendor/github.com/tidwall/sjson/sjson_gae.go
generated
vendored
Normal file
196
vendor/github.com/tidwall/sjson/sjson_gae.go
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
//+build appengine
|
||||
|
||||
package sjson
|
||||
|
||||
import (
|
||||
jsongo "encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func set(jstr, path, raw string,
|
||||
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
||||
if path == "" {
|
||||
return nil, &errorType{"path cannot be empty"}
|
||||
}
|
||||
if !del && optimistic && isOptimisticPath(path) {
|
||||
res := gjson.Get(jstr, path)
|
||||
if res.Exists() && res.Index > 0 {
|
||||
sz := len(jstr) - len(res.Raw) + len(raw)
|
||||
if stringify {
|
||||
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))
|
||||
jbytes := []byte(jstr)
|
||||
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 = append(buf, jstr[:res.Index]...)
|
||||
if stringify {
|
||||
buf = appendStringify(buf, raw)
|
||||
} else {
|
||||
buf = append(buf, raw...)
|
||||
}
|
||||
buf = append(buf, jstr[res.Index+len(res.Raw):]...)
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
// parse the path, make sure that it does not contain invalid characters
|
||||
// such as '#', '?', '*'
|
||||
paths := make([]pathResult, 0, 4)
|
||||
r, err := parsePath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, r)
|
||||
for r.more {
|
||||
if r, err = parsePath(r.path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, r)
|
||||
}
|
||||
|
||||
njson, err := appendRawPaths(nil, jstr, paths, raw, stringify, del)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return njson, nil
|
||||
}
|
||||
|
||||
// SetOptions sets a json value for the specified path with options.
|
||||
// A path is in dot syntax, such as "name.last" or "age".
|
||||
// This function expects that the json is well-formed, and does not validate.
|
||||
// Invalid json will not panic, but it may return back unexpected results.
|
||||
// An error is returned if the path is not valid.
|
||||
func SetOptions(json, path string, value interface{},
|
||||
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))
|
||||
// jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
|
||||
// jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||
jsonb := []byte(json)
|
||||
res, err := SetBytesOptions(jsonb, path, value, opts)
|
||||
return string(res), err
|
||||
}
|
||||
|
||||
// SetBytesOptions sets a json value for the specified path with options.
|
||||
// If working with bytes, this method preferred over
|
||||
// SetOptions(string(data), path, value)
|
||||
func SetBytesOptions(json []byte, path string, value interface{},
|
||||
opts *Options) ([]byte, error) {
|
||||
var optimistic, inplace bool
|
||||
if opts != nil {
|
||||
optimistic = opts.Optimistic
|
||||
inplace = opts.ReplaceInPlace
|
||||
}
|
||||
// jstr := *(*string)(unsafe.Pointer(&json))
|
||||
jstr := string(json)
|
||||
var res []byte
|
||||
var err error
|
||||
switch v := value.(type) {
|
||||
default:
|
||||
b, merr := jsongo.Marshal(value)
|
||||
if merr != nil {
|
||||
return nil, merr
|
||||
}
|
||||
// raw := *(*string)(unsafe.Pointer(&b))
|
||||
raw := string(b)
|
||||
res, err = set(jstr, path, raw, false, false, optimistic, inplace)
|
||||
case dtype:
|
||||
res, err = set(jstr, path, "", false, true, optimistic, inplace)
|
||||
case string:
|
||||
res, err = set(jstr, path, v, true, false, optimistic, inplace)
|
||||
case []byte:
|
||||
// raw := *(*string)(unsafe.Pointer(&v))
|
||||
raw := string(v)
|
||||
res, err = set(jstr, path, raw, true, false, optimistic, inplace)
|
||||
case bool:
|
||||
if v {
|
||||
res, err = set(jstr, path, "true", false, false, optimistic, inplace)
|
||||
} else {
|
||||
res, err = set(jstr, path, "false", false, false, optimistic, inplace)
|
||||
}
|
||||
case int8:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int16:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int32:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int64:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint8:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint16:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint32:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint64:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case float32:
|
||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||
false, false, optimistic, inplace)
|
||||
case float64:
|
||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||
false, false, optimistic, inplace)
|
||||
}
|
||||
if err == errNoChange {
|
||||
return json, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// SetRawBytesOptions sets a raw json value for the specified path with options.
|
||||
// If working with bytes, this method preferred over
|
||||
// SetRawOptions(string(data), path, value, opts)
|
||||
func SetRawBytesOptions(json []byte, path string, value []byte,
|
||||
opts *Options) ([]byte, error) {
|
||||
// jstr := *(*string)(unsafe.Pointer(&json))
|
||||
// vstr := *(*string)(unsafe.Pointer(&value))
|
||||
jstr := string(json)
|
||||
vstr := string(value)
|
||||
var optimistic, inplace bool
|
||||
if opts != nil {
|
||||
optimistic = opts.Optimistic
|
||||
inplace = opts.ReplaceInPlace
|
||||
}
|
||||
res, err := set(jstr, path, vstr, false, false, optimistic, inplace)
|
||||
if err == errNoChange {
|
||||
return json, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
191
vendor/github.com/tidwall/sjson/sjson_ngae.go
generated
vendored
Normal file
191
vendor/github.com/tidwall/sjson/sjson_ngae.go
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
//+build !appengine
|
||||
|
||||
package sjson
|
||||
|
||||
import (
|
||||
jsongo "encoding/json"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func set(jstr, path, raw string,
|
||||
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
||||
if path == "" {
|
||||
return nil, &errorType{"path cannot be empty"}
|
||||
}
|
||||
if !del && optimistic && isOptimisticPath(path) {
|
||||
res := gjson.Get(jstr, path)
|
||||
if res.Exists() && res.Index > 0 {
|
||||
sz := len(jstr) - len(res.Raw) + len(raw)
|
||||
if stringify {
|
||||
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 = append(buf, jstr[:res.Index]...)
|
||||
if stringify {
|
||||
buf = appendStringify(buf, raw)
|
||||
} else {
|
||||
buf = append(buf, raw...)
|
||||
}
|
||||
buf = append(buf, jstr[res.Index+len(res.Raw):]...)
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
// parse the path, make sure that it does not contain invalid characters
|
||||
// such as '#', '?', '*'
|
||||
paths := make([]pathResult, 0, 4)
|
||||
r, err := parsePath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, r)
|
||||
for r.more {
|
||||
if r, err = parsePath(r.path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, r)
|
||||
}
|
||||
|
||||
njson, err := appendRawPaths(nil, jstr, paths, raw, stringify, del)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return njson, nil
|
||||
}
|
||||
|
||||
// SetOptions sets a json value for the specified path with options.
|
||||
// A path is in dot syntax, such as "name.last" or "age".
|
||||
// This function expects that the json is well-formed, and does not validate.
|
||||
// Invalid json will not panic, but it may return back unexpected results.
|
||||
// An error is returned if the path is not valid.
|
||||
func SetOptions(json, path string, value interface{},
|
||||
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))
|
||||
jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
|
||||
jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||
res, err := SetBytesOptions(jsonb, path, value, opts)
|
||||
return string(res), err
|
||||
}
|
||||
|
||||
// SetBytesOptions sets a json value for the specified path with options.
|
||||
// If working with bytes, this method preferred over
|
||||
// SetOptions(string(data), path, value)
|
||||
func SetBytesOptions(json []byte, path string, value interface{},
|
||||
opts *Options) ([]byte, error) {
|
||||
var optimistic, inplace bool
|
||||
if opts != nil {
|
||||
optimistic = opts.Optimistic
|
||||
inplace = opts.ReplaceInPlace
|
||||
}
|
||||
jstr := *(*string)(unsafe.Pointer(&json))
|
||||
var res []byte
|
||||
var err error
|
||||
switch v := value.(type) {
|
||||
default:
|
||||
b, merr := jsongo.Marshal(value)
|
||||
if merr != nil {
|
||||
return nil, merr
|
||||
}
|
||||
raw := *(*string)(unsafe.Pointer(&b))
|
||||
res, err = set(jstr, path, raw, false, false, optimistic, inplace)
|
||||
case dtype:
|
||||
res, err = set(jstr, path, "", false, true, optimistic, inplace)
|
||||
case string:
|
||||
res, err = set(jstr, path, v, true, false, optimistic, inplace)
|
||||
case []byte:
|
||||
raw := *(*string)(unsafe.Pointer(&v))
|
||||
res, err = set(jstr, path, raw, true, false, optimistic, inplace)
|
||||
case bool:
|
||||
if v {
|
||||
res, err = set(jstr, path, "true", false, false, optimistic, inplace)
|
||||
} else {
|
||||
res, err = set(jstr, path, "false", false, false, optimistic, inplace)
|
||||
}
|
||||
case int8:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int16:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int32:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case int64:
|
||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint8:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint16:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint32:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case uint64:
|
||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||
false, false, optimistic, inplace)
|
||||
case float32:
|
||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||
false, false, optimistic, inplace)
|
||||
case float64:
|
||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||
false, false, optimistic, inplace)
|
||||
}
|
||||
if err == errNoChange {
|
||||
return json, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// SetRawBytesOptions sets a raw json value for the specified path with options.
|
||||
// If working with bytes, this method preferred over
|
||||
// SetRawOptions(string(data), path, value, opts)
|
||||
func SetRawBytesOptions(json []byte, path string, value []byte,
|
||||
opts *Options) ([]byte, error) {
|
||||
jstr := *(*string)(unsafe.Pointer(&json))
|
||||
vstr := *(*string)(unsafe.Pointer(&value))
|
||||
var optimistic, inplace bool
|
||||
if opts != nil {
|
||||
optimistic = opts.Optimistic
|
||||
inplace = opts.ReplaceInPlace
|
||||
}
|
||||
res, err := set(jstr, path, vstr, false, false, optimistic, inplace)
|
||||
if err == errNoChange {
|
||||
return json, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
@ -897,10 +897,10 @@
|
||||
"revisionTime": "2016-08-30T17:39:30Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "j1wNJXkZyuFKjYFpPawESOaXYxk=",
|
||||
"checksumSHA1": "IeRoFAG92dA1jUX27nMz1qczlOw=",
|
||||
"path": "github.com/tidwall/sjson",
|
||||
"revision": "6a22caf2fd45d5e2119bfc3717e984f15a7eb7ee",
|
||||
"revisionTime": "2016-12-12T16:53:56Z"
|
||||
"revision": "25fb082a20e29e83fb7b7ef5f5919166aad1f084",
|
||||
"revisionTime": "2019-02-28T20:39:17Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "R/Mpe0uUp5HeqWrdrH5qE2qZuE8=",
|
||||
|
Loading…
Reference in New Issue
Block a user