mirror of
https://gitee.com/dromara/hutool.git
synced 2026-02-09 09:16:26 +08:00
fix code
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Hutool Team.
|
||||
*
|
||||
* 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 cn.hutool.v7.core.io;
|
||||
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
/**
|
||||
* 基于{@link char[]}的{@link Reader}实现,用于支持{@link char[]}的读取<br>
|
||||
* 相比jdk的{@link java.io.CharArrayReader}非线程安全,速度更快。
|
||||
*
|
||||
* @author Looly
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public class FastCharArrayReader extends Reader {
|
||||
protected char[] buf;
|
||||
protected int pos;
|
||||
protected int markedPos = 0;
|
||||
protected int count;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param str 字符串
|
||||
*/
|
||||
public FastCharArrayReader(final CharSequence str){
|
||||
this(StrUtil.toCharArray(str));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param buf 字符数组
|
||||
*/
|
||||
public FastCharArrayReader(final char[] buf) {
|
||||
this.buf = buf;
|
||||
this.pos = 0;
|
||||
this.count = buf.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param buf 字符数组
|
||||
* @param offset 起始位置
|
||||
* @param length 长度
|
||||
*/
|
||||
public FastCharArrayReader(final char[] buf, final int offset, final 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
ensureOpen();
|
||||
if (pos >= count)
|
||||
return -1;
|
||||
else
|
||||
return buf[pos++];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final char[] b, final int off, int len) throws IOException {
|
||||
ensureOpen();
|
||||
if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pos >= count) {
|
||||
return -1;
|
||||
}
|
||||
if (pos + len > count) {
|
||||
len = count - pos;
|
||||
}
|
||||
if (len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
System.arraycopy(buf, pos, b, off, len);
|
||||
pos += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
ensureOpen();
|
||||
if (pos + n > count) {
|
||||
n = count - pos;
|
||||
}
|
||||
if (n < 0) {
|
||||
return 0;
|
||||
}
|
||||
pos += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ready() throws IOException {
|
||||
ensureOpen();
|
||||
return (count - pos) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(final int readAheadLimit) throws IOException {
|
||||
ensureOpen();
|
||||
markedPos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
ensureOpen();
|
||||
pos = markedPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
buf = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保流打开
|
||||
*
|
||||
* @throws IOException 流关闭
|
||||
*/
|
||||
private void ensureOpen() throws IOException {
|
||||
if (buf == null) {
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4242,6 +4242,45 @@ public class CharSequenceUtil extends StrValidator {
|
||||
return (isCodePoint ? str.codePoints() : str.chars()).toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为字符数组
|
||||
*
|
||||
* @param cs 字符串
|
||||
* @return 字符数组
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public static char[] toCharArray(final CharSequence cs) {
|
||||
if (cs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int length = cs.length();
|
||||
if (length == 0) {
|
||||
return new char[0];
|
||||
}
|
||||
|
||||
// 额外优化:优先处理 String 实现类,使用原生 toCharArray() 更高效
|
||||
if (cs instanceof String) {
|
||||
return ((String) cs).toCharArray();
|
||||
}
|
||||
|
||||
// 其他 CharSequence 实现类,采用手动遍历拷贝
|
||||
final char[] resultArray = new char[length];
|
||||
|
||||
if (cs instanceof StringBuilder) {
|
||||
// 复制全部字符:从源索引0到length,写入目标数组索引0开始
|
||||
((StringBuilder) cs).getChars(0, length, resultArray, 0);
|
||||
} else if (cs instanceof StringBuffer) {
|
||||
// 复制全部字符:从源索引0到length,写入目标数组索引0开始
|
||||
((StringBuffer) cs).getChars(0, length, resultArray, 0);
|
||||
} else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
resultArray[i] = cs.charAt(i);
|
||||
}
|
||||
}
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历字符串的每个字符,并处理
|
||||
*
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Hutool Team.
|
||||
*
|
||||
* 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 cn.hutool.v7.core.io;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* FastCharArrayReader Unit Test Class
|
||||
* 快速字符数组读取器单元测试类
|
||||
*/
|
||||
public class FastCharArrayReaderTest {
|
||||
|
||||
private char[] testArray;
|
||||
private FastCharArrayReader reader;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// Initialize test data before each test method
|
||||
// 在每个测试方法前初始化测试数据
|
||||
testArray = "Hello World".toCharArray();
|
||||
reader = new FastCharArrayReader(testArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test constructor with valid character array")
|
||||
void testConstructorWithValidArray() {
|
||||
// Test normal construction with valid character array
|
||||
// 测试使用有效字符数组的正常构造
|
||||
assertNotNull(reader, "Reader should be created successfully");
|
||||
|
||||
// Create another instance to verify different arrays work
|
||||
// 创建另一个实例验证不同数组都能工作
|
||||
final char[] anotherArray = {'A', 'B', 'C'};
|
||||
final FastCharArrayReader anotherReader = new FastCharArrayReader(anotherArray);
|
||||
assertNotNull(anotherReader, "Another reader should be created successfully");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test constructor with empty array")
|
||||
void testConstructorWithEmptyArray() {
|
||||
// Test construction with empty array
|
||||
// 测试空数组构造
|
||||
final char[] emptyArray = {};
|
||||
final FastCharArrayReader emptyReader = new FastCharArrayReader(emptyArray);
|
||||
assertNotNull(emptyReader, "Reader with empty array should be created successfully");
|
||||
}
|
||||
|
||||
@SuppressWarnings({"resource", "DataFlowIssue"})
|
||||
@Test
|
||||
@DisplayName("Test constructor with null array - should handle appropriately")
|
||||
void testConstructorWithNullArray() {
|
||||
// Test construction with null array - expect exception or proper handling
|
||||
// 测试null数组构造 - 期望抛出异常或正确处理
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
new FastCharArrayReader((char[]) null);
|
||||
}, "Constructor should throw NullPointerException for null input");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test single character reading functionality")
|
||||
void testReadSingleCharacter() throws Exception {
|
||||
// Test reading characters one by one
|
||||
// 测试逐个读取字符
|
||||
assertEquals('H', reader.read(), "First character should be 'H'");
|
||||
assertEquals('e', reader.read(), "Second character should be 'e'");
|
||||
assertEquals('l', reader.read(), "Third character should be 'l'");
|
||||
assertEquals('l', reader.read(), "Fourth character should be 'l'");
|
||||
assertEquals('o', reader.read(), "Fifth character should be 'o'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test reading until end of stream")
|
||||
void testReadUntilEnd() throws Exception {
|
||||
// Read all characters and verify EOF (-1) is returned at the end
|
||||
// 读取所有字符并验证在末尾返回EOF(-1)
|
||||
for (int i = 0; i < testArray.length; i++) {
|
||||
final int result = reader.read();
|
||||
assertEquals(testArray[i], (char) result,
|
||||
String.format("Character at position %d should match", i));
|
||||
}
|
||||
|
||||
// After all characters are read, should return -1
|
||||
// 所有字符读取完后应该返回-1
|
||||
assertEquals(-1, reader.read(), "Should return -1 after reaching end of stream");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test batch reading with char array")
|
||||
void testBatchReading() throws Exception {
|
||||
// Test reading multiple characters into a buffer
|
||||
// 测试将多个字符读入缓冲区
|
||||
final char[] buffer = new char[5];
|
||||
final int charsRead = reader.read(buffer, 0, buffer.length);
|
||||
|
||||
assertEquals(5, charsRead, "Should read 5 characters");
|
||||
assertArrayEquals(new char[]{'H', 'e', 'l', 'l', 'o'},
|
||||
buffer, "Buffer should contain first 5 characters");
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
@Test
|
||||
@DisplayName("Test partial batch reading")
|
||||
void testPartialBatchReading() throws Exception {
|
||||
// Test reading when remaining characters are less than requested
|
||||
// 测试剩余字符少于请求数量时的读取
|
||||
// First, advance the reader position
|
||||
for (int i = 0; i < 8; i++) {
|
||||
reader.read(); // Skip first 8 characters
|
||||
}
|
||||
|
||||
final char[] buffer = new char[5]; // Request 5 more but only 3 remain
|
||||
final int charsRead = reader.read(buffer, 0, buffer.length);
|
||||
|
||||
assertEquals(3, charsRead, "Should read only 3 remaining characters");
|
||||
assertArrayEquals(new char[]{'r', 'l', 'd', '\u0000', '\u0000'},
|
||||
buffer, "Buffer should contain last 3 characters and null padding");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test reading with offset and length parameters")
|
||||
void testReadingWithOffsetAndLength() throws Exception {
|
||||
// Test reading with specific offset and length in buffer
|
||||
// 测试带偏移量和长度参数的读取
|
||||
final char[] buffer = new char[10];
|
||||
// Fill buffer with initial values to verify they're overwritten correctly
|
||||
// 用初始值填充缓冲区以验证它们被正确覆盖
|
||||
Arrays.fill(buffer, 'X');
|
||||
|
||||
final int charsRead = reader.read(buffer, 2, 5); // Start at index 2, read 5 chars
|
||||
|
||||
assertEquals(5, charsRead, "Should read 5 characters");
|
||||
assertEquals('X', buffer[0], "Position 0 should remain unchanged");
|
||||
assertEquals('X', buffer[1], "Position 1 should remain unchanged");
|
||||
assertEquals('H', buffer[2], "Position 2 should have first character");
|
||||
assertEquals('o', buffer[6], "Position 6 should have fifth character");
|
||||
assertEquals('X', buffer[7], "Position 7 should remain unchanged");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test boundary conditions for batch reading")
|
||||
void testBatchReadingBoundaryConditions() throws Exception {
|
||||
// Test various boundary conditions for batch reading
|
||||
// 测试批量读取的各种边界条件
|
||||
final char[] buffer = new char[testArray.length + 5]; // Larger buffer
|
||||
|
||||
final int charsRead = reader.read(buffer, 0, buffer.length);
|
||||
assertEquals(testArray.length, charsRead,
|
||||
"Should read exactly the number of available characters");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test invalid parameters for batch reading")
|
||||
void testInvalidParametersForBatchReading() throws Exception {
|
||||
// Test invalid parameters that should throw exceptions
|
||||
// 测试应该抛出异常的无效参数
|
||||
final char[] buffer = new char[5];
|
||||
|
||||
// Test negative offset
|
||||
// 测试负偏移量
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> {
|
||||
reader.read(buffer, -1, 3);
|
||||
});
|
||||
|
||||
// Test negative length
|
||||
// 测试负长度
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> {
|
||||
reader.read(buffer, 0, -1);
|
||||
});
|
||||
|
||||
// Test offset + length exceeding buffer size
|
||||
// 测试偏移量+长度超出缓冲区大小
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> {
|
||||
reader.read(buffer, 3, 10); // 3 + 10 > 5
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test skip functionality")
|
||||
void testSkipFunctionality() throws Exception {
|
||||
// Test skipping characters
|
||||
// 测试跳过字符功能
|
||||
final long skipped = reader.skip(3);
|
||||
assertEquals(3, skipped, "Should skip 3 characters");
|
||||
|
||||
assertEquals('l', reader.read(), "Next character after skipping 3 should be 'l' (position 4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test skip beyond available characters")
|
||||
void testSkipBeyondAvailableCharacters() throws Exception {
|
||||
// Test skipping more characters than available
|
||||
// 测试跳过超过可用字符数的字符
|
||||
final long skipped = reader.skip(100);
|
||||
assertEquals(testArray.length, skipped,
|
||||
"Should skip only as many characters as available");
|
||||
|
||||
assertEquals(-1, reader.read(), "Stream should be at end after skipping all");
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ResultOfMethodCallIgnored", "ConstantValue"})
|
||||
@Test
|
||||
@DisplayName("Test mark and reset functionality if supported")
|
||||
void testMarkAndReset() throws Exception {
|
||||
// Test mark/reset functionality if implemented
|
||||
// 如果实现了则测试标记/重置功能
|
||||
try {
|
||||
// Check if mark is supported
|
||||
// 检查是否支持标记
|
||||
if (reader.markSupported()) {
|
||||
reader.mark(0); // Mark current position
|
||||
// Read some characters
|
||||
// 读取一些字符
|
||||
reader.read(); reader.read(); reader.read();
|
||||
|
||||
reader.reset(); // Reset to marked position
|
||||
// Should be back at beginning
|
||||
// 应该回到开始位置
|
||||
assertEquals('H', reader.read(), "After reset should return to marked position");
|
||||
} else {
|
||||
// If not supported, verify appropriate behavior
|
||||
// 如果不支持,验证适当行为
|
||||
assertFalse(reader.markSupported(), "Mark should not be supported if not implemented");
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
// Handle cases where mark/reset might not be implemented
|
||||
// 处理未实现标记/重置的情况
|
||||
System.out.println("Mark/Reset functionality may not be implemented: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test close functionality")
|
||||
void testCloseFunctionality() throws Exception {
|
||||
// Test closing the reader
|
||||
// 测试关闭读取器
|
||||
reader.close();
|
||||
|
||||
// After closing, further operations might throw exceptions
|
||||
// 关闭后,进一步操作可能会抛出异常
|
||||
// This depends on implementation - some readers throw IOException after close
|
||||
// 这取决于实现 - 一些读取器在关闭后会抛出IOException
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test concurrent access safety if applicable")
|
||||
void testConcurrentAccessSafety() {
|
||||
// If the implementation claims to be thread-safe, test concurrent access
|
||||
// 如果实现声称是线程安全的,则测试并发访问
|
||||
// For now, just document that this would be tested if thread-safety was claimed
|
||||
// 目前,如果声明了线程安全性则会测试
|
||||
// Thread safety testing would involve multiple threads accessing the same instance
|
||||
// 线程安全性测试将涉及多线程访问同一实例
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user