Skip to content

Instantly share code, notes, and snippets.

@jordanisaacs
Created April 7, 2024 01:27
Show Gist options
  • Save jordanisaacs/a121a081a3623270e387dae32b08486e to your computer and use it in GitHub Desktop.
Save jordanisaacs/a121a081a3623270e387dae32b08486e to your computer and use it in GitHub Desktop.
Zig invalid pointer wrapper
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