Skip to content

Commit

Permalink
optimize json
Browse files Browse the repository at this point in the history
  • Loading branch information
notJoon committed Oct 11, 2024
1 parent 912a5db commit 22cd35e
Show file tree
Hide file tree
Showing 7 changed files with 389 additions and 151 deletions.
26 changes: 8 additions & 18 deletions examples/gno.land/p/demo/json/buffer.gno
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (b *buffer) skipUntil(endTokens map[byte]bool) (int, error) {

// significantTokens is a map where the keys are the significant characters in a JSON path.
// The values in the map are all true, which allows us to use the map as a set for quick lookups.
var significantTokens = map[byte]bool{
var significantTokens = [256]bool{
dot: true, // access properties of an object
dollarSign: true, // root object
atSign: true, // current object
Expand All @@ -174,7 +174,7 @@ var significantTokens = map[byte]bool{
}

// filterTokens stores the filter expression tokens.
var filterTokens = map[byte]bool{
var filterTokens = [256]bool{
aesterisk: true, // wildcard
andSign: true,
orSign: true,
Expand All @@ -186,7 +186,7 @@ func (b *buffer) skipToNextSignificantToken() {
for b.index < b.length {
current := b.data[b.index]

if _, ok := significantTokens[current]; ok {
if significantTokens[current] {
break
}

Expand All @@ -199,23 +199,13 @@ func (b *buffer) skipToNextSignificantToken() {
// This is used to check if the current character is escaped. However, unlike the "unescape" function,
// "backslash" only serves to check the number of backslashes.
func (b *buffer) backslash() bool {
if b.index == 0 {
return false
}

count := 0
for i := b.index - 1; ; i-- {
if i >= b.length || b.data[i] != backSlash {
for i := b.index - 1; i >= 0; i-- {
if b.data[i] != backSlash {
break
}

count++

if i == 0 {
break
}
}

return count%2 != 0
}

Expand Down Expand Up @@ -315,15 +305,15 @@ end:
}

func pathStateContainsValidPathToken(c byte) bool {
if _, ok := significantTokens[c]; ok {
if significantTokens[c] {
return true
}

if _, ok := filterTokens[c]; ok {
if filterTokens[c] {
return true
}

if _, ok := numIndex[c]; ok {
if numIndex[c] {
return true
}

Expand Down
142 changes: 142 additions & 0 deletions examples/gno.land/p/demo/json/builder.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// package json

// type NodeBuilder struct {
// node *Node
// }

// func Builder() *NodeBuilder {
// return &NodeBuilder{node: ObjectNode("", nil)}
// }

// func (b *NodeBuilder) WriteString(key, value string) *NodeBuilder {
// b.node.AppendObject(key, StringNode("", value))
// return b
// }

// func (b *NodeBuilder) WriteNumber(key string, value interface{}) *NodeBuilder {
// switch v := value.(type) {
// case int:
// b.node.AppendObject(key, NumberNode("", float64(v)))
// case float64:
// b.node.AppendObject(key, NumberNode("", v))
// }
// return b
// }

// func (b *NodeBuilder) WriteBool(key string, value bool) *NodeBuilder {
// b.node.AppendObject(key, BoolNode("", value))
// return b
// }

// func (b *NodeBuilder) WriteObject(key string, value *Node) *NodeBuilder {
// b.node.AppendObject(key, value)
// return b
// }

// func (b *NodeBuilder) WriteArray(key string, value []*Node) *NodeBuilder {
// b.node.AppendObject(key, ArrayNode("", value))
// return b
// }

// func (b *NodeBuilder) WriteNull(key string) *NodeBuilder {
// b.node.AppendObject(key, NullNode(""))
// return b
// }

// func (b *NodeBuilder) Node() *Node {
// return b.node
// }

package json

type NodeBuilder struct {
node *Node
}

func Builder() *NodeBuilder {
return &NodeBuilder{node: ObjectNode("", nil)}
}

func (b *NodeBuilder) WriteString(key, value string) *NodeBuilder {
b.node.AppendObject(key, StringNode("", value))
return b
}

func (b *NodeBuilder) WriteNumber(key string, value float64) *NodeBuilder {
b.node.AppendObject(key, NumberNode("", value))
return b
}

func (b *NodeBuilder) WriteInt(key string, value int) *NodeBuilder {
return b.WriteNumber(key, float64(value))
}

func (b *NodeBuilder) WriteBool(key string, value bool) *NodeBuilder {
b.node.AppendObject(key, BoolNode("", value))
return b
}

func (b *NodeBuilder) WriteNull(key string) *NodeBuilder {
b.node.AppendObject(key, NullNode(""))
return b
}

func (b *NodeBuilder) WriteObject(key string, fn func(*NodeBuilder)) *NodeBuilder {
nestedBuilder := &NodeBuilder{node: ObjectNode("", nil)}
fn(nestedBuilder)
b.node.AppendObject(key, nestedBuilder.node)
return b
}

func (b *NodeBuilder) WriteArray(key string, fn func(*ArrayBuilder)) *NodeBuilder {
arrayBuilder := &ArrayBuilder{nodes: []*Node{}}
fn(arrayBuilder)
b.node.AppendObject(key, ArrayNode("", arrayBuilder.nodes))
return b
}

func (b *NodeBuilder) Node() *Node {
return b.node
}

type ArrayBuilder struct {
nodes []*Node
}

func (ab *ArrayBuilder) WriteString(value string) *ArrayBuilder {
ab.nodes = append(ab.nodes, StringNode("", value))
return ab
}

func (ab *ArrayBuilder) WriteNumber(value float64) *ArrayBuilder {
ab.nodes = append(ab.nodes, NumberNode("", value))
return ab
}

func (ab *ArrayBuilder) WriteInt(value int) *ArrayBuilder {
return ab.WriteNumber(float64(value))
}

func (ab *ArrayBuilder) WriteBool(value bool) *ArrayBuilder {
ab.nodes = append(ab.nodes, BoolNode("", value))
return ab
}

func (ab *ArrayBuilder) WriteNull() *ArrayBuilder {
ab.nodes = append(ab.nodes, NullNode(""))
return ab
}

func (ab *ArrayBuilder) WriteObject(fn func(*NodeBuilder)) *ArrayBuilder {
nestedBuilder := &NodeBuilder{node: ObjectNode("", nil)}
fn(nestedBuilder)
ab.nodes = append(ab.nodes, nestedBuilder.node)
return ab
}

func (ab *ArrayBuilder) WriteArray(fn func(*ArrayBuilder)) *ArrayBuilder {
nestedArrayBuilder := &ArrayBuilder{nodes: []*Node{}}
fn(nestedArrayBuilder)
ab.nodes = append(ab.nodes, ArrayNode("", nestedArrayBuilder.nodes))
return ab
}
103 changes: 103 additions & 0 deletions examples/gno.land/p/demo/json/builder_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package json

import (
"testing"
)

func TestNodeBuilder(t *testing.T) {
tests := []struct {
name string
build func() *Node
expected string
}{
{
name: "plain object",
build: func() *Node {
return Builder().
WriteString("name", "Alice").
WriteNumber("age", 30).
WriteBool("is_student", false).
Node()
},
expected: `{"name":"Alice","age":30,"is_student":false}`,
},
{
name: "nested object",
build: func() *Node {
return Builder().
WriteString("name", "Alice").
WriteObject("address", func(b *NodeBuilder) {
b.WriteString("city", "New York").
WriteNumber("zipcode", 10001)
}).
Node()
},
expected: `{"name":"Alice","address":{"city":"New York","zipcode":10001}}`,
},
{
name: "null node",
build: func() *Node {
return Builder().WriteNull("foo").Node()
},
expected: `{"foo":null}`,
},
{
name: "array node",
build: func() *Node {
return Builder().
WriteArray("items", func(ab *ArrayBuilder) {
ab.WriteString("item1").
WriteString("item2").
WriteString("item3")
}).
Node()
},
expected: `{"items":["item1","item2","item3"]}`,
},
{
name: "array with objects",
build: func() *Node {
return Builder().
WriteArray("users", func(ab *ArrayBuilder) {
ab.WriteObject(func(b *NodeBuilder) {
b.WriteString("name", "Bob").
WriteInt("age", 25)
}).
WriteObject(func(b *NodeBuilder) {
b.WriteString("name", "Carol").
WriteInt("age", 27)
})
}).
Node()
},
expected: `{"users":[{"name":"Bob","age":25},{"name":"Carol","age":27}]}`,
},
{
name: "array with various types",
build: func() *Node {
return Builder().
WriteArray("values", func(ab *ArrayBuilder) {
ab.WriteString("item1").
WriteNumber(123).
WriteBool(true).
WriteNull()
}).
Node()
},
expected: `{"values":["item1",123,true,null]}`,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
node := tt.build()
value, err := Marshal(node)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
if string(value) != tt.expected {
t.Errorf("expected %s, got %s", tt.expected, string(value))
}
})
}
}
Loading

0 comments on commit 22cd35e

Please sign in to comment.