Skip to content

Commit df77b69

Browse files
securityguyclaude
andcommitted
Fork from buger/jsonparser: rename module, apply security fixes and enhancements
Module renamed from github.com/buger/jsonparser to github.com/securityguy/jsonparser. Security fixes: - Fix GO-2026-4514: Delete() panics on malformed JSON (negative slice index DoS) - Update Dockerfile base image from golang:1.6 to golang:1.21 Bug fixes: - Fix EachKey failing to extract values from arrays of strings (PR buger#180) - Remove duplicate import in parser_test.go (PR buger#263) New APIs (all additive): - ArrayEach callback now accepts *error for early termination; DoneError sentinel added (PR buger#134) - DeleteOnOrig: in-place deletion variant with no allocation (PR buger#256) - ArrayIterator: lazy closure-based array iteration (PR buger#254) - GetRaw, EachRawKey: raw JSON byte extraction without type parsing (PR buger#250) Other: - go.mod bumped to go 1.18 - benchmark/go.mod updated with replace directive pointing to local module - CLAUDE.md added for Claude Code guidance Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0eb47a7 commit df77b69

15 files changed

Lines changed: 521 additions & 87 deletions

CLAUDE.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Status
6+
7+
This is a **released, public library** (`github.com/securityguy/jsonparser`). All changes must maintain backwards compatibility.
8+
9+
## Commands
10+
11+
```bash
12+
make test # Run tests (10s timeout)
13+
make race # Run tests with race detector
14+
make bench # Run benchmarks
15+
make fmt # Format code
16+
make vet # Go vet
17+
```
18+
19+
Run a single test:
20+
```bash
21+
go test -v -run TestGetString .
22+
```
23+
24+
## Architecture
25+
26+
**jsonparser** is a high-performance, zero-allocation JSON parser for Go. It provides direct field access by key path without requiring Go structs.
27+
28+
### Core Files
29+
30+
- `parser.go` — Public API and main parsing logic (`Get`, `Set`, `Delete`, `ArrayEach`, `ObjectEach`, `EachKey`)
31+
- `escape.go` — Unicode/UTF-16 escape sequence decoding
32+
- `bytes.go` — Custom integer/float parsing (~2x faster than `strconv` for JSON numbers)
33+
- `bytes_unsafe.go` / `bytes_safe.go` — Platform-specific byte↔string conversions; `bytes_unsafe.go` is used by default (build tag `!appengine`), `bytes_safe.go` for AppEngine (`appengine` tag)
34+
35+
### Key Design Principles
36+
37+
- **Zero allocation**: The parser avoids heap allocations on hot paths. Avoid changes that introduce allocations in `Get`, `ArrayEach`, `ObjectEach`, or `EachKey`.
38+
- **No schema**: Returns `[]byte` slices into the original data; callers convert types via helpers like `GetString`, `GetInt`, `GetFloat`.
39+
- **`EachKey` is the fastest multi-path API**: It scans the JSON once for multiple keys simultaneously.
40+
41+
### Value Types
42+
43+
```go
44+
const (
45+
NotExist ValueType = iota
46+
String
47+
Number
48+
Object
49+
Array
50+
Boolean
51+
Null
52+
Unknown
53+
)
54+
```
55+
56+
### Benchmarks
57+
58+
The `benchmark/` directory has its own `go.mod` and compares against ffjson, easyjson, and codecgen. Run separately with `cd benchmark && go test -bench=.`.

Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
FROM golang:1.6
1+
FROM golang:1.21
2+
3+
WORKDIR /go/src/github.com/securityguy/jsonparser
4+
ADD . /go/src/github.com/securityguy/jsonparser
25

36
RUN go get github.com/Jeffail/gabs
47
RUN go get github.com/bitly/go-simplejson
@@ -7,6 +10,3 @@ RUN go get github.com/antonholmquist/jason
710
RUN go get github.com/mreiferson/go-ujson
811
RUN go get -tags=unsafe -u github.com/ugorji/go/codec
912
RUN go get github.com/mailru/easyjson
10-
11-
WORKDIR /go/src/github.com/buger/jsonparser
12-
ADD . /go/src/github.com/buger/jsonparser

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
SOURCE = parser.go
22
CONTAINER = jsonparser
3-
SOURCE_PATH = /go/src/github.com/buger/jsonparser
3+
SOURCE_PATH = /go/src/github.com/securityguy/jsonparser
44
BENCHMARK = JsonParser
55
BENCHTIME = 5s
66
TEST = .

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
[![Go Report Card](https://goreportcard.com/badge/github.com/buger/jsonparser)](https://goreportcard.com/report/github.com/buger/jsonparser) ![License](https://img.shields.io/dub/l/vibe-d.svg)
1+
[![Go Report Card](https://goreportcard.com/badge/github.com/securityguy/jsonparser)](https://goreportcard.com/report/github.com/securityguy/jsonparser) ![License](https://img.shields.io/dub/l/vibe-d.svg)
22
# Alternative JSON parser for Go (10x times faster standard library)
33

4-
**This library was forked from buger/jsonparser on March 18, 2026.**
4+
**This library is a fork of [buger/jsonparser](https://github.com/buger/jsonparser), maintained at github.com/securityguy/jsonparser.**
55

66
It does not require you to know the structure of the payload (eg. create structs), and allows accessing fields by providing the path to them. It is up to **10 times faster** than standard `encoding/json` package (depending on payload size and usage), **allocates no memory**. See benchmarks below.
77

@@ -17,7 +17,7 @@ Goal of this project is to push JSON parser to the performance limits and not sa
1717
For the given JSON our goal is to extract the user's full name, number of github followers and avatar.
1818

1919
```go
20-
import "github.com/buger/jsonparser"
20+
import "github.com/securityguy/jsonparser"
2121

2222
...
2323

@@ -96,7 +96,7 @@ jsonparser.EachKey(data, func(idx int, value []byte, vt jsonparser.ValueType, er
9696

9797
Library API is really simple. You just need the `Get` method to perform any operation. The rest is just helpers around it.
9898

99-
You also can view API at [godoc.org](https://godoc.org/github.com/buger/jsonparser)
99+
You also can view API at [pkg.go.dev](https://pkg.go.dev/github.com/securityguy/jsonparser)
100100

101101

102102
### **`Get`**
@@ -250,7 +250,7 @@ Compared libraries:
250250
* https://github.com/ugorji/go/codec
251251
* https://github.com/pquerna/ffjson
252252
* https://github.com/mailru/easyjson
253-
* https://github.com/buger/jsonparser
253+
* https://github.com/securityguy/jsonparser
254254
255255
#### TLDR
256256
If you want to skip next sections we have 2 winner: `jsonparser` and `easyjson`.
@@ -270,7 +270,7 @@ With great power comes great responsibility! :)
270270
271271
Each test processes 190 bytes of http log as a JSON record.
272272
It should read multiple fields.
273-
https://github.com/buger/jsonparser/blob/master/benchmark/benchmark_small_payload_test.go
273+
https://github.com/securityguy/jsonparser/blob/master/benchmark/benchmark_small_payload_test.go
274274
275275
Library | time/op | bytes/op | allocs/op
276276
------ | ------- | -------- | -------
@@ -295,7 +295,7 @@ If you look at memory allocation, jsonparser has no rivals, as it makes no data
295295
Each test processes a 2.4kb JSON record (based on Clearbit API).
296296
It should read multiple nested fields and 1 array.
297297
298-
https://github.com/buger/jsonparser/blob/master/benchmark/benchmark_medium_payload_test.go
298+
https://github.com/securityguy/jsonparser/blob/master/benchmark/benchmark_medium_payload_test.go
299299
300300
| Library | time/op | bytes/op | allocs/op |
301301
| ------- | ------- | -------- | --------- |
@@ -324,7 +324,7 @@ Each test processes a 24kb JSON record (based on Discourse API)
324324
It should read 2 arrays, and for each item in array get a few fields.
325325
Basically it means processing a full JSON file.
326326
327-
https://github.com/buger/jsonparser/blob/master/benchmark/benchmark_large_payload_test.go
327+
https://github.com/securityguy/jsonparser/blob/master/benchmark/benchmark_large_payload_test.go
328328
329329
| Library | time/op | bytes/op | allocs/op |
330330
| --- | --- | --- | --- |

benchmark/benchmark_delete_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package benchmark
33
import (
44
"testing"
55

6-
"github.com/buger/jsonparser"
6+
"github.com/securityguy/jsonparser"
77
)
88

99
func BenchmarkDeleteSmall(b *testing.B) {

benchmark/benchmark_large_payload_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
package benchmark
77

88
import (
9-
"github.com/buger/jsonparser"
9+
"github.com/securityguy/jsonparser"
1010
"testing"
1111
// "github.com/Jeffail/gabs"
1212
// "github.com/bitly/go-simplejson"
@@ -19,16 +19,16 @@ import (
1919
)
2020

2121
/*
22-
github.com/buger/jsonparser
22+
github.com/securityguy/jsonparser
2323
*/
2424
func BenchmarkJsonParserLarge(b *testing.B) {
2525
for i := 0; i < b.N; i++ {
26-
jsonparser.ArrayEach(largeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
26+
jsonparser.ArrayEach(largeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err *error) {
2727
jsonparser.Get(value, "username")
2828
nothing()
2929
}, "users")
3030

31-
jsonparser.ArrayEach(largeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
31+
jsonparser.ArrayEach(largeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err *error) {
3232
jsonparser.GetInt(value, "id")
3333
jsonparser.Get(value, "slug")
3434
nothing()

benchmark/benchmark_medium_payload_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/a8m/djson"
1313
"github.com/antonholmquist/jason"
1414
"github.com/bitly/go-simplejson"
15-
"github.com/buger/jsonparser"
15+
"github.com/securityguy/jsonparser"
1616
jlexer "github.com/mailru/easyjson/jlexer"
1717
"github.com/mreiferson/go-ujson"
1818
"github.com/pquerna/ffjson/ffjson"
@@ -23,15 +23,15 @@ import (
2323
)
2424

2525
/*
26-
github.com/buger/jsonparser
26+
github.com/securityguy/jsonparser
2727
*/
2828
func BenchmarkJsonParserMedium(b *testing.B) {
2929
for i := 0; i < b.N; i++ {
3030
jsonparser.Get(mediumFixture, "person", "name", "fullName")
3131
jsonparser.GetInt(mediumFixture, "person", "github", "followers")
3232
jsonparser.Get(mediumFixture, "company")
3333

34-
jsonparser.ArrayEach(mediumFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
34+
jsonparser.ArrayEach(mediumFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err *error) {
3535
jsonparser.Get(value, "url")
3636
nothing()
3737
}, "person", "gravatar", "avatars")
@@ -69,7 +69,7 @@ func BenchmarkJsonParserEachKeyManualMedium(b *testing.B) {
6969
case 2:
7070
// jsonparser.ParseString(value)
7171
case 3:
72-
jsonparser.ArrayEach(value, func(avalue []byte, dataType jsonparser.ValueType, offset int, err error) {
72+
jsonparser.ArrayEach(value, func(avalue []byte, dataType jsonparser.ValueType, offset int, err *error) {
7373
jsonparser.Get(avalue, "url")
7474
})
7575
}
@@ -105,7 +105,7 @@ func BenchmarkJsonParserEachKeyStructMedium(b *testing.B) {
105105
json.Unmarshal(value, &data.Company) // we don't have a JSON -> map[string]interface{} function yet, so use standard encoding/json here
106106
case 3:
107107
var avatars []*CBAvatar
108-
jsonparser.ArrayEach(value, func(avalue []byte, dataType jsonparser.ValueType, offset int, err error) {
108+
jsonparser.ArrayEach(value, func(avalue []byte, dataType jsonparser.ValueType, offset int, err *error) {
109109
url, _ := jsonparser.ParseString(avalue)
110110
avatars = append(avatars, &CBAvatar{Url: url})
111111
})
@@ -141,7 +141,7 @@ func BenchmarkJsonParserObjectEachStructMedium(b *testing.B) {
141141
missing--
142142
case bytes.Equal(k, gravatarKey):
143143
var avatars []*CBAvatar
144-
jsonparser.ArrayEach(v, func(avalue []byte, dataType jsonparser.ValueType, offset int, err error) {
144+
jsonparser.ArrayEach(v, func(avalue []byte, dataType jsonparser.ValueType, offset int, err *error) {
145145
url, _ := jsonparser.ParseString(avalue)
146146
avatars = append(avatars, &CBAvatar{Url: url})
147147
}, "avatars")

benchmark/benchmark_set_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package benchmark
22

33
import (
4-
"github.com/buger/jsonparser"
4+
"github.com/securityguy/jsonparser"
55
"strconv"
66
"testing"
77
)

benchmark/benchmark_small_payload_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/a8m/djson"
1313
"github.com/antonholmquist/jason"
1414
"github.com/bitly/go-simplejson"
15-
"github.com/buger/jsonparser"
15+
"github.com/securityguy/jsonparser"
1616
jlexer "github.com/mailru/easyjson/jlexer"
1717
"github.com/mreiferson/go-ujson"
1818
"github.com/pquerna/ffjson/ffjson"
@@ -26,7 +26,7 @@ import (
2626
func nothing(_ ...interface{}) {}
2727

2828
/*
29-
github.com/buger/jsonparser
29+
github.com/securityguy/jsonparser
3030
*/
3131
func BenchmarkJsonParserSmall(b *testing.B) {
3232
for i := 0; i < b.N; i++ {

benchmark/go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ require (
66
github.com/antonholmquist/jason v1.0.0
77
github.com/bitly/go-simplejson v0.5.0
88
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
9-
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23
9+
github.com/securityguy/jsonparser v0.0.0-00010101000000-000000000000
1010
github.com/kr/pretty v0.1.0 // indirect
1111
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983
1212
github.com/mreiferson/go-ujson v0.0.0-20160507014224-e88340868a14
1313
github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7
1414
github.com/ugorji/go v1.1.4
1515
)
16+
17+
replace github.com/securityguy/jsonparser => ../

0 commit comments

Comments
 (0)