mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Merge pull request #24454 from ziglang/packed-struct-streams
std.Io: handle packed structs better
This commit is contained in:
commit
a5dbb656b1
5 changed files with 88 additions and 60 deletions
|
|
@ -1096,33 +1096,41 @@ pub inline fn takeInt(r: *Reader, comptime T: type, endian: std.builtin.Endian)
|
|||
return std.mem.readInt(T, try r.takeArray(n), endian);
|
||||
}
|
||||
|
||||
/// Asserts the buffer was initialized with a capacity at least `@bitSizeOf(T) / 8`.
|
||||
pub inline fn peekInt(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
|
||||
const n = @divExact(@typeInfo(T).int.bits, 8);
|
||||
return std.mem.readInt(T, try r.peekArray(n), endian);
|
||||
}
|
||||
|
||||
/// Asserts the buffer was initialized with a capacity at least `n`.
|
||||
pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n: usize) Error!Int {
|
||||
assert(n <= @sizeOf(Int));
|
||||
return std.mem.readVarInt(Int, try r.take(n), endian);
|
||||
}
|
||||
|
||||
/// Obtains an unaligned pointer to the beginning of the stream, reinterpreted
|
||||
/// as a pointer to the provided type, advancing the seek position.
|
||||
///
|
||||
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
|
||||
///
|
||||
/// Advances the seek position.
|
||||
///
|
||||
/// See also:
|
||||
/// * `peekStruct`
|
||||
/// * `takeStructEndian`
|
||||
pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {
|
||||
/// * `peekStructReference`
|
||||
/// * `takeStruct`
|
||||
pub fn takeStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
comptime assert(@typeInfo(T).@"struct".layout != .auto);
|
||||
return @ptrCast(try r.takeArray(@sizeOf(T)));
|
||||
}
|
||||
|
||||
/// Obtains an unaligned pointer to the beginning of the stream, reinterpreted
|
||||
/// as a pointer to the provided type, without advancing the seek position.
|
||||
///
|
||||
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
|
||||
///
|
||||
/// Does not advance the seek position.
|
||||
///
|
||||
/// See also:
|
||||
/// * `takeStruct`
|
||||
/// * `peekStructEndian`
|
||||
pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
|
||||
/// * `takeStructReference`
|
||||
/// * `peekStruct`
|
||||
pub fn peekStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
comptime assert(@typeInfo(T).@"struct".layout != .auto);
|
||||
return @ptrCast(try r.peekArray(@sizeOf(T)));
|
||||
|
|
@ -1134,12 +1142,23 @@ pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
|
|||
/// when `endian` is comptime-known and matches the host endianness.
|
||||
///
|
||||
/// See also:
|
||||
/// * `takeStruct`
|
||||
/// * `peekStructEndian`
|
||||
pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
|
||||
var res = (try r.takeStruct(T)).*;
|
||||
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
|
||||
return res;
|
||||
/// * `takeStructReference`
|
||||
/// * `peekStruct`
|
||||
pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
|
||||
switch (@typeInfo(T)) {
|
||||
.@"struct" => |info| switch (info.layout) {
|
||||
.auto => @compileError("ill-defined memory layout"),
|
||||
.@"extern" => {
|
||||
var res = (try r.takeStructReference(T)).*;
|
||||
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
|
||||
return res;
|
||||
},
|
||||
.@"packed" => {
|
||||
return takeInt(r, info.backing_integer.?, endian);
|
||||
},
|
||||
},
|
||||
else => @compileError("not a struct"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
|
||||
|
|
@ -1148,12 +1167,23 @@ pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin
|
|||
/// when `endian` is comptime-known and matches the host endianness.
|
||||
///
|
||||
/// See also:
|
||||
/// * `takeStructEndian`
|
||||
/// * `peekStruct`
|
||||
pub inline fn peekStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
|
||||
var res = (try r.peekStruct(T)).*;
|
||||
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
|
||||
return res;
|
||||
/// * `takeStruct`
|
||||
/// * `peekStructReference`
|
||||
pub inline fn peekStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
|
||||
switch (@typeInfo(T)) {
|
||||
.@"struct" => |info| switch (info.layout) {
|
||||
.auto => @compileError("ill-defined memory layout"),
|
||||
.@"extern" => {
|
||||
var res = (try r.peekStructReference(T)).*;
|
||||
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
|
||||
return res;
|
||||
},
|
||||
.@"packed" => {
|
||||
return peekInt(r, info.backing_integer.?, endian);
|
||||
},
|
||||
},
|
||||
else => @compileError("not a struct"),
|
||||
}
|
||||
}
|
||||
|
||||
pub const TakeEnumError = Error || error{InvalidEnumTag};
|
||||
|
|
@ -1519,43 +1549,43 @@ test takeVarInt {
|
|||
try testing.expectError(error.EndOfStream, r.takeVarInt(u16, .little, 1));
|
||||
}
|
||||
|
||||
test takeStruct {
|
||||
test takeStructReference {
|
||||
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
|
||||
const S = extern struct { a: u8, b: u16 };
|
||||
switch (native_endian) {
|
||||
.little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStruct(S)).*),
|
||||
.big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStruct(S)).*),
|
||||
.little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStructReference(S)).*),
|
||||
.big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStructReference(S)).*),
|
||||
}
|
||||
try testing.expectError(error.EndOfStream, r.takeStruct(S));
|
||||
try testing.expectError(error.EndOfStream, r.takeStructReference(S));
|
||||
}
|
||||
|
||||
test peekStructReference {
|
||||
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
|
||||
const S = extern struct { a: u8, b: u16 };
|
||||
switch (native_endian) {
|
||||
.little => {
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructReference(S)).*);
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructReference(S)).*);
|
||||
},
|
||||
.big => {
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructReference(S)).*);
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructReference(S)).*);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test takeStruct {
|
||||
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
|
||||
const S = extern struct { a: u8, b: u16 };
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.takeStruct(S, .big));
|
||||
try testing.expectError(error.EndOfStream, r.takeStruct(S, .little));
|
||||
}
|
||||
|
||||
test peekStruct {
|
||||
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
|
||||
const S = extern struct { a: u8, b: u16 };
|
||||
switch (native_endian) {
|
||||
.little => {
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStruct(S)).*);
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStruct(S)).*);
|
||||
},
|
||||
.big => {
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStruct(S)).*);
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStruct(S)).*);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test takeStructEndian {
|
||||
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
|
||||
const S = extern struct { a: u8, b: u16 };
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.takeStructEndian(S, .big));
|
||||
try testing.expectError(error.EndOfStream, r.takeStructEndian(S, .little));
|
||||
}
|
||||
|
||||
test peekStructEndian {
|
||||
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
|
||||
const S = extern struct { a: u8, b: u16 };
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.peekStructEndian(S, .big));
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), try r.peekStructEndian(S, .little));
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.peekStruct(S, .big));
|
||||
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), try r.peekStruct(S, .little));
|
||||
}
|
||||
|
||||
test takeEnum {
|
||||
|
|
|
|||
|
|
@ -796,16 +796,9 @@ pub inline fn writeInt(w: *Writer, comptime T: type, value: T, endian: std.built
|
|||
return w.writeAll(&bytes);
|
||||
}
|
||||
|
||||
pub fn writeStruct(w: *Writer, value: anytype) Error!void {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
|
||||
return w.writeAll(std.mem.asBytes(&value));
|
||||
}
|
||||
|
||||
/// The function is inline to avoid the dead code in case `endian` is
|
||||
/// comptime-known and matches host endianness.
|
||||
/// TODO: make sure this value is not a reference type
|
||||
pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void {
|
||||
pub inline fn writeStruct(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void {
|
||||
switch (@typeInfo(@TypeOf(value))) {
|
||||
.@"struct" => |info| switch (info.layout) {
|
||||
.auto => @compileError("ill-defined memory layout"),
|
||||
|
|
|
|||
|
|
@ -1001,6 +1001,11 @@ test "sigset_t bits" {
|
|||
if (native_os == .wasi or native_os == .windows)
|
||||
return error.SkipZigTest;
|
||||
|
||||
if (true) {
|
||||
// https://github.com/ziglang/zig/issues/24380
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
const S = struct {
|
||||
var expected_sig: i32 = undefined;
|
||||
var handler_called_count: u32 = 0;
|
||||
|
|
|
|||
|
|
@ -2809,7 +2809,7 @@ pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir {
|
|||
var buffer: [2000]u8 = undefined;
|
||||
var file_reader = cache_file.reader(&buffer);
|
||||
return result: {
|
||||
const header = file_reader.interface.takeStruct(Zir.Header) catch |err| break :result err;
|
||||
const header = file_reader.interface.takeStructReference(Zir.Header) catch |err| break :result err;
|
||||
break :result loadZirCacheBody(gpa, header.*, &file_reader.interface);
|
||||
} catch |err| switch (err) {
|
||||
error.ReadFailed => return file_reader.err.?,
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ fn loadZirZoirCache(
|
|||
const cache_br = &cache_fr.interface;
|
||||
|
||||
// First we read the header to determine the lengths of arrays.
|
||||
const header = (cache_br.takeStruct(Header) catch |err| switch (err) {
|
||||
const header = (cache_br.takeStructReference(Header) catch |err| switch (err) {
|
||||
error.ReadFailed => return cache_fr.err.?,
|
||||
// This can happen if Zig bails out of this function between creating
|
||||
// the cached file and writing it.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue