feat(bytes): 添加ByteBuffer工具类和ByteFlags数据结构

- 新增ByteBufferUtils.kt文件,提供字符串、布尔值、位标志等类型的读写扩展函数
- 新增ByteFlags.kt文件,实现8位布尔标志集合的数据结构和位操作功能
- 添加putString/getString方法支持C风格字符串的null终止符处理
- 实现ByteFlags的位操作功能,包括AND、OR、XOR、NOT等运算
- 添加二进制字符串、十六进制字符串的转换和格式化输出功能
- 移除NettyUtils中重复的toByteBuffer扩展函数
- 更新项目版本号从4.2.6到4.2.7
This commit is contained in:
Armamem0t 2026-01-07 10:07:48 +08:00
parent 2bcd4b329c
commit 2db24530d6
Signed by: minglipro
GPG Key ID: 5F355A77B22AA93B
4 changed files with 668 additions and 12 deletions

View File

@ -16,13 +16,13 @@
# ProjectName mingli-utils
# ModuleName mingli-utils
# CurrentFile gradle.properties
# LastUpdate 2026-01-06 14:35:41
# LastUpdate 2026-01-07 10:02:36
# UpdateUser MingLiPro
#
JDKVERSIONS=1.8
GROUPSID=com.mingliqiye.utils
ARTIFACTID=mingli-utils
VERSIONS=4.2.6
VERSIONS=4.2.7
signing.keyId=B22AA93B
signing.password=
signing.secretKeyRingFile=secret.gpg

View File

@ -0,0 +1,205 @@
/*
* Copyright 2026 mingliqiye
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ByteBufferUtils.kt
* LastUpdate 2026-01-07 10:01:45
* UpdateUser MingLiPro
*/
@file:JvmName("ByteBufferUtil")
package com.mingliqiye.utils.bytes
import java.nio.ByteBuffer
import java.nio.charset.Charset
/**
* 将字符串以指定字符集编码后写入 [ByteBuffer]并在末尾追加一个空字节`0x00`作为字符串终止符
*
* 此方法适用于需要 C 风格字符串null-terminated string的协议或场景
*
* @param string 要写入的字符串不可为 null
* @param charset 字符编码默认为 [Charsets.UTF_8]
* @return 当前 [ByteBuffer] 实例支持链式调用
*/
fun ByteBuffer.putString(string: String, charset: Charset = Charsets.UTF_8): ByteBuffer =
this.put(string.toByteArray(charset)).put(0x00)
/**
* [ByteBuffer] 中读取一个以空字节`0x00`结尾的字符串
*
* 该方法会持续读取字节直到遇到第一个 `0x00`null terminator为止并将之前读取的字节按指定字符集解码为字符串
* **注意**此操作会修改缓冲区的位置position且不处理缓冲区越界情况若无终止符会抛出异常
*
* @param charset 字符编码默认为 [Charsets.UTF_8]
* @return 解码后的字符串不含终止符
* @throws BufferUnderflowException 如果缓冲区中没有足够的字节即未找到终止符
*/
fun ByteBuffer.getString(charset: Charset = Charsets.UTF_8): String {
val data = ArrayList<Byte>()
while (true) {
val byte = this.get()
if (byte == 0x00.toByte()) {
break
}
data.add(byte)
}
return String(data.toByteArray(), charset)
}
/**
* [ByteBuffer] 中剩余的全部字节读取为一个 [ByteArray]
*
* 此方法会读取从当前位置到 limit 之间的所有字节并推进 position
*
* @return 包含剩余字节的新字节数组
*/
fun ByteBuffer.toByteArray(): ByteArray {
val ba = ByteArray(this.remaining())
this.get(ba)
return ba
}
/**
* 将一个布尔列表长度必须为 8转换为 [ByteFlags] 并写入 [ByteBuffer]
*
* 列表索引 0~7 分别对应位 b0~b7b0 为最低有效位
*
* @param bit 长度为 8 的布尔列表表示 8 个位的状态
* @return 当前 [ByteBuffer] 实例支持链式调用
* @throws IndexOutOfBoundsException 如果列表长度不足 8
*/
fun ByteBuffer.putByteFlags(
bit: List<Boolean> = List(8) { false },
): ByteBuffer = this.putByteFlags(
ByteFlags(
bit[0],
bit[1],
bit[2],
bit[3],
bit[4],
bit[5],
bit[6],
bit[7],
)
)
/**
* 将一个布尔数组长度必须为 8转换为 [ByteFlags] 并写入 [ByteBuffer]
*
* 数组索引 0~7 分别对应位 b0~b7b0 为最低有效位
*
* @param bit 长度为 8 的布尔数组
* @return 当前 [ByteBuffer] 实例支持链式调用
* @throws IndexOutOfBoundsException 如果数组长度不足 8
*/
fun ByteBuffer.putByteFlags(
bit: BooleanArray = BooleanArray(8),
): ByteBuffer = this.putByteFlags(
ByteFlags(
bit[0],
bit[1],
bit[2],
bit[3],
bit[4],
bit[5],
bit[6],
bit[7],
)
)
/**
* [ByteFlags] 对象转换为字节并写入 [ByteBuffer]
*
* @param byteFlags 要写入的位标志对象
* @return 当前 [ByteBuffer] 实例支持链式调用
*/
fun ByteBuffer.putByteFlags(byteFlags: ByteFlags): ByteBuffer = this.put(byteFlags.toByte())
/**
* 通过 8 个独立的布尔参数构造 [ByteFlags] 并写入 [ByteBuffer]
*
* 参数顺序`bit0`LSB `bit7`MSB
*
* @param bit0 0 最低位
* @param bit1 1
* @param bit2 2
* @param bit3 3
* @param bit4 4
* @param bit5 5
* @param bit6 6
* @param bit7 7 最高位
* @return 当前 [ByteBuffer] 实例支持链式调用
*/
fun ByteBuffer.putByteFlags(
bit0: Boolean = false,
bit1: Boolean = false,
bit2: Boolean = false,
bit3: Boolean = false,
bit4: Boolean = false,
bit5: Boolean = false,
bit6: Boolean = false,
bit7: Boolean = false,
): ByteBuffer = this.putByteFlags(
ByteFlags(
bit0,
bit1,
bit2,
bit3,
bit4,
bit5,
bit6,
bit7,
)
)
/**
* [ByteBuffer] 中读取一个字节并将其解析为 [ByteFlags] 对象
*
* @return 表示该字节各位状态的 [ByteFlags] 实例
*/
fun ByteBuffer.getByteFlags(): ByteFlags = ByteFlags(this.get())
/**
* [ByteBuffer] 中读取一个字节并将其解释为布尔值
*
* 约定`0x01` 表示 `true`其他值包括 `0x00`均视为 `false`
*
* @return 布尔值
*/
fun ByteBuffer.getBoolean(): Boolean = this.get() == 0x01.toByte()
/**
* 将布尔值写入 [ByteBuffer]
*
* 约定`true` 写为 `0x01``false` 写为 `0x00`
*
* @param boolean 要写入的布尔值
* @return 当前 [ByteBuffer] 实例支持链式调用
*/
fun ByteBuffer.putBoolean(boolean: Boolean): ByteBuffer = this.put(if (boolean) 0x01 else 0x00)
/**
* 将当前字节数组包装为一个只读的 [ByteBuffer]
*
* 使用 [ByteBuffer.wrap] 创建position 初始为 0limit 为数组长度
*
* @return 包装后的 [ByteBuffer] 实例
*/
fun ByteArray.toByteBuffer(): ByteBuffer {
return ByteBuffer.wrap(this)
}

View File

@ -0,0 +1,460 @@
/*
* Copyright 2026 mingliqiye
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ByteFlags.kt
* LastUpdate 2026-01-07 09:37:09
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.bytes
/**
* 表示一个字节8 的布尔标志集合
*
* 每一位对应一个布尔值其中
* - `b0` 表示最低有效位LSBbit 0值为 0x01
* - `b7` 表示最高有效位MSBbit 7值为 0x80
*
* 该类支持从字节布尔数组二进制字符串等构造并提供位操作状态查询和格式化输出等功能
*/
data class ByteFlags(
/** 第 0 位最低位0x01 */
var b0: Boolean = false,
/** 第 1 位0x02 */
var b1: Boolean = false,
/** 第 2 位0x04 */
var b2: Boolean = false,
/** 第 3 位0x08 */
var b3: Boolean = false,
/** 第 4 位0x10 */
var b4: Boolean = false,
/** 第 5 位0x20 */
var b5: Boolean = false,
/** 第 6 位0x40 */
var b6: Boolean = false,
/** 第 7 位最高位0x80 */
var b7: Boolean = false
) {
/**
* 通过一个 [Byte] 值构造 [ByteFlags] 实例
*
* @param byte 用于初始化的字节值仅使用低 8
*/
constructor(byte: Byte) : this() {
fromByte(byte)
}
/**
* 从给定的 [Byte] 值解析并设置各个位标志
*
* @param byte 要解析的字节
*/
fun fromByte(byte: Byte) {
val value = byte.toInt() and 0xFF
b0 = (value and 0x01) != 0
b1 = (value and 0x02) != 0
b2 = (value and 0x04) != 0
b3 = (value and 0x08) != 0
b4 = (value and 0x10) != 0
b5 = (value and 0x20) != 0
b6 = (value and 0x40) != 0
b7 = (value and 0x80) != 0
}
/**
* 将当前标志状态转换为一个 [Byte]
*
* @return 对应的字节值范围 0x00 ~ 0xFF以有符号字节形式返回
*/
fun toByte(): Byte {
var result = 0
if (b0) result = result or 0x01
if (b1) result = result or 0x02
if (b2) result = result or 0x04
if (b3) result = result or 0x08
if (b4) result = result or 0x10
if (b5) result = result or 0x20
if (b6) result = result or 0x40
if (b7) result = result or 0x80
return result.toByte()
}
/**
* 通过索引0~7获取对应位的布尔值
*
* @param index 位索引0 = LSB, 7 = MSB
* @return 该位是否为 true
* @throws IndexOutOfBoundsException 如果索引不在 0~7 范围内
*/
operator fun get(index: Int): Boolean {
return when (index) {
0 -> b0
1 -> b1
2 -> b2
3 -> b3
4 -> b4
5 -> b5
6 -> b6
7 -> b7
else -> throw IndexOutOfBoundsException("索引必须在0-7之间")
}
}
/**
* 通过索引0~7设置对应位的布尔值
*
* @param index 位索引0 = LSB, 7 = MSB
* @param value 要设置的布尔值
* @throws IndexOutOfBoundsException 如果索引不在 0~7 范围内
*/
operator fun set(index: Int, value: Boolean) {
when (index) {
0 -> b0 = value
1 -> b1 = value
2 -> b2 = value
3 -> b3 = value
4 -> b4 = value
5 -> b5 = value
6 -> b6 = value
7 -> b7 = value
else -> throw IndexOutOfBoundsException("索引必须在0-7之间")
}
}
/**
* 将所有 8 个位设置为 `true`
*/
fun setAll() {
b0 = true
b1 = true
b2 = true
b3 = true
b4 = true
b5 = true
b6 = true
b7 = true
}
/**
* 将所有 8 个位设置为 `false`
*/
fun clearAll() {
b0 = false
b1 = false
b2 = false
b3 = false
b4 = false
b5 = false
b6 = false
b7 = false
}
/**
* 对所有位执行逻辑取反true false
*/
fun toggleAll() {
b0 = !b0
b1 = !b1
b2 = !b2
b3 = !b3
b4 = !b4
b5 = !b5
b6 = !b6
b7 = !b7
}
/**
* 切换指定索引位的状态true false反之亦然
*
* @param index 要切换的位索引0~7
* @throws IndexOutOfBoundsException 如果索引无效
*/
fun toggle(index: Int) {
this[index] = !this[index]
}
/**
* 检查是否所有位都为 `true`
*
* @return 若全部为 true 则返回 true否则 false
*/
fun allTrue(): Boolean {
return b0 && b1 && b2 && b3 && b4 && b5 && b6 && b7
}
/**
* 检查是否所有位都为 `false`
*
* @return 若全部为 false 则返回 true否则 false
*/
fun allFalse(): Boolean {
return !b0 && !b1 && !b2 && !b3 && !b4 && !b5 && !b6 && !b7
}
/**
* 统计值为 `true` 的位数量
*
* @return true 位的个数0~8
*/
fun countTrue(): Int {
var count = 0
if (b0) count++
if (b1) count++
if (b2) count++
if (b3) count++
if (b4) count++
if (b5) count++
if (b6) count++
if (b7) count++
return count
}
/**
* 统计值为 `false` 的位数量
*
* @return false 位的个数0~8
*/
fun countFalse(): Int = 8 - countTrue()
/**
* 通过位名称 "b0""bit3" 获取对应位的值
*
* 支持别名`bN` `bitN`不区分大小写
*
* @param name 位的名称例如 "b0", "BIT5"
* @return 对应位的布尔值
* @throws IllegalArgumentException 如果名称无效
*/
fun getByName(name: String): Boolean {
return when (name.lowercase()) {
"b0", "bit0" -> b0
"b1", "bit1" -> b1
"b2", "bit2" -> b2
"b3", "bit3" -> b3
"b4", "bit4" -> b4
"b5", "bit5" -> b5
"b6", "bit6" -> b6
"b7", "bit7" -> b7
else -> throw IllegalArgumentException("未知的位名称: $name")
}
}
/**
* 将当前状态转换为标准二进制字符串MSB 在前
*
* 例如 b7=true, b6=false, ..., b0=true则返回 "10000001"
*
* @return 8 位二进制字符串高位在左
*/
fun toBinaryString(): String {
return buildString {
if (b7) append('1') else append('0')
if (b6) append('1') else append('0')
if (b5) append('1') else append('0')
if (b4) append('1') else append('0')
if (b3) append('1') else append('0')
if (b2) append('1') else append('0')
if (b1) append('1') else append('0')
if (b0) append('1') else append('0')
}
}
/**
* 将当前状态转换为十六进制字符串 0x 前缀大写两位
*
* 例如若值为 10则返回 "0x0A"
*
* @return 格式如 "0xXX" 的十六进制字符串
*/
fun toHexString(): String {
return "0x" + (toByte().toInt() and 0xFF).toString(16).padStart(2, '0').uppercase()
}
/**
* 将当前位状态转换为布尔数组
*
* 数组顺序为 [b0, b1, b2, ..., b7]LSB 在前
*
* @return 长度为 8 的布尔数组
*/
fun toBooleanArray(): BooleanArray {
return booleanArrayOf(b0, b1, b2, b3, b4, b5, b6, b7)
}
/**
* 从布尔数组初始化位状态
*
* 使用数组的前 8 个元素索引 0~7分别对应 b0~b7
*
* @param array 布尔数组长度至少为 8
* @throws IllegalArgumentException 如果数组长度不足 8
*/
fun fromBooleanArray(array: BooleanArray) {
require(array.size >= 8) { "数组长度至少为8" }
b0 = array[0]
b1 = array[1]
b2 = array[2]
b3 = array[3]
b4 = array[4]
b5 = array[5]
b6 = array[6]
b7 = array[7]
}
/**
* 对两个 [ByteFlags] 执行按位操作AND
*
* 结果的每一位 = this位 && other位
*
* @param other 另一个 ByteFlags 实例
* @return 新的 ByteFlags 实例表示 AND 结果
*/
infix fun and(other: ByteFlags): ByteFlags {
return ByteFlags(
this.b0 && other.b0,
this.b1 && other.b1,
this.b2 && other.b2,
this.b3 && other.b3,
this.b4 && other.b4,
this.b5 && other.b5,
this.b6 && other.b6,
this.b7 && other.b7
)
}
/**
* 对两个 [ByteFlags] 执行按位操作OR
*
* 结果的每一位 = this位 || other位
*
* @param other 另一个 ByteFlags 实例
* @return 新的 ByteFlags 实例表示 OR 结果
*/
infix fun or(other: ByteFlags): ByteFlags {
return ByteFlags(
this.b0 || other.b0,
this.b1 || other.b1,
this.b2 || other.b2,
this.b3 || other.b3,
this.b4 || other.b4,
this.b5 || other.b5,
this.b6 || other.b6,
this.b7 || other.b7
)
}
/**
* 对两个 [ByteFlags] 执行按位异或操作XOR
*
* 结果的每一位 = this位 != other位
*
* @param other 另一个 ByteFlags 实例
* @return 新的 ByteFlags 实例表示 XOR 结果
*/
infix fun xor(other: ByteFlags): ByteFlags {
return ByteFlags(
this.b0 != other.b0,
this.b1 != other.b1,
this.b2 != other.b2,
this.b3 != other.b3,
this.b4 != other.b4,
this.b5 != other.b5,
this.b6 != other.b6,
this.b7 != other.b7
)
}
/**
* 对当前 [ByteFlags] 执行按位操作NOT
*
* 每一位取反
*
* @return 新的 ByteFlags 实例所有位取反
*/
operator fun not(): ByteFlags {
return ByteFlags(
!b0, !b1, !b2, !b3, !b4, !b5, !b6, !b7
)
}
/**
* 返回可读的字符串表示包含二进制和十六进制形式
*
* 示例`ByteFlags(10100001 = 0xA1)`
*
* @return 字符串描述
*/
override fun toString(): String {
return "ByteFlags(${toBinaryString()} = ${toHexString()})"
}
companion object {
/**
* 创建一个所有位均为 `true` [ByteFlags] 实例
*
* @return 1 ByteFlags值为 0xFF
*/
fun allTrue(): ByteFlags {
return ByteFlags(true, true, true, true, true, true, true, true)
}
/**
* 创建一个所有位均为 `false` [ByteFlags] 实例
*
* @return 0 ByteFlags值为 0x00
*/
fun allFalse(): ByteFlags {
return ByteFlags()
}
/**
* 8 位二进制字符串创建 [ByteFlags] 实例
*
* 字符串格式高位在前MSB first "10000001" 表示 b7=1, b0=1
*
* @param binary 8 位二进制字符串仅含 '0' '1'
* @return 对应的 ByteFlags 实例
* @throws IllegalArgumentException 如果字符串长度不是 8 或包含非法字符
*/
fun fromBinaryString(binary: String): ByteFlags {
require(binary.length == 8) { "二进制字符串长度必须为8" }
require(binary.all { it == '0' || it == '1' }) { "二进制字符串只能包含0和1" }
return ByteFlags(
binary[7] == '1', // b0 是最低位(对应字符串最后一位)
binary[6] == '1',
binary[5] == '1',
binary[4] == '1',
binary[3] == '1',
binary[2] == '1',
binary[1] == '1',
binary[0] == '1' // b7 是最高位(对应字符串第一位)
)
}
/**
* 从整数创建 [ByteFlags] 实例仅使用低 8
*
* @param value 输入整数
* @return 对应的 ByteFlags 实例
*/
fun fromInt(value: Int): ByteFlags {
return ByteFlags((value and 0xFF).toByte())
}
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NettyUtils.kt
* LastUpdate 2026-01-06 14:33:44
* LastUpdate 2026-01-07 10:00:31
* UpdateUser MingLiPro
*/
@ -79,12 +79,3 @@ fun ByteBuf.toByteArray(): ByteArray {
fun ByteArray.toByteBuf(): ByteBuf {
return Unpooled.wrappedBuffer(this)
}
/**
* 将字节数组转换为ByteBuffer对象
*
* @return 转换后的ByteBuffer对象该ByteBuffer包装了原始字节数组
*/
fun ByteArray.toByteBuffer(): ByteBuffer {
return ByteBuffer.wrap(this)
}