1 Star 0 Fork 0

朕与将军解战袍 / zbinrw

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

English | 中文

ZBinRW

一个声明式二进制文件解析库

example

为了使用 zbinrw,你需要在你的类型中声明一个公有的 ZBINRW_META 类型。

parse to struct

通过在你的结构体中声明一个类型为 zbinrw.meta.Endian 的公有常量 DEFAULT_ENDIAN,来指定读写时的字节序:

const zbinrw = @import("zbinrw");
const meta = zbinrw.meta;
const attr = meta.attr;
const SliceReader = zbinrw.io.reader.SliceReader;
const BinRW = zbinrw.BinRW;

const Color = struct {
    pub const ZBINRW_META: type = struct {
        pub const DEFUALT_ENDIAN: attr.Endian = .Little;
    };
    r: u16,
    g: u16,
    b: u16,
};

const data = &[_]u8 { 
    0x01, 0x02,
    0x03, 0x04,
    0x05, 0x06
};
const expected = Color {
    .r = 0x0201,
    .g = 0x0403,
    .b = 0x0605,
};

var slice_reader = SliceReader.new(data);
// read function accept a std.io.Reader
const color = try BinRW(Color).read(slice_reader.reader());

try std.testing.expectEqual(expected, color);

通过在你的结构体中声明一个类型为 []const u8 的公有常量 MAGIC,来指定读写时该类型对应的二进制文件的 Magic Number

const zbinrw = @import("zbinrw");
const meta = zbinrw.meta;
const attr = meta.attr;
const SliceReader = zbinrw.io.reader.SliceReader;
const BinRW = zbinrw.BinRW;

const Color = struct {
    pub const ZBINRW_META: type = struct {
        pub const MAGIC: []const u8 = "Color";
        pub const DEFUALT_ENDIAN: attr.Endian = .Little;
    };
    r: u16,
    g: u16,
    b: u16,
};

const data = "Color" ++ &[_]u8 { 
    0x01, 0x02,
    0x03, 0x04,
    0x05, 0x06
};
const expected = Color {
    .r = 0x0201,
    .g = 0x0403,
    .b = 0x0605,
};

var slice_reader = SliceReader.new(data);
const color = try BinRW(Color).read(slice_reader.reader());

try std.testing.expectEqual(expected, color);

如果结构体中的某几个字段的读写字节序和其它字段不同,你可以:

const zbinrw = @import("../zbinrw.zig");
const meta = zbinrw.meta;
const attr = meta.attr;
const SliceReader = zbinrw.io.reader.SliceReader;
const Value = meta.Value;
const BinRW = zbinrw.BinRW;

const Color = struct {
    pub const ZBINRW_META: type = struct {
        pub const DEFUALT_ENDIAN: attr.Endian = .Big;
        // the type of g in binary is u16, and its Read/Write attributes is `{ Endian.Little }`,
        // which means zbinrw would read this field with little-endian order
        g: Value.InBin(u16).RW(.{attr.Endian.Little}).done(),
    };
    r: u16,
    g: u16,
    b: u16,
};

const data = &[_]u8 { 
    0x01, 0x02,
    0x03, 0x04,
    0x05, 0x06
};
var slice_reader = SliceReader.new(data);
const color = try BinRW(Color).read(slice_reader.reader());
const expected = Color {
    .r = 0x0102,
    .g = 0x0403,
    .b = 0x0506,
};

try std.testing.expectEqual(expected, color);

如果一个字段的 BinTypeT,而你想将其转化成 U 类型,你可以:

const zbinrw = @import("../zbinrw.zig");
const meta = zbinrw.meta;
const attr = meta.attr;
const SliceReader = zbinrw.io.reader.SliceReader;
const Value = meta.Value;
const BinRW = zbinrw.BinRW;

const truncatU16 = struct {
    pub fn truncate(v: u16) u8 {
        return @truncate(v);
    }
}.truncate;

const Color = struct {
    pub const ZBINRW_META: type = struct {
        pub const MAGIC: []const u8 = "Color";
        pub const DEFUALT_ENDIAN: attr.Endian = .Little;
        // `BinType` is u16, `ValueType` is u8, so zbinrw need a read map to map(u16) -> u8
        g: Value.InBin(u16).Read(.{attr.Map(truncatU16)}).done(),
    };
    r: u16,
    g: u8,
    b: u16,
};

const data = "Color" ++ &[_]u8 { 
    0x01, 0x02,
    0x03, 0x04,
    0x05, 0x06
};
var slice_reader = SliceReader.new(data);
const color = try BinRW(Color).read(slice_reader.reader());
const expected = Color {
    .r = 0x0201,
    .g = 0x0003, // truncate(0x0403) == 0x0003
    .b = 0x0605,
};

try std.testing.expectEqual(expected, color);

如果某个字段是一个指针,你需要通过 Value.Size(len_ref) 来指定指针指向内存的长度(可以理解为数组的 size):

const Code = struct {
    pub const ZBINRW_META: type = struct {
        pub const MAGIC: []const u8 = "XVM";
        pub const DEFUALT_ENDIAN: attr.Endian = .Little;
        bytecode: Value.InBin([]const u8).Size("len").done()
    };
    //                                          |
    // .-----------------------------------------
    // v 
    len: usize,
    bytecode: []const u8,
};

const bytecode = &[_]u8 { 
    0x01, 0x02,
    0x03, 0x04,
    0x05, 0x06
};
const len: usize = bytecode.len;
const len_bytes = &[_]u8 { @intCast(len) } ++ (&[_]u8 { 0 } ** (@sizeOf(usize) - 1) );
const data = "XVM" ++ len_bytes[0..] ++ bytecode;
const expected = Code {
    .len = len,
    .bytecode = bytecode,
};

var slice_reader = SliceReader.new(data);
const code = try BinRW(Code).read(slice_reader.reader());

try std.testing.expectEqual(expected.len, code.len);
try std.testing.expect(std.mem.eql(u8, expected.bytecode, code.bytecode));

更多的例子请看 tests 目录

parse to union

zbinrw 只支持 tagged union.

const Circle = struct {
    r: u32,
};

const Rectangle = struct {
    w: u32,
    h: u32,
};

const ShapeTag = enum(u8) {
    Circle = 0x01,
    Rectangle = 0x02,
};

const Shape = union(ShapeTag) {
    pub const ZBINRW_META: type = struct {
        pub const DEFUALT_ENDIAN: attr.Endian = .Little;
    };
    Circle: Circle,
    Rectangle: Rectangle,
};

const circle_data = &[_] u8 {
    0x01, // circle tag,
    0x02, 0x03, 0x04, 0x05, // r
};
const circle_expected = Shape {
    .Circle = Circle { .r = 0x05040302 }
};
var circle_reader = SliceReader.new(circle_data);
const circle = try BinRW(Shape).read(circle_reader.reader());
try std.testing.expectEqual(circle_expected, circle);

const rectangle_data = &[_] u8 {
    0x02, // rectangle tag,
    0x02, 0x03, 0x04, 0x05, // w
    0x06, 0x07, 0x08, 0x09, // h
};
const rectangle_expected = Shape {
    .Rectangle = Rectangle { 
        .w = 0x05040302,
        .h = 0x09080706,
    }
};
var rectangle_reader = SliceReader.new(rectangle_data);
const rectangle = try BinRW(Shape).read(rectangle_reader.reader());
try std.testing.expectEqual(rectangle_expected, rectangle);

编译时检查以及可读性高的错误信息

zbinrw 会在编译期完成所有的检查,并且会提供可读性非常高的错误信息。

example

如果我们没有提供读写字节序:

const Color = struct {
    pub const ZBINRW_META: type = struct {
        // ops! wo forgot it!
        // pub const DEFUALT_ENDIAN: attr.Endian = .Little;
    };
    r: u16,
    g: u16,
    b: u16,
};

我们会得到错误信息,如下:

error: Missing Read Endian of `tests.simple_struct.test.little.Color.r`.

如果我们没有提供 map 函数:

const Color = struct {
    pub const ZBINRW_META: type = struct {
        pub const MAGIC: []const u8 = "Color";
        pub const DEFUALT_ENDIAN: attr.Endian = .Little;
        g: Value
            .InBin(u16)
            // ops! wo forgot it!
            // .Read(.{attr.Map(truncatU16)})
            .done(),
    };
    r: u16,
    g: u8,
    b: u16,
};

我们会得到错误信息,如下:

Please define a Read Attr Map(map(u16) -> u8)
using Value.Read for field g in tests.simple_struct.test.with_map.Color.ZBINRW_META

如果 Size attribute 引用了一个不存在的字段:

const Code = struct {
    pub const ZBINRW_META: type = struct {
        pub const MAGIC: []const u8 = "XVM";
        pub const DEFUALT_ENDIAN: attr.Endian = .Little;
        bytecode: Value
                    .InBin([]const u8)
                    // wait! there is no such field named "le"
                    .Size("le")
                    .done()
    };

    len: usize,
    bytecode: []const u8,
};

我们会得到错误信息,如下:

error: There is no such field named `le` in `tests.struct_with_ptr.test.byte_ptr.Code` while the Size Attr of field `bytecode` reffernece to `le`

zbinrw 还在编译期进行其它更多的检查以确保读写的正确性。

空文件

简介

一个声明式二进制文件解析库 展开 收起
取消

发行版 (1)

全部

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/L_Lawliet_moon/zbinrw.git
git@gitee.com:L_Lawliet_moon/zbinrw.git
L_Lawliet_moon
zbinrw
zbinrw
main

搜索帮助