Skip to content

Commit ca48fdb

Browse files
committed
Merge pull request #24 from marcbachmann/performance-improvements
Performance improvement
2 parents 3537776 + f5de538 commit ca48fdb

File tree

2 files changed

+113
-80
lines changed

2 files changed

+113
-80
lines changed

benchmark.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
var jsonpointer = require('./')
2+
3+
var i
4+
var obj = {
5+
a: 1,
6+
b: {
7+
c: 2
8+
},
9+
d: {
10+
e: [{ a: 3 }, { b: 4 }, { c: 5 }]
11+
}
12+
}
13+
14+
// Get
15+
console.time('get first level property')
16+
for (i = 0; i < 1e6; i++) {
17+
jsonpointer.get(obj, '/a')
18+
}
19+
console.timeEnd('get first level property')
20+
21+
console.time('get second level property')
22+
for (i = 0; i < 1e6; i++) {
23+
jsonpointer.get(obj, '/d/e')
24+
}
25+
console.timeEnd('get second level property')
26+
27+
console.time('get third level property')
28+
for (i = 0; i < 1e6; i++) {
29+
jsonpointer.get(obj, '/d/e/0')
30+
}
31+
console.timeEnd('get third level property')
32+
33+
// Set
34+
console.time('set first level property')
35+
for (i = 0; i < 1e6; i++) {
36+
jsonpointer.set(obj, '/a', 'bla')
37+
}
38+
console.timeEnd('set first level property')
39+
40+
console.time('set second level property')
41+
for (i = 0; i < 1e6; i++) {
42+
jsonpointer.set(obj, '/d/e', 'bla')
43+
}
44+
console.timeEnd('set second level property')
45+
46+
console.time('set third level property')
47+
for (i = 0; i < 1e6; i++) {
48+
jsonpointer.set(obj, '/d/e/0', 'bla')
49+
}
50+
console.timeEnd('set third level property')
51+
52+
console.time('push property into array')
53+
for (i = 0; i < 1e6; i++) {
54+
jsonpointer.set(obj, '/d/e/-', 'bla')
55+
}
56+
console.timeEnd('push property into array')

jsonpointer.js

Lines changed: 57 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,82 @@
1-
var untilde = function (str) {
2-
return str.replace(/~./g, function (m) {
3-
switch (m) {
4-
case '~0': return '~'
5-
case '~1': return '/'
6-
}
7-
throw new Error('Invalid tilde escape: ' + m)
8-
})
1+
var hasExcape = /~/
2+
var escapeMatcher = /~[01]/g
3+
function escapeReplacer (m) {
4+
switch (m) {
5+
case '~1': return '/'
6+
case '~0': return '~'
7+
}
8+
throw new Error('Invalid tilde escape: ' + m)
99
}
1010

11-
var traverse = function (obj, pointer, value) {
12-
var part = untilde(pointer.shift())
13-
var isJustReading = arguments.length === 2
14-
15-
if (obj[part] == null) {
16-
if (isJustReading) return null
17-
18-
// support setting of /-
19-
if (part === '-' && obj instanceof Array) {
20-
part = obj.length
21-
}
22-
23-
// support nested objects/array when setting values
24-
var nextPart = pointer[0]
25-
if (nextPart === '-' || !isNaN(nextPart)) {
26-
obj[part] = []
27-
} else if (nextPart) {
28-
obj[part] = {}
29-
}
30-
}
11+
function untilde (str) {
12+
if (!hasExcape.test(str)) return str
13+
return str.replace(escapeMatcher, escapeReplacer)
14+
}
3115

32-
// keep traversing
33-
if (pointer.length !== 0) {
34-
if (isJustReading) {
35-
return traverse(obj[part], pointer)
36-
} else {
37-
return traverse(obj[part], pointer, value)
16+
function setter (obj, pointer, value) {
17+
var part
18+
var hasNextPart
19+
20+
for (var p = 1, len = pointer.length; p < len;) {
21+
part = untilde(pointer[p++])
22+
hasNextPart = len > p
23+
24+
if (typeof obj[part] === 'undefined') {
25+
// support setting of /-
26+
if (Array.isArray(obj) && part === '-') {
27+
part = obj.length
28+
}
29+
30+
// support nested objects/array when setting values
31+
if (hasNextPart) {
32+
if ((pointer[p] !== '' && pointer[p] < Infinity) || pointer[p] === '-') obj[part] = []
33+
else obj[part] = {}
34+
}
3835
}
39-
}
4036

41-
// we're done
42-
if (isJustReading) {
43-
return obj[part]
37+
if (!hasNextPart) break
38+
obj = obj[part]
4439
}
4540

46-
// set new value, return old value
4741
var oldValue = obj[part]
48-
if (value === null) {
49-
delete obj[part]
50-
} else {
51-
obj[part] = value
52-
}
42+
if (value === null) delete obj[part]
43+
else obj[part] = value
5344
return oldValue
5445
}
5546

56-
var compilePointer = function (pointer) {
57-
if (pointer === '') {
58-
return []
59-
}
60-
61-
if (!pointer) {
62-
throw new Error('Invalid JSON pointer.')
63-
}
64-
65-
if (!(pointer instanceof Array)) {
47+
function compilePointer (pointer) {
48+
if (typeof pointer === 'string') {
6649
pointer = pointer.split('/')
67-
if (pointer.shift() !== '') throw new Error('Invalid JSON pointer.')
68-
} else {
69-
// Clone the pointer array
70-
var newPointer = []
71-
for (var i = 0; i < pointer.length; i++) newPointer[i] = pointer[i]
72-
pointer = newPointer
50+
if (pointer[0] === '') return pointer
51+
throw new Error('Invalid JSON pointer.')
52+
} else if (Array.isArray(pointer)) {
53+
return pointer
7354
}
7455

75-
return pointer
56+
throw new Error('Invalid JSON pointer.')
7657
}
7758

78-
var validateInput = function (obj, pointer) {
79-
if (typeof obj !== 'object') {
80-
throw new Error('Invalid input object.')
81-
}
59+
function get (obj, pointer) {
60+
if (typeof obj !== 'object') throw new Error('Invalid input object.')
61+
pointer = compilePointer(pointer)
62+
var len = pointer.length
63+
if (len === 1) return obj
8264

83-
return compilePointer(pointer)
84-
}
85-
86-
var get = function (obj, pointer) {
87-
pointer = validateInput(obj, pointer)
88-
if (pointer.length === 0) {
89-
return obj
65+
for (var p = 1; p < len;) {
66+
obj = obj[untilde(pointer[p++])]
67+
if (len === p) return obj
68+
if (typeof obj !== 'object') return null
9069
}
91-
return traverse(obj, pointer)
9270
}
9371

94-
var set = function (obj, pointer, value) {
95-
pointer = validateInput(obj, pointer)
96-
if (pointer.length === 0) {
97-
throw new Error('Invalid JSON pointer for set.')
98-
}
99-
return traverse(obj, pointer, value)
72+
function set (obj, pointer, value) {
73+
if (typeof obj !== 'object') throw new Error('Invalid input object.')
74+
pointer = compilePointer(pointer)
75+
if (pointer.length === 0) throw new Error('Invalid JSON pointer for set.')
76+
return setter(obj, pointer, value)
10077
}
10178

102-
var compile = function (pointer) {
79+
function compile (pointer) {
10380
var compiled = compilePointer(pointer)
10481
return {
10582
get: function (object) {

0 commit comments

Comments
 (0)