From 7420a15e1cba9b46dad4667e7ab703602129cbeb Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Wed, 2 Oct 2024 21:30:06 +0000 Subject: [PATCH] Draft: CharArrayReader --- .../classes/java/io/CharArrayReader.java | 101 ++------ .../share/classes/java/io/Reader.java | 225 ++++++++++++++++++ test/jdk/java/io/Reader/Of.java | 6 +- 3 files changed, 246 insertions(+), 86 deletions(-) diff --git a/src/java.base/share/classes/java/io/CharArrayReader.java b/src/java.base/share/classes/java/io/CharArrayReader.java index 2b412ab530411..d61bbb89c0f98 100644 --- a/src/java.base/share/classes/java/io/CharArrayReader.java +++ b/src/java.base/share/classes/java/io/CharArrayReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,29 +36,15 @@ * @since 1.1 */ public class CharArrayReader extends Reader { - /** The character buffer. */ - protected char[] buf; - /** The current buffer position. */ - protected int pos; - - /** The position of mark in buffer. */ - protected int markedPos = 0; - - /** - * The index of the end of this buffer. There is no valid - * data at or beyond this index. - */ - protected int count; + private final Reader r; /** * Creates a CharArrayReader from the specified array of chars. * @param buf Input buffer (not copied) */ public CharArrayReader(char[] buf) { - this.buf = buf; - this.pos = 0; - this.count = buf.length; + this(buf, 0, buf.length); } /** @@ -79,20 +65,7 @@ public CharArrayReader(char[] buf) { * @param length Number of chars to read */ public CharArrayReader(char[] buf, int offset, int length) { - if ((offset < 0) || (offset > buf.length) || (length < 0) || - ((offset + length) < 0)) { - throw new IllegalArgumentException(); - } - this.buf = buf; - this.pos = offset; - this.count = Math.min(offset + length, buf.length); - this.markedPos = offset; - } - - /** Checks to make sure that the stream has not been closed */ - private void ensureOpen() throws IOException { - if (buf == null) - throw new IOException("Stream closed"); + r = Reader.of(buf, offset, length); } /** @@ -102,11 +75,7 @@ private void ensureOpen() throws IOException { */ public int read() throws IOException { synchronized (lock) { - ensureOpen(); - if (pos >= count) - return -1; - else - return buf[pos++]; + return r.read(); } } @@ -130,43 +99,14 @@ public int read() throws IOException { */ public int read(char[] cbuf, int off, int len) throws IOException { synchronized (lock) { - ensureOpen(); - Objects.checkFromIndexSize(off, len, cbuf.length); - if (len == 0) { - return 0; - } - - if (pos >= count) { - return -1; - } - - int avail = count - pos; - if (len > avail) { - len = avail; - } - if (len <= 0) { - return 0; - } - System.arraycopy(buf, pos, cbuf, off, len); - pos += len; - return len; + return r.read(cbuf, off, len); } } @Override public int read(CharBuffer target) throws IOException { synchronized (lock) { - ensureOpen(); - - if (pos >= count) { - return -1; - } - - int avail = count - pos; - int len = Math.min(avail, target.remaining()); - target.put(buf, pos, len); - pos += len; - return len; + return r.read(target); } } @@ -187,17 +127,7 @@ public int read(CharBuffer target) throws IOException { */ public long skip(long n) throws IOException { synchronized (lock) { - ensureOpen(); - - long avail = count - pos; - if (n > avail) { - n = avail; - } - if (n < 0) { - return 0; - } - pos += (int) n; - return n; + return r.skip(n); } } @@ -209,8 +139,7 @@ public long skip(long n) throws IOException { */ public boolean ready() throws IOException { synchronized (lock) { - ensureOpen(); - return (count - pos) > 0; + return r.ready(); } } @@ -235,8 +164,7 @@ public boolean markSupported() { */ public void mark(int readAheadLimit) throws IOException { synchronized (lock) { - ensureOpen(); - markedPos = pos; + r.mark(readAheadLimit); } } @@ -248,8 +176,7 @@ public void mark(int readAheadLimit) throws IOException { */ public void reset() throws IOException { synchronized (lock) { - ensureOpen(); - pos = markedPos; + r.reset(); } } @@ -262,7 +189,11 @@ public void reset() throws IOException { */ public void close() { synchronized (lock) { - buf = null; + try { + r.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } } } diff --git a/src/java.base/share/classes/java/io/Reader.java b/src/java.base/share/classes/java/io/Reader.java index ff09b2c891fba..0f3096b36374b 100644 --- a/src/java.base/share/classes/java/io/Reader.java +++ b/src/java.base/share/classes/java/io/Reader.java @@ -261,6 +261,231 @@ public void close() { }; } + /** + * Creates a CharArrayReader from the specified array of chars. + * + *

The resulting reader will start reading at the given + * {@code offset}. The total number of {@code char} values that can be + * read from this reader will be either {@code length} or + * {@code buf.length-offset}, whichever is smaller. + * + * @throws IllegalArgumentException + * If {@code offset} is negative or greater than + * {@code buf.length}, or if {@code length} is negative, or if + * the sum of these two values is negative. + * + * @param c Input buffer (not copied) + * + * @return Reader + */ + public static Reader of(char[] c) { + return Reader.of(c, 0, c.length); + } + + /** + * Creates a CharArrayReader from the specified array of chars. + * + *

The resulting reader will start reading at the given + * {@code offset}. The total number of {@code char} values that can be + * read from this reader will be either {@code length} or + * {@code buf.length-offset}, whichever is smaller. + * + * @throws IllegalArgumentException + * If {@code offset} is negative or greater than + * {@code buf.length}, or if {@code length} is negative, or if + * the sum of these two values is negative. + * + * @param c Input buffer (not copied) + * @param offset Offset of the first char to read + * @param length Number of chars to read + * + * @return Reader + */ + public static Reader of(char[] c, int offset, int length) { + if ((offset < 0) || (offset > c.length) || (length < 0) || + ((offset + length) < 0)) { + throw new IllegalArgumentException(); + } + + return new Reader() { + /** The character buffer. */ + private char[] buf = c; + + /** The current buffer position. */ + private int pos = offset; + + /** The position of mark in buffer. */ + private int markedPos = offset; + + /** + * The index of the end of this buffer. There is no valid + * data at or beyond this index. + */ + protected int count = Math.min(offset + length, c.length); + + /** Checks to make sure that the stream has not been closed */ + private void ensureOpen() throws IOException { + if (buf == null) + throw new IOException("Stream closed"); + } + + /** + * Reads a single character. + * + * @throws IOException If an I/O error occurs + */ + public int read() throws IOException { + ensureOpen(); + if (pos >= count) + return -1; + else + return buf[pos++]; + } + + /** + * Reads characters into a portion of an array. + * + *

If {@code len} is zero, then no characters are read and {@code 0} is + * returned; otherwise, there is an attempt to read at least one character. + * If no character is available because the stream is at its end, the value + * {@code -1} is returned; otherwise, at least one character is read and + * stored into {@code cbuf}. + * + * @param cbuf {@inheritDoc} + * @param off {@inheritDoc} + * @param len {@inheritDoc} + * + * @return {@inheritDoc} + * + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + public int read(char[] cbuf, int off, int len) throws IOException { + ensureOpen(); + Objects.checkFromIndexSize(off, len, cbuf.length); + if (len == 0) { + return 0; + } + + if (pos >= count) { + return -1; + } + + int avail = count - pos; + if (len > avail) { + len = avail; + } + if (len <= 0) { + return 0; + } + System.arraycopy(buf, pos, cbuf, off, len); + pos += len; + return len; + } + + @Override + public int read(CharBuffer target) throws IOException { + ensureOpen(); + + if (pos >= count) { + return -1; + } + + int avail = count - pos; + int len = Math.min(avail, target.remaining()); + target.put(buf, pos, len); + pos += len; + return len; + } + + /** + * Skips characters. If the stream is already at its end before this method + * is invoked, then no characters are skipped and zero is returned. + * + *

The {@code n} parameter may be negative, even though the + * {@code skip} method of the {@link Reader} superclass throws + * an exception in this case. If {@code n} is negative, then + * this method does nothing and returns {@code 0}. + * + * @param n {@inheritDoc} + * + * @return {@inheritDoc} + * + * @throws IOException {@inheritDoc} + */ + public long skip(long n) throws IOException { + ensureOpen(); + + long avail = count - pos; + if (n > avail) { + n = avail; + } + if (n < 0) { + return 0; + } + pos += (int) n; + return n; + } + + /** + * Tells whether this stream is ready to be read. Character-array readers + * are always ready to be read. + * + * @throws IOException If an I/O error occurs + */ + public boolean ready() throws IOException { + ensureOpen(); + return (count - pos) > 0; + } + + /** + * Tells whether this stream supports the mark() operation, which it does. + */ + public boolean markSupported() { + return true; + } + + /** + * Marks the present position in the stream. Subsequent calls to reset() + * will reposition the stream to this point. + * + * @param readAheadLimit Limit on the number of characters that may be + * read while still preserving the mark. Because + * the stream's input comes from a character array, + * there is no actual limit; hence this argument is + * ignored. + * + * @throws IOException If an I/O error occurs + */ + public void mark(int readAheadLimit) throws IOException { + ensureOpen(); + markedPos = pos; + } + + /** + * Resets the stream to the most recent mark, or to the beginning if it has + * never been marked. + * + * @throws IOException If an I/O error occurs + */ + public void reset() throws IOException { + ensureOpen(); + pos = markedPos; + } + + /** + * Closes the stream and releases any system resources associated with + * it. Once the stream has been closed, further read(), ready(), + * mark(), reset(), or skip() invocations will throw an IOException. + * Closing a previously closed stream has no effect. This method will block + * while there is another thread blocking on the reader. + */ + public void close() { + buf = null; + } + }; + } + /** * The object used to synchronize operations on this stream. For * efficiency, a character-stream object may use an object other than diff --git a/test/jdk/java/io/Reader/Of.java b/test/jdk/java/io/Reader/Of.java index 491c0499e6b1d..f1446664f638f 100644 --- a/test/jdk/java/io/Reader/Of.java +++ b/test/jdk/java/io/Reader/Of.java @@ -22,6 +22,7 @@ */ import java.io.Reader; +import java.io.CharArrayReader; import java.io.StringReader; import java.io.IOException; import java.io.StringWriter; @@ -77,7 +78,10 @@ public String toString() { // Reader.Of's result SHALL NOT convert to String throw new UnsupportedOperationException(); } - }) + }), + new CharArrayReader(CONTENT.toCharArray()), + Reader.of(CONTENT.toCharArray()), + Reader.of(CONTENT.toCharArray(), 0, CONTENT.length()) }; }