Skip to content

Commit fa48588

Browse files
committed
improve string parsing performance with CharSequenceReader
1 parent 2ee5bf1 commit fa48588

File tree

3 files changed

+190
-1
lines changed

3 files changed

+190
-1
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package org.json;
2+
3+
import java.io.Reader;
4+
5+
/**
6+
* a Reader to read from a {@link CharSequence} e.g. a {@link String}.
7+
*/
8+
public class CharSequenceReader extends Reader {
9+
10+
private final CharSequence charSequence;
11+
private int index;
12+
private int markedIndex;
13+
14+
/**
15+
* creates a new CharSequenceReader to read from the given CharSequence
16+
* @param charSequence the input to read.
17+
*/
18+
public CharSequenceReader(CharSequence charSequence) {
19+
this.charSequence = charSequence;
20+
}
21+
22+
/**
23+
* reads the next character from the input.
24+
* @return returns the read character or -1 if the end of the input is reached.
25+
*/
26+
@Override
27+
public int read() {
28+
if (index < charSequence.length()) {
29+
return charSequence.charAt(index++);
30+
}
31+
return -1;
32+
}
33+
34+
35+
/**
36+
* reads a maximum number of characters from the input into the charBuffer starting at the offset
37+
* and returns the number of actual read characters.
38+
*
39+
* @param charBuffer the target charBuffer to write the read characters into
40+
* @param offset the offset index where to start writing into the charBuffer
41+
* @param length the maximum number of characters to read
42+
* @return the number of characters read into the charBuffer or -1 if the end of the input is reached.
43+
*/
44+
@Override
45+
public int read(char[] charBuffer, int offset, int length) {
46+
if (charBuffer == null) {
47+
throw new NullPointerException("The target charBuffer cannot be null");
48+
} else if (offset < 0) {
49+
throw new IndexOutOfBoundsException("Offset must not be negative");
50+
} else if (length < 0) {
51+
throw new IndexOutOfBoundsException("Length must not be negative");
52+
} else if (offset + length > charBuffer.length) {
53+
throw new IndexOutOfBoundsException("Offset + length must not be larger than the charBuffer length");
54+
} else if (index >= charSequence.length()) {
55+
return -1;
56+
}
57+
58+
int charsToRead = Math.min(length, charSequence.length() - index);
59+
for (int i = 0; i < charsToRead; i++) {
60+
charBuffer[offset + i] = charSequence.charAt(index++);
61+
}
62+
63+
return charsToRead;
64+
}
65+
66+
/**
67+
* returns true to indicate that mark is supported.
68+
* @return true
69+
*/
70+
@Override
71+
public boolean markSupported() {
72+
return true;
73+
}
74+
75+
/**
76+
* marks the reader on the current position to later jump back to it by using {@link #reset()}.
77+
* @param readAheadLimit will be ignored. This reader has no readAheadLimit.
78+
*/
79+
@Override
80+
public void mark(int readAheadLimit) {
81+
markedIndex = this.index;
82+
}
83+
84+
/**
85+
* resets the reader to the previous marked position used by {@link #mark(int)}.
86+
*/
87+
@Override
88+
public void reset() {
89+
index = markedIndex;
90+
}
91+
92+
93+
/**
94+
* does nothing.
95+
*/
96+
@Override
97+
public void close() {
98+
99+
}
100+
101+
}

src/main/java/org/json/JSONTokener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public JSONTokener(InputStream inputStream) {
6767
* @param s A source string.
6868
*/
6969
public JSONTokener(String s) {
70-
this(new StringReader(s));
70+
this(new CharSequenceReader(s));
7171
}
7272

7373

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.json.junit;
2+
3+
import org.json.CharSequenceReader;
4+
import org.junit.Test;
5+
6+
import static org.junit.Assert.*;
7+
8+
public class CharSequenceReaderTest {
9+
10+
@Test
11+
public void testReadSingleCharacter() {
12+
CharSequenceReader reader = new CharSequenceReader("test");
13+
assertEquals('t', reader.read());
14+
assertEquals('e', reader.read());
15+
assertEquals('s', reader.read());
16+
assertEquals('t', reader.read());
17+
assertEquals(-1, reader.read());
18+
assertEquals(-1, reader.read());
19+
}
20+
21+
@Test
22+
public void testReadToCharBuffer() {
23+
CharSequenceReader reader = new CharSequenceReader("test");
24+
char[] buffer = new char[10];
25+
int read = reader.read(buffer, 2, 3);
26+
assertEquals(3, read);
27+
assertArrayEquals(new char[]{0, 0, 't', 'e', 's', 0, 0, 0, 0, 0}, buffer);
28+
}
29+
30+
@Test
31+
public void testReadPastEnd() {
32+
CharSequenceReader reader = new CharSequenceReader("test");
33+
char[] buffer = new char[5];
34+
assertEquals(4, reader.read(buffer, 0, 5));
35+
assertEquals(-1, reader.read(buffer, 0, 5));
36+
}
37+
38+
@Test
39+
public void testMarkAndReset() {
40+
CharSequenceReader reader = new CharSequenceReader("test");
41+
assertEquals('t', reader.read());
42+
reader.mark(0);
43+
assertEquals('e', reader.read());
44+
assertEquals('s', reader.read());
45+
reader.reset();
46+
assertEquals('e', reader.read());
47+
}
48+
49+
@Test(expected = NullPointerException.class)
50+
public void testReadToNullBuffer() {
51+
CharSequenceReader reader = new CharSequenceReader("test");
52+
reader.read(null, 0, 1);
53+
}
54+
55+
@Test(expected = IndexOutOfBoundsException.class)
56+
public void testNegativeOffset() {
57+
CharSequenceReader reader = new CharSequenceReader("test");
58+
char[] buffer = new char[10];
59+
reader.read(buffer, -1, 5);
60+
}
61+
62+
@Test(expected = IndexOutOfBoundsException.class)
63+
public void testNegativeLength() {
64+
CharSequenceReader reader = new CharSequenceReader("test");
65+
char[] buffer = new char[10];
66+
reader.read(buffer, 0, -5);
67+
}
68+
69+
@Test(expected = IndexOutOfBoundsException.class)
70+
public void testOffsetPlusLengthExceedsBuffer() {
71+
CharSequenceReader reader = new CharSequenceReader("test");
72+
char[] buffer = new char[5];
73+
reader.read(buffer, 3, 3);
74+
}
75+
76+
@Test
77+
public void testCloseDoesNothing() {
78+
CharSequenceReader reader = new CharSequenceReader("test");
79+
reader.close();
80+
assertEquals('t', reader.read());
81+
}
82+
83+
@Test
84+
public void testMarkSupported() {
85+
CharSequenceReader reader = new CharSequenceReader("test");
86+
assertTrue(reader.markSupported());
87+
}
88+
}

0 commit comments

Comments
 (0)