Library enhancement proposal for copying data from/to Reader/Writer InputStream/OutputStream
Pavel Rappo
pavel.rappo at oracle.com
Thu Nov 27 15:56:59 UTC 2014
Patrick, as soon as I get feedback from Alan and Paul I'll merge these changes into the current version.
1. I wonder if it makes sense to provide an additional method where client specifies CharBuffer to be used?
Moreover if the first argument is CharSequence then we don't even need a buffer:
/**
* Reads all characters from a readable and appends them to an appendable.
*
* @param source the readable to read from ({@code source != null})
* @param target the appendable to append to ({@code target != null})
*
* @return the number of characters copied
*
* @throws IOException if an I/O error occurs when reading or appending
*/
public static long copy(Readable source, Appendable target)
throws IOException {
Objects.requireNonNull(source, "source");
Objects.requireNonNull(target, "target");
if (source instanceof CharSequence)
return copy1((CharSequence) source, target);
return copy0(source, target, CharBuffer.allocate(BUFFER_SIZE));
}
/**
* Reads all characters from a readable and appends them to an appendable.
*
* @param source the readable to read from ({@code source != null})
* @param target the appendable to append to ({@code target != null})
* @param buffer the intermediate buffer to use
* ({@code buffer != null && buffer.hasRemaining()
* && !buffer.isReadOnly()})
* @return the number of characters copied
* @throws IOException if an I/O error occurs when reading or appending
*/
public static long copy(Readable source, Appendable target,
CharBuffer buffer) throws IOException {
Objects.requireNonNull(source, "source");
Objects.requireNonNull(target, "target");
Objects.requireNonNull(buffer, "buffer");
if (!buffer.hasRemaining())
throw new IllegalArgumentException("Insufficient buffer size");
if (buffer.isReadOnly())
throw new IllegalArgumentException("buffer is read only");
return copy0(source, target, buffer);
}
private static long copy1(CharSequence source, Appendable target)
throws IOException {
long copied = source.length();
target.append(source);
return copied;
}
private static long copy0(Readable source, Appendable target,
CharBuffer buffer) throws IOException {
2. I think guts of the copy method could be changed to something like this:
long copied = 0L;
+ int oldLim = buffer.limit();
for (int read; (read = source.read(buffer)) > -1; copied += read) {
buffer.flip();
target.append(buffer, 0, read);
+ buffer.limit(oldLim);
}
return copied;
Otherwise on each iteration you're potentially shrinking available buffer storage based on
amount of data read into it (if I'm not mistaken). So each time you should restore the limit.
3. We should think of revisiting and including the only method from sun.misc.IOUtils. Removing
the class and reparenting its dependencies.
-Pavel
> diff -r 194458fe7339 src/java.base/share/classes/java/io/IOUtil.java
> --- a/src/java.base/share/classes/java/io/IOUtil.java Thu Nov 27 13:50:36 2014 +0100
> +++ b/src/java.base/share/classes/java/io/IOUtil.java Thu Nov 27 16:08:09 2014 +0100
> @@ -47,10 +47,28 @@
> /**
> * Reads all bytes from an input stream and writes them to an output stream.
> *
> - * @param source the input stream to read from
> - * @param target the output stream to write to
> - * @return the number of bytes successfully copied
> - * @throws IOException if an I/O error occurs when reading or writing
> + * <p> If an I/O error occurs reading from the input stream or writing to
> + * the output stream, then it may do so after the some bytes have been read
> + * or written. Consequently the input stream may not be at end of stream and
> + * may be in an inconsistent state. It is strongly recommended that the input
> + * and output stream be promptly closed if an I/O error occurs.
> + *
> + * <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 stream 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 source
> + * the input stream to read from
> + * @param target
> + * the output stream to write to
> + *
> + * @return the number of bytes successfully copied
> + * @throws IOException
> + * if an I/O error occurs when reading or writing. In particular,
> + * an <code>IOException</code> may be thrown if the output stream
> + * has been closed.
> */
> public static long copy(InputStream source, OutputStream target)
> throws IOException {
> @@ -62,16 +80,34 @@
> /**
> * Reads all bytes from an input stream and writes them to an output stream.
> *
> - * @param source the input stream to read from (not {@code null})
> - * @param target the output stream to write to (not {@code null})
> - * @param buffer the intermediate buffer to use (not {@code null},
> - * and {@code buffer.length >= 1})
> - * @return the number of bytes successfully copied
> - * @throws IOException if an I/O error occurs when reading or writing
> + * <p> If an I/O error occurs reading from the input stream or writing to
> + * the output stream, then it may do so after the some bytes have been read
> + * or written. Consequently the input stream may not be at end of stream and
> + * may be in an inconsistent state. It is strongly recommended that the input
> + * and output stream be promptly closed if an I/O error occurs.
> + *
> + * <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 stream 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 source
> + * the input stream to read from (not {@code null})
> + * @param target
> + * the output stream to write to (not {@code null})
> + * @param buffer
> + * the intermediate buffer to use (not {@code null},
> + * and {@code buffer.length >= 1})
> + *
> + * @return the number of bytes successfully copied
> + * @throws IOException
> + * if an I/O error occurs when reading or writing. In particular,
> + * an <code>IOException</code> may be thrown if the output stream
> + * has been closed.
> */
> public static long copy(InputStream source, OutputStream target,
> byte[] buffer) throws IOException {
> -
> Objects.requireNonNull(source, "source");
> Objects.requireNonNull(target, "target");
> Objects.requireNonNull(buffer, "buffer");
> @@ -92,23 +128,39 @@
> /**
> * Reads all characters from an readable and writes them to an appendable.
> *
> - * @param source the readable to read from
> - * @param target the appendable to write to
> + * <p> If an I/O error occurs reading from the readable or writing to the
> + * appendable, then it may do so after the some characters have been read
> + * or written. Consequently the readable may not be at end of data and may
> + * be in an inconsistent state. It is strongly recommended that the readable
> + * and writable be promptly closed (in case of closables) if an I/O error
> + * occurs.
> *
> - * @return the number of characters successfully read and written
> + * <p> This method may block indefinitely reading from the readable (or
> + * writing to the appendable). The behavior for the case that the input and
> + * appendable is <i>asynchronously closed</i> or the thread interrupted
> + * during the copy is highly readable and appendable system provider specific
> + * and therefore not specified.
> *
> - * @throws IOException if an I/O error occurs when reading or writing
> + * @param source
> + * the readable to read from
> + * @param target
> + * the appendable to write to
> + *
> + * @return the number of characters successfully copied
> + *
> + * @throws IOException
> + * if an I/O error occurs when reading or writing.
> */
> public static long copy(Readable source, Appendable target) throws IOException {
> - long totalRead = 0L;
> + Objects.requireNonNull(source, "source");
> + Objects.requireNonNull(target, "target");
> + long copied = 0L;
> CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE);
> - int read;
> - while ((read = source.read(buffer)) > -1) {
> + for (int read; (read = source.read(buffer)) > -1; copied += read) {
> buffer.flip();
> target.append(buffer, 0, read);
> - totalRead += read;
> }
> - return totalRead;
> + return copied;
> }
> }
>
>
More information about the core-libs-dev
mailing list