Skip to content

Commit 64079a4

Browse files
authored
Merge pull request #59 from fredbi/chore/perf-minor-optimizations
perf: minor performance optimization for escape/unescape
2 parents dcadc9b + 8f78758 commit 64079a4

File tree

3 files changed

+30
-23
lines changed

3 files changed

+30
-23
lines changed

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ linters:
1616
- godox
1717
- gosmopolitan
1818
- inamedparam
19-
- intrange # disabled while < go1.22
19+
#- intrange # disabled while < go1.22
2020
- ireturn
2121
- lll
2222
- musttag

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ Completed YES
1313
Tested YES
1414

1515
## References
16-
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
16+
17+
<https://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07>
18+
19+
also known as [RFC6901](https://www.rfc-editor.org/rfc/rfc6901)
1720

1821
### Note
22+
1923
The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented.
24+
25+
That is because our implementation of the JSON pointer only supports explicit references to array elements: the provision in the spec
26+
to resolve non-existent members as "the last element in the array", using the special trailing character "-".

pointer.go

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ const (
4141
pointerSeparator = `/`
4242
)
4343

44-
var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
45-
var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
44+
var (
45+
jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
46+
jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
47+
)
4648

4749
// JSONPointable is an interface for structs to implement when they need to customize the
4850
// json pointer process
@@ -56,18 +58,17 @@ type JSONSetable interface {
5658
JSONSet(string, any) error
5759
}
5860

59-
// Pointer the json pointer reprsentation
61+
// Pointer is a representation of a json pointer
6062
type Pointer struct {
6163
referenceTokens []string
6264
}
6365

6466
// New creates a new json pointer for the given string
6567
func New(jsonPointerString string) (Pointer, error) {
66-
6768
var p Pointer
6869
err := p.parse(jsonPointerString)
69-
return p, err
7070

71+
return p, err
7172
}
7273

7374
// Get uses the pointer to retrieve a value from a JSON document
@@ -80,7 +81,7 @@ func (p *Pointer) Set(document any, value any) (any, error) {
8081
return document, p.set(document, value, jsonname.DefaultJSONNameProvider)
8182
}
8283

83-
// DecodedTokens returns the decoded tokens
84+
// DecodedTokens returns the decoded tokens of this JSON pointer
8485
func (p *Pointer) DecodedTokens() []string {
8586
result := make([]string, 0, len(p.referenceTokens))
8687
for _, t := range p.referenceTokens {
@@ -102,9 +103,7 @@ func (p *Pointer) String() string {
102103
return emptyPointer
103104
}
104105

105-
pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
106-
107-
return pointerString
106+
return pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
108107
}
109108

110109
func (p *Pointer) Offset(document string) (int64, error) {
@@ -185,7 +184,7 @@ func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, refle
185184
func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error {
186185
knd := reflect.ValueOf(node).Kind()
187186

188-
if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
187+
if knd != reflect.Pointer && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
189188
return errors.Join(
190189
ErrUnsupportedValueType,
191190
ErrPointer,
@@ -225,7 +224,7 @@ func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error
225224
return err
226225
}
227226
fld := reflect.ValueOf(r)
228-
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
227+
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer {
229228
node = fld.Addr().Interface()
230229
continue
231230
}
@@ -240,7 +239,7 @@ func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error
240239
return fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer)
241240
}
242241
fld := rValue.FieldByName(nm)
243-
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
242+
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer {
244243
node = fld.Addr().Interface()
245244
continue
246245
}
@@ -253,7 +252,7 @@ func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error
253252
if !mv.IsValid() {
254253
return fmt.Errorf("object has no key %q: %w", decodedToken, ErrPointer)
255254
}
256-
if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
255+
if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Pointer {
257256
node = mv.Addr().Interface()
258257
continue
259258
}
@@ -270,7 +269,7 @@ func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error
270269
}
271270

272271
elem := rValue.Index(tokenIndex)
273-
if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
272+
if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Pointer {
274273
node = elem.Addr().Interface()
275274
continue
276275
}
@@ -291,7 +290,7 @@ func isNil(input any) bool {
291290

292291
kind := reflect.TypeOf(input).Kind()
293292
switch kind { //nolint:exhaustive
294-
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
293+
case reflect.Pointer, reflect.Map, reflect.Slice, reflect.Chan:
295294
return reflect.ValueOf(input).IsNil()
296295
default:
297296
return false
@@ -520,16 +519,17 @@ const (
520519
decRefTok1 = `/`
521520
)
522521

522+
var (
523+
encRefTokReplacer = strings.NewReplacer(encRefTok1, decRefTok1, encRefTok0, decRefTok0)
524+
decRefTokReplacer = strings.NewReplacer(decRefTok1, encRefTok1, decRefTok0, encRefTok0)
525+
)
526+
523527
// Unescape unescapes a json pointer reference token string to the original representation
524528
func Unescape(token string) string {
525-
step1 := strings.ReplaceAll(token, encRefTok1, decRefTok1)
526-
step2 := strings.ReplaceAll(step1, encRefTok0, decRefTok0)
527-
return step2
529+
return encRefTokReplacer.Replace(token)
528530
}
529531

530532
// Escape escapes a pointer reference token string
531533
func Escape(token string) string {
532-
step1 := strings.ReplaceAll(token, decRefTok0, encRefTok0)
533-
step2 := strings.ReplaceAll(step1, decRefTok1, encRefTok1)
534-
return step2
534+
return decRefTokReplacer.Replace(token)
535535
}

0 commit comments

Comments
 (0)