redesign InternPool encoding
This commit is contained in:
		
							parent
							
								
									db97a19233
								
							
						
					
					
						commit
						0817d6008b
					
				@ -255,9 +255,11 @@ pub fn interpret(
 | 
				
			|||||||
                    );
 | 
					                    );
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                const field_name = try interpreter.ip.get(interpreter.allocator, .{
 | 
				
			||||||
 | 
					                    .bytes = tree.tokenSlice(container_field.ast.main_token),
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
                const field: InternPool.Struct.Field = .{
 | 
					                const field: InternPool.Struct.Field = .{
 | 
				
			||||||
                    .name = tree.tokenSlice(container_field.ast.main_token),
 | 
					                    .name = field_name,
 | 
				
			||||||
                    .ty = init_type_value.val,
 | 
					                    .ty = init_type_value.val,
 | 
				
			||||||
                    .default_value = default_value,
 | 
					                    .default_value = default_value,
 | 
				
			||||||
                    .alignment = 0, // TODO,
 | 
					                    .alignment = 0, // TODO,
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,8 @@ const builtin = @import("builtin");
 | 
				
			|||||||
const Allocator = std.mem.Allocator;
 | 
					const Allocator = std.mem.Allocator;
 | 
				
			||||||
const assert = std.debug.assert;
 | 
					const assert = std.debug.assert;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const encoding = @import("encoding.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const Int = packed struct {
 | 
					pub const Int = packed struct {
 | 
				
			||||||
    signedness: std.builtin.Signedness,
 | 
					    signedness: std.builtin.Signedness,
 | 
				
			||||||
    bits: u16,
 | 
					    bits: u16,
 | 
				
			||||||
@ -40,8 +42,8 @@ pub const Struct = struct {
 | 
				
			|||||||
    layout: std.builtin.Type.ContainerLayout = .Auto,
 | 
					    layout: std.builtin.Type.ContainerLayout = .Auto,
 | 
				
			||||||
    backing_int_ty: Index,
 | 
					    backing_int_ty: Index,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub const Field = struct {
 | 
					    pub const Field = packed struct {
 | 
				
			||||||
        name: []const u8,
 | 
					        name: Index,
 | 
				
			||||||
        ty: Index,
 | 
					        ty: Index,
 | 
				
			||||||
        default_value: Index = .none,
 | 
					        default_value: Index = .none,
 | 
				
			||||||
        alignment: u16 = 0,
 | 
					        alignment: u16 = 0,
 | 
				
			||||||
@ -59,12 +61,7 @@ pub const ErrorUnion = packed struct {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const ErrorSet = struct {
 | 
					pub const ErrorSet = struct {
 | 
				
			||||||
    /// must be sorted
 | 
					    names: []const Index,
 | 
				
			||||||
    names: []const []const u8,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn sort(self: *ErrorSet) void {
 | 
					 | 
				
			||||||
        std.sort.sort([]const []const u8, self.names, u8, std.mem.lessThan);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const Enum = struct {
 | 
					pub const Enum = struct {
 | 
				
			||||||
@ -73,8 +70,8 @@ pub const Enum = struct {
 | 
				
			|||||||
    namespace: NamespaceIndex,
 | 
					    namespace: NamespaceIndex,
 | 
				
			||||||
    tag_type_infered: bool,
 | 
					    tag_type_infered: bool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub const Field = struct {
 | 
					    pub const Field = packed struct {
 | 
				
			||||||
        name: []const u8,
 | 
					        name: Index,
 | 
				
			||||||
        val: Index,
 | 
					        val: Index,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -97,8 +94,8 @@ pub const Union = struct {
 | 
				
			|||||||
    namespace: NamespaceIndex,
 | 
					    namespace: NamespaceIndex,
 | 
				
			||||||
    layout: std.builtin.Type.ContainerLayout = .Auto,
 | 
					    layout: std.builtin.Type.ContainerLayout = .Auto,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub const Field = struct {
 | 
					    pub const Field = packed struct {
 | 
				
			||||||
        name: []const u8,
 | 
					        name: Index,
 | 
				
			||||||
        ty: Index,
 | 
					        ty: Index,
 | 
				
			||||||
        alignment: u16,
 | 
					        alignment: u16,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@ -675,7 +672,7 @@ pub const Key = union(enum) {
 | 
				
			|||||||
                try writer.writeAll("error{");
 | 
					                try writer.writeAll("error{");
 | 
				
			||||||
                for (names) |name, i| {
 | 
					                for (names) |name, i| {
 | 
				
			||||||
                    if (i != 0) try writer.writeByte(',');
 | 
					                    if (i != 0) try writer.writeByte(',');
 | 
				
			||||||
                    try writer.writeAll(name);
 | 
					                    try writer.writeAll(ip.indexToKey(name).bytes);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                try writer.writeByte('}');
 | 
					                try writer.writeByte('}');
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -853,7 +850,8 @@ pub const Key = union(enum) {
 | 
				
			|||||||
                while (i < aggregate.len) : (i += 1) {
 | 
					                while (i < aggregate.len) : (i += 1) {
 | 
				
			||||||
                    if (i != 0) try writer.writeAll(", ");
 | 
					                    if (i != 0) try writer.writeAll(", ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    try writer.print(".{s} = ", .{struct_info.fields[i].name});
 | 
					                    const field_name = ip.indexToKey(struct_info.fields[i].name).bytes;
 | 
				
			||||||
 | 
					                    try writer.print(".{s} = ", .{field_name});
 | 
				
			||||||
                    try printValue(ip.indexToKey(aggregate[i]), struct_info.fields[i].ty, ip, writer);
 | 
					                    try printValue(ip.indexToKey(aggregate[i]), struct_info.fields[i].ty, ip, writer);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                try writer.writeByte('}');
 | 
					                try writer.writeByte('}');
 | 
				
			||||||
@ -861,9 +859,9 @@ pub const Key = union(enum) {
 | 
				
			|||||||
            .union_value => |union_value| {
 | 
					            .union_value => |union_value| {
 | 
				
			||||||
                const union_info = ip.indexToKey(ty).union_type;
 | 
					                const union_info = ip.indexToKey(ty).union_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const name = ip.indexToKey(union_info.fields[union_value.field_index].name).bytes;
 | 
				
			||||||
                try writer.print(".{{ .{} = {} }}", .{
 | 
					                try writer.print(".{{ .{} = {} }}", .{
 | 
				
			||||||
                    std.zig.fmtId(union_info.fields[union_value.field_index].name),
 | 
					                    std.zig.fmtId(name),
 | 
				
			||||||
                    // union_value.tag.fmtValue(union_info.tag_type, ip),
 | 
					 | 
				
			||||||
                    union_value.val.fmtValue(union_info.fields[union_value.field_index].ty, ip),
 | 
					                    union_value.val.fmtValue(union_info.fields[union_value.field_index].ty, ip),
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -1182,20 +1180,21 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 {
 | 
					fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 {
 | 
				
			||||||
    comptime if (@sizeOf(@TypeOf(extra)) <= 4) {
 | 
					    const T = @TypeOf(extra);
 | 
				
			||||||
        @compileError(@typeName(@TypeOf(extra)) ++ " fits into a u32! Consider directly storing this extra in Item's data field");
 | 
					    comptime if (@sizeOf(T) <= 4) {
 | 
				
			||||||
 | 
					        @compileError(@typeName(T) ++ " fits into a u32! Consider directly storing this extra in Item's data field");
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO this doesn't correctly store types with slices like `Struct` `Aggregate`
 | 
					 | 
				
			||||||
    const result = @intCast(u32, ip.extra.items.len);
 | 
					    const result = @intCast(u32, ip.extra.items.len);
 | 
				
			||||||
    try ip.extra.appendSlice(gpa, &std.mem.toBytes(extra));
 | 
					    var managed = ip.extra.toManaged(gpa);
 | 
				
			||||||
 | 
					    defer ip.extra = managed.moveToUnmanaged();
 | 
				
			||||||
 | 
					    try encoding.encode(&managed, T, extra);
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn extraData(ip: InternPool, comptime T: type, index: usize) T {
 | 
					fn extraData(ip: InternPool, comptime T: type, index: usize) T {
 | 
				
			||||||
    const size = @sizeOf(T);
 | 
					    var bytes: []const u8 = ip.extra.items[index..];
 | 
				
			||||||
    const bytes = @ptrCast(*const [size]u8, ip.extra.items.ptr + index);
 | 
					    return encoding.decode(&bytes, T);
 | 
				
			||||||
    return std.mem.bytesToValue(T, bytes);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const KeyAdapter = struct {
 | 
					const KeyAdapter = struct {
 | 
				
			||||||
@ -1217,7 +1216,7 @@ fn deepEql(a: anytype, b: @TypeOf(a)) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    switch (@typeInfo(T)) {
 | 
					    switch (@typeInfo(T)) {
 | 
				
			||||||
        .Struct => |info| {
 | 
					        .Struct => |info| {
 | 
				
			||||||
            if (comptime std.meta.trait.hasUniqueRepresentation(T)) {
 | 
					            if (info.layout == .Packed and comptime std.meta.trait.hasUniqueRepresentation(T)) {
 | 
				
			||||||
                return std.mem.eql(u8, std.mem.asBytes(&a), std.mem.asBytes(&b));
 | 
					                return std.mem.eql(u8, std.mem.asBytes(&a), std.mem.asBytes(&b));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            inline for (info.fields) |field_info| {
 | 
					            inline for (info.fields) |field_info| {
 | 
				
			||||||
@ -1239,40 +1238,39 @@ fn deepEql(a: anytype, b: @TypeOf(a)) bool {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        .Pointer => |info| {
 | 
					        .Pointer => |info| switch (info.size) {
 | 
				
			||||||
            if (info.size != .Slice) {
 | 
					            .One => return deepEql(a.*, b.*),
 | 
				
			||||||
                @compileError("cannot compare non slice pointer type " ++ @typeName(T));
 | 
					            .Slice => {
 | 
				
			||||||
            }
 | 
					                if (a.len != b.len) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (@typeInfo(info.child) == .Int) {
 | 
					                var i: usize = 0;
 | 
				
			||||||
                return std.mem.eql(info.child, a, b);
 | 
					                while (i < a.len) : (i += 1) {
 | 
				
			||||||
            }
 | 
					                    if (!deepEql(a[i], b[i])) return false;
 | 
				
			||||||
            if (a.len != b.len) return false;
 | 
					                }
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
            var i: usize = 0;
 | 
					            },
 | 
				
			||||||
            while (i < a.len) : (i += 1) {
 | 
					            .Many,
 | 
				
			||||||
                if (!deepEql(a[i], b[i])) return false;
 | 
					            .C,
 | 
				
			||||||
            }
 | 
					            => @compileError("Unable to equality compare pointer " ++ @typeName(T)),
 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        .Bool,
 | 
					        .Bool,
 | 
				
			||||||
        .Int,
 | 
					        .Int,
 | 
				
			||||||
        .Float,
 | 
					        .Float,
 | 
				
			||||||
        .Enum,
 | 
					        .Enum,
 | 
				
			||||||
        => return a == b,
 | 
					        => return a == b,
 | 
				
			||||||
        else => unreachable,
 | 
					        else => @compileError("Unable to equality compare type " ++ @typeName(T)),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn deepHash(hasher: anytype, key: anytype) void {
 | 
					fn deepHash(hasher: anytype, key: anytype) void {
 | 
				
			||||||
    const Inner = @TypeOf(key);
 | 
					    const T = @TypeOf(key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (@typeInfo(Inner)) {
 | 
					    switch (@typeInfo(T)) {
 | 
				
			||||||
        .Int => {
 | 
					        .Int => {
 | 
				
			||||||
            if (comptime std.meta.trait.hasUniqueRepresentation(Inner)) {
 | 
					            if (comptime std.meta.trait.hasUniqueRepresentation(Tuple)) {
 | 
				
			||||||
                hasher.update(std.mem.asBytes(&key));
 | 
					                hasher.update(std.mem.asBytes(&key));
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(Inner), 8) catch unreachable;
 | 
					                const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(T), 8) catch unreachable;
 | 
				
			||||||
                hasher.update(std.mem.asBytes(&key)[0..byte_size]);
 | 
					                hasher.update(std.mem.asBytes(&key)[0..byte_size]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -1288,22 +1286,25 @@ fn deepHash(hasher: anytype, key: anytype) void {
 | 
				
			|||||||
            else => unreachable,
 | 
					            else => unreachable,
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .Pointer => |info| {
 | 
					        .Pointer => |info| switch (info.size) {
 | 
				
			||||||
            if (info.size != .Slice) {
 | 
					            .One => {
 | 
				
			||||||
                @compileError("");
 | 
					                deepHash(hasher, key.*);
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
 | 
					            .Slice => {
 | 
				
			||||||
            if (comptime std.meta.trait.hasUniqueRepresentation(info.child)) {
 | 
					                if (info.child == u8) {
 | 
				
			||||||
                hasher.update(std.mem.sliceAsBytes(key));
 | 
					                    hasher.update(key);
 | 
				
			||||||
            } else {
 | 
					                } else {
 | 
				
			||||||
                for (key) |item| {
 | 
					                    for (key) |item| {
 | 
				
			||||||
                    deepHash(hasher, item);
 | 
					                        deepHash(hasher, item);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            },
 | 
				
			||||||
 | 
					            .Many,
 | 
				
			||||||
 | 
					            .C,
 | 
				
			||||||
 | 
					            => @compileError("Unable to hash pointer " ++ @typeName(T)),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					 | 
				
			||||||
        .Struct => |info| {
 | 
					        .Struct => |info| {
 | 
				
			||||||
            if (comptime std.meta.trait.hasUniqueRepresentation(Inner)) {
 | 
					            if (info.layout == .Packed and comptime std.meta.trait.hasUniqueRepresentation(T)) {
 | 
				
			||||||
                hasher.update(std.mem.asBytes(&key));
 | 
					                hasher.update(std.mem.asBytes(&key));
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                inline for (info.fields) |field| {
 | 
					                inline for (info.fields) |field| {
 | 
				
			||||||
@ -1324,7 +1325,7 @@ fn deepHash(hasher: anytype, key: anytype) void {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        else => unreachable,
 | 
					        else => @compileError("Unable to hash type " ++ @typeName(T)),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2797,23 +2798,26 @@ test "error set type" {
 | 
				
			|||||||
    var ip: InternPool = .{};
 | 
					    var ip: InternPool = .{};
 | 
				
			||||||
    defer ip.deinit(gpa);
 | 
					    defer ip.deinit(gpa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const foo_name = try ip.get(gpa, .{ .bytes = "foo" });
 | 
				
			||||||
 | 
					    const bar_name = try ip.get(gpa, .{ .bytes = "bar" });
 | 
				
			||||||
 | 
					    const baz_name = try ip.get(gpa, .{ .bytes = "baz" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } });
 | 
					    const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const error_set_0 = try ip.get(gpa, .{ .error_set_type = .{
 | 
					    const error_set_0 = try ip.get(gpa, .{ .error_set_type = .{
 | 
				
			||||||
        .names = &.{ "foo", "bar", "baz" },
 | 
					        .names = &.{ foo_name, bar_name, baz_name },
 | 
				
			||||||
    } });
 | 
					    } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const foo: [3]u8 = "foo".*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const error_set_1 = try ip.get(gpa, .{ .error_set_type = .{
 | 
					    const error_set_1 = try ip.get(gpa, .{ .error_set_type = .{
 | 
				
			||||||
        .names = &.{ &foo, "bar", "baz" },
 | 
					        .names = &.{ foo_name, bar_name },
 | 
				
			||||||
    } });
 | 
					    } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try std.testing.expect(empty_error_set != error_set_0);
 | 
					    try std.testing.expect(empty_error_set != error_set_0);
 | 
				
			||||||
    try std.testing.expect(error_set_0 == error_set_1);
 | 
					    try std.testing.expect(error_set_0 != error_set_1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try testExpectFmtType(ip, empty_error_set, "error{}");
 | 
					    try testExpectFmtType(ip, empty_error_set, "error{}");
 | 
				
			||||||
    try testExpectFmtType(ip, error_set_0, "error{foo,bar,baz}");
 | 
					    try testExpectFmtType(ip, error_set_0, "error{foo,bar,baz}");
 | 
				
			||||||
 | 
					    try testExpectFmtType(ip, error_set_1, "error{foo,bar}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test "error union type" {
 | 
					test "error union type" {
 | 
				
			||||||
@ -2875,9 +2879,13 @@ test "struct type" {
 | 
				
			|||||||
    const u64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 64 } });
 | 
					    const u64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 64 } });
 | 
				
			||||||
    const bool_type = try ip.get(gpa, .{ .simple = .bool });
 | 
					    const bool_type = try ip.get(gpa, .{ .simple = .bool });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const field1 = Struct.Field{ .name = "foo", .ty = u64_type };
 | 
					    const foo_name = try ip.get(gpa, .{ .bytes = "foo" });
 | 
				
			||||||
    const field2 = Struct.Field{ .name = "bar", .ty = i32_type };
 | 
					    const bar_name = try ip.get(gpa, .{ .bytes = "bar" });
 | 
				
			||||||
    const field3 = Struct.Field{ .name = "baz", .ty = bool_type };
 | 
					    const baz_name = try ip.get(gpa, .{ .bytes = "baz" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const field1 = Struct.Field{ .name = foo_name, .ty = u64_type };
 | 
				
			||||||
 | 
					    const field2 = Struct.Field{ .name = bar_name, .ty = i32_type };
 | 
				
			||||||
 | 
					    const field3 = Struct.Field{ .name = baz_name, .ty = bool_type };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const struct_type_0 = try ip.get(gpa, .{ .struct_type = .{
 | 
					    const struct_type_0 = try ip.get(gpa, .{ .struct_type = .{
 | 
				
			||||||
        .fields = &.{ field1, field2, field3 },
 | 
					        .fields = &.{ field1, field2, field3 },
 | 
				
			||||||
@ -2902,6 +2910,9 @@ test "enum type" {
 | 
				
			|||||||
    var ip: InternPool = .{};
 | 
					    var ip: InternPool = .{};
 | 
				
			||||||
    defer ip.deinit(gpa);
 | 
					    defer ip.deinit(gpa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const zig_name = try ip.get(gpa, .{ .bytes = "zig" });
 | 
				
			||||||
 | 
					    const cpp_name = try ip.get(gpa, .{ .bytes = "cpp" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const empty_enum1 = try ip.get(gpa, .{ .enum_type = .{
 | 
					    const empty_enum1 = try ip.get(gpa, .{ .enum_type = .{
 | 
				
			||||||
        .tag_type = .none,
 | 
					        .tag_type = .none,
 | 
				
			||||||
        .fields = &.{},
 | 
					        .fields = &.{},
 | 
				
			||||||
@ -2916,8 +2927,8 @@ test "enum type" {
 | 
				
			|||||||
        .tag_type_infered = true,
 | 
					        .tag_type_infered = true,
 | 
				
			||||||
    } });
 | 
					    } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const field1 = Enum.Field{ .name = "zig", .val = .none };
 | 
					    const field1 = Enum.Field{ .name = zig_name, .val = .none };
 | 
				
			||||||
    const field2 = Enum.Field{ .name = "cpp", .val = .none };
 | 
					    const field2 = Enum.Field{ .name = cpp_name, .val = .none };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const enum1 = try ip.get(gpa, .{ .enum_type = .{
 | 
					    const enum1 = try ip.get(gpa, .{ .enum_type = .{
 | 
				
			||||||
        .tag_type = .none,
 | 
					        .tag_type = .none,
 | 
				
			||||||
@ -2992,8 +3003,11 @@ test "union type" {
 | 
				
			|||||||
    const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } });
 | 
					    const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } });
 | 
				
			||||||
    const void_type = try ip.get(gpa, .{ .simple = .void });
 | 
					    const void_type = try ip.get(gpa, .{ .simple = .void });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var field1 = Union.Field{ .name = "Ok", .ty = u32_type, .alignment = 0 };
 | 
					    const ok_name = try ip.get(gpa, .{ .bytes = "Ok" });
 | 
				
			||||||
    var field2 = Union.Field{ .name = "Err", .ty = void_type, .alignment = 0 };
 | 
					    const err_name = try ip.get(gpa, .{ .bytes = "Err" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var field1 = Union.Field{ .name = ok_name, .ty = u32_type, .alignment = 0 };
 | 
				
			||||||
 | 
					    var field2 = Union.Field{ .name = err_name, .ty = void_type, .alignment = 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const union_type1 = try ip.get(gpa, .{
 | 
					    const union_type1 = try ip.get(gpa, .{
 | 
				
			||||||
        .union_type = .{
 | 
					        .union_type = .{
 | 
				
			||||||
@ -3026,8 +3040,11 @@ test "union value" {
 | 
				
			|||||||
    const int_value = try ip.get(gpa, .{ .int_u64_value = 1 });
 | 
					    const int_value = try ip.get(gpa, .{ .int_u64_value = 1 });
 | 
				
			||||||
    const f16_value = try ip.get(gpa, .{ .float_16_value = 0.25 });
 | 
					    const f16_value = try ip.get(gpa, .{ .float_16_value = 0.25 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var field1 = Union.Field{ .name = "int", .ty = u32_type, .alignment = 0 };
 | 
					    const int_name = try ip.get(gpa, .{ .bytes = "int" });
 | 
				
			||||||
    var field2 = Union.Field{ .name = "float", .ty = f16_type, .alignment = 0 };
 | 
					    const float_name = try ip.get(gpa, .{ .bytes = "float" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var field1 = Union.Field{ .name = int_name, .ty = u32_type, .alignment = 0 };
 | 
				
			||||||
 | 
					    var field2 = Union.Field{ .name = float_name, .ty = f16_type, .alignment = 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const union_type = try ip.get(gpa, .{
 | 
					    const union_type = try ip.get(gpa, .{
 | 
				
			||||||
        .union_type = .{
 | 
					        .union_type = .{
 | 
				
			||||||
@ -3093,22 +3110,18 @@ test "vector type" {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test "bytes value" {
 | 
					test "bytes value" {
 | 
				
			||||||
    if (true) return error.SkipZigTest; // TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const gpa = std.testing.allocator;
 | 
					    const gpa = std.testing.allocator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var ip: InternPool = .{};
 | 
					    var ip: InternPool = .{};
 | 
				
			||||||
    defer ip.deinit(gpa);
 | 
					    defer ip.deinit(gpa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var str1: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*;
 | 
					    var str1: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    const bytes_value1 = try ip.get(gpa, .{ .bytes = &str1 });
 | 
					    const bytes_value1 = try ip.get(gpa, .{ .bytes = &str1 });
 | 
				
			||||||
    @memset(&str1, 0, str1.len);
 | 
					    @memset(&str1, 0, str1.len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var str2: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*;
 | 
					    var str2: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*;
 | 
				
			||||||
    @memset(&str2, 0, str2.len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const bytes_value2 = try ip.get(gpa, .{ .bytes = &str2 });
 | 
					    const bytes_value2 = try ip.get(gpa, .{ .bytes = &str2 });
 | 
				
			||||||
 | 
					    @memset(&str2, 0, str2.len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var str3: [26]u8 = "https://www.duckduckgo.com".*;
 | 
					    var str3: [26]u8 = "https://www.duckduckgo.com".*;
 | 
				
			||||||
    const bytes_value3 = try ip.get(gpa, .{ .bytes = &str3 });
 | 
					    const bytes_value3 = try ip.get(gpa, .{ .bytes = &str3 });
 | 
				
			||||||
@ -3117,9 +3130,11 @@ test "bytes value" {
 | 
				
			|||||||
    try std.testing.expect(bytes_value1 == bytes_value2);
 | 
					    try std.testing.expect(bytes_value1 == bytes_value2);
 | 
				
			||||||
    try std.testing.expect(bytes_value2 != bytes_value3);
 | 
					    try std.testing.expect(bytes_value2 != bytes_value3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try std.testing.expect(str1 != ip.indexToKey(bytes_value1).bytes);
 | 
					    try std.testing.expect(@ptrToInt(&str1) != @ptrToInt(ip.indexToKey(bytes_value1).bytes.ptr));
 | 
				
			||||||
    try std.testing.expect(str2 != ip.indexToKey(bytes_value2).bytes);
 | 
					    try std.testing.expect(@ptrToInt(&str2) != @ptrToInt(ip.indexToKey(bytes_value2).bytes.ptr));
 | 
				
			||||||
    try std.testing.expect(str3 != ip.indexToKey(bytes_value3).bytes);
 | 
					    try std.testing.expect(@ptrToInt(&str3) != @ptrToInt(ip.indexToKey(bytes_value3).bytes.ptr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try std.testing.expectEqual(ip.indexToKey(bytes_value1).bytes.ptr, ip.indexToKey(bytes_value2).bytes.ptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try std.testing.expectEqualStrings("https://www.youtube.com/watch?v=dQw4w9WgXcQ", ip.indexToKey(bytes_value1).bytes);
 | 
					    try std.testing.expectEqualStrings("https://www.youtube.com/watch?v=dQw4w9WgXcQ", ip.indexToKey(bytes_value1).bytes);
 | 
				
			||||||
    try std.testing.expectEqualStrings("https://www.youtube.com/watch?v=dQw4w9WgXcQ", ip.indexToKey(bytes_value2).bytes);
 | 
					    try std.testing.expectEqualStrings("https://www.youtube.com/watch?v=dQw4w9WgXcQ", ip.indexToKey(bytes_value2).bytes);
 | 
				
			||||||
 | 
				
			|||||||
@ -600,10 +600,11 @@ fn typeToCompletion(
 | 
				
			|||||||
            switch (key) {
 | 
					            switch (key) {
 | 
				
			||||||
                .struct_type => |struct_info| {
 | 
					                .struct_type => |struct_info| {
 | 
				
			||||||
                    for (struct_info.fields) |field| {
 | 
					                    for (struct_info.fields) |field| {
 | 
				
			||||||
 | 
					                        const field_name = co.interpreter.ip.indexToKey(field.name).bytes;
 | 
				
			||||||
                        try list.append(allocator, .{
 | 
					                        try list.append(allocator, .{
 | 
				
			||||||
                            .label = field.name,
 | 
					                            .label = field_name,
 | 
				
			||||||
                            .kind = .Field,
 | 
					                            .kind = .Field,
 | 
				
			||||||
                            .insertText = field.name,
 | 
					                            .insertText = field_name,
 | 
				
			||||||
                            .insertTextFormat = .PlainText,
 | 
					                            .insertTextFormat = .PlainText,
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										210
									
								
								src/encoding.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								src/encoding.zig
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,210 @@
 | 
				
			|||||||
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					const builtin = @import("builtin");
 | 
				
			||||||
 | 
					const Allocator = std.mem.Allocator;
 | 
				
			||||||
 | 
					const assert = std.debug.assert;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Index = usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn encode(extra: *std.ArrayList(u8), comptime T: type, data: anytype) Allocator.Error!void {
 | 
				
			||||||
 | 
					    switch (@typeInfo(T)) {
 | 
				
			||||||
 | 
					        .Type,
 | 
				
			||||||
 | 
					        .NoReturn,
 | 
				
			||||||
 | 
					        .ComptimeFloat,
 | 
				
			||||||
 | 
					        .ComptimeInt,
 | 
				
			||||||
 | 
					        .Undefined,
 | 
				
			||||||
 | 
					        .Null,
 | 
				
			||||||
 | 
					        .ErrorUnion,
 | 
				
			||||||
 | 
					        .ErrorSet,
 | 
				
			||||||
 | 
					        .Fn,
 | 
				
			||||||
 | 
					        .Opaque,
 | 
				
			||||||
 | 
					        .Frame,
 | 
				
			||||||
 | 
					        .AnyFrame,
 | 
				
			||||||
 | 
					        .EnumLiteral,
 | 
				
			||||||
 | 
					        => @compileError("Unable to encode type " ++ @typeName(T)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .Void => {},
 | 
				
			||||||
 | 
					        .Bool => try encode(extra, u1, @boolToInt(data)),
 | 
				
			||||||
 | 
					        .Int => try extra.appendSlice(std.mem.asBytes(&data)),
 | 
				
			||||||
 | 
					        .Float => |info| switch (info.bits) {
 | 
				
			||||||
 | 
					            16 => try encode(extra, u16, @bitCast(u16, data)),
 | 
				
			||||||
 | 
					            32 => try encode(extra, u32, @bitCast(u32, data)),
 | 
				
			||||||
 | 
					            64 => try encode(extra, u64, @bitCast(u64, data)),
 | 
				
			||||||
 | 
					            80 => try encode(extra, u80, @bitCast(u80, data)),
 | 
				
			||||||
 | 
					            128 => try encode(extra, u128, @bitCast(u128, data)),
 | 
				
			||||||
 | 
					            else => @compileError("Unable to encode type " ++ @typeName(T)),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Pointer => |info| {
 | 
				
			||||||
 | 
					            switch (info.size) {
 | 
				
			||||||
 | 
					                .One => {
 | 
				
			||||||
 | 
					                    if (comptime canEncodeAsBytes(info.child)) {
 | 
				
			||||||
 | 
					                        try extra.appendNTimes(undefined, std.mem.alignPointerOffset(extra.items.ptr + extra.items.len, info.alignment).?);
 | 
				
			||||||
 | 
					                        try encode(extra, info.child, data.*);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        @compileError("Encoding " ++ @typeName(T) ++ " would require allocation");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                .Slice => {
 | 
				
			||||||
 | 
					                    if (comptime canEncodeAsBytes(info.child)) {
 | 
				
			||||||
 | 
					                        try encode(extra, u32, @intCast(u32, data.len));
 | 
				
			||||||
 | 
					                        try extra.appendNTimes(undefined, std.mem.alignPointerOffset(extra.items.ptr + extra.items.len, info.alignment).?);
 | 
				
			||||||
 | 
					                        try extra.appendSlice(std.mem.sliceAsBytes(data));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        @compileError("Encoding " ++ @typeName(T) ++ " would require allocation");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                .Many,
 | 
				
			||||||
 | 
					                .C,
 | 
				
			||||||
 | 
					                => @compileError("Unable to encode type " ++ @typeName(T)),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Array => |info| {
 | 
				
			||||||
 | 
					            for (data) |item| {
 | 
				
			||||||
 | 
					                try encode(extra, info.child, item);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Struct => |info| {
 | 
				
			||||||
 | 
					            switch (info.layout) {
 | 
				
			||||||
 | 
					                .Packed,
 | 
				
			||||||
 | 
					                .Extern,
 | 
				
			||||||
 | 
					                => return try extra.appendSlice(std.mem.asBytes(&data)),
 | 
				
			||||||
 | 
					                .Auto => {
 | 
				
			||||||
 | 
					                    inline for (info.fields) |field| {
 | 
				
			||||||
 | 
					                        try encode(extra, field.type, @field(data, field.name));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Optional => {
 | 
				
			||||||
 | 
					            try encode(extra, bool, data == null);
 | 
				
			||||||
 | 
					            if (data) |item| {
 | 
				
			||||||
 | 
					                try encode(extra, item);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Enum => |info| try encode(extra, info.tag_type, @enumToInt(data)),
 | 
				
			||||||
 | 
					        .Union => @compileError("TODO"),
 | 
				
			||||||
 | 
					        .Vector => |info| {
 | 
				
			||||||
 | 
					            const array: [info.len]info.child = data;
 | 
				
			||||||
 | 
					            try encode(extra, array);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn decode(extra: *[]const u8, comptime T: type) T {
 | 
				
			||||||
 | 
					    return switch (@typeInfo(T)) {
 | 
				
			||||||
 | 
					        .Type,
 | 
				
			||||||
 | 
					        .NoReturn,
 | 
				
			||||||
 | 
					        .ComptimeFloat,
 | 
				
			||||||
 | 
					        .ComptimeInt,
 | 
				
			||||||
 | 
					        .Undefined,
 | 
				
			||||||
 | 
					        .Null,
 | 
				
			||||||
 | 
					        .ErrorUnion,
 | 
				
			||||||
 | 
					        .ErrorSet,
 | 
				
			||||||
 | 
					        .Fn,
 | 
				
			||||||
 | 
					        .Opaque,
 | 
				
			||||||
 | 
					        .Frame,
 | 
				
			||||||
 | 
					        .AnyFrame,
 | 
				
			||||||
 | 
					        .EnumLiteral,
 | 
				
			||||||
 | 
					        => @compileError("Unable to decode type " ++ @typeName(T)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .Void => {},
 | 
				
			||||||
 | 
					        .Bool => decode(extra, u1) == 1,
 | 
				
			||||||
 | 
					        .Int => std.mem.bytesToValue(T, readArray(extra, @sizeOf(T))),
 | 
				
			||||||
 | 
					        .Float => |info| switch (info.bits) {
 | 
				
			||||||
 | 
					            16 => @bitCast(T, decode(extra, u16)),
 | 
				
			||||||
 | 
					            32 => @bitCast(T, decode(extra, u32)),
 | 
				
			||||||
 | 
					            64 => @bitCast(T, decode(extra, u64)),
 | 
				
			||||||
 | 
					            80 => @bitCast(T, decode(extra, u80)),
 | 
				
			||||||
 | 
					            128 => @bitCast(T, decode(extra, u128)),
 | 
				
			||||||
 | 
					            else => @compileError("Unable to decode type " ++ @typeName(T)),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Pointer => |info| {
 | 
				
			||||||
 | 
					            switch (info.size) {
 | 
				
			||||||
 | 
					                .One => {
 | 
				
			||||||
 | 
					                    if (comptime canEncodeAsBytes(info.child)) {
 | 
				
			||||||
 | 
					                        extra.* = alignForward(extra.*, info.alignment);
 | 
				
			||||||
 | 
					                        return std.mem.bytesAsValue(T, readArray(extra, @sizeOf(info.child)));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        @compileError("Decoding " ++ @typeName(T) ++ " would require allocation");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                .Slice => {
 | 
				
			||||||
 | 
					                    if (comptime canEncodeAsBytes(info.child)) {
 | 
				
			||||||
 | 
					                        const len = decode(extra, u32);
 | 
				
			||||||
 | 
					                        extra.* = alignForward(extra.*, info.alignment);
 | 
				
			||||||
 | 
					                        const bytes = readBytes(extra, len * @sizeOf(info.child));
 | 
				
			||||||
 | 
					                        return std.mem.bytesAsSlice(info.child, @alignCast(info.alignment, bytes));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        @compileError("Decoding " ++ @typeName(T) ++ " would require allocation");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                .Many,
 | 
				
			||||||
 | 
					                .C,
 | 
				
			||||||
 | 
					                => @compileError("Unable to decode type " ++ @typeName(T)),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Array => |info| blk: {
 | 
				
			||||||
 | 
					            var array: T = undefined;
 | 
				
			||||||
 | 
					            var i: usize = 0;
 | 
				
			||||||
 | 
					            while (i < info.len) {
 | 
				
			||||||
 | 
					                array[i] = decode(extra, info.child);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break :blk array;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Struct => |info| {
 | 
				
			||||||
 | 
					            switch (info.layout) {
 | 
				
			||||||
 | 
					                .Packed,
 | 
				
			||||||
 | 
					                .Extern,
 | 
				
			||||||
 | 
					                => return std.mem.bytesToValue(T, readArray(extra, @sizeOf(T))),
 | 
				
			||||||
 | 
					                .Auto => {
 | 
				
			||||||
 | 
					                    var result: T = undefined;
 | 
				
			||||||
 | 
					                    inline for (info.fields) |field| {
 | 
				
			||||||
 | 
					                        @field(result, field.name) = decode(extra, field.type);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return result;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Optional => |info| blk: {
 | 
				
			||||||
 | 
					            const is_null = decode(extra, bool);
 | 
				
			||||||
 | 
					            if (is_null) {
 | 
				
			||||||
 | 
					                break :blk null;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                break :blk decode(extra, info.child);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        .Enum => |info| @intToEnum(T, decode(extra, info.tag_type)),
 | 
				
			||||||
 | 
					        .Union => @compileError("TODO"),
 | 
				
			||||||
 | 
					        .Vector => |info| decode(extra, [info.len]info.child),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn canEncodeAsBytes(comptime T: type) bool {
 | 
				
			||||||
 | 
					    return switch (@typeInfo(T)) {
 | 
				
			||||||
 | 
					        .Void, .Bool, .Int, .Float, .Enum, .Vector => true,
 | 
				
			||||||
 | 
					        .Array => |info| canEncodeAsBytes(info.child),
 | 
				
			||||||
 | 
					        .Struct => |info| info.layout != .Auto,
 | 
				
			||||||
 | 
					        .Union => |info| info.layout != .Auto,
 | 
				
			||||||
 | 
					        else => false,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// forward aligns `extra` until it has the given alignment
 | 
				
			||||||
 | 
					pub fn alignForward(extra: []const u8, alignment: usize) []const u8 {
 | 
				
			||||||
 | 
					    const unaligned = @ptrToInt(extra.ptr);
 | 
				
			||||||
 | 
					    const offset = std.mem.alignForward(unaligned, alignment) - unaligned;
 | 
				
			||||||
 | 
					    const result = extra[offset..];
 | 
				
			||||||
 | 
					    std.debug.assert(std.mem.isAligned(@ptrToInt(result.ptr), alignment));
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn readBytes(extra: *[]const u8, n: usize) []const u8 {
 | 
				
			||||||
 | 
					    defer extra.* = extra.*[n..];
 | 
				
			||||||
 | 
					    return extra.*[0..n];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn readArray(extra: *[]const u8, comptime n: usize) *const [n]u8 {
 | 
				
			||||||
 | 
					    defer extra.* = extra.*[n..];
 | 
				
			||||||
 | 
					    return extra.*[0..n];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user