Skip to content

Commit f8f64d2

Browse files
committed
fix: create our own wrapping
1 parent d54fd81 commit f8f64d2

File tree

3 files changed

+177
-2
lines changed

3 files changed

+177
-2
lines changed

src/core/wrapping.zig

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
const std = @import("std");
2+
const assert = std.debug.assert;
3+
4+
/// Special values for Wrapped types.
5+
const Wrapped = enum(usize) { null = 0, true = 1, false = 2, void = 3 };
6+
7+
/// Wraps the given value into a specified integer type.
8+
/// The value must fit within the size of the given I.
9+
pub fn wrap(comptime I: type, value: anytype) I {
10+
assert(@typeInfo(I) == .Int);
11+
assert(@typeInfo(I).Int.signedness == .unsigned);
12+
13+
if (comptime @bitSizeOf(@TypeOf(value)) > @bitSizeOf(I)) {
14+
@compileError("type: " ++ @typeName(value) ++ " is larger than given integer (" ++ @typeName(I) ++ ")");
15+
}
16+
17+
return context: {
18+
switch (comptime @typeInfo(@TypeOf(value))) {
19+
.Pointer => break :context @intFromPtr(value),
20+
.Void => break :context @intFromEnum(Wrapped.void),
21+
.Int => |info| {
22+
const uint = @Type(std.builtin.Type{
23+
.Int = .{
24+
.signedness = .unsigned,
25+
.bits = info.bits,
26+
},
27+
});
28+
break :context @intCast(@as(uint, @bitCast(value)));
29+
},
30+
.ComptimeInt => break :context @intCast(value),
31+
.Float => |info| {
32+
const uint = @Type(std.builtin.Type{
33+
.Int = .{
34+
.signedness = .unsigned,
35+
.bits = info.bits,
36+
},
37+
});
38+
break :context @intCast(@as(uint, @bitCast(value)));
39+
},
40+
.ComptimeFloat => break :context @intCast(@as(I, @bitCast(value))),
41+
.Struct => |info| {
42+
const uint = @Type(std.builtin.Type{
43+
.Int = .{
44+
.signedness = .unsigned,
45+
.bits = @bitSizeOf(info.backing_integer.?),
46+
},
47+
});
48+
break :context @intCast(@as(uint, @bitCast(value)));
49+
},
50+
.Bool => break :context if (value) @intFromEnum(Wrapped.true) else @intFromEnum(Wrapped.false),
51+
.Optional => break :context if (value) |v| wrap(I, v) else @intFromEnum(Wrapped.null),
52+
else => @compileError("wrapping unsupported type: " ++ @typeName(@TypeOf(value))),
53+
}
54+
};
55+
}
56+
57+
/// Unwraps a specified type from an underlying value.
58+
/// The value must be an unsigned integer type, typically a usize.
59+
pub fn unwrap(comptime T: type, value: anytype) T {
60+
const I = @TypeOf(value);
61+
assert(@typeInfo(I) == .Int);
62+
assert(@typeInfo(I).Int.signedness == .unsigned);
63+
if (comptime @bitSizeOf(@TypeOf(T)) > @bitSizeOf(I)) {
64+
@compileError("type: " ++ @typeName(value) ++ "is larger than given integer (" ++ @typeName(T) ++ ")");
65+
}
66+
67+
return context: {
68+
switch (comptime @typeInfo(T)) {
69+
.Pointer => break :context @ptrFromInt(value),
70+
.Void => break :context {},
71+
.Int => |info| {
72+
const uint = @Type(std.builtin.Type{
73+
.Int = .{
74+
.signedness = .unsigned,
75+
.bits = info.bits,
76+
},
77+
});
78+
break :context @bitCast(@as(uint, @intCast(value)));
79+
},
80+
.Float => |info| {
81+
const uint = @Type(std.builtin.Type{
82+
.Int = .{
83+
.signedness = .unsigned,
84+
.bits = info.bits,
85+
},
86+
});
87+
const float = @Type(std.builtin.Type{
88+
.Float = .{
89+
.bits = info.bits,
90+
},
91+
});
92+
break :context @as(float, @bitCast(@as(uint, @intCast(value))));
93+
},
94+
.Struct => |info| {
95+
const uint = @Type(std.builtin.Type{
96+
.Int = .{
97+
.signedness = .unsigned,
98+
.bits = @bitSizeOf(info.backing_integer.?),
99+
},
100+
});
101+
break :context @bitCast(@as(uint, @intCast(value)));
102+
},
103+
.Bool => {
104+
assert(value == @intFromEnum(Wrapped.true) or value == @intFromEnum(Wrapped.false));
105+
break :context if (value == @intFromEnum(Wrapped.false)) false else true;
106+
},
107+
.Optional => |info| break :context if (value == @intFromEnum(Wrapped.null))
108+
null
109+
else
110+
unwrap(info.child, value),
111+
else => unreachable,
112+
}
113+
};
114+
}
115+
116+
const testing = std.testing;
117+
118+
test "wrap/unwrap - integers" {
119+
try testing.expectEqual(@as(usize, 42), wrap(usize, @as(u8, 42)));
120+
try testing.expectEqual(@as(usize, 42), wrap(usize, @as(u16, 42)));
121+
try testing.expectEqual(@as(usize, 42), wrap(usize, @as(u32, 42)));
122+
123+
try testing.expectEqual(@as(usize, 42), wrap(usize, @as(i8, 42)));
124+
try testing.expectEqual(@as(usize, 42), wrap(usize, @as(i16, 42)));
125+
try testing.expectEqual(@as(usize, 42), wrap(usize, @as(i32, 42)));
126+
127+
try testing.expectEqual(@as(u8, 42), unwrap(u8, @as(usize, 42)));
128+
try testing.expectEqual(@as(i16, 42), unwrap(i16, @as(usize, 42)));
129+
}
130+
131+
test "wrap/unwrap - floats" {
132+
const pi_32: f32 = 3.14159;
133+
const pi_64: f64 = 3.14159;
134+
135+
const wrapped_f32 = wrap(usize, pi_32);
136+
const wrapped_f64 = wrap(usize, pi_64);
137+
138+
try testing.expectEqual(pi_32, unwrap(f32, wrapped_f32));
139+
try testing.expectEqual(pi_64, unwrap(f64, wrapped_f64));
140+
}
141+
142+
test "wrap/unwrap - booleans" {
143+
try testing.expectEqual(@as(usize, @intFromEnum(Wrapped.true)), wrap(usize, true));
144+
try testing.expectEqual(@as(usize, @intFromEnum(Wrapped.false)), wrap(usize, false));
145+
146+
try testing.expectEqual(true, unwrap(bool, @as(usize, @intFromEnum(Wrapped.true))));
147+
try testing.expectEqual(false, unwrap(bool, @as(usize, @intFromEnum(Wrapped.false))));
148+
}
149+
150+
test "wrap/unwrap - optionals" {
151+
const optional_int: ?i32 = 42;
152+
const optional_none: ?i32 = null;
153+
154+
try testing.expectEqual(@as(usize, 42), wrap(usize, optional_int));
155+
try testing.expectEqual(@as(usize, 0), wrap(usize, optional_none));
156+
157+
try testing.expectEqual(@as(?i32, 42), unwrap(?i32, @as(usize, 42)));
158+
try testing.expectEqual(@as(?i32, null), unwrap(?i32, @as(usize, 0)));
159+
}
160+
161+
test "wrap/unwrap - void" {
162+
try testing.expectEqual(@as(usize, @intFromEnum(Wrapped.void)), wrap(usize, {}));
163+
try testing.expectEqual({}, unwrap(void, @as(usize, @intFromEnum(Wrapped.void))));
164+
}
165+
166+
test "wrap/unwrap - pointers" {
167+
var value: i32 = 42;
168+
const ptr = &value;
169+
170+
const wrapped = wrap(usize, ptr);
171+
const unwrapped = unwrap(*i32, wrapped);
172+
173+
try testing.expectEqual(&value, unwrapped);
174+
try testing.expectEqual(@as(i32, 42), unwrapped.*);
175+
}

src/http/router/middleware.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ const log = std.log.scoped(.@"zzz/router/middleware");
33
const assert = std.debug.assert;
44

55
const Runtime = @import("tardy").Runtime;
6-
const wrap = @import("tardy").wrap;
76

7+
const wrap = @import("../../core/wrapping.zig").wrap;
88
const Pseudoslice = @import("../../core/pseudoslice.zig").Pseudoslice;
99
const Server = @import("../server.zig").Server;
1010

src/http/router/route.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const builtin = @import("builtin");
33
const log = std.log.scoped(.@"zzz/http/route");
44
const assert = std.debug.assert;
55

6-
const wrap = @import("tardy").wrap;
6+
const wrap = @import("../../core/wrapping.zig").wrap;
77

88
const Method = @import("../method.zig").Method;
99
const Request = @import("../request.zig").Request;

0 commit comments

Comments
 (0)