Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LRU cache for JSONPointerToSlice and DotPathToSlice. #136

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: 1.16.x
go-version: 1.18.x

- name: Checkout code
uses: actions/checkout@v3
Expand Down
28 changes: 28 additions & 0 deletions gabs.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"os"
"strconv"
"strings"

lru "github.com/hashicorp/golang-lru/v2"

Check failure on line 35 in gabs.go

View workflow job for this annotation

GitHub Actions / golangci-lint

import 'github.com/hashicorp/golang-lru/v2' is not allowed from list 'Main' (depguard)
)

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -79,11 +81,17 @@
var (
r1 *strings.Replacer
r2 *strings.Replacer
// Cache for DotPathToSlice
dotPathCache *lru.Cache[string, []string]
// Cache for JSONPointerToSlice
jsonPointerCache *lru.Cache[string, []string]
)

func init() {
r1 = strings.NewReplacer("~1", "/", "~0", "~")
r2 = strings.NewReplacer("~1", ".", "~0", "~")
dotPathCache, _ = lru.New[string, []string](1024)
jsonPointerCache, _ = lru.New[string, []string](1024)
}

//------------------------------------------------------------------------------
Expand All @@ -105,10 +113,20 @@
if path == "/" {
return []string{""}, nil
}

item, ok := jsonPointerCache.Get(path)

if ok {
return item, nil
}

hierarchy := strings.Split(path, "/")[1:]
for i, v := range hierarchy {
hierarchy[i] = r1.Replace(v)
}

jsonPointerCache.Add(path, hierarchy)

return hierarchy, nil
}

Expand All @@ -118,10 +136,20 @@
// if it appears in the reference key. Likewise, '~' (%x7E) must be encoded
// as '~0' since it is the escape character for encoding '.'.
func DotPathToSlice(path string) []string {
item, ok := dotPathCache.Get(path)

if ok {
return item
}

hierarchy := strings.Split(path, ".")

for i, v := range hierarchy {
hierarchy[i] = r2.Replace(v)
}

dotPathCache.Add(path, hierarchy)

return hierarchy
}

Expand Down Expand Up @@ -720,7 +748,7 @@
flat[path] = struct{}{}
}
for elePath, v := range obj {
if len(path) > 0 {

Check failure on line 751 in gabs.go

View workflow job for this annotation

GitHub Actions / golangci-lint

emptyStringTest: replace `len(path) > 0` with `path != ""` (gocritic)
elePath = path + "." + elePath
}
switch t := v.(type) {
Expand All @@ -740,7 +768,7 @@
}
for i, ele := range arr {
elePath := strconv.Itoa(i)
if len(path) > 0 {

Check failure on line 771 in gabs.go

View workflow job for this annotation

GitHub Actions / golangci-lint

emptyStringTest: replace `len(path) > 0` with `path != ""` (gocritic)
elePath = path + "." + elePath
}
switch t := ele.(type) {
Expand Down Expand Up @@ -897,7 +925,7 @@

// ParseJSONFile reads a file and unmarshals the contents into a *Container.
func ParseJSONFile(path string) (*Container, error) {
if len(path) > 0 {

Check failure on line 928 in gabs.go

View workflow job for this annotation

GitHub Actions / golangci-lint

emptyStringTest: replace `len(path) > 0` with `path != ""` (gocritic)
cBytes, err := os.ReadFile(path)
if err != nil {
return nil, err
Expand Down
34 changes: 34 additions & 0 deletions gabs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@

var result *Container
result, err = root.JSONPointer(test.path)
if len(test.err) > 0 {

Check failure on line 220 in gabs_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

emptyStringTest: replace `len(test.err) > 0` with `test.err != ""` (gocritic)
if err == nil {
tt.Errorf("Expected error: %v", test.err)
} else if exp, act := test.err, err.Error(); exp != act {
Expand Down Expand Up @@ -1950,3 +1950,37 @@
val.Search([]string{"test", "*", "value"}...)
}
}

func BenchmarkJsonPointerToSlice(b *testing.B) {
samples := []string{
"/test/field",
"/field/test",
"/user/name",
"/user/email/address",
"/host/ip/addres",
"/host/os/family",
"/host/os/version",
"/test/field",
"/field/test",
"/user/name",
"/user/email/address",
"/host/ip/addres",
"/host/os/family",
"/host/os/version",
"/test/field",
"/field/test",
"/user/name",
"/user/email/address",
"/host/ip/addres",
"/host/os/family",
"/host/os/version",
}
b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
for _, s := range samples {
JSONPointerToSlice(s)

Check failure on line 1983 in gabs_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

Error return value is not checked (errcheck)
}
}
}
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/Jeffail/gabs/v2

go 1.16
go 1.18

require github.com/hashicorp/golang-lru/v2 v2.0.2
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU=
github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
Loading