All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.power4j.coca.kit.common.io.buffer.ByteData Maven / Gradle / Ivy

/*
 * Copyright 2021 ChenJun ([email protected])
 *
 * 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 com.power4j.coca.kit.common.io.buffer;

import com.power4j.coca.kit.common.text.Display;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.springframework.lang.Nullable;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.Iterator;
import java.util.Random;

/**
 * Container to store byte data.
 * 

*

 *     Buffer Layout
 *     +-----------+-------------+
 *     | readable  |  writeable  |
 *     +-----------+-------------+
 *                 ^             ^
 *             writeIndex     capacity
 * 
*
    *
  • This class provides various write methods like {@code writeU8()} to write data into * a buffer. The buffer may need to be reallocated during writing to accommodate the * data.
  • *
  • To delegate writing to a {@code ByteBufferWriter}, use {@code bufferWriter()}. In * this case, ensureWriteBytes() needs to be called beforehand to allocate enough space in * the buffer.
  • *
  • To delegate reading to a {@code ByteBufferWriter}, use {@code bufferReader()}.
  • *
* * @author cj * @date 2023/7/21 * @since 1.0 */ public class ByteData implements Display { private static final byte[] EMPTY_BUFFER = new byte[0]; private byte[] buffer; private int writeIndex; /** * 无数据构造 * @param capacity 初始容量 */ protected ByteData(int capacity) { this(capacity == 0 ? EMPTY_BUFFER : new byte[capacity], 0); } /** * 拷贝数据构造 * @param src 源数据 * @param offset 偏移量 * @param length 拷贝长度,可以大于源数据长度,将进行默认填充 */ protected ByteData(byte[] src, int offset, int length) { this(Arrays.copyOfRange(src, offset, offset + length), length); } /** * 共享数据构造 * @param buffer 初始buffer * @param writeIndex 初始写指针 */ protected ByteData(byte[] buffer, int writeIndex) { this.buffer = buffer; this.writeIndex = writeIndex; } // ~ static method // =================================================================================================== /** * 创建BinData对象 * @param original 源数据 * @return 新的BinData对象,初始内部buffer指向源数据 */ public static ByteData shardOf(byte[] original) { return new ByteData(original, original.length); } /** * 创建BinData对象 * @param capacity 初始容量 * @return BinData对象 */ public static ByteData ofCapacity(int capacity) { return new ByteData(capacity); } /** * 创建BinData对象 * @return 新的BinData对象 */ public static ByteData ofEmpty() { return ByteData.ofCapacity(0); } /** * 创建BinData对象 * @param original 源数据 * @param offset 偏移量 * @param length 拷贝长度,可以大于源数据长度,将进行默认填充 * @return 新的BinData对象,包含源数据的拷贝 * @throws ArrayIndexOutOfBoundsException if {@code offset < 0} * @throws NullPointerException if {@code original} is null */ public static ByteData copyOf(byte[] original, int offset, int length) { return new ByteData(original, offset, length); } /** * 创建BinData对象 * @param original 源数据 * @return 新的BinData对象,包含源数据的拷贝 */ public static ByteData copyOf(byte[] original) { return copyOf(original, 0, original.length); } /** * 多个BinData对象的数据拷贝 * @param array 源对象数组 * @return 新的BinData对象,按照参数顺序拷贝可读数据 */ public static ByteData copyOf(ByteData... array) { ByteData data = ByteData.ofCapacity(128); for (ByteData src : array) { data.write(src); } return data; } /** * 多个BinData对象的数据拷贝 * @param itr 迭代器 * @return 新的BinData对象,按照参数顺序拷贝可读数据 */ public static ByteData copyOf(Iterator itr) { ByteData data = ByteData.ofCapacity(128); itr.forEachRemaining(data::write); return data; } /** * 创建BinData对象,用固定值填充 * @param val byte 填充值 * @param count 数量 * @return 新的BinData对象 */ public static ByteData ofRepeat(int val, int count) { ByteData byteData = ByteData.ofCapacity(count); Arrays.fill(byteData.buffer, 0, count, (byte) val); byteData.writeIndex(count); return byteData; } /** * 创建BinData对象,用随机值填充 * @param size 数量 * @return 新的BinData对象 */ public static ByteData ofRandom(int size) { ByteData byteData = ByteData.ofCapacity(size); new Random().nextBytes(byteData.buffer); byteData.writeIndex(size); return byteData; } /** * 创建BinData对象 * @param hexString 16进制字符串,长度必须是偶数 * @return 新的BinData对象 * @throws IllegalArgumentException hexString不是16进制字符串 */ public static ByteData ofHex(String hexString) { try { byte[] bytes = Hex.decodeHex(hexString); return new ByteData(bytes, bytes.length); } catch (DecoderException e) { throw new IllegalArgumentException("not hex string", e); } } /** * 创建BinData对象 * @param base64String BASE64 * @return ByteData * @throws IllegalArgumentException base64String 不是base64 编码的字符串 */ public static ByteData ofBase64(String base64String) { byte[] bytes = Base64.getDecoder().decode(base64String); return new ByteData(bytes, bytes.length); } // ~ buffer & index // =================================================================================================== /** * 内部buffer * @return 内部buffer */ public byte[] buffer() { return buffer; } /** * 容量 * @return 返回当前容量 */ public int capacity() { return buffer.length; } /** * 当前写指针 * @return 当前写指针位置 */ public int writeIndex() { return writeIndex; } /** * 重置当前写指针位置 * @param value 位置 * @throws IndexOutOfBoundsException 写指针位置超过上下限 * @return 返回当前ByteData对象 */ public ByteData writeIndex(int value) { if (value > buffer.length || value < 0) { throw new IndexOutOfBoundsException("write index overflow"); } writeIndex = value; return this; } /** * 写指针移动 * @param distance 距离,负数向前移动,正数向后移动 * @throws IndexOutOfBoundsException 写指针位置超过上下限 * @return 返回当前ByteData对象 */ public ByteData writeIndexAdvance(int distance) { writeIndex(writeIndex + distance); return this; } /** * 写指针移动 * @param max 最大距离,负数向前移动,正数向后移动 * @return 返回当前ByteData对象 */ public ByteData writeIndexAdvanceSome(int max) { int value = writeIndex + max; if (value < 0) { value = 0; } if (value > buffer.length) { value = buffer.length; } writeIndex(value); return this; } /** * 调整容量,并调整写指针使其不会越界 * @param count 增量,负数缩容,正数扩容 * @return 返回当前ByteData对象 */ public ByteData expandBy(int count) { int capacity = buffer.length; int newCapacity = capacity + count; if (newCapacity <= 0) { buffer = EMPTY_BUFFER; writeIndex = 0; return this; } if (newCapacity != capacity) { buffer = Arrays.copyOf(buffer, newCapacity); writeIndex = Math.min(writeIndex, newCapacity); } return this; } /** * 自动扩展容量 * @return 返回当前ByteData对象 */ public ByteData expand() { expandBy(Math.max(1, buffer.length >> 1)); return this; } /** * 保证可写容量 * @param size 数据量 * @return 返回当前ByteData对象 */ public ByteData ensureWriteBytes(int size) { int more = size - writableBytes(); if (more > 0) { expandBy(more); } return this; } /** * 缓冲区清零 * @return 返回当前ByteData对象 */ public ByteData zeroMemory() { Arrays.fill(buffer, (byte) 0); return this; } /** * 可读数据的长度 * @return 可读数据的字节数 */ public int readableBytes() { return readableBytes(0); } /** * 可读数据的长度 * @param offset 偏移量 * @return 可读数据的字节数 */ public int readableBytes(int offset) { if (offset >= writeIndex) { return 0; } return writeIndex - offset; } /** * 可写容量 * @return 可读数据的字节数 */ public int writableBytes() { return buffer.length - writeIndex; } // ~ read operation // =================================================================================================== /** * 读出可读数据 * @param offset 可读数据偏移量 {@code [0,writeIndex)} * @return 返回偏移量上的值 * @throws IndexOutOfBoundsException offset < 0 * @throws IllegalArgumentException 超过最大可读数据 */ public byte readAt(int offset) { assertReadPos(offset); return buffer[offset]; } /** * 读出可读数据 * @param offset 可读数据偏移量 {@code [0,writeIndex)} * @param count 长度 {@code [0,readableBytes(offset))},负数表示全部可读数据 * @return 返回数据拷贝,如果读偏移量大于等于写指针,返回空数组 * @throws IndexOutOfBoundsException offset < 0 * @throws IllegalArgumentException 超过最大可读数据 */ public byte[] read(int offset, int count) { assertReadPos(offset); if (count < 0) { count = readableBytes(offset); } assertReadable(offset, count); return Arrays.copyOfRange(buffer, offset, offset + count); } /** * 读出可读数据 * @param count 长度 {@code [0,readableBytes())},负数表示全部可读数据 * @return 数据拷贝 * @throws IndexOutOfBoundsException 可读数据访问越界 */ public byte[] read(int count) { return read(0, count); } /** * 读出可读数据 * @return 数据拷贝 * @throws IndexOutOfBoundsException 可读数据访问越界 */ public byte[] readAll() { return read(0, readableBytes()); } /** * 读出可读数据 * @param offset 可读数据偏移量 {@code [0,writeIndex)} * @param maxCount 长度上限 * @return 数据拷贝 * @throws IndexOutOfBoundsException 偏移量越界 * @throws IllegalArgumentException {@code maxCount} 小于0 */ public byte[] readSome(int offset, int maxCount) { assertReadPos(offset); if (maxCount < 0) { throw new IllegalArgumentException("max count < 0"); } return Arrays.copyOfRange(buffer, offset, Math.min(offset + maxCount, writeIndex)); } /** * 读出可读数据 * @param maxCount 长度上限 * @return 数据拷贝 * @throws IndexOutOfBoundsException 偏移量越界,{@code maxCount} 小于0 */ public byte[] readSome(int maxCount) { return readSome(0, maxCount); } /** * 将可读数据作为字符串读取 * @param offset 偏移量 * @param length 长度,负数表示全部可读数据 * @param charset 字符集 * @return String * @throws IndexOutOfBoundsException 可读数据访问越界 */ public String readString(int offset, int length, Charset charset) { assertReadPos(offset); if (length < 0) { length = readableBytes(offset); } assertReadable(offset, length); return new String(buffer, offset, length, charset); } /** * 将可读数据作为字符串读取 * @param offset 偏移量 * @param length 长度,负数表示全部可读数据 * @return String * @throws IndexOutOfBoundsException 可读数据访问越界 */ public String readUtf8String(int offset, int length) { return readString(offset, length, StandardCharsets.UTF_8); } /** * 读出可读数据,并编码为HEX字符串 * @param offset 偏移量 * @param length 长度,负数表示全部可读数据 * @return 小写16进制字符串 * @throws IndexOutOfBoundsException 可读数据访问越界 */ public String readHexLower(int offset, int length) { return Hex.encodeHexString(read(offset, length), true); } /** * 读出可读数据,并编码为HEX字符串 * @param offset 偏移量 * @param length 长度,负数表示全部可读数据 * @return 大写16进制字符串 * @throws IndexOutOfBoundsException 可读数据访问越界 */ public String readHexUpper(int offset, int length) { return Hex.encodeHexString(read(offset, length), false); } /** * 读出可读数据,并编码为BASE64字符串 * @param offset 偏移量 * @param length 长度,负数表示全部可读数据 * @return 大写16进制字符串 * @throws IndexOutOfBoundsException 可读数据访问越界 */ public String readBase64(int offset, int length) { return Base64.getEncoder().encodeToString(read(offset, length)); } /** * 读出可读数据,写入目标数组.数据不足用填充值进行填充 * @param offset 读区域偏移 * @param dest 目标区域 * @param destOffset 目标的偏移量 * @param length 填充长度 * @param padding 填充值,以byte截断 * @return 返回填值padding的填充次数 * @throws IndexOutOfBoundsException 可读数据访问越界,目标目标区域访问越界,填充长度小于0 */ public int readTo(int offset, byte[] dest, int destOffset, int length, int padding) { assertReadPos(offset); int readable = readableBytes(offset); int count = 0; for (; count < length; ++count) { dest[destOffset + count] = (count < readable) ? buffer[offset + count] : (byte) padding; } return Math.max(0, length - readable); } /** * 读出可读数据,写入目标数组.数据不足用0进行填充 * @param offset 读区域偏移 * @param dest 目标数组 * @param padding 填充值,以byte截断 * @return 返回填值padding的填充次数 * @throws IndexOutOfBoundsException 可读数据访问越界,目标目标区域访问越界,填充长度 小于0 */ public int readTo(int offset, byte[] dest, int padding) { return readTo(offset, dest, 0, dest.length, padding); } // ~ Write operation // =================================================================================================== /** * 写入数据 * @param src 源数据 * @return 返回当前对象 */ public ByteData write(ByteData src) { return writeBytes(src.buffer, 0, src.writeIndex); } /** * 写入数据 * @param b byte值 * @return 返回当前ByteData对象 */ public ByteData writeInt8(int b) { ensureWriteBytes(1); buffer[writeIndex++] = (byte) b; return this; } /** * 写入数据 * @param value short 值 * @param order 字节序 * @return 返回当前ByteData对象 */ public ByteData writeInt16(int value, ByteOrder order) { final int size = Short.BYTES; ensureWriteBytes(size); bufferWriter(order).writeShort((short) value); writeIndexAdvance(size); return this; } /** * 写入数据 * @param value int 值 * @param order 字节序 * @return 返回当前ByteData对象 */ public ByteData writeInt32(int value, ByteOrder order) { final int size = Integer.BYTES; ensureWriteBytes(size); bufferWriter(order).writeInt(value); writeIndexAdvance(size); return this; } /** * 写入数据 * @param value long 值 * @param order 字节序 * @return 返回当前ByteData对象 */ public ByteData writeInt64(long value, ByteOrder order) { final int size = Long.BYTES; ensureWriteBytes(size); bufferWriter(order).writeLong(value); writeIndexAdvance(size); return this; } /** * 写入数据 * @param value long 值 * @param order 字节序 * @return 返回当前ByteData对象 */ public ByteData writeFloat(float value, ByteOrder order) { final int size = Float.BYTES; ensureWriteBytes(size); bufferWriter(order).writeFloat(value); writeIndexAdvance(size); return this; } /** * 写入数据 * @param value long 值 * @param order 字节序 * @return 返回当前ByteData对象 */ public ByteData writeDouble(double value, ByteOrder order) { final int size = Double.BYTES; ensureWriteBytes(size); bufferWriter(order).writeDouble(value); writeIndexAdvance(size); return this; } /** * 写入数据 * @param src 源数据 * @param offset 偏移量 * @param length 长度 * @return 返回当前ByteData对象 */ public ByteData writeBytes(byte[] src, int offset, int length) { expandBy(length); for (int i = 0; i < length; ++i) { buffer[writeIndex++] = src[offset + i]; } return this; } /** * 写入数据 * @param src 源数据 * @return 返回当前ByteData对象 */ public ByteData writeBytes(byte[] src) { return writeBytes(src, 0, src.length); } // ~ Misc // =================================================================================================== /** * 比较可读数据是否完全相同(数量和每一个元素) *

* 注意:两个空的BinData比较结果是相同 *

* @param that 比较对象 * @return 返回true表示包含相同数据 */ public boolean dataEquals(ByteData that) { return dataEquals(that.read(-1)); } /** * 比较可读数据是否完全相同(数量和每一个元素) *

* 注意:两个空数组的比较结果是相同 *

* @param data 对比数据 * @return 返回true表示包含相同数据 */ public boolean dataEquals(@Nullable byte[] data) { return Arrays.equals(read(-1), data); } /** * 将可写区域委托给 {@code ByteBufferWriter},注意:写入操作不会同步内部写指针 * @param order 字节序 * @return ByteBufferWriter * @see ByteBufferWriter */ public ByteBufferWriter bufferWriter(ByteOrder order) { return ByteBufferWriter.of(ByteBuffer.wrap(buffer, writeIndex, writableBytes()), order); } /** * 将可写区域委托给 {@code ByteBufferWriter},使用本机字节序,注意:写入操作不会同步内部写指针 * @return ByteBufferWriter * @see ByteBufferWriter */ public ByteBufferWriter bufferWriter() { return bufferWriter(ByteOrder.nativeOrder()); } /** * 将可读区域委托给 {@code ByteBufferReader} * @param order 字节序 * @return ByteBufferReader * @see ByteBufferReader */ public ByteBufferReader bufferReader(ByteOrder order) { return ByteBufferReader.of(ByteBuffer.wrap(buffer, 0, readableBytes()), order); } /** * 将可读区域委托给 {@code ByteBufferReader},使用本机字节序 * @return ByteBufferReader * @see ByteBufferReader */ public ByteBufferReader bufferReader() { return bufferReader(ByteOrder.nativeOrder()); } /** * 拷贝内部buffer的一部分创建新的ByteData * @param offset 起始偏移量 * @param length 拷贝长度,可以大于源数据长度,将进行默认填充 * @return 返回新的ByteData * @throws ArrayIndexOutOfBoundsException if {@code offset < 0} */ public ByteData buffCopy(int offset, int length) { return ByteData.copyOf(buffer, offset, length); } @Override public String toString() { return readHexLower(0, readableBytes()); } @Override public String display() { return String.format("[%d]%s", readableBytes(), readHexLower(0, readableBytes())); } // ~ Internal // =================================================================================================== protected void assertReadPos(int pos) { if (pos < 0) { throw new IndexOutOfBoundsException("Index out of range: " + pos); } } protected void assertReadable(int pos, int length) { final int endPos = pos + length; if (endPos > writeIndex) { throw new IndexOutOfBoundsException("Index out of range: " + endPos); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy