Skip to content

Commit c77299f

Browse files
committed
Further work on #818 to reduce memory usage
1 parent 38d2454 commit c77299f

File tree

2 files changed

+42
-10
lines changed

2 files changed

+42
-10
lines changed

src/main/java/com/fasterxml/jackson/core/JsonPointer.java

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,17 @@ public class JsonPointer implements Serializable
6565
/**
6666
* We will retain representation of the pointer, as a String,
6767
* so that {@link #toString} should be as efficient as possible.
68+
*<p>
69+
* NOTE: starting with 2.14, there is no accompanying
70+
* {@link #_asStringOffset} that MUST be considered with this String;
6871
*/
6972
protected final String _asString;
7073

74+
/**
75+
* @since 2.14
76+
*/
77+
protected final int _asStringOffset;
78+
7179
protected final String _matchingPropertyName;
7280

7381
protected final int _matchingElementIndex;
@@ -88,21 +96,26 @@ protected JsonPointer() {
8896
_matchingPropertyName = null;
8997
_matchingElementIndex = -1;
9098
_asString = "";
99+
_asStringOffset = 0;
91100
}
92101

93102
// Constructor used for creating non-empty Segments
94-
protected JsonPointer(String fullString, String segment, JsonPointer next) {
103+
protected JsonPointer(String fullString, int fullStringOffset,
104+
String segment, JsonPointer next)
105+
{
95106
_asString = fullString;
107+
_asStringOffset = fullStringOffset;
96108
_nextSegment = next;
97109
// Ok; may always be a property
98110
_matchingPropertyName = segment;
99111
// but could be an index, if parsable
100112
_matchingElementIndex = _parseIndex(segment);
101113
}
102114

103-
// @since 2.5
104-
protected JsonPointer(String fullString, String segment, int matchIndex, JsonPointer next) {
115+
protected JsonPointer(String fullString, int fullStringOffset,
116+
String segment, int matchIndex, JsonPointer next) {
105117
_asString = fullString;
118+
_asStringOffset = fullStringOffset;
106119
_nextSegment = next;
107120
_matchingPropertyName = segment;
108121
_matchingElementIndex = matchIndex;
@@ -199,11 +212,11 @@ public static JsonPointer forPath(JsonStreamContext context,
199212
if (seg == null) { // is this legal?
200213
seg = "";
201214
}
202-
tail = new JsonPointer(_fullPath(tail, seg), seg, tail);
215+
tail = new JsonPointer(_fullPath(tail, seg), 0, seg, tail);
203216
} else if (context.inArray() || includeRoot) {
204217
int ix = context.getCurrentIndex();
205218
String ixStr = String.valueOf(ix);
206-
tail = new JsonPointer(_fullPath(tail, ixStr), ixStr, ix, tail);
219+
tail = new JsonPointer(_fullPath(tail, ixStr), 0, ixStr, ix, tail);
207220
}
208221
// NOTE: this effectively drops ROOT node(s); should have 1 such node,
209222
// as the last one, but we don't have to care (probably some paths have
@@ -517,7 +530,13 @@ public JsonPointer head() {
517530
/**********************************************************
518531
*/
519532

520-
@Override public String toString() { return _asString; }
533+
@Override public String toString() {
534+
if (_asStringOffset <= 0) {
535+
return _asString;
536+
}
537+
return _asString.substring(_asStringOffset);
538+
}
539+
521540
@Override public int hashCode() { return _asString.hashCode(); }
522541

523542
@Override public boolean equals(Object o) {
@@ -605,9 +624,9 @@ protected static JsonPointer _parseTail(String fullPath)
605624

606625
private static JsonPointer _buildPath(String fullPath, String segment,
607626
PointerParent parent) {
608-
JsonPointer curr = new JsonPointer(fullPath, segment, EMPTY);
627+
JsonPointer curr = new JsonPointer(fullPath, 0, segment, EMPTY);
609628
for (; parent != null; parent = parent.parent) {
610-
curr = new JsonPointer(parent.fullPath, parent.segment, curr);
629+
curr = new JsonPointer(parent.fullPath, 0, parent.segment, curr);
611630
}
612631
return curr;
613632
}
@@ -669,7 +688,8 @@ protected JsonPointer _constructHead()
669688
// and from that, length of suffix to drop
670689
int suffixLength = last._asString.length();
671690
JsonPointer next = _nextSegment;
672-
return new JsonPointer(_asString.substring(0, _asString.length() - suffixLength), _matchingPropertyName,
691+
return new JsonPointer(_asString.substring(0, _asString.length() - suffixLength), 0,
692+
_matchingPropertyName,
673693
_matchingElementIndex, next._constructHead(suffixLength, last));
674694
}
675695

@@ -680,7 +700,8 @@ protected JsonPointer _constructHead(int suffixLength, JsonPointer last)
680700
}
681701
JsonPointer next = _nextSegment;
682702
String str = _asString;
683-
return new JsonPointer(str.substring(0, str.length() - suffixLength), _matchingPropertyName,
703+
return new JsonPointer(str.substring(0, str.length() - suffixLength), 0,
704+
_matchingPropertyName,
684705
_matchingElementIndex, next._constructHead(suffixLength, last));
685706
}
686707

src/test/java/com/fasterxml/jackson/core/fuzz/Fuzz51806JsonPointerParse818Test.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
public class Fuzz51806JsonPointerParse818Test extends BaseTest
99
{
1010
// Before fix, looks like this is enough to cause StackOverflowError
11+
// private final static int TOO_DEEP_PATH = 50_000;
1112
private final static int TOO_DEEP_PATH = 10_000;
1213

1314
// Verify that a very deep/long (by number of segments) JsonPointer
@@ -28,6 +29,16 @@ private void _testJsonPointer(String pathExpr)
2829
assertNotNull(p);
2930
// But also verify it didn't change
3031
assertEquals(pathExpr, p.toString());
32+
33+
// And then verify segment by segment, easiest way is to
34+
// check that tail segment is proper substring
35+
JsonPointer curr = p;
36+
37+
while ((curr = curr.tail()) != null) {
38+
String act = curr.toString();
39+
String exp = pathExpr.substring(pathExpr.length() - act.length());
40+
assertEquals(exp, act);
41+
}
3142
}
3243

3344
private String _generatePath(int depth, boolean escaped) {

0 commit comments

Comments
 (0)