From 8ac2162786398846b049743d70128b8190e129bd Mon Sep 17 00:00:00 2001 From: Jason Phan Date: Fri, 3 Feb 2023 22:18:45 -0600 Subject: [PATCH] de: Add support for std.IntegerBitSet --- src/de/blocks.zig | 3 + src/de/blocks/integer_bit_set.zig | 117 +++++++++++++++++++++++ src/de/impls/visitor/integer_bit_set.zig | 70 ++++++++++++++ src/de/tuples.zig | 1 + 4 files changed, 191 insertions(+) create mode 100644 src/de/blocks/integer_bit_set.zig create mode 100644 src/de/impls/visitor/integer_bit_set.zig diff --git a/src/de/blocks.zig b/src/de/blocks.zig index cd6c1906..0afdfbf4 100644 --- a/src/de/blocks.zig +++ b/src/de/blocks.zig @@ -78,6 +78,9 @@ pub const BufMap = @import("blocks/buf_map.zig"); /// Deserialization block for `std.HashMap` values. pub const HashMap = _HashMap; +/// Deserialization block for `std.IntegerBitSet` values. +pub const IntegerBitSet = @import("blocks/integer_bit_set.zig"); + /// Deserialization block for `std.HashMapUnmanaged` values. pub const HashMapUnmanaged = _HashMap; diff --git a/src/de/blocks/integer_bit_set.zig b/src/de/blocks/integer_bit_set.zig new file mode 100644 index 00000000..aa784a41 --- /dev/null +++ b/src/de/blocks/integer_bit_set.zig @@ -0,0 +1,117 @@ +const std = @import("std"); + +const IntegerBitSetVisitor = @import("../impls/visitor/integer_bit_set.zig").Visitor; +const testing = @import("../testing.zig"); + +const Self = @This(); + +/// Specifies all types that can be deserialized by this block. +pub fn is( + /// The type being deserialized into. + comptime T: type, +) bool { + return comptime std.mem.startsWith(u8, @typeName(T), "bit_set.IntegerBitSet"); +} + +/// Specifies the deserialization process for types relevant to this block. +pub fn deserialize( + /// An optional memory allocator. + allocator: ?std.mem.Allocator, + /// The type being deserialized into. + comptime T: type, + /// A `getty.Deserializer` interface value. + deserializer: anytype, + /// A `getty.de.Visitor` interface value. + visitor: anytype, +) !@TypeOf(visitor).Value { + _ = T; + + return try deserializer.deserializeSeq(allocator, visitor); +} + +/// Returns a type that implements `getty.de.Visitor`. +pub fn Visitor( + /// The type being deserialized into. + comptime T: type, +) type { + return IntegerBitSetVisitor(T); +} + +test "deserialize - std.IntegerBitSet" { + const tests = .{ + .{ + .name = "zero-sized", + .tokens = &.{ + .{ .Seq = .{ .len = 0 } }, + .{ .SeqEnd = {} }, + }, + .want = std.StaticBitSet(0).initEmpty(), + }, + .{ + .name = "empty", + .tokens = &.{ + .{ .Seq = .{ .len = 3 } }, + .{ .I32 = 0 }, + .{ .I32 = 0 }, + .{ .I32 = 0 }, + .{ .SeqEnd = {} }, + }, + .want = std.StaticBitSet(3).initEmpty(), + }, + .{ + .name = "full", + .tokens = &.{ + .{ .Seq = .{ .len = 3 } }, + .{ .I32 = 1 }, + .{ .I32 = 1 }, + .{ .I32 = 1 }, + .{ .SeqEnd = {} }, + }, + .want = std.StaticBitSet(3).initFull(), + }, + .{ + .name = "mixed (I)", + .tokens = &.{ + .{ .Seq = .{ .len = 5 } }, + .{ .I32 = 1 }, + .{ .I32 = 1 }, + .{ .I32 = 0 }, + .{ .I32 = 1 }, + .{ .I32 = 0 }, + .{ .SeqEnd = {} }, + }, + .want = blk: { + var want = std.StaticBitSet(5).initEmpty(); + want.set(1); + want.set(3); + want.set(4); + break :blk want; + }, + }, + .{ + .name = "mixed (I*)", + .tokens = &.{ + .{ .Seq = .{ .len = 5 } }, + .{ .I32 = 1 }, + .{ .I32 = 0 }, + .{ .I32 = 0 }, + .{ .I32 = 1 }, + .{ .I32 = 1 }, + .{ .SeqEnd = {} }, + }, + .want = blk: { + var want = std.StaticBitSet(5).initEmpty(); + want.set(0); + want.set(1); + want.set(4); + break :blk want; + }, + }, + }; + + inline for (tests) |t| { + const Want = @TypeOf(t.want); + const got = try testing.deserialize(null, t.name, Self, Want, t.tokens); + try testing.expectEqual(t.name, t.want, got); + } +} diff --git a/src/de/impls/visitor/integer_bit_set.zig b/src/de/impls/visitor/integer_bit_set.zig new file mode 100644 index 00000000..881bffaa --- /dev/null +++ b/src/de/impls/visitor/integer_bit_set.zig @@ -0,0 +1,70 @@ +const std = @import("std"); + +const free = @import("../../free.zig").free; +const Ignored = @import("../../impls/seed/ignored.zig").Ignored; +const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor; + +pub fn Visitor(comptime IntegerBitSet: type) type { + return struct { + const Self = @This(); + + pub usingnamespace VisitorInterface( + Self, + Value, + .{ + .visitSeq = visitSeq, + }, + ); + + const Value = IntegerBitSet; + + fn visitSeq(_: Self, allocator: ?std.mem.Allocator, comptime Deserializer: type, seq: anytype) Deserializer.Error!Value { + var bitset = Value.initEmpty(); + + if (Value.bit_length == 0) { + if (try seq.nextElement(allocator, Value.MaskInt) != null) { + return error.InvalidLength; + } + + return bitset; + } + + // Deserialize bits from N to 1, where N is the bitset's bit + // length. + // + // NOTE: The 0th bit needs to be deserialized separately due to + // compile errors related to the shift bit being too large or + // something. + comptime var i: usize = Value.bit_length - 1; + inline while (i > 0) : (i -= 1) { + if (try seq.nextElement(allocator, Value.MaskInt)) |bit| { + switch (bit) { + 0 => {}, + 1 => bitset.set(i), + else => return error.InvalidValue, + } + } else { + return error.InvalidValue; + } + } + + // Deserialize 0th bit. + if (try seq.nextElement(allocator, Value.MaskInt)) |bit| { + switch (bit) { + 0 => {}, + 1 => bitset.set(0), + else => return error.InvalidValue, + } + } else { + return error.InvalidValue; + } + + // Check for end of sequence. + if (try seq.nextElement(allocator, Value.MaskInt) != null) { + return error.InvalidLength; + } + + return bitset; + } + }; +} diff --git a/src/de/tuples.zig b/src/de/tuples.zig index 7af16d70..52ad0034 100644 --- a/src/de/tuples.zig +++ b/src/de/tuples.zig @@ -49,6 +49,7 @@ pub const default = .{ // - std.StringArrayHashMapUnmanaged blocks.HashMap, + blocks.IntegerBitSet, blocks.MultiArrayList, blocks.SinglyLinkedList, blocks.NetAddress,