Skip to content

Commit 15e0e5b

Browse files
feat(anytype): better formatting, improved casting, more tests
1 parent 142cb24 commit 15e0e5b

File tree

2 files changed

+60
-21
lines changed

2 files changed

+60
-21
lines changed

any+.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
/// Anytype in runtime
22
pub const Anytype = @import("any+/anytype.zig");
3+
4+
test {
5+
_ = Anytype;
6+
}

any+/anytype.zig

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ const std = @import("std");
22
const Self = @This();
33

44
// TODO: use any-writer when it exists
5-
const PointerFormat = *const fn (*anyopaque, options: std.fmt.FormatOptions, std.io.FixedBufferStream(u8)) anyerror!void;
5+
const PointerFormat = *const fn (*Self, options: std.fmt.FormatOptions, *std.io.FixedBufferStream([]u8)) anyerror!void;
66

77
type: []const u8,
88
size: usize = 0,
9-
ptr: *anyopaque,
10-
ptrFormat: ?PointerFormat = null,
9+
ptr: ?*anyopaque,
10+
ptrFormat: PointerFormat,
1111

1212
/// Initializes a runtime anytype from a comptime anytype.
1313
pub inline fn init(value: anytype) Self {
@@ -17,20 +17,24 @@ pub inline fn init(value: anytype) Self {
1717
/// Explicitly initialize a runtime anytype to fit the type T.
1818
pub inline fn initExplicit(comptime T: type, value: T) Self {
1919
var size: usize = @sizeOf(T);
20-
var ptrFormat: ?PointerFormat = null;
21-
const ptr: *anyopaque = switch (@typeInfo(T)) {
20+
var ptrFormat: PointerFormat = (struct {
21+
fn func(t: *Self, options: std.fmt.FormatOptions, stream: *std.io.FixedBufferStream([]u8)) !void {
22+
const self: T = try t.cast(T);
23+
return std.fmt.formatType(self, "", options, stream.writer(), 3);
24+
}
25+
}).func;
26+
27+
const ptr: ?*anyopaque = switch (@typeInfo(T)) {
2228
.Int, .ComptimeInt => @ptrFromInt(value),
23-
.Float, .ComptimeFloat => @ptrFromInt(@as(usize, @bitCast(@as(f128, @floatCast(value))))),
29+
.Float, .ComptimeFloat => @ptrFromInt(@as(u64, @bitCast(@as(f64, @floatCast(value))))),
2430
.Enum => @ptrFromInt(@intFromEnum(value)),
2531
.Struct, .Union => blk: {
26-
if (@hasDecl(T, "format")) {
27-
ptrFormat = (struct {
28-
fn func(selfPointer: *anyopaque, options: std.fmt.FormatOptions, stream: std.io.FixedBufferStream(u8)) !void {
29-
const self: *T = @ptrCast(@alignCast(selfPointer));
30-
return self.format("", options, stream.writer());
31-
}
32-
}).func;
33-
}
32+
ptrFormat = (struct {
33+
fn func(t: *Self, options: std.fmt.FormatOptions, stream: *std.io.FixedBufferStream([]u8)) !void {
34+
const self: T = try t.cast(T);
35+
return if (@hasDecl(T, "format")) self.format("", options, stream.writer()) else std.fmt.formatType(self, "", options, stream.writer(), 3);
36+
}
37+
}).func;
3438
break :blk @constCast(&value);
3539
},
3640
.Pointer => |p| switch (@typeInfo(p.child)) {
@@ -61,13 +65,13 @@ pub inline fn cast(self: Self, comptime T: type) error{InvalidCast}!T {
6165
/// Casts a type without any of the safety
6266
pub inline fn unsafeCast(self: Self, comptime T: type) T {
6367
return switch (@typeInfo(T)) {
64-
.Int, .ComptimeInt => @intFromPtr(self.ptr),
65-
.Float, .ComptimeFloat => @floatCast(@as(f128, @bitCast(self.ptr))),
68+
.Int, .ComptimeInt => @intCast(@intFromPtr(self.ptr)),
69+
.Float, .ComptimeFloat => @floatCast(@as(f64, @bitCast(@intFromPtr(self.ptr)))),
6670
.Enum => |e| @enumFromInt(@as(e.tag_type, @ptrFromInt(self.ptr))),
67-
.Struct, .Union => @ptrCast(@alignCast(self.ptr)),
71+
.Struct, .Union => @as(*T, @ptrCast(@alignCast(self.ptr.?))).*,
6872
.Pointer => |p| switch (@typeInfo(p.child)) {
6973
.Array => blk: {
70-
break :blk @as([*]p.child, @ptrCast(@alignCast(self.ptr)))[0..self.len(T)];
74+
break :blk @as([*]p.child, @ptrCast(@alignCast(self.ptr.?)))[0..self.len(T)];
7175
},
7276
else => |f| @compileError("Unsupported pointer type: " ++ @tagName(f)),
7377
},
@@ -93,8 +97,8 @@ pub inline fn format(self: Self, comptime fmt: []const u8, options: std.fmt.Form
9397
const trunc_msg = "(msg truncated)";
9498
var buf: [size + trunc_msg.len]u8 = undefined;
9599

96-
const stream = std.io.fixedBufferStream(buf[0..size]);
97-
ptrFormat(self.ptr, options, stream) catch |err| switch (err) {
100+
var stream = std.io.fixedBufferStream(buf[0..size]);
101+
ptrFormat(self, options, &stream) catch |err| switch (err) {
98102
error.NoSpaceLeft => blk: {
99103
@memcpy(buf[size..], trunc_msg);
100104
break :blk &buf;
@@ -115,12 +119,43 @@ pub inline fn format(self: Self, comptime fmt: []const u8, options: std.fmt.Form
115119
}
116120

117121
test "Casting integers and floats" {
122+
@setEvalBranchQuota(100_000);
118123
comptime var i: usize = 0;
119124
inline while (i < std.math.maxInt(u8)) : (i += 1) {
120125
try std.testing.expectEqual(i, try initExplicit(u8, i).cast(u8));
121126
}
122127

123-
try std.testing.expectEqual(123.456, try initExplicit(f32, 123.456).cast(f32));
128+
try std.testing.expectEqual(@as(f32, 123.456), try initExplicit(f32, 123.456).cast(f32));
129+
}
130+
131+
test "Casting structs and unions" {
132+
const UnionType = union(enum) {
133+
a: u32,
134+
b: [2]u16,
135+
c: [4]u8,
136+
};
137+
138+
const StructType = struct {
139+
a: u32,
140+
b: u16,
141+
c: u8,
142+
};
143+
144+
try std.testing.expectEqualDeep(UnionType{
145+
.a = 4096,
146+
}, try initExplicit(UnionType, UnionType{
147+
.a = 4096,
148+
}).cast(UnionType));
149+
150+
try std.testing.expectEqualDeep(StructType{
151+
.a = 4096,
152+
.b = 1024,
153+
.c = 255,
154+
}, try initExplicit(StructType, StructType{
155+
.a = 4096,
156+
.b = 1024,
157+
.c = 255,
158+
}).cast(StructType));
124159
}
125160

126161
test "Invalid casts" {

0 commit comments

Comments
 (0)