Skip to content

Commit 94ee533

Browse files
committed
PDFBOX-6083: optimize DataInputRandomAccessRead.readBytes() with a RandomAccess.readFully() method
git-svn-id: https://svn.apache.org/repos/asf/pdfbox/trunk@1929123 13f79535-47bb-0310-9956-ffa450edef68
1 parent e0e8b65 commit 94ee533

File tree

3 files changed

+147
-8
lines changed

3 files changed

+147
-8
lines changed

fontbox/src/main/java/org/apache/fontbox/cff/DataInputRandomAccessRead.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,8 @@ public byte[] readBytes(int length) throws IOException
170170
{
171171
throw new IOException("length is negative");
172172
}
173-
if (randomAccessRead.length() - randomAccessRead.getPosition() < length)
174-
{
175-
throw new IOException("Premature end of buffer reached");
176-
}
177173
byte[] bytes = new byte[length];
178-
for (int i = 0; i < length; i++)
179-
{
180-
bytes[i] = readByte();
181-
}
174+
randomAccessRead.readFully(bytes);
182175
return bytes;
183176
}
184177

io/src/main/java/org/apache/pdfbox/io/RandomAccessRead.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.apache.pdfbox.io;
1818

1919
import java.io.Closeable;
20+
import java.io.EOFException;
2021
import java.io.IOException;
2122

2223
/**
@@ -159,4 +160,44 @@ default void skip(int length) throws IOException
159160
*
160161
*/
161162
RandomAccessReadView createView(long startPosition, long streamLength) throws IOException;
163+
164+
/**
165+
* Same as {@link #read(byte[])} but will loop until exactly length bytes are read or
166+
* it will throw an exception.
167+
*
168+
* @param b The buffer to write the data to.
169+
* @throws IOException
170+
*/
171+
default void readFully(byte[] b) throws IOException
172+
{
173+
readFully(b, 0, b.length);
174+
}
175+
176+
/**
177+
* Same as {@link #read(byte[], int, int)} but will loop until exactly length bytes are read or
178+
* it will throw an exception.
179+
*
180+
* @param b The buffer to write the data to.
181+
* @param offset Offset into the buffer to start writing.
182+
* @param length The exact amount of data to attempt to read.
183+
* @throws IOException
184+
*/
185+
default void readFully(byte[] b, int offset, int length) throws IOException
186+
{
187+
if (length() - getPosition() < length)
188+
{
189+
throw new EOFException("Premature end of buffer reached");
190+
}
191+
int bytesReadTotal = 0;
192+
do
193+
{
194+
int bytesReadNow = read(b, offset + bytesReadTotal, length - bytesReadTotal);
195+
if (bytesReadNow <= 0)
196+
{
197+
throw new EOFException("EOF, should have been detected earlier");
198+
}
199+
bytesReadTotal += bytesReadNow;
200+
}
201+
while (bytesReadTotal < length);
202+
}
162203
}

io/src/test/java/org/apache/pdfbox/io/RandomAccessReadBufferedFileTest.java

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@
1616

1717
package org.apache.pdfbox.io;
1818

19+
import java.io.EOFException;
1920
import static org.junit.jupiter.api.Assertions.assertEquals;
2021
import static org.junit.jupiter.api.Assertions.assertFalse;
2122
import static org.junit.jupiter.api.Assertions.assertTrue;
2223

2324
import java.io.File;
25+
import java.io.FileInputStream;
2426
import java.io.IOException;
27+
import java.io.InputStream;
2528
import java.net.URISyntaxException;
29+
import java.nio.charset.StandardCharsets;
2630
import java.nio.file.Paths;
2731

2832
import org.junit.jupiter.api.Assertions;
@@ -191,4 +195,105 @@ void testView() throws IOException, URISyntaxException
191195
assertEquals(3, view.getPosition());
192196
}
193197
}
198+
199+
@Test
200+
void testReadFully1() throws IOException, URISyntaxException
201+
{
202+
try (RandomAccessRead randomAccessSource = new RandomAccessReadBufferedFile(
203+
new File(getClass().getResource("RandomAccessReadFile1.txt").toURI())))
204+
{
205+
byte[] b = new byte[10];
206+
randomAccessSource.seek(1);
207+
randomAccessSource.readFully(b);
208+
String s = new String(b, StandardCharsets.US_ASCII);
209+
assertEquals("1234567890", s);
210+
}
211+
}
212+
213+
@Test
214+
void testReadFully2() throws IOException, URISyntaxException
215+
{
216+
try (RandomAccessRead randomAccessSource = new RandomAccessReadBufferedFile(
217+
new File(getClass().getResource("RandomAccessReadFile1.txt").toURI())))
218+
{
219+
byte[] b = new byte[10];
220+
randomAccessSource.readFully(b, 2, 8);
221+
String s = new String(b, 2, 8, StandardCharsets.US_ASCII);
222+
assertEquals("01234567", s);
223+
assertEquals(0, b[0]);
224+
assertEquals(0, b[1]);
225+
}
226+
}
227+
228+
@Test
229+
void testReadFully3() throws IOException, URISyntaxException
230+
{
231+
try (RandomAccessRead randomAccessSource = new RandomAccessReadBufferedFile(
232+
new File(getClass().getResource("RandomAccessReadFile1.txt").toURI())))
233+
{
234+
byte[] b = new byte[10];
235+
randomAccessSource.seek(randomAccessSource.length() - b.length);
236+
randomAccessSource.readFully(b);
237+
String s = new String(b, StandardCharsets.US_ASCII);
238+
assertEquals("0123456789", s);
239+
}
240+
}
241+
242+
@Test
243+
void testReadFullyEOF() throws IOException, URISyntaxException
244+
{
245+
try (RandomAccessRead randomAccessSource = new RandomAccessReadBufferedFile(
246+
new File(getClass().getResource("RandomAccessReadFile1.txt").toURI())))
247+
{
248+
byte[] b = new byte[10];
249+
randomAccessSource.seek(randomAccessSource.length() - b.length + 1);
250+
Assertions.assertThrows(EOFException.class, () -> randomAccessSource.readFully(b));
251+
}
252+
}
253+
254+
@Test
255+
void testReadFullyExact() throws IOException, URISyntaxException
256+
{
257+
try (RandomAccessRead randomAccessSource = new RandomAccessReadBufferedFile(
258+
new File(getClass().getResource("RandomAccessReadFile1.txt").toURI())))
259+
{
260+
int length = (int) randomAccessSource.length();
261+
byte[] b = new byte[length];
262+
randomAccessSource.readFully(b);
263+
byte[] allBytes = getClass().getResourceAsStream("RandomAccessReadFile1.txt").readAllBytes();
264+
Assertions.assertArrayEquals(allBytes, b);
265+
}
266+
}
267+
268+
@Test
269+
void testReadFullyAcrossBuffers() throws IOException, URISyntaxException
270+
{
271+
int bufferLen;
272+
File file = new File("src/test/java/org/apache/pdfbox/io/NonSeekableRandomAccessReadInputStreamTest.java");
273+
try (RandomAccessRead randomAccessSource = new RandomAccessReadBufferedFile(file))
274+
{
275+
int length = (int) randomAccessSource.length();
276+
byte[] b = new byte[length];
277+
bufferLen = randomAccessSource.read(b);
278+
// make sure that the double buffer size is smaller than the length
279+
// if this test fails, we'll need a larger file
280+
assertTrue(bufferLen * 2 < length);
281+
}
282+
byte[] expectedBytes;
283+
try (InputStream is = new FileInputStream(file))
284+
{
285+
long skipped = is.skip(bufferLen / 2);
286+
assertEquals(skipped, bufferLen / 2);
287+
expectedBytes = new byte[bufferLen * 2];
288+
int actualRead = is.read(expectedBytes, 1, expectedBytes.length - 1);
289+
assertEquals(expectedBytes.length - 1, actualRead);
290+
}
291+
try (RandomAccessRead randomAccessSource = new RandomAccessReadBufferedFile(file))
292+
{
293+
randomAccessSource.seek(bufferLen / 2);
294+
byte[] b = new byte[bufferLen * 2];
295+
randomAccessSource.readFully(b, 1, b.length - 1);
296+
Assertions.assertArrayEquals(expectedBytes, b);
297+
}
298+
}
194299
}

0 commit comments

Comments
 (0)