~alva/zig-bare

3d7f0e8adadc03bceb9f323c3a4ccd8e65d2c2a6 — ugla 7 months ago af7547f
More updates for self-hosted
5 files changed, 68 insertions(+), 49 deletions(-)

M README.md
M build.zig
M src/bare.zig
M src/main.zig
M src/test.zig
M README.md => README.md +3 -1
@@ 1,7 1,9 @@
# zig-bare [![builds.sr.ht status](https://builds.sr.ht/~alva/zig-bare.svg)](https://builds.sr.ht/~alva/zig-bare?)
# zig-bare

[BARE Message Encoding](https://baremessages.org/) implementation in Zig.

[![builds.sr.ht status](https://builds.sr.ht/~alva/zig-bare/commits/trunk.svg)](https://builds.sr.ht/~alva/zig-bare?)

## Building

The `trunk` branch is tested on latest stable Zig,

M build.zig => build.zig +8 -1
@@ 19,11 19,18 @@ pub fn build(b: *Builder) !void {
    lib.install();
    exe.setBuildMode(mode);
    exe.setTarget(target);
    //    exe.linkLibrary(lib);

    exe.addPackage(std.build.Pkg{
        .name = "bare",
        .source = std.build.FileSource{ .path = "src/bare.zig" },
    });
    exe.single_threaded = true;
    exe.link_function_sections = true;
    exe.install();

    @"test".setBuildMode(mode);
    @"test".setTarget(target);

    bench.setBuildMode(mode);

    if (mode != .Debug)

M src/bare.zig => src/bare.zig +14 -7
@@ 27,7 27,7 @@ pub const Decoder = struct {
    }

    /// Decode a supported BARE type.
    pub fn decode(self: *Self, comptime T: type, reader: anytype) !if (isHashMap(T)) T.Unmanaged else T {
    pub fn decode(self: *Self, comptime T: type, reader: anytype) !ReturnType(T) {
        return switch (@typeInfo(T)) {
            .Int => self.decodeInt(T, reader),
            .Float => self.decodeFloat(T, reader),


@@ 213,6 213,10 @@ pub const Decoder = struct {

        return map;
    }

    fn ReturnType(comptime T: type) type {
        return if (isHashMap(T)) T.Unmanaged else T;
    }
};

/// Encodes a BARE message.


@@ 376,14 380,17 @@ pub const Encoder = struct {

fn isHashMap(comptime T: type) bool {
    switch (@typeInfo(T)) {
        .Struct => {},
        .Struct => {
            // These are the only parts of the HashMap API that are used.
            // `HashMapKeyType` and `HashMapValueType` add further constraints.
            const has1 = @hasDecl(T, "iterator");
            const has2 = @hasDecl(T, "putAssumeCapacity");
            const has3 = @hasDecl(T, "ensureCapacity");
            const has4 = @hasDecl(T, "Size");
            return has1 and has2 and has3 and has4;
        },
        else => return false,
    }
    // These are the only parts of the HashMap API that are used.
    // `HashMapKeyType` and `HashMapValueType` add further constraints.
    const has1 = @hasDecl(T, "iterator") or @hasDecl(T, "items");
    const has2 = @hasDecl(T, "putAssumeCapacity") and @hasDecl(T, "ensureCapacity");
    return has1 and has2 and @hasDecl(T, "Size") and @hasDecl(T, "Unmanaged");
}

fn HashMapKeyType(comptime T: type) type {

M src/main.zig => src/main.zig +1 -1
@@ 2,7 2,7 @@ const std = @import("std");
const fs = std.fs;
const io = std.io;
const assert = std.debug.assert;
const bare = @import("bare.zig");
const bare = @import("bare");
const Decoder = bare.Decoder;
const Encoder = bare.Encoder;


M src/test.zig => src/test.zig +42 -39
@@ 14,7 14,7 @@ const expectEqual = testing.expectEqual;
const expectEqualSlices = testing.expectEqualSlices;
const expectError = testing.expectError;

test "" {
test "refAllDecls" {
    std.testing.refAllDecls(bare);
}



@@ 143,7 143,7 @@ test "read struct 6" {

test "read struct with void members" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  const Foo = struct { a: u8, b: void };
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};


@@ 267,7 267,7 @@ test "read map overflow" {

test "read map[u8]void" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  const HM = std.AutoHashMap(u8, void);
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};


@@ 288,7 288,7 @@ test "read tagged union" {

test "read untagged union" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  const Foo = union { a: u8, b: void };
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};


@@ 300,7 300,7 @@ test "read untagged union" {

test "read void" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        \\  var d = Decoder.init(gpa.allocator());


@@ 311,7 311,7 @@ test "read void" {

test "read unsupported integer type" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        \\  var d = Decoder.init(gpa.allocator());


@@ 322,7 322,7 @@ test "read unsupported integer type" {

test "read unsupported float type" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        \\  var d = Decoder.init(gpa.allocator());


@@ 333,7 333,7 @@ test "read unsupported float type" {

test "read unsupported pointer type" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        \\  var d = Decoder.init(gpa.allocator());


@@ 378,7 378,7 @@ test "write struct" {

test "write struct with void members" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  const Foo = struct { x: u8, y: void };
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);


@@ 425,7 425,7 @@ test "write optional u8 null" {

test "write optional void" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: ?void = null;


@@ 448,7 448,7 @@ test "write u8 array" {

test "write array of void" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: [4]void = .{ {}, {}, {}, {} };


@@ 474,7 474,7 @@ test "write slice" {

test "write slice of void" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: []const void = &[_]void{ {}, {} };


@@ 521,7 521,7 @@ test "write map[string]u8" {

test "write map[u8]void" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo = std.AutoHashMap(u8, void);


@@ 560,7 560,7 @@ test "write tagged union with void member active" {

test "write untagged union" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  const Foo = union { a: u8, b: void };
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var e = Encoder.init();


@@ 571,7 571,7 @@ test "write untagged union" {

test "write void" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  var e = Encoder.init();


@@ 582,7 582,7 @@ test "write void" {

test "write unsupported integer type" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: u7 = 7;


@@ 594,7 594,7 @@ test "write unsupported integer type" {

test "write unsupported float type" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const foo: f16 = 4.2;


@@ 606,7 606,7 @@ test "write unsupported float type" {

test "write unsupported pointer type" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  const x: u8 = 2;


@@ 755,7 755,7 @@ test "bare.Decoder frees its memory" {

test "invariant: fixed-length array must have length > 0 (read)" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        \\  var d = Decoder.init(gpa.allocator());


@@ 766,7 766,7 @@ test "invariant: fixed-length array must have length > 0 (read)" {

test "invariant: fixed-length array must have length > 0 (write)" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  var foo = [0]u8{};


@@ 778,7 778,7 @@ test "invariant: fixed-length array must have length > 0 (write)" {

test "invariant: structs must have at least 1 field (read)" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  const Foo = struct {};
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};


@@ 790,7 790,7 @@ test "invariant: structs must have at least 1 field (read)" {

test "invariant: structs must have at least 1 field (write)" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  const Foo = struct {};
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);


@@ 802,50 802,55 @@ test "invariant: structs must have at least 1 field (write)" {

test "invariant: unions must have at least 1 field (read)" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  const Foo = union(enum) {};
        \\  var fbs = io.fixedBufferStream("lol");
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        \\  var d = Decoder.init(gpa.allocator());
        \\  try d.decode(Foo{}, fbs.reader());
        \\  _ = try d.decode(Foo, fbs.reader());
        \\}
    , "union declarations must have at least one tag");
}

test "invariant: unions must have at least 1 field (write)" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  const Foo = union(enum) {};
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  var e = Encoder.init();
        \\  try e.encode(Foo{}, fbs.writer());
        \\  const foo: Foo = .{};
        \\  _ = try e.encode(foo, fbs.writer());
        \\}
    , "union declarations must have at least one tag");
}

test "invariant: hashmap keys must be of primitive type" {
    try testCompileError(
        \\pub fn main() void {
        \\pub fn main() !void {
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        \\  const hm = std.AutoHashMap([64]u8, void).init(gpa.allocator());
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  var e = Encoder.init();
        \\  try e.encode(hm, fbs.writer());
        \\  _ = try e.encode(hm, fbs.writer());
        \\}
    , "unsupported hashmap key type [64]u8");
}

test "invariant: enum values must be unique" {
    try testCompileError(
       \\pub fn main() void {
       \\  const Foo = enum(u8) {
       \\    x = 1,
       \\    y = 1,
       \\  };
       \\  _ = Foo;
       \\}
        \\pub fn main() !void {
        \\  const Foo = enum(u8) {
        \\    x = 1,
        \\    y = 1,
        \\  };
        \\  var buf: [0x100]u8 = undefined;
        \\  var fbs = io.fixedBufferStream(&buf);
        \\  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        \\  var d = Decoder.init(gpa.allocator());
        \\  _ = try d.decode(Foo, fbs.reader());
        \\}
    , "enum tag value 1 already taken");
}



@@ 890,8 895,6 @@ fn checkErrorMessage(haystack: []const u8, message: []const u8) !void {
        return;
    };
    const errorslice = haystack[idx + errorstart.len ..];
    const starts_with = mem.startsWith(u8, errorslice, message);
    if (!starts_with)
        std.log.err("\nunexpected output: `{s}`; expected: `{s}`", .{ errorslice, message });
    try expect(starts_with);

    try testing.expectStringStartsWith(errorslice, message);
}