Skip to content

halifox/dart_bit_buffer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bit_buffer

bit_buffer 是一个面向协议解析、底层二进制处理和位字段编解码的 Dart 库。

它适合这类场景:

  • 给定一段 Uint8List,按几个字节或几个 bit 提取字段
  • 按协议文档中的“字节偏移 + bit 偏移”解析数据
  • intBigIntfloat32float64、UTF-8 和原始字节之间转换
  • 构建或解析 CAN 报文、寄存器映射、自定义二进制协议

库同时提供:

  • ByteBufferStorage:基于 Uint8List 的字节后端,适合高效协议解析
  • BitBufferStorage:位压缩后端,适合更细粒度的 bit 存储
  • BinaryReader / BinaryWriter:顺序读写 API
  • ImmutableBinaryView:零拷贝只读视图
  • 显式的 BitOrderByteOrder

Features

  • 支持 bool、有符号/无符号整数、BigIntfloat32float64、UTF-8 和原始字节
  • 同时支持按绝对 bitOffset 访问,以及按 byteOffset + bitOffsetInByte 访问
  • 适合协议字段解析,也适合顺序式读写编码
  • 字节后端支持高效的字节对齐读写和零拷贝字节视图
  • 支持 view(...) 创建局部视图,支持 copy(...) 创建独立副本

Installation

pubspec.yaml 中添加:

dependencies:
  stun:
    git:
      url: https://github.com/halifox/dart_bit_buffer
      ref: ^3.0.0

导入:

import 'package:bit_buffer/bit_buffer.dart';

Quick start

下面的例子展示了一个很典型的协议解析入口:先包装已有字节数组,再依次读取单个 bit、连续多个 bit、多字节整数和 UTF-8 字符串。

import 'dart:typed_data';
import 'package:bit_buffer/bit_buffer.dart';

void main() {
  // 示例报文:标志位 + 3 bit 模式字段 + 小端 16 位整数 + UTF-8 文本 "ABC"
  final payload = Uint8List.fromList([0x91, 0xAC, 0x34, 0x12, 0x03, 0x41, 0x42, 0x43]);
  final buffer = ByteBufferStorage.wrap(payload);

  final flag = buffer.readBoolAt(0, bitOffsetInByte: 0); // 第 1 个字节最高位
  final mode = buffer.readBitsAt(1, bitOffsetInByte: 2, bitLength: 3); // 第 2 个字节,从第 3 个 bit 起读 3 bit
  final value = buffer.readUintAt(2, bitLength: 16, byteOrder: ByteOrder.littleEndian); // 第 3~4 个字节,小端 16 位整数
  final text = buffer.readUtf8At(5, 3); // 第 6~8 个字节,按 UTF-8 解码

  print(flag); // 标志位:true
  print(mode); // 模式字段:5
  print(value); // 小端 16 位值:4660
  print(text); // 文本字段:ABC
}

这个示例里的关键参数可以这样理解:

  • readBoolAt(0, bitOffsetInByte: 0):读取第 1 个字节的最高位
  • readBitsAt(1, bitOffsetInByte: 2, bitLength: 3):从第 2 个字节的第 3 个 bit 开始,连续读取 3 个 bit
  • readUintAt(2, bitLength: 16, byteOrder: ByteOrder.littleEndian):从第 3 个字节开始读取 16 个 bit,并按小端解释
  • readUtf8At(5, 3):从第 6 个字节开始读取 3 个字节,并按 UTF-8 解码
  • bitOffsetInByte 的取值范围是 0..7,其中 0 表示字节最高位,7 表示最低位

如何理解 readXXX 参数

readXXX 系列方法可以分成两类:

  • readBool(...)readBit(...)readBits(...)readUint(...)readInt(...)readBytes(...)readUtf8(...) 这类方法使用绝对 bitOffset,适合你已经知道字段在整个缓冲区里的 bit 起点
  • readBoolAt(...)readBitAt(...)readBitsAt(...)readUintAt(...)readIntAt(...)readBytesAt(...)readUtf8At(...) 这类方法使用 byteOffset,更适合照着协议表里的“第几个字节、第几个 bit”来解析

常用参数说明:

  • bitOffset:从整个缓冲区开头开始计算的绝对 bit 偏移,0 表示第 1 个字节的最高位
  • byteOffset:从第几个字节开始,0 表示第 1 个字节
  • bitOffsetInByte:当前字节内的 bit 偏移,范围是 0..7,其中 0 表示最高位,7 表示最低位
  • bitLength:连续读取多少个 bit
  • byteLength:连续读取多少个字节
  • bitOrder:字段内部 bit 的方向,默认是 BitOrder.msbFirst
  • byteOrder:多字节整数或浮点数的字节序,默认是 ByteOrder.bigEndian

最短示例:

final flag = buffer.readBool(bitOffset: 0); // 第 1 个字节最高位
final mode = buffer.readBits(bitOffset: 10, bitLength: 3); // 从绝对第 10 个 bit 起读 3 bit
final value = buffer.readUintAt(2, bitLength: 16, byteOrder: ByteOrder.littleEndian); // 从第 3 个字节起读 16 bit
final bytes = buffer.readBytesAt(4, 2); // 从第 5 个字节起读 2 个字节
final text = buffer.readUtf8At(6, 3); // 从第 7 个字节起读 3 个字节并解码

如果你只想快速判断应该用哪个 readXXX

  • 读单个 bit:用 readBool(...) / readBit(...)readBoolAt(...) / readBitAt(...)
  • 读多个 bit:用 readBits(...) / readBitsAt(...)
  • 读整数:用 readUint(...) / readUintAt(...)readInt(...) / readIntAt(...)
  • 读字节:用 readBytes(...) / readBytesAt(...)
  • 读字符串:用 readUtf8(...) / readUtf8At(...)

Common use cases

解析现有报文

如果你已经拿到一段原始字节数组,通常从 ByteBufferStorage.wrap(...) 开始:

import 'dart:typed_data';
import 'package:bit_buffer/bit_buffer.dart';

void main() {
  final payload = Uint8List.fromList([0x12, 0x34, 0x56]);
  final buffer = ByteBufferStorage.wrap(payload);
  final field = buffer.readUint(bitOffset: 4, bitLength: 12);
  print(field); // 564 == 0x234
}

按协议文档中的字段位置读取

如果协议文档写的是“第几个字节的第几个 bit”,优先使用 read*At(...)

import 'dart:typed_data';
import 'package:bit_buffer/bit_buffer.dart';

void main() {
  final payload = Uint8List.fromList([0x91, 0xAC, 0x34, 0x12]);
  final buffer = ByteBufferStorage.wrap(payload);
  print(buffer.readBoolAt(0, bitOffsetInByte: 0)); // true
  print(buffer.readUintAt(1, bitOffsetInByte: 2, bitLength: 3)); // 5
  print(buffer.readUintAt(2, bitLength: 16, byteOrder: ByteOrder.littleEndian)); // 4660
}

顺序写入和顺序读取

当你在构建报文,或者按固定顺序连续解析字段时,BinaryWriter / BinaryReader 更方便:

import 'package:bit_buffer/bit_buffer.dart';

void main() {
  final MutableBinaryBuffer buffer = ByteBufferStorage();
  final BinaryWriter writer = buffer.writer();
  writer.writeBool(true);
  writer.writeUint(0x1234, bitLength: 16, byteOrder: ByteOrder.littleEndian);
  writer.writeBits(BigInt.from(5), bitLength: 3);
  writer.writeUtf8('OK');
  final BinaryReader reader = buffer.reader();
  print(reader.readBool()); // true
  print(reader.readUint(bitLength: 16, byteOrder: ByteOrder.littleEndian)); // 4660
  print(reader.readBits(bitLength: 3)); // 5
  print(reader.readUtf8(2)); // OK
}

创建零拷贝视图

当你只想看一段局部 bit 区间,而不想复制底层数据时,可以使用 view(...)

import 'dart:typed_data';
import 'package:bit_buffer/bit_buffer.dart';

void main() {
  final source = Uint8List.fromList([0x12, 0x34, 0x56, 0x78]);
  final buffer = ByteBufferStorage.wrap(source);
  final view = buffer.view(bitOffset: 4, bitLength: 12);
  print(view.readUint(bitOffset: 0, bitLength: 12)); // 564 == 0x234
  final clone = view.copy(storageKind: BinaryStorageKind.bit);
  print(clone.readUint(bitOffset: 0, bitLength: 12)); // 564
}

使用位压缩后端

如果你更关心位级存储,可以使用 BitBufferStorage

import 'package:bit_buffer/bit_buffer.dart';

void main() {
  final buffer = BitBufferStorage();
  buffer.writeBits(0, BigInt.from(43), bitLength: 6);
  print(buffer.readBits(bitOffset: 0, bitLength: 6)); // 43
  print(buffer.toUint8List()); // [172]
}

API guide

什么时候用 ByteBufferStorage

  • 你已经有一段 Uint8List
  • 你主要在做协议解析、报文读写、基础类型转换
  • 你希望字节对齐字段有更好的性能
  • 你希望在条件满足时通过 asUint8ListView() 直接拿到底层字节视图

通常解析外部输入时,推荐这样开始:

final buffer = ByteBufferStorage.wrap(bytes);

什么时候用 BitBufferStorage

  • 你更关心位级压缩存储
  • 你需要大量 bit 级写入
  • 你不依赖直接暴露底层 Uint8List

什么时候用 readUint(...)

已知字段的绝对 bit 起始位置时,直接使用:

final value = buffer.readUint(bitOffset: 11, bitLength: 13);

什么时候用 readUintAt(...)

协议文档按“第几个字节、第几个 bit”描述字段时,更推荐使用:

final value = buffer.readUintAt(1, bitOffsetInByte: 2, bitLength: 3);

这里有两个重要约定:

  • bitOffsetInByte 采用 0..7 的 0-based 计数
  • bitOffsetInByte == 0 表示该字节的最高位,不是最低位

BitOrderByteOrder 的区别

  • BitOrder 控制字段内部 bit 的方向
  • ByteOrder 控制多字节数值的字节顺序

例如:

import 'package:bit_buffer/bit_buffer.dart';

void main() {
  final buffer = ByteBufferStorage();
  buffer.writeUint(0, 0x1234, bitLength: 16, bitOrder: BitOrder.lsbFirst, byteOrder: ByteOrder.littleEndian);
  print(buffer.toUint8List()); // [44, 72]
}

Notes

  • ByteBufferStorage.wrap(bytes) 是零拷贝包装。修改 bytes 会影响缓冲区,修改缓冲区也会反映到 bytes
  • view(...) 返回零拷贝只读视图;copy(...) 返回拥有独立底层存储的可变副本
  • readBytes(...) 读取的是完整字节;如果最后一个字节只写了一部分 bit,请使用 toUint8List() 导出带零填充的结果
  • asUint8ListView() 只有在缓冲区是字节后端且当前长度字节对齐时才可能返回非空
  • writeUint(...)writeInt(...) 会检查值是否能放进指定的位宽,不符合时会抛出 RangeError
  • 如果字段宽度可能超过普通 int,请使用 readUnsignedBigInt(...) / readBigInt(...)

Development

dart analyze
dart test

License

本项目使用 LGPL-3.0 License

About

一个高效的 Dart 位级缓冲区库,提供按位读写能力,支持多种数据类型及大端/小端位序,适用于协议编解码、数据打包和高性能网络传输等场景。

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages