std.Io.Writer: fix writeSliceSwap

tried to be too clever, wrote bad code
This commit is contained in:
Andrew Kelley 2025-07-19 21:53:48 -07:00
parent 741a66e03c
commit c40fb96ca3
7 changed files with 35 additions and 46 deletions

View File

@ -10,10 +10,10 @@ pub const std_options: std.Options = .{
}; };
var log_err_count: usize = 0; var log_err_count: usize = 0;
var fba_buffer: [8192]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&fba_buffer); var fba = std.heap.FixedBufferAllocator.init(&fba_buffer);
var stdin_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined; var fba_buffer: [8192]u8 = undefined;
var stdout_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined; var stdin_buffer: [4096]u8 = undefined;
var stdout_buffer: [4096]u8 = undefined;
const crippled = switch (builtin.zig_backend) { const crippled = switch (builtin.zig_backend) {
.stage2_powerpc, .stage2_powerpc,
@ -68,8 +68,8 @@ pub fn main() void {
fn mainServer() !void { fn mainServer() !void {
@disableInstrumentation(); @disableInstrumentation();
var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer); var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer);
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
var server = try std.zig.Server.init(.{ var server = try std.zig.Server.init(.{
.in = &stdin_reader.interface, .in = &stdin_reader.interface,
.out = &stdout_writer.interface, .out = &stdout_writer.interface,
@ -104,7 +104,7 @@ fn mainServer() !void {
defer testing.allocator.free(expected_panic_msgs); defer testing.allocator.free(expected_panic_msgs);
for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| { for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| {
name.* = @as(u32, @intCast(string_bytes.items.len)); name.* = @intCast(string_bytes.items.len);
try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1); try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1);
string_bytes.appendSliceAssumeCapacity(test_fn.name); string_bytes.appendSliceAssumeCapacity(test_fn.name);
string_bytes.appendAssumeCapacity(0); string_bytes.appendAssumeCapacity(0);

View File

@ -1742,7 +1742,7 @@ fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
.tag = tag, .tag = tag,
.bytes_len = 0, .bytes_len = 0,
}; };
try file.writeAll(std.mem.asBytes(&header)); try file.writeAll(@ptrCast(&header));
} }
fn sendRunTestMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag, index: u32) !void { fn sendRunTestMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag, index: u32) !void {

View File

@ -1108,9 +1108,9 @@ pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n:
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
/// ///
/// See also: /// See also:
/// * `peekStructReference` /// * `peekStructPointer`
/// * `takeStruct` /// * `takeStruct`
pub fn takeStructReference(r: *Reader, comptime T: type) Error!*align(1) T { pub fn takeStructPointer(r: *Reader, comptime T: type) Error!*align(1) T {
// Only extern and packed structs have defined in-memory layout. // Only extern and packed structs have defined in-memory layout.
comptime assert(@typeInfo(T).@"struct".layout != .auto); comptime assert(@typeInfo(T).@"struct".layout != .auto);
return @ptrCast(try r.takeArray(@sizeOf(T))); return @ptrCast(try r.takeArray(@sizeOf(T)));
@ -1122,9 +1122,9 @@ pub fn takeStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
/// ///
/// See also: /// See also:
/// * `takeStructReference` /// * `takeStructPointer`
/// * `peekStruct` /// * `peekStruct`
pub fn peekStructReference(r: *Reader, comptime T: type) Error!*align(1) T { pub fn peekStructPointer(r: *Reader, comptime T: type) Error!*align(1) T {
// Only extern and packed structs have defined in-memory layout. // Only extern and packed structs have defined in-memory layout.
comptime assert(@typeInfo(T).@"struct".layout != .auto); comptime assert(@typeInfo(T).@"struct".layout != .auto);
return @ptrCast(try r.peekArray(@sizeOf(T))); return @ptrCast(try r.peekArray(@sizeOf(T)));
@ -1136,14 +1136,14 @@ pub fn peekStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
/// when `endian` is comptime-known and matches the host endianness. /// when `endian` is comptime-known and matches the host endianness.
/// ///
/// See also: /// See also:
/// * `takeStructReference` /// * `takeStructPointer`
/// * `peekStruct` /// * `peekStruct`
pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T { pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.@"struct" => |info| switch (info.layout) { .@"struct" => |info| switch (info.layout) {
.auto => @compileError("ill-defined memory layout"), .auto => @compileError("ill-defined memory layout"),
.@"extern" => { .@"extern" => {
var res = (try r.takeStructReference(T)).*; var res = (try r.takeStructPointer(T)).*;
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res); if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
return res; return res;
}, },
@ -1162,13 +1162,13 @@ pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endia
/// ///
/// See also: /// See also:
/// * `takeStruct` /// * `takeStruct`
/// * `peekStructReference` /// * `peekStructPointer`
pub inline fn peekStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T { pub inline fn peekStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.@"struct" => |info| switch (info.layout) { .@"struct" => |info| switch (info.layout) {
.auto => @compileError("ill-defined memory layout"), .auto => @compileError("ill-defined memory layout"),
.@"extern" => { .@"extern" => {
var res = (try r.peekStructReference(T)).*; var res = (try r.peekStructPointer(T)).*;
if (native_endian != endian) std.mem.byteSwapAllFields(T, &res); if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
return res; return res;
}, },
@ -1557,27 +1557,27 @@ test takeVarInt {
try testing.expectError(error.EndOfStream, r.takeVarInt(u16, .little, 1)); try testing.expectError(error.EndOfStream, r.takeVarInt(u16, .little, 1));
} }
test takeStructReference { test takeStructPointer {
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 }); var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
const S = extern struct { a: u8, b: u16 }; const S = extern struct { a: u8, b: u16 };
switch (native_endian) { switch (native_endian) {
.little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStructReference(S)).*), .little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStructPointer(S)).*),
.big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStructReference(S)).*), .big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStructPointer(S)).*),
} }
try testing.expectError(error.EndOfStream, r.takeStructReference(S)); try testing.expectError(error.EndOfStream, r.takeStructPointer(S));
} }
test peekStructReference { test peekStructPointer {
var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 }); var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
const S = extern struct { a: u8, b: u16 }; const S = extern struct { a: u8, b: u16 };
switch (native_endian) { switch (native_endian) {
.little => { .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.peekStructPointer(S)).*);
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructReference(S)).*); try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructPointer(S)).*);
}, },
.big => { .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.peekStructPointer(S)).*);
try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructReference(S)).*); try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructPointer(S)).*);
}, },
} }
} }

View File

@ -851,9 +851,6 @@ pub inline fn writeStruct(w: *Writer, value: anytype, endian: std.builtin.Endian
} }
} }
/// If, `endian` is not native,
/// * Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`
/// * Asserts that the buffer is aligned enough for `@alignOf(Elem)`.
pub inline fn writeSliceEndian( pub inline fn writeSliceEndian(
w: *Writer, w: *Writer,
Elem: type, Elem: type,
@ -867,18 +864,11 @@ pub inline fn writeSliceEndian(
} }
} }
/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`
///
/// Asserts that the buffer is aligned enough for `@alignOf(Elem)`.
pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void { pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void {
var i: usize = 0; for (slice) |elem| {
while (i < slice.len) { var tmp = elem;
const dest_bytes = try w.writableSliceGreedy(@sizeOf(Elem)); std.mem.byteSwapAllFields(Elem, &tmp);
const dest: []Elem = @alignCast(@ptrCast(dest_bytes[0 .. dest_bytes.len - dest_bytes.len % @sizeOf(Elem)])); try w.writeAll(@ptrCast(&tmp));
const copy_len = @min(dest.len, slice.len - i);
@memcpy(dest[0..copy_len], slice[i..][0..copy_len]);
i += copy_len;
std.mem.byteSwapAllElements(Elem, dest);
} }
} }
@ -2650,9 +2640,10 @@ test writeStruct {
} }
test writeSliceEndian { test writeSliceEndian {
var buffer: [4]u8 align(2) = undefined; var buffer: [5]u8 align(2) = undefined;
var w: Writer = .fixed(&buffer); var w: Writer = .fixed(&buffer);
try w.writeByte('x');
const array: [2]u16 = .{ 0x1234, 0x5678 }; const array: [2]u16 = .{ 0x1234, 0x5678 };
try writeSliceEndian(&w, u16, &array, .big); try writeSliceEndian(&w, u16, &array, .big);
try testing.expectEqualSlices(u8, &.{ 0x12, 0x34, 0x56, 0x78 }, &buffer); try testing.expectEqualSlices(u8, &.{ 'x', 0x12, 0x34, 0x56, 0x78 }, &buffer);
} }

View File

@ -118,8 +118,6 @@ pub fn init(options: Options) !Server {
.in = options.in, .in = options.in,
.out = options.out, .out = options.out,
}; };
assert(s.out.buffer.len >= 4);
std.debug.assertAligned(s.out.buffer.ptr, .@"4");
try s.serveStringMessage(.zig_version, options.zig_version); try s.serveStringMessage(.zig_version, options.zig_version);
return s; return s;
} }
@ -203,8 +201,8 @@ pub const TestMetadata = struct {
pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
const header: OutMessage.TestMetadata = .{ const header: OutMessage.TestMetadata = .{
.tests_len = @as(u32, @intCast(test_metadata.names.len)), .tests_len = @intCast(test_metadata.names.len),
.string_bytes_len = @as(u32, @intCast(test_metadata.string_bytes.len)), .string_bytes_len = @intCast(test_metadata.string_bytes.len),
}; };
const trailing = 2; const trailing = 2;
const bytes_len = @sizeOf(OutMessage.TestMetadata) + const bytes_len = @sizeOf(OutMessage.TestMetadata) +

View File

@ -2821,7 +2821,7 @@ pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir {
var buffer: [2000]u8 = undefined; var buffer: [2000]u8 = undefined;
var file_reader = cache_file.reader(&buffer); var file_reader = cache_file.reader(&buffer);
return result: { return result: {
const header = file_reader.interface.takeStructReference(Zir.Header) catch |err| break :result err; const header = file_reader.interface.takeStructPointer(Zir.Header) catch |err| break :result err;
break :result loadZirCacheBody(gpa, header.*, &file_reader.interface); break :result loadZirCacheBody(gpa, header.*, &file_reader.interface);
} catch |err| switch (err) { } catch |err| switch (err) {
error.ReadFailed => return file_reader.err.?, error.ReadFailed => return file_reader.err.?,

View File

@ -349,7 +349,7 @@ fn loadZirZoirCache(
const cache_br = &cache_fr.interface; const cache_br = &cache_fr.interface;
// First we read the header to determine the lengths of arrays. // First we read the header to determine the lengths of arrays.
const header = (cache_br.takeStructReference(Header) catch |err| switch (err) { const header = (cache_br.takeStructPointer(Header) catch |err| switch (err) {
error.ReadFailed => return cache_fr.err.?, error.ReadFailed => return cache_fr.err.?,
// This can happen if Zig bails out of this function between creating // This can happen if Zig bails out of this function between creating
// the cached file and writing it. // the cached file and writing it.