Skip to content

Commit bb9ca39

Browse files
committed
Refactoring
1 parent ffaa70a commit bb9ca39

File tree

8 files changed

+64
-38
lines changed

8 files changed

+64
-38
lines changed

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
SOURCE = parser.go
22
CONTAINER = jsonparser
33
SOURCE_PATH = /go/src/github.com/buger/jsonparser
4-
BENCHMARK = JsonParser
4+
BENCHMARK = JsonParserSmall
55
BENCHTIME = 5s
66
TEST = .
77

@@ -14,6 +14,9 @@ race:
1414
bench:
1515
docker run -v `pwd`:$(SOURCE_PATH) -i -t $(CONTAINER) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -benchtime $(BENCHTIME) -v
1616

17+
bench_local:
18+
docker run -v `pwd`:$(SOURCE_PATH) -i -t $(CONTAINER) go test $(LDFLAGS) -test.benchmem -bench . $(ARGS) -benchtime $(BENCHTIME) -v
19+
1720
profile:
1821
docker run -v `pwd`:$(SOURCE_PATH) -i -t $(CONTAINER) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -memprofile mem.mprof -v
1922
docker run -v `pwd`:$(SOURCE_PATH) -i -t $(CONTAINER) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -cpuprofile cpu.out -v

fastbytes.go renamed to bytes.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package jsonparser
22

3-
func BytesParseInt(bytes []byte) (v int64, ok bool) {
3+
// About 3x faster then strconv.ParseInt because does not check for range error and support only base 10, which is enough for JSON
4+
func parseInt(bytes []byte) (v int64, ok bool) {
45
if len(bytes) == 0 {
56
return 0, false
67
}

bytes_safe.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// +build appengine appenginevm
2+
3+
package jsonparser
4+
5+
import (
6+
"strconv"
7+
)
8+
9+
// See fastbytes_unsafe.go for explanation on why *[]byte is used (signatures must be consistent with those in that file)
10+
11+
func equalStr(b *[]byte, s string) bool {
12+
return string(*b) == s
13+
}
14+
15+
func parseFloat(b *[]byte) (float64, error) {
16+
return strconv.ParseFloat(string(*b), 64)
17+
}
18+
19+
func bytesToString(b *[]byte) string {
20+
return string(*b)
21+
}

fastbytes_test.go renamed to bytes_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ var parseIntTests = []ParseIntTest{
7070

7171
func TestBytesParseInt(t *testing.T) {
7272
for _, test := range parseIntTests {
73-
out, ok := BytesParseInt([]byte(test.in))
73+
out, ok := parseInt([]byte(test.in))
7474
if ok != !test.isErr {
7575
t.Errorf("Test '%s' error return did not match expectation (obtained %t, expected %t)", test.in, !ok, test.isErr)
7676
} else if ok && out != test.out {
@@ -82,7 +82,7 @@ func TestBytesParseInt(t *testing.T) {
8282
func BenchmarkParseInt(b *testing.B) {
8383
bytes := []byte("123")
8484
for i := 0; i < b.N; i++ {
85-
BytesParseInt(bytes)
85+
parseInt(bytes)
8686
}
8787
}
8888

fastbytesunsafe.go renamed to bytes_unsafe.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,18 @@ import (
1414
// inlined, whereas the pointer []byte version is equally fast to the manually inlined
1515
// version. Instruction count in assembly taken from "go tool compile" confirms this difference.
1616
//
17-
18-
func BytesEqualStr(abytesptr *[]byte, bstr string) bool {
19-
return *(*string)(unsafe.Pointer(abytesptr)) == bstr
17+
// TODO: Remove hack after Go 1.7 release
18+
//
19+
func equalStr(b *[]byte, s string) bool {
20+
return *(*string)(unsafe.Pointer(b)) == s
2021
}
2122

22-
func BytesParseFloat(bytesptr *[]byte, bitSize int) (float64, error) {
23-
return strconv.ParseFloat(*(*string)(unsafe.Pointer(bytesptr)), bitSize)
23+
func parseFloat(b *[]byte) (float64, error) {
24+
return strconv.ParseFloat(*(*string)(unsafe.Pointer(b)), 64)
2425
}
26+
27+
// A hack until issue golang/go#2632 is fixed.
28+
// See: https://github.com/golang/go/issues/2632
29+
func bytesToString(b *[]byte) string {
30+
return *(*string)(unsafe.Pointer(b))
31+
}

fastbytesunsafe_test.go renamed to bytes_unsafe_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ func bytesEqualStrUnsafeSlower(abytes *[]byte, bstr string) bool {
2727
}
2828

2929
func TestEqual(t *testing.T) {
30-
if !BytesEqualStr(&[]byte{}, "") {
31-
t.Errorf(`BytesEqualStr("", ""): expected true, obtained false`)
30+
if !equalStr(&[]byte{}, "") {
31+
t.Errorf(`equalStr("", ""): expected true, obtained false`)
3232
return
3333
}
3434

@@ -37,20 +37,20 @@ func TestEqual(t *testing.T) {
3737
s1, s2 := longstr[:i]+"1", longstr[:i]+"2"
3838
b1 := []byte(s1)
3939

40-
if !BytesEqualStr(&b1, s1) {
41-
t.Errorf(`BytesEqualStr("a"*%d + "1", "a"*%d + "1"): expected true, obtained false`, i, i)
40+
if !equalStr(&b1, s1) {
41+
t.Errorf(`equalStr("a"*%d + "1", "a"*%d + "1"): expected true, obtained false`, i, i)
4242
break
4343
}
44-
if BytesEqualStr(&b1, s2) {
45-
t.Errorf(`BytesEqualStr("a"*%d + "1", "a"*%d + "2"): expected false, obtained true`, i, i)
44+
if equalStr(&b1, s2) {
45+
t.Errorf(`equalStr("a"*%d + "1", "a"*%d + "2"): expected false, obtained true`, i, i)
4646
break
4747
}
4848
}
4949
}
5050

51-
func BenchmarkBytesEqualStr(b *testing.B) {
51+
func BenchmarkEqualStr(b *testing.B) {
5252
for i := 0; i < b.N; i++ {
53-
BytesEqualStr(&benchmarkBytes, benchmarkString)
53+
equalStr(&benchmarkBytes, benchmarkString)
5454
}
5555
}
5656

fastbytessafe.go

Lines changed: 0 additions & 17 deletions
This file was deleted.

parser.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ func searchKeys(data []byte, keys ...string) int {
125125

126126
// if string is a Key, and key level match
127127
if data[i] == ':' {
128-
candidateKey := data[keyBegin:keyEnd]
128+
key := data[keyBegin:keyEnd]
129129

130130
if keyLevel == level-1 && // If key nesting level match current object nested level
131-
BytesEqualStr(&candidateKey, keys[level-1]) {
131+
equalStr(&key, keys[level-1]) {
132132
keyLevel++
133133
// If we found all keys in path
134134
if keyLevel == lk {
@@ -341,6 +341,17 @@ func ArrayEach(data []byte, cb func(value []byte, dataType ValueType, offset int
341341
return nil
342342
}
343343

344+
// GetUnsafeString returns the value retrieved by `Get`, use creates string without memory allocation by mapping string to slice memory. It does not handle escape symbols.
345+
func GetUnsafeString(data []byte, keys ...string) (val string, err error) {
346+
v, _, _, e := Get(data, keys...)
347+
348+
if e != nil {
349+
return "", e
350+
}
351+
352+
return bytesToString(&v), nil
353+
}
354+
344355
// GetString returns the value retrieved by `Get`, cast to a string if possible, trying to properly handle escape and utf8 symbols
345356
// If key data type do not match, it will return an error.
346357
func GetString(data []byte, keys ...string) (val string, err error) {
@@ -378,7 +389,7 @@ func GetFloat(data []byte, keys ...string) (val float64, err error) {
378389
return 0, fmt.Errorf("Value is not a number: %s", string(v))
379390
}
380391

381-
val, err = BytesParseFloat(&v, 64)
392+
val, err = parseFloat(&v)
382393
return
383394
}
384395

@@ -395,7 +406,7 @@ func GetInt(data []byte, keys ...string) (val int64, err error) {
395406
return 0, fmt.Errorf("Value is not a number: %s", string(v))
396407
}
397408

398-
if val, ok := BytesParseInt(v); !ok {
409+
if val, ok := parseInt(v); !ok {
399410
return 0, MalformedValueError
400411
} else {
401412
return val, nil

0 commit comments

Comments
 (0)