Created
April 7, 2024 01:27
-
-
Save jordanisaacs/a121a081a3623270e387dae32b08486e to your computer and use it in GitHub Desktop.
Zig invalid pointer wrapper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const builtin = @import("builtin"); | |
const InvalidInt: isize = -1; | |
pub fn InvalidPtr(T: anytype, constant: bool) type { | |
const InvalidPtrType, const TPtrType = blk: { | |
if (constant) { | |
break :blk .{ *const anyopaque, *const T }; | |
} else { | |
break :blk .{ *anyopaque, *T }; | |
} | |
}; | |
const InvalidPtrVal: InvalidPtrType = @ptrFromInt(@as(usize, @bitCast(InvalidInt))); | |
return struct { | |
const Self = @This(); | |
ptr: InvalidPtrType = InvalidPtrVal, | |
fn init(val: TPtrType) Self { | |
return .{ .ptr = @ptrCast(@alignCast(val)) }; | |
} | |
fn isInvalid(self: *const @This()) bool { | |
return self.ptr == InvalidPtrVal; | |
} | |
fn set(self: *@This(), val: TPtrType) void { | |
self.ptr = @ptrCast(val); | |
} | |
fn get(self: *const @This()) TPtrType { | |
std.debug.assert(!self.isInvalid()); | |
return @ptrCast(@alignCast(self.ptr)); | |
} | |
}; | |
} | |
pub fn AtomicInvalidPtr(T: anytype, constant: bool, optional: bool) type { | |
const InvalidPtrType, const TPtrType = blk: { | |
if (constant and optional) { | |
break :blk .{ ?*const anyopaque, ?*const T }; | |
} else if (constant) { | |
break :blk .{ *const anyopaque, *const T }; | |
} else if (optional) { | |
break :blk .{ ?*anyopaque, ?*T }; | |
} else { | |
break :blk .{ *anyopaque, *T }; | |
} | |
}; | |
const InvalidPtrVal: InvalidPtrType = @ptrFromInt(@as(usize, @bitCast(InvalidInt))); | |
const AtomicVal = std.atomic.Value(InvalidPtrType); | |
return struct { | |
const Self = @This(); | |
ptr: AtomicVal = AtomicVal.init(InvalidPtrVal), | |
fn init(val: TPtrType) Self { | |
return .{ .ptr = AtomicVal.init(@ptrCast(@alignCast(val))) }; | |
} | |
fn isInvalid(self: *const @This(), ordering: std.builtin.AtomicOrder) bool { | |
return self.ptr.load(ordering) == InvalidPtrVal; | |
} | |
inline fn convert(ptr: InvalidPtrType) ?TPtrType { | |
if (ptr == InvalidPtrVal) { | |
return null; | |
} else { | |
const v: TPtrType = @ptrCast(@alignCast(ptr)); | |
return v; | |
} | |
} | |
fn swap(self: *@This(), val: TPtrType, comptime ordering: std.builtin.AtomicOrder) ?TPtrType { | |
return convert(self.ptr.swap(@ptrCast(val), ordering)); | |
} | |
fn load(self: *const @This(), comptime ordering: std.builtin.AtomicOrder) ?TPtrType { | |
return convert(self.ptr.load(ordering)); | |
} | |
}; | |
} | |
test "Invalid Ptr const" { | |
const InvalidUsize = InvalidPtr(usize, true); | |
var invalidPtr: InvalidUsize = .{}; | |
try std.testing.expect(invalidPtr.isInvalid()); | |
const val: usize = 1; | |
invalidPtr.set(&val); | |
try std.testing.expectEqual(val, invalidPtr.get().*); | |
} | |
test "Invalid Ptr non-const" { | |
const InvalidUsize = InvalidPtr(usize, false); | |
var invalidPtr: InvalidUsize = .{}; | |
try std.testing.expect(invalidPtr.isInvalid()); | |
var val: usize = 1; | |
invalidPtr.set(&val); | |
const t = invalidPtr.get(); | |
t.* = 10; | |
try std.testing.expectEqual(val, invalidPtr.get().*); | |
} | |
test "Invalid ptr atomic" { | |
const InvalidUsize = AtomicInvalidPtr(usize, false, true); | |
var t: InvalidUsize = .{}; | |
try std.testing.expectEqual(null, t.load(.monotonic)); | |
try std.testing.expectEqual(null, t.swap(null, .monotonic)); | |
try std.testing.expectEqual(null, t.load(.monotonic).?); | |
var val: usize = 10; | |
try std.testing.expectEqual(null, t.swap(&val, .monotonic).?); | |
try std.testing.expectEqual(10, t.load(.monotonic).?.?.*); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment