Skip to content

ByteBufferUtils ​

这是一个专为 java.nio.ByteBuffer 设计的工具类,旨在简化和扩展 ByteBuffer 的常用操作。ByteBuffer 是 Java NIO 的核心组件,用于高效的 I/O 操作,但其原生 API 在某些场景下使用起来较为繁琐,例如处理特定格式的数据(如按行读取)、复制、拆分等。ByteBufferUtils 封装了这些复杂操作,提供了更易于使用的静态方法。

设计理念 ​

  • 静态工具类: 所有方法都是静态的,无需实例化,方便直接调用。
  • 副作用明确:
    • 读取 (read) / 写入 (write) 方法: 通常会改变 ByteBuffer 的 position 指针,因为它们消耗或填充了数据。
    • 复制 (copy) 方法: 保留源缓冲区的 position、limit 和 mark;指定目标缓冲区的重载也保留目标的这些状态。搜索 (indexOf/lineEnd) 方法会推进 position。
  • 健壮性: 关键方法(如 readLine)包含了最大长度检查(maxlength),可以抛出 LengthOverflowException,防止恶意或错误的数据包耗尽服务器内存。
  • 协议友好: 提供了处理网络协议中常见数据格式(行、定长字符串、不同字节序的整数)的专用方法,极大地简化了网络编程中的编解码逻辑。

方法详解 ​

缓冲区操作 (Buffer Manipulation) ​

composite(ByteBuffer byteBuffer1, ByteBuffer byteBuffer2) ​

  • 功能: 将两个 ByteBuffer 的可读内容(从 position 到 limit)合并成一个新的 ByteBuffer。
  • 参数:
    • byteBuffer1: 第一个源缓冲区。
    • byteBuffer2: 第二个源缓冲区。
  • 返回: 一个新的 ByteBuffer,其内容是 byteBuffer1 和 byteBuffer2 可读部分的顺序拼接。新缓冲区的 position 为 0,limit 为其容量。
  • 场景: 当一个逻辑上的数据包被网络拆分成两个物理包时,可以用此方法将其拼接成一个完整的包进行处理。

copy(ByteBuffer src, int srcStartindex, ByteBuffer dest, int destStartIndex, int length) ​

  • 功能: 按缓冲区自身的索引复制指定范围,正确处理切片的数组偏移。支持堆缓冲区、直接缓冲区和只读源视图,并支持源与目标存储区重叠。
  • 注意: 索引必须非负,范围不能超过各自的 limit,目标必须可写。无效范围在复制前被拒绝;只读目标抛出 ReadOnlyBufferException。两个缓冲区的 position、limit 和 mark 保持不变。

copy(ByteBuffer src, int startindex, int endindex) ​

  • 功能: 复制源 ByteBuffer 中指定索引范围([startindex, endindex))的内容到一个新的 ByteBuffer 中。
  • 副作用: 无。此方法不改变源 ByteBuffer 的 position、limit 和 mark。范围按源缓冲区索引计算,并受 limit 限制。
  • 返回: 一个新的、包含了复制内容且已 flip() 的 ByteBuffer。

copy(ByteBuffer src) ​

  • 功能: 复制源 ByteBuffer 的所有可读内容(从 position 到 limit)到一个新的 ByteBuffer 中。
  • 副作用: 无。
  • 返回: 一个新的、包含了复制内容且已 flip() 的 ByteBuffer。

split(ByteBuffer src, int unitSize) ​

  • 功能: 将一个 ByteBuffer 按照指定的单元大小(unitSize)拆分成一个 ByteBuffer 数组。
  • 返回:
    • 如果 unitSize 大于或等于 src.remaining(),说明不需要拆分,返回 null。
    • 否则,返回一个 ByteBuffer 数组,其中每个元素都是一个分块,最后一个元素的大小可能是不足 unitSize 的剩余部分。
  • 场景: 当需要将一个大文件或大数据块分片发送时非常有用。

数据读取 (Data Reading) ​

readBytes(ByteBuffer buffer, int length) ​

  • 功能: 从 buffer 的当前 position 读取指定 length 的字节。
  • 副作用: 有。buffer 的 position 会向前移动 length。
  • 返回: 一个包含所读取字节的 byte[] 数组。

readString(ByteBuffer buffer, int length, String charset) ​

  • 功能: 从 buffer 读取指定 length 的字节,并使用指定的 charset 解码成字符串。
  • 副作用: 有。buffer 的 position 会向前移动 length。
  • 返回: 解码后的字符串。

readLine(ByteBuffer buffer, String charset, Integer maxlength) ​

  • 功能: 核心方法之一。从 buffer 的当前 position 开始,查找行分隔符(\n 或 \r\n),并读取这一行的数据作为字符串返回。
  • 副作用: 有。buffer 的 position 会移动到行分隔符之后,为下一次读取做准备。
  • 参数:
    • maxlength: 允许读取的最大行长度,用于防止因数据格式错误或恶意攻击导致的内存溢出。
  • 返回:
    • 如果找到一个完整的行,则返回该行内容的字符串(不包含 \r\n)。
    • 如果缓冲区中没有完整的行(即没有找到行分隔符),则返回 null,表示需要等待更多数据。
  • 场景: 处理基于文本行的协议,如 HTTP, SMTP, POP3, FTP 等。

readLine(ByteBuffer buffer, String charset) ​

  • 功能: readLine 的便捷版本,内部调用 readLine(buffer, charset, Integer.MAX_VALUE),即不限制最大行长度。

readString(ByteBuffer buffer, String charset, char endChar, Integer maxlength) ​

  • 功能: 从 buffer 读取字符串,直到遇到指定的结束字符 endChar 为止。
  • 副作用: 有。buffer 的 position 会移动到 endChar 之后。
  • 返回:
    • 如果找到 endChar,返回从开始到 endChar 之前的内容字符串。
    • 如果未找到,返回 null。

read(ByteBuffer buffer) / readShort(ByteBuffer buffer) ​

  • 功能: 分别从 buffer 读取一个 byte 或一个 short。
  • 副作用: 有。position 会相应移动 1 或 2 个字节。

readUB1(ByteBuffer buffer) / readUB2(ByteBuffer buffer) / readUB4(ByteBuffer buffer) ​

  • 功能: 读取 1、2 或 4 个字节,并将其解析为无符号整数(Unsigned Byte)。由于 Java 没有原生无符号类型,结果会存放在更大的数据类型中(如 int 或 long)。此系列方法按小端字节序 (Little-Endian) 读取。
  • 副作用: 有。

readUB2WithBigEdian(ByteBuffer buffer) / readUB4WithBigEdian(ByteBuffer buffer) ​

  • 功能: 同上,但按大端字节序 (Big-Endian) 读取。网络协议(如 TCP/IP)的标准字节序就是大端。
  • 副作用: 有。

数据写入 (Data Writing) ​

writeUB2(ByteBuffer buffer, int i) / writeUB4(ByteBuffer buffer, long l) ​

  • 功能: 将一个整数 i 或长整数 l 的低 2 或 4 个字节按小端字节序写入 buffer。
  • 副作用: 有。buffer 的 position 会相应移动 2 或 4 个字节。

writeUB2WithBigEdian(ByteBuffer buffer, int i) / writeUB4WithBigEdian(ByteBuffer buffer, long l) ​

  • 功能: 同上,但按大端字节序写入。
  • 副作用: 有。

搜索与定位 (Searching & Positioning) ​

lineEnd(ByteBuffer buffer, int maxlength) ​

  • 功能: 在 buffer 中从当前 position 开始查找行结束符(\n 或 \r\n)的位置。
  • 副作用: 有。找到换行符后,position 位于 \n 之后;未找到时推进到 limit,不会自动恢复。需要保留原缓冲区位置时,对 duplicate() 创建的视图执行搜索。
  • 返回:
    • 如果找到,返回行内容结束的索引(即 \r 或 \n 的位置)。
    • 如果未找到,返回 -1。
  • 注意: 这是 readLine 的底层实现。

indexOf(ByteBuffer buffer, char theChar, int maxlength) ​

  • 功能: 在 buffer 中从当前 position 开始查找指定字符 theChar 首次出现的位置。
  • 副作用: 有。找到时推进到匹配字节之后,未找到时推进到 limit。
  • 返回:
    • 如果找到,返回该字符的绝对索引。
    • 如果未找到,返回 -1。

编码转换 (Encoding) ​

getUtf8(byte[] bytes) ​

  • 功能: 一个将 byte[] 数组转换为 UTF-8 编码字符串的便捷方法。
  • 返回: UTF-8 字符串。

保留原始状态的复制示例 ​

java
import java.nio.ByteBuffer;
import nexus.io.tio.core.utils.ByteBufferUtils;

public class BufferCopyExample {
  public ByteBuffer prefix(ByteBuffer input, int length) {
    return ByteBufferUtils.copy(input, 0, length);
  }

  public void copyInto(ByteBuffer source, ByteBuffer destination, int length) {
    ByteBufferUtils.copy(source, source.position(), destination, destination.position(), length);
    // 复制字节后,两个缓冲区的位置仍保持原值。
  }
}

composite 会消费两个输入缓冲区的可读部分;split 使用独立视图,不移动原缓冲区位置。readLine 或按结束符读取字符串未找到分隔符时,也会推进位置。增量解码需要保存起始位置,或在独立视图上尝试解析。

MIT Licensed