@@ -31,7 +31,13 @@ public sealed class JsonPointer(
31
31
*/
32
32
public fun atIndex (index : Int ): JsonPointer {
33
33
require(index >= 0 ) { " negative index: $index " }
34
- return atProperty(index.toString())
34
+ return insertLast(
35
+ SegmentPointer (
36
+ propertyName = index.toString(),
37
+ depth = 1 ,
38
+ index = index,
39
+ ),
40
+ )
35
41
}
36
42
37
43
/* *
@@ -42,7 +48,10 @@ public sealed class JsonPointer(
42
48
* val pointer = JsonPointer.ROOT.atProperty("prop1").atProperty("prop2") // "/prop1/prop2"
43
49
* ```
44
50
*/
45
- public fun atProperty (property : String ): JsonPointer = insertLast(SegmentPointer (property))
51
+ public fun atProperty (property : String ): JsonPointer =
52
+ insertLast(
53
+ SegmentPointer (depth = 1 , propertyName = property),
54
+ )
46
55
47
56
override fun toString (): String {
48
57
val str = asString
@@ -68,7 +77,33 @@ public sealed class JsonPointer(
68
77
if (this !is SegmentPointer ) {
69
78
return last
70
79
}
71
- return insertLastDeepCopy(this , last)
80
+ if (depth < MAX_POINTER_DEPTH_FOR_RECURSIVE_INSERT ) {
81
+ return insertLastDeepCopy(this , last)
82
+ }
83
+ // avoid recursion when pointer depth is greater than a specified limit
84
+ // this should help with avoiding stack-overflow error
85
+ // when this method called for a pointer that has too many segments
86
+ //
87
+ // Using queue is less efficient than recursion (around 10%) but saves us from crash
88
+ val queue = ArrayDeque <SegmentPointer >(depth)
89
+ var cur: JsonPointer = this
90
+ while (cur is SegmentPointer ) {
91
+ queue.add(cur)
92
+ cur = cur.next
93
+ }
94
+ val additionalDepth = last.depth
95
+ var result = last
96
+ while (queue.isNotEmpty()) {
97
+ val segment = queue.removeLast()
98
+ result =
99
+ SegmentPointer (
100
+ propertyName = segment.propertyName,
101
+ depth = segment.depth + additionalDepth,
102
+ index = segment.index,
103
+ next = result,
104
+ )
105
+ }
106
+ return result
72
107
}
73
108
74
109
// there might be an issue with stack in case this function is called deep on the stack
@@ -77,15 +112,18 @@ public sealed class JsonPointer(
77
112
last : SegmentPointer ,
78
113
): JsonPointer =
79
114
with (pointer) {
115
+ val additionalDepth = last.depth
80
116
if (next is SegmentPointer ) {
81
117
SegmentPointer (
82
118
propertyName = propertyName,
119
+ depth = depth + additionalDepth,
83
120
index = index,
84
121
next = insertLastDeepCopy(next, last),
85
122
)
86
123
} else {
87
124
SegmentPointer (
88
125
propertyName = propertyName,
126
+ depth = depth + additionalDepth,
89
127
index = index,
90
128
next = last,
91
129
)
@@ -145,6 +183,7 @@ public sealed class JsonPointer(
145
183
}
146
184
147
185
public companion object {
186
+ private const val MAX_POINTER_DEPTH_FOR_RECURSIVE_INSERT = 20
148
187
internal const val SEPARATOR : Char = ' /'
149
188
internal const val QUOTATION : Char = ' ~'
150
189
internal const val QUOTATION_ESCAPE : Char = ' 0'
@@ -187,13 +226,15 @@ public sealed class JsonPointer(
187
226
lastSegment : SegmentPointer ,
188
227
parent : PointerParent ? ,
189
228
): JsonPointer {
229
+ var depth = lastSegment.depth
190
230
var curr = lastSegment
191
231
var parentValue = parent
192
232
while (parentValue != null ) {
193
233
curr =
194
234
parentValue.run {
195
235
SegmentPointer (
196
236
segment,
237
+ ++ depth,
197
238
curr,
198
239
)
199
240
}
@@ -283,6 +324,7 @@ internal object EmptyPointer : JsonPointer()
283
324
284
325
internal class SegmentPointer (
285
326
val propertyName : String ,
327
+ val depth : Int = 1 ,
286
328
override val next : JsonPointer = EmptyPointer ,
287
329
val index : Int = parseIndex(propertyName),
288
330
) : JsonPointer(next) {
0 commit comments