Library enhancement proposal for copying data from/to Reader/Writer InputStream/OutputStream

Patrick Reinhart patrick at reini.net
Thu Dec 4 19:45:18 UTC 2014


Hi Pavel,

> Patrick, thanks a lot for doing this! I've had a look at these usages,  and I
> think it's safe to say that in the JDK it has never been required (yet) to
> provide a copy method with custom byte array. So let's skip it for now.

Welcome, unfortunately I did not manage it to check those on the JDK9 
code base due the fact, that the NetBeans project does not work with the 
modularized codebase jet.

> I think more and more about your initial suggestion of introducing a 'copyTo'
> method directly into the types:
>
>     InputStream, OutputStream, Readable and Appendable

Having a "copyTo" method on InputStream and Readable was indeed my fist 
idea to have. Also to have a "copyFrom" method on the OutputStream and 
Appendable was then a natural fit. Looking at those methods it became 
clear to me that in fact we would have a the same code on either 
InputStream and OutputStream or Readable and Appendable.

Of course for OutputStream we could also have something like:

public long copyFrom(InputStream source) throws IOException {
     return source.copyTo(this);
}

For the Appendable could it be the same same with a Readable as the 
source in that case.

> I believe it will suit us a lot more. If we go this way, we can fully utilize
> polymorphic calls. Implementations could then provide their own more efficient
> solutions. Have a look at this:
>
> /**
>   * Reads all remaining bytes from this input stream and writes them to
>   * a specified output stream.
>   * <p>
>   * There are no guarantees on streams state in a case error occurs. Even
>   * if method returns normally you can't rely on previously marked positions
>   * or the contents of any internal buffers.
>   * That said, it is a terminal operation. It is strongly recommended that
>   * both streams are promptly closed after this method returns:
>   * <pre>{@code
>   *     try (InputStream is = ...; OutputStream os = ...;) {
>   *         is.read(os);
>   *     } catch (IOException e) {
>   *         ...
>   *     }
>   * }</pre>
>   * <p>
>   * This method may block indefinitely reading from the input stream
>   * (or writing to the output stream). The behavior for the case that the
>   * input and output streams is <i>asynchronously closed</i> or the thread
>   * interrupted during the copy is highly input and output stream
>   * system provider specific and therefore not specified.
>   *
>   * @param  target      the output stream to write to
>   *                     ({@code target != null})
>   * @return             the number of bytes copied
>   * @throws IOException if an I/O error occurs when reading or writing
>   *
>   * @since 1.9
>   */
> public long copyTo(OutputStream target) throws IOException {
>      Objects.requireNonNull(target, "target");
>      int copied = 0;
>      byte[] buffer = new byte[8192];
>      for (int read; (read = this.read(buffer)) > -1; copied += read) {
>          target.write(buffer, 0, read);
>      }
>      return copied;
> }
>
> This method will be defined in java.io.InputStream. And the following methods
> will override 'copyTo' in java.io.ByteArrayInputStream and
> java.io.BufferedInputStream respectively:

Should those methods also be on the FilterInput- and OutputStream?

> /**
>   * {@inheritDoc}
>   */
> @Override
> public synchronized long copyTo(OutputStream target) throws IOException {
>      Objects.requireNonNull(target, "target");
>      int avail = count - pos;
>      target.write(buf, pos, avail);
>      return avail;
> }
>
> /**
>   * {@inheritDoc}
>   */
> @Override
> public synchronized long copyTo(OutputStream target) throws IOException {
>      Objects.requireNonNull(target, "target");
>      long copied = 0;
>      // 1. Get whatever is left unread in the buffer
>      if (pos < count) {
>          int len = count - pos;
>          target.write(getBufIfOpen(), pos, len);
>          copied += len;
>      }
>      // 2. Get everything else directly from the underlying stream
>      for (int read; (read = getInIfOpen().read(getBufIfOpen())) > -1;
>           copied += read) {
>          target.write(getBufIfOpen(), 0, read);
>      }
>      return copied;
> }
>
>
> 1. Please note, that this is not the actual code that will be pushed (if at
>     all). It's merely a sketch to illustrate your proposal.
> 2. The equivalent thing will apply for Readable/Appendable.
>
> -Pavel

I like that solution almost more then having a separate utility class 
though.

I'm not quit sure about the impact on may already existing customer code 
that had implemented such a method without declaring a IOException for 
example. In this case the existing code would may break? (as a comment 
of Alan Bateman earlier before)

-Patrick



More information about the core-libs-dev mailing list