358 lines
10 KiB
Zig
358 lines
10 KiB
Zig
const std = @import("std");
|
|
const Value = @import("value.zig").Value;
|
|
const assert = std.debug.assert;
|
|
|
|
/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
|
|
/// It's important for this struct to be small.
|
|
/// It is not copyable since it may contain references to its inner data.
|
|
/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
|
|
/// of obtaining a lock on a global type table, as well as making the
|
|
/// garbage collection bookkeeping simpler.
|
|
/// This union takes advantage of the fact that the first page of memory
|
|
/// is unmapped, giving us 4096 possible enum tags that have no payload.
|
|
pub const Type = extern union {
|
|
/// If the tag value is less than Tag.no_payload_count, then no pointer
|
|
/// dereference is needed.
|
|
tag_if_small_enough: usize,
|
|
ptr_otherwise: *Payload,
|
|
|
|
pub fn zigTypeTag(self: Type) std.builtin.TypeId {
|
|
switch (self.tag()) {
|
|
.@"u8",
|
|
.@"i8",
|
|
.@"isize",
|
|
.@"usize",
|
|
.@"c_short",
|
|
.@"c_ushort",
|
|
.@"c_int",
|
|
.@"c_uint",
|
|
.@"c_long",
|
|
.@"c_ulong",
|
|
.@"c_longlong",
|
|
.@"c_ulonglong",
|
|
.@"c_longdouble",
|
|
=> return .Int,
|
|
|
|
.@"f16",
|
|
.@"f32",
|
|
.@"f64",
|
|
.@"f128",
|
|
=> return .Float,
|
|
|
|
.@"c_void" => return .Opaque,
|
|
.@"bool" => return .Bool,
|
|
.@"void" => return .Void,
|
|
.@"type" => return .Type,
|
|
.@"anyerror" => return .ErrorSet,
|
|
.@"comptime_int" => return .ComptimeInt,
|
|
.@"comptime_float" => return .ComptimeFloat,
|
|
.@"noreturn" => return .NoReturn,
|
|
|
|
.array, .array_u8_sentinel_0 => return .Array,
|
|
.single_const_pointer => return .Pointer,
|
|
.const_slice_u8 => return .Pointer,
|
|
}
|
|
}
|
|
|
|
pub fn initTag(comptime small_tag: Tag) Type {
|
|
comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
|
|
return .{ .tag_if_small_enough = @enumToInt(small_tag) };
|
|
}
|
|
|
|
pub fn initPayload(payload: *Payload) Type {
|
|
assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
|
|
return .{ .ptr_otherwise = payload };
|
|
}
|
|
|
|
pub fn tag(self: Type) Tag {
|
|
if (self.tag_if_small_enough < Tag.no_payload_count) {
|
|
return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
|
|
} else {
|
|
return self.ptr_otherwise.tag;
|
|
}
|
|
}
|
|
|
|
pub fn cast(self: Type, comptime T: type) ?*T {
|
|
if (self.tag_if_small_enough < Tag.no_payload_count)
|
|
return null;
|
|
|
|
const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
|
|
if (self.ptr_otherwise.tag != expected_tag)
|
|
return null;
|
|
|
|
return @fieldParentPtr(T, "base", self.ptr_otherwise);
|
|
}
|
|
|
|
pub fn format(
|
|
self: Type,
|
|
comptime fmt: []const u8,
|
|
options: std.fmt.FormatOptions,
|
|
out_stream: var,
|
|
) !void {
|
|
comptime assert(fmt.len == 0);
|
|
var ty = self;
|
|
while (true) {
|
|
const t = ty.tag();
|
|
switch (t) {
|
|
.@"u8",
|
|
.@"i8",
|
|
.@"isize",
|
|
.@"usize",
|
|
.@"c_short",
|
|
.@"c_ushort",
|
|
.@"c_int",
|
|
.@"c_uint",
|
|
.@"c_long",
|
|
.@"c_ulong",
|
|
.@"c_longlong",
|
|
.@"c_ulonglong",
|
|
.@"c_longdouble",
|
|
.@"c_void",
|
|
.@"f16",
|
|
.@"f32",
|
|
.@"f64",
|
|
.@"f128",
|
|
.@"bool",
|
|
.@"void",
|
|
.@"type",
|
|
.@"anyerror",
|
|
.@"comptime_int",
|
|
.@"comptime_float",
|
|
.@"noreturn",
|
|
=> return out_stream.writeAll(@tagName(t)),
|
|
|
|
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
|
|
|
|
.array_u8_sentinel_0 => {
|
|
const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
|
|
return out_stream.print("[{}:0]u8", .{payload.len});
|
|
},
|
|
.array => {
|
|
const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
|
|
try out_stream.print("[{}]", .{payload.len});
|
|
ty = payload.elem_type;
|
|
continue;
|
|
},
|
|
.single_const_pointer => {
|
|
const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise);
|
|
try out_stream.writeAll("*const ");
|
|
ty = payload.pointee_type;
|
|
continue;
|
|
},
|
|
}
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
pub fn isSinglePointer(self: Type) bool {
|
|
return switch (self.tag()) {
|
|
.@"u8",
|
|
.@"i8",
|
|
.@"isize",
|
|
.@"usize",
|
|
.@"c_short",
|
|
.@"c_ushort",
|
|
.@"c_int",
|
|
.@"c_uint",
|
|
.@"c_long",
|
|
.@"c_ulong",
|
|
.@"c_longlong",
|
|
.@"c_ulonglong",
|
|
.@"c_longdouble",
|
|
.@"f16",
|
|
.@"f32",
|
|
.@"f64",
|
|
.@"f128",
|
|
.@"c_void",
|
|
.@"bool",
|
|
.@"void",
|
|
.@"type",
|
|
.@"anyerror",
|
|
.@"comptime_int",
|
|
.@"comptime_float",
|
|
.@"noreturn",
|
|
.array,
|
|
.array_u8_sentinel_0,
|
|
.const_slice_u8,
|
|
=> false,
|
|
|
|
.single_const_pointer => true,
|
|
};
|
|
}
|
|
|
|
pub fn isSlice(self: Type) bool {
|
|
return switch (self.tag()) {
|
|
.@"u8",
|
|
.@"i8",
|
|
.@"isize",
|
|
.@"usize",
|
|
.@"c_short",
|
|
.@"c_ushort",
|
|
.@"c_int",
|
|
.@"c_uint",
|
|
.@"c_long",
|
|
.@"c_ulong",
|
|
.@"c_longlong",
|
|
.@"c_ulonglong",
|
|
.@"c_longdouble",
|
|
.@"f16",
|
|
.@"f32",
|
|
.@"f64",
|
|
.@"f128",
|
|
.@"c_void",
|
|
.@"bool",
|
|
.@"void",
|
|
.@"type",
|
|
.@"anyerror",
|
|
.@"comptime_int",
|
|
.@"comptime_float",
|
|
.@"noreturn",
|
|
.array,
|
|
.array_u8_sentinel_0,
|
|
.single_const_pointer,
|
|
=> false,
|
|
|
|
.const_slice_u8 => true,
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is a pointer type.
|
|
pub fn pointerIsConst(self: Type) bool {
|
|
return switch (self.tag()) {
|
|
.@"u8",
|
|
.@"i8",
|
|
.@"isize",
|
|
.@"usize",
|
|
.@"c_short",
|
|
.@"c_ushort",
|
|
.@"c_int",
|
|
.@"c_uint",
|
|
.@"c_long",
|
|
.@"c_ulong",
|
|
.@"c_longlong",
|
|
.@"c_ulonglong",
|
|
.@"c_longdouble",
|
|
.@"f16",
|
|
.@"f32",
|
|
.@"f64",
|
|
.@"f128",
|
|
.@"c_void",
|
|
.@"bool",
|
|
.@"void",
|
|
.@"type",
|
|
.@"anyerror",
|
|
.@"comptime_int",
|
|
.@"comptime_float",
|
|
.@"noreturn",
|
|
.array,
|
|
.array_u8_sentinel_0,
|
|
=> unreachable,
|
|
|
|
.single_const_pointer, .const_slice_u8 => true,
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is a pointer or array type.
|
|
pub fn elemType(self: Type) Type {
|
|
return switch (self.tag()) {
|
|
.@"u8",
|
|
.@"i8",
|
|
.@"isize",
|
|
.@"usize",
|
|
.@"c_short",
|
|
.@"c_ushort",
|
|
.@"c_int",
|
|
.@"c_uint",
|
|
.@"c_long",
|
|
.@"c_ulong",
|
|
.@"c_longlong",
|
|
.@"c_ulonglong",
|
|
.@"c_longdouble",
|
|
.@"f16",
|
|
.@"f32",
|
|
.@"f64",
|
|
.@"f128",
|
|
.@"c_void",
|
|
.@"bool",
|
|
.@"void",
|
|
.@"type",
|
|
.@"anyerror",
|
|
.@"comptime_int",
|
|
.@"comptime_float",
|
|
.@"noreturn",
|
|
=> unreachable,
|
|
|
|
.array => self.cast(Payload.Array).?.elem_type,
|
|
.single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,
|
|
.array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.@"u8"),
|
|
};
|
|
}
|
|
|
|
/// This enum does not directly correspond to `std.builtin.TypeId` because
|
|
/// it has extra enum tags in it, as a way of using less memory. For example,
|
|
/// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
|
|
/// but with different alignment values, in this data structure they are represented
|
|
/// with different enum tags, because the the former requires more payload data than the latter.
|
|
/// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
|
|
pub const Tag = enum {
|
|
// The first section of this enum are tags that require no payload.
|
|
const_slice_u8,
|
|
@"u8",
|
|
@"i8",
|
|
@"isize",
|
|
@"usize",
|
|
@"c_short",
|
|
@"c_ushort",
|
|
@"c_int",
|
|
@"c_uint",
|
|
@"c_long",
|
|
@"c_ulong",
|
|
@"c_longlong",
|
|
@"c_ulonglong",
|
|
@"c_longdouble",
|
|
@"c_void",
|
|
@"f16",
|
|
@"f32",
|
|
@"f64",
|
|
@"f128",
|
|
@"bool",
|
|
@"void",
|
|
@"type",
|
|
@"anyerror",
|
|
@"comptime_int",
|
|
@"comptime_float",
|
|
@"noreturn", // See last_no_payload_tag below.
|
|
// After this, the tag requires a payload.
|
|
|
|
array_u8_sentinel_0,
|
|
array,
|
|
single_const_pointer,
|
|
|
|
pub const last_no_payload_tag = Tag.@"noreturn";
|
|
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
|
};
|
|
|
|
pub const Payload = struct {
|
|
tag: Tag,
|
|
|
|
pub const Array_u8_Sentinel0 = struct {
|
|
base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
|
|
|
|
len: u64,
|
|
};
|
|
|
|
pub const Array = struct {
|
|
base: Payload = Payload{ .tag = .array },
|
|
|
|
elem_type: Type,
|
|
len: u64,
|
|
};
|
|
|
|
pub const SingleConstPointer = struct {
|
|
base: Payload = Payload{ .tag = .single_const_pointer },
|
|
|
|
pointee_type: Type,
|
|
};
|
|
};
|
|
};
|