On Wed, 22 Mar 2023 20:34:08 GMT, Sergey Tsypanov <stsypa...@openjdk.org> wrote:

> By default `BufferedInputStream` is constructed with internal buffer with 
> capacity 8192. In some cases this buffer is never used, e.g. when we call 
> `IS.readNBytes()` or `IS.readAllBytes()` (relying on `BIS.read1()`) or when 
> `BufferedInputStream` is cascaded.

Also, you should document `getBufIfOpen` to indicate that if a caller simply 
wants to ensure open, they should call `getInIfOpen` instead.

With the `getClass() == BufferedInputStream.class` check, the impact to 
subclasses is totally averted, and most BufferedInputStream usages, I believe, 
do not involve subclasses. And this patch should be acceptable without 
compatibility concerns.

Marked as reviewed by liach (Author).

This change exposes the `CLOSED` array and modifies the specification for 
`buf`, both of which are protected and thus public APIs. Having the 
subclass-accessible `buf` `null` after constructor is executed may 
significantly affect subclasses that reuse such a buffer.

I mean that additional API exposure is acceptable and backward compatible, but 
the previous contract to subclasses that the protected `buf` is initialized 
after superclass constructor execution breaks. Before this change proceeds, we 
have to check how many subclasses of `BufferedInputStream` access this `buf` in 
a wide range of libraries and evaluate the impact, which I anticipate to be 
huge.

A slightly less impactful approach would be sharing a `byte[0]` for a newly 
initialized state, like in ArrayList, so users anticipating a closed 
`BufferedInputStream` to have a null `buf` will still work. But users 
anticipating a usable buf after constructor will still break: for instance, 
since `getBufIfOpen` is private but the `buf` field it uses is protected, 
nothing prevents subclasses from copying the same implementation over; and the 
same implementation will stop working with this new optimization.

src/java.base/share/classes/java/io/BufferedInputStream.java line 78:

> 76:     private final InternalLock lock;
> 77: 
> 78:     /**

These should be reverted, now that the subclasses will see the same behavior as 
before.

src/java.base/share/classes/java/io/BufferedInputStream.java line 211:

> 209:             throw new IllegalArgumentException("Buffer size <= 0");
> 210:         }
> 211:         buf = new byte[0];

Two recommendations:
1. This `new byte[0]` can be safely shared, so I recommend you to put it in a 
static final field to avoid reallocation
2. The subclass compatibility can be retained by:
Suggestion:

        buf = (getClass() == BufferedInputStream.class) ? new byte[0] : new 
byte[size];

src/java.base/share/classes/java/io/BufferedInputStream.java line 216:

> 214:             throw new IllegalArgumentException("Buffer size <= 0");
> 215:         }
> 216:         buf = getClass() == BufferedInputStream.class ? EMPTY : new 
> byte[size];

Actually, you can merge this `getClass()` check with the one below for locks.

src/java.base/share/classes/java/io/BufferedInputStream.java line 217:

> 215:         }
> 216:         this.size = size;
> 217:         // use monitors when BufferedInputStream is sub-classed

Maybe update this comment to be like:
Suggestion:

        // keep using monitors and initializing buf when BufferedInputStream is 
sub-classed
        // for compatibility

-------------

PR Review: https://git.openjdk.org/jdk/pull/13150#pullrequestreview-1354736052
PR Review: https://git.openjdk.org/jdk/pull/13150#pullrequestreview-1354917871
PR Comment: https://git.openjdk.org/jdk/pull/13150#issuecomment-1480363127
PR Comment: https://git.openjdk.org/jdk/pull/13150#issuecomment-1480711123
PR Review Comment: https://git.openjdk.org/jdk/pull/13150#discussion_r1146272964
PR Review Comment: https://git.openjdk.org/jdk/pull/13150#discussion_r1146268740
PR Review Comment: https://git.openjdk.org/jdk/pull/13150#discussion_r1146390661
PR Review Comment: https://git.openjdk.org/jdk/pull/13150#discussion_r1146465984

Reply via email to