Bytes - Go 二进制流序列化/反序列化库
类似 Rust deku 库的 Go 语言实现,用于 TCP/UDP 字节流协议的解析和序列化。
特性
- 使用 struct tag 描述二进制格式
- 支持大端序和小端序
- 支持位级操作
- 支持条件字段
- 支持数组、切片
- 支持嵌套结构体
- 支持自定义序列化/反序列化
- 支持字段填充
安装
go get github.com/teafull/bytes
快速开始
基本使用
package main
import (
"fmt"
"github.com/teafull/bytes"
)
type PacketHeader struct {
Magic uint16 `deku:"endian=big"`
Version uint8 `deku:"bits=4"`
Type uint8 `deku:"bits=4"`
Length uint16 `deku:"endian=big"`
}
func main() {
// 序列化
header := PacketHeader{
Magic: 0x1234,
Version: 1,
Type: 2,
Length: 16,
}
data, err := bytes.Marshal(header)
if err != nil {
panic(err)
}
fmt.Printf("Serialized: %x\n", data)
// 反序列化
var decoded PacketHeader
_, err = bytes.Unmarshal(data, &decoded)
if err != nil {
panic(err)
}
fmt.Printf("Decoded: %+v\n", decoded)
}
位字段
type BitFields struct {
Flag1 uint8 `deku:"bits=1"`
Flag2 uint8 `deku:"bits=1"`
Reserved uint8 `deku:"bits=6"`
Value uint8
}
条件字段
type ConditionalPacket struct {
HasOption bool `deku:"bits=1"`
Reserved uint8 `deku:"bits=7"`
Option uint8 `deku:"cond=HasOption"` // 仅当 HasOption 为 true 时存在
Payload uint16 `deku:"endian=big"`
}
动态数组
type DynamicArray struct {
Type uint8
Count uint8
Values []uint16 `deku:"count=Count,endian=big"`
}
嵌套结构体
type Inner struct {
Value1 uint16 `deku:"endian=big"`
Value2 uint16 `deku:"endian=big"`
}
type Outer struct {
Header Inner
Length uint16 `deku:"endian=big"`
}
自定义序列化
type CustomField struct {
Data []byte
}
func (c *CustomField) MarshalBinary() ([]byte, error) {
// 自定义序列化逻辑
return c.Data, nil
}
func (c *CustomField) UnmarshalBinary(data []byte) error {
// 自定义反序列化逻辑
c.Data = make([]byte, len(data))
copy(c.Data, data)
return nil
}
字段填充
type PaddedPacket struct {
Type uint8
Padding byte `deku:"pad=3"` // 填充3个字节
Length uint16 `deku:"endian=big"`
}
Tag 说明
| Tag | 说明 | 示例 |
|---|---|---|
endian | 字节序,可选值 big、little、native | deku:"endian=big" |
bits | 位宽,用于位字段(1-64位) | deku:"bits=4" |
bytes | 字节宽度 | deku:"bytes=8" |
cond | 条件字段,仅当条件为真时解析 | deku:"cond=HasFlag" |
count | 数组/切片长度,支持表达式 | deku:"count=Length" 或 deku:"count=Length+1" |
skip | 跳过字段 | deku:"skip=true" |
pad | 填充字节数 | deku:"pad=3" |
条件表达式
支持以下格式的条件表达式:
- 布尔字段名:
HasOption - 等于:
Field == 1 - 不等于:
Field != 0 - 大于:
Field > 5 - 小于:
Field < 10 - 大于等于:
Field >= 5 - 小于等于:
Field <= 10
计数表达式
支持以下格式的计数表达式:
- 字段名:
Count - 字段名加减乘除:
Count + 1、Length * 2 - 直接数字:
4
支持的类型
- 整数:
uint8,uint16,uint32,uint64,int8,int16,int32,int64 - 布尔:
bool - 数组:
[N]T - 切片:
[]T - 结构体:嵌套结构体
- 字符串:
string(支持固定长度和 C 字符串)
API
Marshal
将结构体序列化为字节数组:
func Marshal(v interface{}) ([]byte, error)
Unmarshal
从字节数组反序列化到结构体:
func Unmarshal(data []byte, v interface{}) (int, error)
返回读取的字节数和错误。
测试
运行测试:
go test -v
查看示例:
go test -run Example -v
性能
本库使用位级操作和反射,适合处理网络协议等二进制数据。对于性能敏感的场景,建议使用自定义序列化。
基准测试结果
测试环境:
- OS: macOS (Darwin)
- CPU: Apple M2 Pro (arm64)
- Go: 1.21
- 测试时间: 3s
序列化性能
| 测试 | ns/op | B/op | allocs/op |
|---|---|---|---|
| Small Packet (5 bytes) | 560.7 | 362 | 13 |
| Medium Packet (10 bytes) | 1196 | 565 | 22 |
| Large Packet (148 bytes) | 2390 | 976 | 26 |
| Bit Field (4 bytes) | 818.8 | 464 | 15 |
| Dynamic Array (22 bytes) | 474.6 | 308 | 16 |
| Nested Struct (6 bytes) | 609.8 | 368 | 15 |
| Conditional (4 bytes) | 618.0 | 360 | 12 |
反序列化性能
| 测试 | ns/op | B/op | allocs/op |
|---|---|---|---|
| Small Packet (5 bytes) | 591.7 | 314 | 13 |
| Medium Packet (10 bytes) | 1251 | 517 | 22 |
| Large Packet (148 bytes) | 2612 | 672 | 25 |
| Bit Field (4 bytes) | 848.2 | 416 | 15 |
| Dynamic Array (22 bytes) | 665.9 | 308 | 18 |
| Nested Struct (6 bytes) | 644.5 | 320 | 15 |
| Conditional (4 bytes) | 651.0 | 312 | 12 |
往返性能 (Marshal + Unmarshal)
| 测试 | ns/op | B/op | allocs/op |
|---|---|---|---|
| Small Packet | 1170 | 677 | 26 |
| Medium Packet | 2489 | 1082 | 44 |
| Large Packet | 4977 | 1648 | 51 |
位操作性能
| 测试 | ns/op | B/op | allocs/op |
|---|---|---|---|
| Bit Write | 20.91 | 0 | 0 |
| Bit Read | 15.64 | 0 | 0 |
性能分析
-
序列化性能
- 小型数据包(5字节):~560 ns/op,适合高频场景
- 大型数据包(148字节):~2.4 μs/op,包含128字节数组
- 位字段操作额外开销:~818 ns/op
-
反序列化性能
- 小型数据包(5字节):~590 ns/op
- 大型数据包(148字节):~2.6 μs/op
- 与序列化性能基本一致
-
内存分配
- 小型数据包:~360 B/op,13次分配
- 大型数据包:~976 B/op,26次分配
- 位操作:0分配,使用预分配缓冲区
-
位操作性能
- 纯位读写操作:15-21 ns/op,0分配
- 非常高效,接近内存访问速度
运行基准测试
运行所有基准测试:
go test -bench=. -benchmem
运行特定基准测试:
go test -bench=BenchmarkMarshal -benchmem
运行基准测试并显示内存分配详情:
go test -bench=. -benchmem -benchtime=5s
性能优化建议
-
减少反射开销
- 对于热点路径,考虑使用代码生成
- 缓存反射类型信息
-
内存分配优化
- 重用字节缓冲区
- 使用对象池减少分配
-
位操作优化
- 批量处理位操作
- 减少对齐操作
许可证
MIT License
📦 项目成果
核心功能实现
✅ 基础数据类型支持
- 整数类型:uint8/16/32/64, int8/16/32/64
- 布尔类型:bool
- 数组和切片
- 嵌套结构体
- 字符串类型
✅ 高级特性
- 位级操作(1-64位)
- 字节序控制(大端序/小端序/本机字节序)
- 条件字段(基于其他字段值)
- 动态数组(支持表达式定义长度)
- 字段填充(自动对齐)
- 自定义序列化(实现接口)
✅ Struct Tag 支持
deku:"endian=big" // 字节序
deku:"bits=4" // 位宽
deku:"cond=HasFlag" // 条件字段
deku:"count=Length" // 数组长度
deku:"pad=3" // 填充字节
项目文件结构
bytes/
├── bit_reader.go # 位读取器
├── bit_writer.go # 位写入器
├── types.go # 核心类型定义
├── tags.go # Tag 解析器
├── marshal.go # 序列化实现
├── unmarshal.go # 反序列化实现
├── evaluator.go # 条件表达式求值
├── example_test.go # 测试用例(8个)
├── examples.go # 使用示例
├── demo/main.go # 演示程序
├── README.md # 完整文档
├── PROJECT_OVERVIEW.md # 项目概览
└── go.mod # Go 模块
测试结果
- ✅ 所有测试通过(8个测试用例)
- ✅ 代码覆盖率:48.0%
- ✅ 演示程序运行正常
- ✅ 无编译错误
使用示例
type Packet struct {
Magic uint16 `deku:"endian=big"`
Version uint8 `deku:"bits=4"`
Type uint8 `deku:"bits=4"`
Length uint16 `deku:"endian=big"`
}
// 序列化
data, _ := bytes.Marshal(packet)
// 反序列化
bytes.Unmarshal(data, &packet)
与 deku 库对比
| 特性 | deku (Rust) | bytes (Go) | 状态 |
|---|---|---|---|
| 位级操作 | ✓ | ✓ | ✅ |
| 条件字段 | ✓ | ✓ | ✅ |
| 动态数组 | ✓ | ✓ | ✅ |
| 自定义序列化 | ✓ | ✓ | ✅ |
| 字节序支持 | ✓ | ✓ | ✅ |
| 嵌套结构体 | ✓ | ✓ | ✅ |
适用场景
- TCP/UDP 网络协议解析
- 二进制文件格式处理
- 通信协议实现
- 数据序列化与反序列化