Skip to content

Commit

Permalink
feat:(ast) node support concurrently-read
Browse files Browse the repository at this point in the history
  • Loading branch information
AsterDY committed Jun 29, 2024
1 parent f16c69f commit e0fda16
Show file tree
Hide file tree
Showing 14 changed files with 534 additions and 234 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,19 @@ exist, err := root.UnsetByIndex(1) // exist == true
println(root.Get("key4").Check()) // "value not exist"
```

#### SearchOption
```go
opts := ast.SearchOption{ CopyReturn: true ... }
val, err := ast.GetWithOption(JSON, opts, "key"...)
```
`Searcher` provides some options for use to meet different needs:
- CopyReturn
Indicate the searcher to copy the result JSON string instead of refer from the input. This can help to reduce memory usage if you cache the results
- ConcurentRead
Since `ast.Node` use `Lazy-Load` design, it doesn't support Concurrently-Read by default. If you want to read it concurrently, please specify it.
- ValidateJSON
Indicate the searcher to validate the entire JSON. This option is enabled by default.

#### Serialize

To encode `ast.Node` as json, use `MarshalJson()` or `json.Marshal()` (MUST pass the node's pointer)
Expand Down Expand Up @@ -466,6 +479,7 @@ For better performance, in previous case the `ast.Visitor` will be the better ch

But `ast.Visitor` is not a very handy API. You might need to write a lot of code to implement your visitor and carefully maintain the tree hierarchy during decoding. Please read the comments in [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) carefully if you decide to use this API.


## Community

Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem.
6 changes: 6 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ func Get(src []byte, path ...interface{}) (ast.Node, error) {
return GetCopyFromString(rt.Mem2Str(src), path...)
}

func GetWithOptions(src []byte, opts ast.SearchOptions, path ...interface{}) (ast.Node, error) {
s := ast.NewSearcher(rt.Mem2Str(src))
s.SearchOptions = opts
return s.GetByPath(path...)
}

// GetFromString is same with Get except src is string.
//
// WARNING: The returned JSON is **Referenced** from the input.
Expand Down
25 changes: 14 additions & 11 deletions ast/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
package ast

import (
`sync`
`unicode/utf8`
)
"sync"
"unicode/utf8"

const (
_MaxBuffer = 1024 // 1KB buffer size
"github.com/bytedance/sonic/option"
)

func quoteString(e *[]byte, s string) {
Expand Down Expand Up @@ -109,7 +107,7 @@ func newBuffer() *[]byte {
if ret := bytesPool.Get(); ret != nil {
return ret.(*[]byte)
} else {
buf := make([]byte, 0, _MaxBuffer)
buf := make([]byte, 0, option.DefaultAstEncoderBufferSize)
return &buf
}
}
Expand All @@ -120,10 +118,10 @@ func freeBuffer(buf *[]byte) {
}

func (self *Node) encode(buf *[]byte) error {
if self.IsRaw() {
if self.isRaw() {
return self.encodeRaw(buf)
}
switch self.Type() {
switch int(self.itype()) {
case V_NONE : return ErrNotExist
case V_ERROR : return self.Check()
case V_NULL : return self.encodeNull(buf)
Expand All @@ -139,9 +137,14 @@ func (self *Node) encode(buf *[]byte) error {
}

func (self *Node) encodeRaw(buf *[]byte) error {
raw, err := self.Raw()
if err != nil {
return err
lock := self.rlock()
if !self.isRaw() {
self.runlock()
return self.encode(buf)
}
raw := self.toString()
if lock {
self.runlock()
}
*buf = append(*buf, raw...)
return nil
Expand Down
4 changes: 2 additions & 2 deletions ast/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func TestEncodeNode(t *testing.T) {
if string(ret) != data {
t.Fatal(string(ret))
}
root.loadAllKey()
root.Load()
ret, err = root.MarshalJSON()
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -228,7 +228,7 @@ func BenchmarkEncodeLoad_Sonic(b *testing.B) {
if e != 0 {
b.Fatal(root)
}
root.loadAllKey()
root.Load()
_, err := root.MarshalJSON()
if err != nil {
b.Fatal(err)
Expand Down
7 changes: 5 additions & 2 deletions ast/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type Pair struct {

// Values returns iterator for array's children traversal
func (self *Node) Values() (ListIterator, error) {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
if err := self.should(types.V_ARRAY); err != nil {
return ListIterator{}, err
}
return self.values(), nil
Expand All @@ -41,7 +41,7 @@ func (self *Node) values() ListIterator {

// Properties returns iterator for object's children traversal
func (self *Node) Properties() (ObjectIterator, error) {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return ObjectIterator{}, err
}
return self.properties(), nil
Expand Down Expand Up @@ -168,6 +168,9 @@ type Scanner func(path Sequence, node *Node) bool
//
// NOTICE: A unsetted node WON'T trigger sc, but its index still counts into Path.Index
func (self *Node) ForEach(sc Scanner) error {
if err := self.checkRaw(); err != nil {
return err
}
switch self.itype() {
case types.V_ARRAY:
iter, err := self.Values()
Expand Down
Loading

0 comments on commit e0fda16

Please sign in to comment.