Skip to content

Commit c9ddb37

Browse files
Fix DataReader.findNewLine with multiple lone EOL character
When there is multiple lone CR within a buffer, the virtual index is accumulated each time with the relative index. The virtual index may be incorrectly seen as >= max, which ignores CRLN within max. - Fix virtual index accumulation - Re-use the branch that changes the buffer - Add unit tests
1 parent bf787bf commit c9ddb37

2 files changed

Lines changed: 35 additions & 13 deletions

File tree

common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -412,22 +412,13 @@ public int findNewLine(int max) throws IncorrectNewLineException {
412412
+ ") without preceding CR. :\n" + this.debugDataHex());
413413
}
414414
}
415-
// not found, continue with next buffer
416-
idx += maxLength;
417-
if (idx >= max) {
418-
// not found and reached the limit
419-
return max;
420-
}
421-
n = n.next();
422-
indexWithinNode = n.position;
423-
continue;
424415
} else {
425416
// found, next byte should be LF
426417
if (crIndex == barr.length - 1) {
427418
// found CR as the last byte of the current node, peek next node
428419
byte nextByte = n.next().peek();
429420
if (nextByte == Bytes.LF_BYTE) {
430-
return idx + crIndex - n.position;
421+
return idx + crIndex - indexWithinNode;
431422
}
432423
if (!ignoreLoneEol) {
433424
throw new IncorrectNewLineException("Found CR (" + (idx + crIndex - n.position)
@@ -437,23 +428,25 @@ public int findNewLine(int max) throws IncorrectNewLineException {
437428
// found CR within the current array
438429
byte nextByte = barr[crIndex + 1];
439430
if (nextByte == Bytes.LF_BYTE) {
440-
return idx + crIndex - n.position;
431+
return idx + crIndex - indexWithinNode;
441432
}
442433
if (!ignoreLoneEol) {
443434
throw new IncorrectNewLineException("Found CR (" + idx
444435
+ ") without following LF. :\n" + this.debugDataHex());
445436
}
437+
idx += (crIndex - indexWithinNode + 1);
446438
indexWithinNode = crIndex + 1;
447-
idx += indexWithinNode;
448439
if (idx >= max) {
449440
return max;
450441
}
451442
continue;
452443
}
453444
}
454445

446+
// not found, continue with next buffer
455447
idx += maxLength;
456448
if (idx >= max) {
449+
// not found and reached the limit
457450
return max;
458451
}
459452
n = n.next();

common/buffers/src/test/java/io/helidon/common/buffers/DataReaderTest.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package io.helidon.common.buffers;
1818

19+
import java.nio.charset.StandardCharsets;
1920
import java.util.concurrent.atomic.AtomicReference;
2021

2122
import org.junit.jupiter.api.Test;
@@ -30,7 +31,7 @@ void testFindNewLineWithLoneCR() {
3031
// reading N bytes at a time until a new line is found
3132
// with data containing a lone CR
3233

33-
byte[] data = new byte[] {0, 0, (byte) '\r', 0, (byte) '\r', (byte) '\n'};
34+
byte[] data = "00\r0\r\n".getBytes(StandardCharsets.US_ASCII);
3435
AtomicReference<byte[]> ref = new AtomicReference<>(data);
3536
DataReader dataReader = new DataReader(() -> ref.getAndSet(null), true);
3637

@@ -41,4 +42,32 @@ void testFindNewLineWithLoneCR() {
4142
dataReader.skip(n);
4243
assertThat(dataReader.findNewLine(n), is(0));
4344
}
45+
46+
@Test
47+
void testFindNewLineWithMultipleLoneCR() {
48+
// if the virtual index is accumulated with the relative index for each lone CR
49+
// it may exceed max and the new line is ignored
50+
51+
byte[] data = "00\r\r\r\n".getBytes(StandardCharsets.US_ASCII);
52+
AtomicReference<byte[]> ref = new AtomicReference<>(data);
53+
DataReader dataReader = new DataReader(() -> ref.getAndSet(null), true);
54+
55+
int n = 5;
56+
assertThat(dataReader.findNewLine(n), is(4));
57+
}
58+
59+
@Test
60+
void testFindNewLineWithMultipleLoneWithinMax() {
61+
// if the virtual index is not updated for each lone CR
62+
// the computed search range is too big and a value greater than max is returned
63+
64+
byte[] data = "00\r00\r\n00".getBytes(StandardCharsets.US_ASCII);
65+
AtomicReference<byte[]> ref = new AtomicReference<>(data);
66+
DataReader dataReader = new DataReader(() -> ref.getAndSet(null), true);
67+
68+
int n = 4;
69+
assertThat(dataReader.findNewLine(n), is(n));
70+
dataReader.skip(n);
71+
assertThat(dataReader.findNewLine(n), is(1));
72+
}
4473
}

0 commit comments

Comments
 (0)