mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-09 15:19:07 +00:00
316 lines
8.7 KiB
Zig
316 lines
8.7 KiB
Zig
map: std.AutoArrayHashMapUnmanaged(void, void) = .{},
|
|
items: std.MultiArrayList(Item) = .{},
|
|
extra: std.ArrayListUnmanaged(u32) = .{},
|
|
|
|
const InternPool = @This();
|
|
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const assert = std.debug.assert;
|
|
|
|
const KeyAdapter = struct {
|
|
intern_pool: *const InternPool,
|
|
|
|
pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool {
|
|
_ = b_void;
|
|
return ctx.intern_pool.indexToKey(@intToEnum(Index, b_map_index)).eql(a);
|
|
}
|
|
|
|
pub fn hash(ctx: @This(), a: Key) u32 {
|
|
_ = ctx;
|
|
return a.hash();
|
|
}
|
|
};
|
|
|
|
pub const Key = union(enum) {
|
|
int_type: struct {
|
|
signedness: std.builtin.Signedness,
|
|
bits: u16,
|
|
},
|
|
ptr_type: struct {
|
|
elem_type: Index,
|
|
sentinel: Index,
|
|
alignment: u16,
|
|
size: std.builtin.Type.Pointer.Size,
|
|
is_const: bool,
|
|
is_volatile: bool,
|
|
is_allowzero: bool,
|
|
address_space: std.builtin.AddressSpace,
|
|
},
|
|
array_type: struct {
|
|
len: u64,
|
|
child: Index,
|
|
sentinel: Index,
|
|
},
|
|
vector_type: struct {
|
|
len: u32,
|
|
child: Index,
|
|
},
|
|
optional_type: struct {
|
|
payload_type: Index,
|
|
},
|
|
error_union_type: struct {
|
|
error_set_type: Index,
|
|
payload_type: Index,
|
|
},
|
|
simple: Simple,
|
|
|
|
pub fn hash(key: Key) u32 {
|
|
var hasher = std.hash.Wyhash.init(0);
|
|
switch (key) {
|
|
.int_type => |int_type| {
|
|
std.hash.autoHash(&hasher, int_type);
|
|
},
|
|
.array_type => |array_type| {
|
|
std.hash.autoHash(&hasher, array_type);
|
|
},
|
|
else => @panic("TODO"),
|
|
}
|
|
return @truncate(u32, hasher.final());
|
|
}
|
|
|
|
pub fn eql(a: Key, b: Key) bool {
|
|
const KeyTag = std.meta.Tag(Key);
|
|
const a_tag: KeyTag = a;
|
|
const b_tag: KeyTag = b;
|
|
if (a_tag != b_tag) return false;
|
|
switch (a) {
|
|
.int_type => |a_info| {
|
|
const b_info = b.int_type;
|
|
return std.meta.eql(a_info, b_info);
|
|
},
|
|
.array_type => |a_info| {
|
|
const b_info = b.array_type;
|
|
return std.meta.eql(a_info, b_info);
|
|
},
|
|
else => @panic("TODO"),
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const Item = struct {
|
|
tag: Tag,
|
|
/// The doc comments on the respective Tag explain how to interpret this.
|
|
data: u32,
|
|
};
|
|
|
|
/// Represents an index into `map`. It represents the canonical index
|
|
/// of a `Value` within this `InternPool`. The values are typed.
|
|
/// Two values which have the same type can be equality compared simply
|
|
/// by checking if their indexes are equal, provided they are both in
|
|
/// the same `InternPool`.
|
|
pub const Index = enum(u32) {
|
|
none = std.math.maxInt(u32),
|
|
_,
|
|
};
|
|
|
|
pub const Tag = enum(u8) {
|
|
/// An integer type.
|
|
/// data is number of bits
|
|
type_int_signed,
|
|
/// An integer type.
|
|
/// data is number of bits
|
|
type_int_unsigned,
|
|
/// An array type.
|
|
/// data is payload to Array.
|
|
type_array,
|
|
/// A type or value that can be represented with only an enum tag.
|
|
/// data is Simple enum value
|
|
simple,
|
|
/// An unsigned integer value that can be represented by u32.
|
|
/// data is integer value
|
|
int_u32,
|
|
/// An unsigned integer value that can be represented by i32.
|
|
/// data is integer value bitcasted to u32.
|
|
int_i32,
|
|
/// A positive integer value that does not fit in 32 bits.
|
|
/// data is a extra index to BigInt.
|
|
int_big_positive,
|
|
/// A negative integer value that does not fit in 32 bits.
|
|
/// data is a extra index to BigInt.
|
|
int_big_negative,
|
|
/// A float value that can be represented by f32.
|
|
/// data is float value bitcasted to u32.
|
|
float_f32,
|
|
/// A float value that can be represented by f64.
|
|
/// data is payload index to Float64.
|
|
float_f64,
|
|
/// A float value that can be represented by f128.
|
|
/// data is payload index to Float128.
|
|
float_f128,
|
|
};
|
|
|
|
pub const Simple = enum(u32) {
|
|
f16,
|
|
f32,
|
|
f64,
|
|
f80,
|
|
f128,
|
|
usize,
|
|
isize,
|
|
c_short,
|
|
c_ushort,
|
|
c_int,
|
|
c_uint,
|
|
c_long,
|
|
c_ulong,
|
|
c_longlong,
|
|
c_ulonglong,
|
|
c_longdouble,
|
|
anyopaque,
|
|
bool,
|
|
void,
|
|
type,
|
|
anyerror,
|
|
comptime_int,
|
|
comptime_float,
|
|
noreturn,
|
|
@"anyframe",
|
|
null_type,
|
|
undefined_type,
|
|
enum_literal_type,
|
|
@"undefined",
|
|
void_value,
|
|
@"null",
|
|
bool_true,
|
|
bool_false,
|
|
};
|
|
|
|
pub const Array = struct {
|
|
len: u32,
|
|
child: Index,
|
|
};
|
|
|
|
pub fn deinit(ip: *InternPool, gpa: Allocator) void {
|
|
ip.map.deinit(gpa);
|
|
ip.items.deinit(gpa);
|
|
ip.extra.deinit(gpa);
|
|
}
|
|
|
|
pub fn indexToKey(ip: InternPool, index: Index) Key {
|
|
const item = ip.items.get(@enumToInt(index));
|
|
const data = item.data;
|
|
return switch (item.tag) {
|
|
.type_int_signed => .{
|
|
.int_type = .{
|
|
.signedness = .signed,
|
|
.bits = @intCast(u16, data),
|
|
},
|
|
},
|
|
.type_int_unsigned => .{
|
|
.int_type = .{
|
|
.signedness = .unsigned,
|
|
.bits = @intCast(u16, data),
|
|
},
|
|
},
|
|
.type_array => {
|
|
const array_info = ip.extraData(Array, data);
|
|
return .{ .array_type = .{
|
|
.len = array_info.len,
|
|
.child = array_info.child,
|
|
.sentinel = .none,
|
|
} };
|
|
},
|
|
.simple => .{ .simple = @intToEnum(Simple, data) },
|
|
|
|
else => @panic("TODO"),
|
|
};
|
|
}
|
|
|
|
pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
|
|
const adapter: KeyAdapter = .{ .intern_pool = ip };
|
|
const gop = try ip.map.getOrPutAdapted(gpa, key, adapter);
|
|
if (gop.found_existing) {
|
|
return @intToEnum(Index, gop.index);
|
|
}
|
|
switch (key) {
|
|
.int_type => |int_type| {
|
|
const tag: Tag = switch (int_type.signedness) {
|
|
.signed => .type_int_signed,
|
|
.unsigned => .type_int_unsigned,
|
|
};
|
|
try ip.items.append(gpa, .{
|
|
.tag = tag,
|
|
.data = int_type.bits,
|
|
});
|
|
},
|
|
.array_type => |array_type| {
|
|
const len = @intCast(u32, array_type.len); // TODO have a big_array encoding
|
|
assert(array_type.sentinel == .none); // TODO have a sentinel_array encoding
|
|
try ip.items.append(gpa, .{
|
|
.tag = .type_array,
|
|
.data = try ip.addExtra(gpa, Array{
|
|
.len = len,
|
|
.child = array_type.child,
|
|
}),
|
|
});
|
|
},
|
|
else => @panic("TODO"),
|
|
}
|
|
return @intToEnum(Index, ip.items.len - 1);
|
|
}
|
|
|
|
fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 {
|
|
const fields = std.meta.fields(@TypeOf(extra));
|
|
try ip.extra.ensureUnusedCapacity(gpa, fields.len);
|
|
return ip.addExtraAssumeCapacity(extra);
|
|
}
|
|
|
|
fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
|
|
const fields = std.meta.fields(@TypeOf(extra));
|
|
const result = @intCast(u32, ip.extra.items.len);
|
|
inline for (fields) |field| {
|
|
ip.extra.appendAssumeCapacity(switch (field.field_type) {
|
|
u32 => @field(extra, field.name),
|
|
Index => @enumToInt(@field(extra, field.name)),
|
|
i32 => @bitCast(u32, @field(extra, field.name)),
|
|
else => @compileError("bad field type"),
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
fn extraData(ip: InternPool, comptime T: type, index: usize) T {
|
|
const fields = std.meta.fields(T);
|
|
var i: usize = index;
|
|
var result: T = undefined;
|
|
inline for (fields) |field| {
|
|
@field(result, field.name) = switch (field.field_type) {
|
|
u32 => ip.extra.items[i],
|
|
Index => @intToEnum(Index, ip.extra.items[i]),
|
|
i32 => @bitCast(i32, ip.extra.items[i]),
|
|
else => @compileError("bad field type"),
|
|
};
|
|
i += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
test "basic usage" {
|
|
const gpa = std.testing.allocator;
|
|
|
|
var ip: InternPool = .{};
|
|
defer ip.deinit(gpa);
|
|
|
|
const i32_type = try ip.get(gpa, .{ .int_type = .{
|
|
.signedness = .signed,
|
|
.bits = 32,
|
|
} });
|
|
const array_i32 = try ip.get(gpa, .{ .array_type = .{
|
|
.len = 10,
|
|
.child = i32_type,
|
|
.sentinel = .none,
|
|
} });
|
|
|
|
const another_i32_type = try ip.get(gpa, .{ .int_type = .{
|
|
.signedness = .signed,
|
|
.bits = 32,
|
|
} });
|
|
try std.testing.expect(another_i32_type == i32_type);
|
|
|
|
const another_array_i32 = try ip.get(gpa, .{ .array_type = .{
|
|
.len = 10,
|
|
.child = i32_type,
|
|
.sentinel = .none,
|
|
} });
|
|
try std.testing.expect(another_array_i32 == array_i32);
|
|
}
|