Skip to content

Commit e52a2ae

Browse files
Sergey Tsypanovjaikiran
Sergey Tsypanov
authored andcommittedApr 6, 2023
8304745: Lazily initialize byte[] in java.io.BufferedInputStream
Reviewed-by: liach, bpb, jpai
1 parent 6580c4e commit e52a2ae

File tree

1 file changed

+45
-11
lines changed

1 file changed

+45
-11
lines changed
 

‎src/java.base/share/classes/java/io/BufferedInputStream.java

+45-11
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public class BufferedInputStream extends FilterInputStream {
5757

5858
private static final int DEFAULT_BUFFER_SIZE = 8192;
5959

60+
private static final byte[] EMPTY = new byte[0];
61+
6062
/**
6163
* As this class is used early during bootstrap, it's motivated to use
6264
* Unsafe.compareAndSetObject instead of AtomicReferenceFieldUpdater
@@ -70,6 +72,9 @@ public class BufferedInputStream extends FilterInputStream {
7072
// initialized to null when BufferedInputStream is sub-classed
7173
private final InternalLock lock;
7274

75+
// initial buffer size (DEFAULT_BUFFER_SIZE or size specified to constructor)
76+
private final int initialSize;
77+
7378
/**
7479
* The internal buffer array where the data is stored. When necessary,
7580
* it may be replaced by another array of
@@ -166,16 +171,42 @@ private InputStream getInIfOpen() throws IOException {
166171
}
167172

168173
/**
169-
* Check to make sure that buffer has not been nulled out due to
170-
* close; if not return it;
174+
* Returns the internal buffer, optionally allocating it if empty.
175+
* @param allocateIfEmpty true to allocate if empty
176+
* @throws IOException if the stream is closed (buf is null)
171177
*/
172-
private byte[] getBufIfOpen() throws IOException {
178+
private byte[] getBufIfOpen(boolean allocateIfEmpty) throws IOException {
173179
byte[] buffer = buf;
174-
if (buffer == null)
180+
if (allocateIfEmpty && buffer == EMPTY) {
181+
buffer = new byte[initialSize];
182+
if (!U.compareAndSetReference(this, BUF_OFFSET, EMPTY, buffer)) {
183+
// re-read buf
184+
buffer = buf;
185+
}
186+
}
187+
if (buffer == null) {
175188
throw new IOException("Stream closed");
189+
}
176190
return buffer;
177191
}
178192

193+
/**
194+
* Returns the internal buffer, allocating it if empty.
195+
* @throws IOException if the stream is closed (buf is null)
196+
*/
197+
private byte[] getBufIfOpen() throws IOException {
198+
return getBufIfOpen(true);
199+
}
200+
201+
/**
202+
* Throws IOException if the stream is closed (buf is null).
203+
*/
204+
private void ensureOpen() throws IOException {
205+
if (buf == null) {
206+
throw new IOException("Stream closed");
207+
}
208+
}
209+
179210
/**
180211
* Creates a {@code BufferedInputStream}
181212
* and saves its argument, the input stream
@@ -205,13 +236,15 @@ public BufferedInputStream(InputStream in, int size) {
205236
if (size <= 0) {
206237
throw new IllegalArgumentException("Buffer size <= 0");
207238
}
208-
buf = new byte[size];
209-
210-
// use monitors when BufferedInputStream is sub-classed
239+
initialSize = size;
211240
if (getClass() == BufferedInputStream.class) {
241+
// use internal lock and lazily create buffer when not subclassed
212242
lock = InternalLock.newLockOrNull();
243+
buf = EMPTY;
213244
} else {
245+
// use monitors and eagerly create buffer when subclassed
214246
lock = null;
247+
buf = new byte[size];
215248
}
216249
}
217250

@@ -307,7 +340,8 @@ private int read1(byte[] b, int off, int len) throws IOException {
307340
if there is no mark/reset activity, do not bother to copy the
308341
bytes into the local buffer. In this way buffered streams will
309342
cascade harmlessly. */
310-
if (len >= getBufIfOpen().length && markpos == -1) {
343+
int size = Math.max(getBufIfOpen(false).length, initialSize);
344+
if (len >= size && markpos == -1) {
311345
return getInIfOpen().read(b, off, len);
312346
}
313347
fill();
@@ -374,7 +408,7 @@ public int read(byte[] b, int off, int len) throws IOException {
374408
}
375409

376410
private int implRead(byte[] b, int off, int len) throws IOException {
377-
getBufIfOpen(); // Check for closed stream
411+
ensureOpen();
378412
if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
379413
throw new IndexOutOfBoundsException();
380414
} else if (len == 0) {
@@ -421,7 +455,7 @@ public long skip(long n) throws IOException {
421455
}
422456

423457
private long implSkip(long n) throws IOException {
424-
getBufIfOpen(); // Check for closed stream
458+
ensureOpen();
425459
if (n <= 0) {
426460
return 0;
427461
}
@@ -544,7 +578,7 @@ public void reset() throws IOException {
544578
}
545579

546580
private void implReset() throws IOException {
547-
getBufIfOpen(); // Cause exception if closed
581+
ensureOpen();
548582
if (markpos < 0)
549583
throw new IOException("Resetting to invalid mark");
550584
pos = markpos;

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on Apr 6, 2023

@openjdk-notifier[bot]
Please sign in to comment.