Skip to content

Commit a5dbb65

Browse files
authored
Merge pull request #24454 from ziglang/packed-struct-streams
std.Io: handle packed structs better
2 parents d3cd0b2 + deb9f3e commit a5dbb65

File tree

5 files changed

+75
-47
lines changed

5 files changed

+75
-47
lines changed

lib/std/Io/Reader.zig

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,33 +1096,41 @@ pub inline fn takeInt(r: *Reader, comptime T: type, endian: std.builtin.Endian)
10961096
return std.mem.readInt(T, try r.takeArray(n), endian);
10971097
}
10981098

1099+
/// Asserts the buffer was initialized with a capacity at least `@bitSizeOf(T) / 8`.
1100+
pub inline fn peekInt(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1101+
const n = @divExact(@typeInfo(T).int.bits, 8);
1102+
return std.mem.readInt(T, try r.peekArray(n), endian);
1103+
}
1104+
10991105
/// Asserts the buffer was initialized with a capacity at least `n`.
11001106
pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n: usize) Error!Int {
11011107
assert(n <= @sizeOf(Int));
11021108
return std.mem.readVarInt(Int, try r.take(n), endian);
11031109
}
11041110

1105-
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
1111+
/// Obtains an unaligned pointer to the beginning of the stream, reinterpreted
1112+
/// as a pointer to the provided type, advancing the seek position.
11061113
///
1107-
/// Advances the seek position.
1114+
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
11081115
///
11091116
/// See also:
1110-
/// * `peekStruct`
1111-
/// * `takeStructEndian`
1112-
pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {
1117+
/// * `peekStructReference`
1118+
/// * `takeStruct`
1119+
pub fn takeStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
11131120
// Only extern and packed structs have defined in-memory layout.
11141121
comptime assert(@typeInfo(T).@"struct".layout != .auto);
11151122
return @ptrCast(try r.takeArray(@sizeOf(T)));
11161123
}
11171124

1118-
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
1125+
/// Obtains an unaligned pointer to the beginning of the stream, reinterpreted
1126+
/// as a pointer to the provided type, without advancing the seek position.
11191127
///
1120-
/// Does not advance the seek position.
1128+
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
11211129
///
11221130
/// See also:
1123-
/// * `takeStruct`
1124-
/// * `peekStructEndian`
1125-
pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
1131+
/// * `takeStructReference`
1132+
/// * `peekStruct`
1133+
pub fn peekStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
11261134
// Only extern and packed structs have defined in-memory layout.
11271135
comptime assert(@typeInfo(T).@"struct".layout != .auto);
11281136
return @ptrCast(try r.peekArray(@sizeOf(T)));
@@ -1134,12 +1142,23 @@ pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
11341142
/// when `endian` is comptime-known and matches the host endianness.
11351143
///
11361144
/// See also:
1137-
/// * `takeStruct`
1138-
/// * `peekStructEndian`
1139-
pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1140-
var res = (try r.takeStruct(T)).*;
1141-
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
1142-
return res;
1145+
/// * `takeStructReference`
1146+
/// * `peekStruct`
1147+
pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1148+
switch (@typeInfo(T)) {
1149+
.@"struct" => |info| switch (info.layout) {
1150+
.auto => @compileError("ill-defined memory layout"),
1151+
.@"extern" => {
1152+
var res = (try r.takeStructReference(T)).*;
1153+
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
1154+
return res;
1155+
},
1156+
.@"packed" => {
1157+
return takeInt(r, info.backing_integer.?, endian);
1158+
},
1159+
},
1160+
else => @compileError("not a struct"),
1161+
}
11431162
}
11441163

11451164
/// 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
11481167
/// when `endian` is comptime-known and matches the host endianness.
11491168
///
11501169
/// See also:
1151-
/// * `takeStructEndian`
1152-
/// * `peekStruct`
1153-
pub inline fn peekStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1154-
var res = (try r.peekStruct(T)).*;
1155-
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
1156-
return res;
1170+
/// * `takeStruct`
1171+
/// * `peekStructReference`
1172+
pub inline fn peekStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1173+
switch (@typeInfo(T)) {
1174+
.@"struct" => |info| switch (info.layout) {
1175+
.auto => @compileError("ill-defined memory layout"),
1176+
.@"extern" => {
1177+
var res = (try r.peekStructReference(T)).*;
1178+
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
1179+
return res;
1180+
},
1181+
.@"packed" => {
1182+
return peekInt(r, info.backing_integer.?, endian);
1183+
},
1184+
},
1185+
else => @compileError("not a struct"),
1186+
}
11571187
}
11581188

11591189
pub const TakeEnumError = Error || error{InvalidEnumTag};
@@ -1519,43 +1549,43 @@ test takeVarInt {
15191549
try testing.expectError(error.EndOfStream, r.takeVarInt(u16, .little, 1));
15201550
}
15211551

1522-
test takeStruct {
1552+
test takeStructReference {
15231553
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
15241554
const S = extern struct { a: u8, b: u16 };
15251555
switch (native_endian) {
1526-
.little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStruct(S)).*),
1527-
.big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStruct(S)).*),
1556+
.little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStructReference(S)).*),
1557+
.big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStructReference(S)).*),
15281558
}
1529-
try testing.expectError(error.EndOfStream, r.takeStruct(S));
1559+
try testing.expectError(error.EndOfStream, r.takeStructReference(S));
15301560
}
15311561

1532-
test peekStruct {
1562+
test peekStructReference {
15331563
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
15341564
const S = extern struct { a: u8, b: u16 };
15351565
switch (native_endian) {
15361566
.little => {
1537-
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStruct(S)).*);
1538-
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStruct(S)).*);
1567+
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructReference(S)).*);
1568+
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructReference(S)).*);
15391569
},
15401570
.big => {
1541-
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStruct(S)).*);
1542-
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStruct(S)).*);
1571+
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructReference(S)).*);
1572+
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructReference(S)).*);
15431573
},
15441574
}
15451575
}
15461576

1547-
test takeStructEndian {
1577+
test takeStruct {
15481578
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
15491579
const S = extern struct { a: u8, b: u16 };
1550-
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.takeStructEndian(S, .big));
1551-
try testing.expectError(error.EndOfStream, r.takeStructEndian(S, .little));
1580+
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.takeStruct(S, .big));
1581+
try testing.expectError(error.EndOfStream, r.takeStruct(S, .little));
15521582
}
15531583

1554-
test peekStructEndian {
1584+
test peekStruct {
15551585
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
15561586
const S = extern struct { a: u8, b: u16 };
1557-
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.peekStructEndian(S, .big));
1558-
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), try r.peekStructEndian(S, .little));
1587+
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.peekStruct(S, .big));
1588+
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), try r.peekStruct(S, .little));
15591589
}
15601590

15611591
test takeEnum {

lib/std/Io/Writer.zig

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -796,16 +796,9 @@ pub inline fn writeInt(w: *Writer, comptime T: type, value: T, endian: std.built
796796
return w.writeAll(&bytes);
797797
}
798798

799-
pub fn writeStruct(w: *Writer, value: anytype) Error!void {
800-
// Only extern and packed structs have defined in-memory layout.
801-
comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
802-
return w.writeAll(std.mem.asBytes(&value));
803-
}
804-
805799
/// The function is inline to avoid the dead code in case `endian` is
806800
/// comptime-known and matches host endianness.
807-
/// TODO: make sure this value is not a reference type
808-
pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void {
801+
pub inline fn writeStruct(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void {
809802
switch (@typeInfo(@TypeOf(value))) {
810803
.@"struct" => |info| switch (info.layout) {
811804
.auto => @compileError("ill-defined memory layout"),

lib/std/posix/test.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,11 @@ test "sigset_t bits" {
10011001
if (native_os == .wasi or native_os == .windows)
10021002
return error.SkipZigTest;
10031003

1004+
if (true) {
1005+
// https://github.com/ziglang/zig/issues/24380
1006+
return error.SkipZigTest;
1007+
}
1008+
10041009
const S = struct {
10051010
var expected_sig: i32 = undefined;
10061011
var handler_called_count: u32 = 0;

src/Zcu.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2809,7 +2809,7 @@ pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir {
28092809
var buffer: [2000]u8 = undefined;
28102810
var file_reader = cache_file.reader(&buffer);
28112811
return result: {
2812-
const header = file_reader.interface.takeStruct(Zir.Header) catch |err| break :result err;
2812+
const header = file_reader.interface.takeStructReference(Zir.Header) catch |err| break :result err;
28132813
break :result loadZirCacheBody(gpa, header.*, &file_reader.interface);
28142814
} catch |err| switch (err) {
28152815
error.ReadFailed => return file_reader.err.?,

src/Zcu/PerThread.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ fn loadZirZoirCache(
349349
const cache_br = &cache_fr.interface;
350350

351351
// First we read the header to determine the lengths of arrays.
352-
const header = (cache_br.takeStruct(Header) catch |err| switch (err) {
352+
const header = (cache_br.takeStructReference(Header) catch |err| switch (err) {
353353
error.ReadFailed => return cache_fr.err.?,
354354
// This can happen if Zig bails out of this function between creating
355355
// the cached file and writing it.

0 commit comments

Comments
 (0)