diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/CharSequenceReader.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/CharSequenceReader.java new file mode 100644 index 000000000..79612f612 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/CharSequenceReader.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.core.io; + +import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.text.StrUtil; + +import java.io.Reader; +import java.util.Objects; + +/** + * 基于{@link CharSequence}的{@link Reader}实现,用于支持{@link CharSequence}的读取
+ * 相比jdk的StringReader非线程安全,速度更快。 + * + * @author Looly + * @since 6.0.0 + */ +public class CharSequenceReader extends Reader { + + private final int start; + private final int end; + + private final CharSequence str; + /** + * 读取到的位置 + */ + private int next; + /** + * 读取标记位置 + */ + private int mark; + + /** + * 构造 + * + * @param str {@link CharSequence} + * @param startInclude 开始位置(包含) + * @param endExclude 结束位置(不包含) + */ + public CharSequenceReader(CharSequence str, final int startInclude, final int endExclude) { + Assert.isTrue(startInclude >= 0, "Start index is less than zero: {}", startInclude); + Assert.isTrue(endExclude > startInclude, "End index is less than start {}: {}", startInclude, endExclude); + + if (null == str) { + str = StrUtil.EMPTY; + } + this.str = str; + final int length = str.length(); + this.start = Math.min(length, startInclude); + this.end = Math.min(length, endExclude); + + this.next = startInclude; + this.mark = startInclude; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public void mark(final int readAheadLimit) { + mark = next; + } + + @Override + public void reset() { + next = mark; + } + + @Override + public int read() { + if (next >= end) { + return IoUtil.EOF; + } + return str.charAt(next++); + } + + @Override + public int read(final char[] array, final int offset, final int length) { + if (next >= end) { + return IoUtil.EOF; + } + Objects.requireNonNull(array, "array"); + if (length < 0 || offset < 0 || offset + length > array.length) { + throw new IndexOutOfBoundsException("Array Size=" + array.length + + ", offset=" + offset + ", length=" + length); + } + + if (str instanceof String) { + final int count = Math.min(length, end - next); + ((String) str).getChars(next, next + count, array, offset); + next += count; + return count; + } + if (str instanceof StringBuilder) { + final int count = Math.min(length, end - next); + ((StringBuilder) str).getChars(next, next + count, array, offset); + next += count; + return count; + } + if (str instanceof StringBuffer) { + final int count = Math.min(length, end - next); + ((StringBuffer) str).getChars(next, next + count, array, offset); + next += count; + return count; + } + + int count = 0; + for (int i = 0; i < length; i++) { + final int c = read(); + if (c == IoUtil.EOF) { + return count; + } + array[offset + i] = (char) c; + count++; + } + return count; + } + + @Override + public long skip(final long n) { + if (n < 0) { + throw new IllegalArgumentException("Number of characters to skip is less than zero: " + n); + } + if (next >= end) { + return 0; + } + final int dest = (int) Math.min(end, next + n); + final int count = dest - next; + next = dest; + return count; + } + + @Override + public boolean ready() { + return next < end; + } + + @Override + public void close() { + next = start; + mark = start; + } + + @Override + public String toString() { + return str.subSequence(start, end).toString(); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/FastStringReader.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/FastStringReader.java deleted file mode 100644 index 57b3df033..000000000 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/FastStringReader.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2013-2024 Hutool Team and hutool.cn - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.dromara.hutool.core.io; - -import java.io.IOException; -import java.io.Reader; - -/** - * 快速字符串读取,相比jdk的StringReader非线程安全,速度更快。 - * - * @author looly - */ -public class FastStringReader extends Reader { - - private String str; - private final int length; - - private int next = 0; - private int mark = 0; - - /** - * 构造 - * - * @param s 供读取的String - */ - public FastStringReader(final String s) { - this.str = s; - this.length = s.length(); - } - - /** - * 读取单个字符 - * - * @return 读取的字符, -1表示读取结束 - * @throws IOException IO异常 - */ - @Override - public int read() throws IOException { - ensureOpen(); - if (next >= length) { - return -1; - } - return str.charAt(next++); - } - - /** - * 将多个字符读取到提供的字符数组中 - * - * @param charBuffer 目标buffer - * @param off 开始位置 - * @param len 读取最大长度 - * @return 读取的字符长度, -1表示读取到了末尾 - * @throws IOException IO异常 - */ - @Override - public int read(final char[] charBuffer, final int off, final int len) throws IOException { - ensureOpen(); - if ((off < 0) || (off > charBuffer.length) || (len < 0) || - ((off + len) > charBuffer.length) || ((off + len) < 0)) { - throw new IndexOutOfBoundsException(); - } - - if (len == 0) { - return 0; - } - if (next >= length) { - return -1; - } - - final int n = Math.min(length - next, len); - str.getChars(next, next + n, charBuffer, off); - next += n; - return n; - } - - /** - * 跳过指定长度,返回跳过的字符数。 - * - *

{@code ns} 参数可能为负数, 负数表示向前跳过,跳到开头则停止 - * - *

如果字符串所有字符被读取或跳过, 此方法无效,始终返回0. - * - * @throws IOException IO异常 - */ - @Override - public long skip(final long ns) throws IOException { - ensureOpen(); - if (next >= length){ - return 0; - } - - // Bound skip by beginning and end of the source - long n = Math.min(length - next, ns); - n = Math.max(-next, n); - next += n; - return n; - } - - @Override - public boolean ready() throws IOException { - ensureOpen(); - return true; - } - - @Override - public boolean markSupported() { - return true; - } - - @Override - public void mark(final int readAheadLimit) throws IOException { - if (readAheadLimit < 0) { - throw new IllegalArgumentException("Read-ahead limit < 0"); - } - ensureOpen(); - mark = next; - } - - @Override - public void reset() throws IOException { - ensureOpen(); - next = mark; - } - - @Override - public void close() { - str = null; - } - - /** - * Check to make sure that the stream has not been closed - */ - private void ensureOpen() throws IOException { - if (str == null){ - throw new IOException("Stream closed"); - } - } -}