Skip to content

Commit 385b5ef

Browse files
Claude Opus 4.5claude
andcommitted
Performance: Add FastReader.readUntil() for bulk character reading
- Reads characters into buffer until one of two delimiters is found - Delimiter character is NOT consumed - remains for next read() - Optimized for scanning strings (stop at quote or backslash) - Enables bulk string parsing optimization in json-io JsonParser Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9172dff commit 385b5ef

2 files changed

Lines changed: 59 additions & 0 deletions

File tree

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
* Now compares URLs using `toExternalForm()` for reliable, deterministic comparison
77
* **MAINTENANCE**: Migrated test files from deprecated `JsonIo.toObjects()` to `JsonIo.toJava().asClass()` API
88
* Updated 8 calls in `CompactMapTest` and `ConverterEverythingTest`
9+
* **PERFORMANCE**: Added `FastReader.readUntil()` for bulk character reading until delimiter
10+
* Reads characters into destination buffer until one of two delimiters is found
11+
* Delimiter character is left unconsumed for subsequent read
12+
* Enables bulk string parsing optimization in json-io's JsonParser
913

1014
#### 4.89.0 - 2026-01-31
1115
* **PERFORMANCE**: `FastReader.getLastSnippet()` now returns bounded 200-char context

src/main/java/com/cedarsoftware/util/FastReader.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,61 @@ public int read(char[] cbuf, int off, int len) {
148148
return charsRead;
149149
}
150150

151+
/**
152+
* Reads characters into the destination array until one of the two delimiter characters is found.
153+
* The delimiter character is NOT consumed - it remains available for the next read() call.
154+
* This method is optimized for scanning strings where we want to read until we hit a quote or backslash.
155+
*
156+
* @param dest the destination buffer to read characters into
157+
* @param off the offset in the destination buffer to start writing
158+
* @param maxLen the maximum number of characters to read
159+
* @param delim1 first delimiter character to stop at (typically quote char)
160+
* @param delim2 second delimiter character to stop at (typically backslash)
161+
* @return the number of characters read (not including delimiter), or -1 if EOF reached before any chars read
162+
*/
163+
public int readUntil(char[] dest, int off, int maxLen, char delim1, char delim2) {
164+
if (in == null) {
165+
ExceptionUtilities.uncheckedThrow(new IOException("in is null"));
166+
}
167+
168+
int totalRead = 0;
169+
170+
// First, drain any pushback buffer
171+
while (pushbackPosition < pushbackBufferSize && totalRead < maxLen) {
172+
char c = pushbackBuffer[pushbackPosition];
173+
if (c == delim1 || c == delim2) {
174+
// Found delimiter in pushback - don't consume it
175+
return totalRead > 0 ? totalRead : 0;
176+
}
177+
dest[off++] = c;
178+
pushbackPosition++;
179+
totalRead++;
180+
}
181+
182+
// Now read from main buffer
183+
while (totalRead < maxLen) {
184+
fill();
185+
if (limit == -1) {
186+
// EOF reached
187+
return totalRead > 0 ? totalRead : -1;
188+
}
189+
190+
// Scan current buffer for delimiters
191+
while (position < limit && totalRead < maxLen) {
192+
char c = buf[position];
193+
if (c == delim1 || c == delim2) {
194+
// Found delimiter - don't consume it
195+
return totalRead;
196+
}
197+
dest[off++] = c;
198+
position++;
199+
totalRead++;
200+
}
201+
}
202+
203+
return totalRead;
204+
}
205+
151206
@Override
152207
public void close() {
153208
if (in != null) {

0 commit comments

Comments
 (0)