mirror of
https://github.com/ziglang/zig.git
synced 2024-11-21 11:32:24 +00:00
Merge 98c4e6ea45
into f845fa04a0
This commit is contained in:
commit
9c13b8c742
@ -324,20 +324,49 @@ pub fn isBytes(self: Self, slice: []const u8) anyerror!bool {
|
||||
return matches;
|
||||
}
|
||||
|
||||
/// Read a struct from the stream.
|
||||
/// Only packed and extern structs are supported, as they have a defined in-memory layout.
|
||||
/// Packed structs must have a `@bitSizeOf` that is a multiple of eight.
|
||||
pub fn readStruct(self: Self, comptime T: type) anyerror!T {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
comptime assert(@typeInfo(T).@"struct".layout != .auto);
|
||||
var res: [1]T = undefined;
|
||||
try self.readNoEof(mem.sliceAsBytes(res[0..]));
|
||||
return res[0];
|
||||
switch (@typeInfo(T).@"struct".layout) {
|
||||
.auto => @compileError("readStruct only supports packed and extern structs, " ++
|
||||
"but the given type: " ++ @typeName(T) ++ " is a normal struct."),
|
||||
.@"extern" => {
|
||||
var res: [1]T = undefined;
|
||||
try self.readNoEof(mem.sliceAsBytes(res[0..]));
|
||||
return res[0];
|
||||
},
|
||||
.@"packed" => {
|
||||
var bytes: [@divExact(@bitSizeOf(T), 8)]u8 = undefined;
|
||||
try self.readNoEof(&bytes);
|
||||
return @bitCast(bytes);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a struct having the specified endianness into the host endianness representation.
|
||||
/// Only packed and extern structs are supported, as they have a defined in-memory layout.
|
||||
/// Packed structs must have a `@bitSizeOf` that is a multiple of eight.
|
||||
pub fn readStructEndian(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
|
||||
var res = try self.readStruct(T);
|
||||
if (native_endian != endian) {
|
||||
mem.byteSwapAllFields(T, &res);
|
||||
switch (@typeInfo(T).@"struct".layout) {
|
||||
.auto => @compileError("readStructEndian only supports packed and extern structs, " ++
|
||||
"but the given type: " ++ @typeName(T) ++ " is a normal struct."),
|
||||
.@"extern" => {
|
||||
var res = try self.readStruct(T);
|
||||
if (native_endian != endian) {
|
||||
mem.byteSwapAllFields(T, &res);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
.@"packed" => {
|
||||
var bytes: [@divExact(@bitSizeOf(T), 8)]u8 = undefined;
|
||||
try self.readNoEof(&bytes);
|
||||
if (native_endian != endian) {
|
||||
mem.reverse(u8, &bytes);
|
||||
}
|
||||
return @bitCast(bytes);
|
||||
},
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Reads an integer with the same size as the given enum's tag type. If the integer matches
|
||||
|
@ -1,6 +1,7 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("../../std.zig");
|
||||
const testing = std.testing;
|
||||
const native_endian = @import("builtin").target.cpu.arch.endian();
|
||||
|
||||
test "Reader" {
|
||||
var buf = "a\x02".*;
|
||||
@ -370,3 +371,130 @@ test "readIntoBoundedBytes correctly reads into a provided bounded array" {
|
||||
try reader.readIntoBoundedBytes(10000, &bounded_array);
|
||||
try testing.expectEqualStrings(bounded_array.slice(), test_string);
|
||||
}
|
||||
|
||||
test "readStructEndian reads packed structs without padding and in correct field order" {
|
||||
const buf = [3]u8{ 11, 12, 13 };
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const reader = fis.reader();
|
||||
|
||||
const PackedStruct = packed struct(u24) { a: u8, b: u8, c: u8 };
|
||||
|
||||
try testing.expectEqual(
|
||||
PackedStruct{ .a = 11, .b = 12, .c = 13 },
|
||||
reader.readStructEndian(PackedStruct, .little),
|
||||
);
|
||||
fis.reset();
|
||||
try testing.expectEqual(
|
||||
PackedStruct{ .a = 13, .b = 12, .c = 11 },
|
||||
reader.readStructEndian(PackedStruct, .big),
|
||||
);
|
||||
}
|
||||
|
||||
test "readStruct reads packed structs without padding and in correct field order" {
|
||||
const buf = [3]u8{ 11, 12, 13 };
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const reader = fis.reader();
|
||||
|
||||
const PackedStruct = packed struct(u24) { a: u8, b: u8, c: u8 };
|
||||
|
||||
switch (native_endian) {
|
||||
.little => {
|
||||
try testing.expectEqual(
|
||||
PackedStruct{ .a = 11, .b = 12, .c = 13 },
|
||||
reader.readStruct(PackedStruct),
|
||||
);
|
||||
},
|
||||
.big => {
|
||||
try testing.expectEqual(
|
||||
PackedStruct{ .a = 13, .b = 12, .c = 11 },
|
||||
reader.readStruct(PackedStruct),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test "readStruct writeStruct round-trip with packed structs" {
|
||||
var buf: [8]u8 = undefined;
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const reader = fis.reader();
|
||||
const writer = fis.writer();
|
||||
|
||||
const PackedStruct = packed struct(u64) {
|
||||
a: u16 = 123,
|
||||
b: u16 = 245,
|
||||
c: u16 = 456,
|
||||
d: i13 = -345,
|
||||
e: i3 = 2,
|
||||
};
|
||||
|
||||
const expected_packed_struct = PackedStruct{};
|
||||
try writer.writeStruct(expected_packed_struct);
|
||||
fis.reset();
|
||||
try testing.expectEqual(expected_packed_struct, try reader.readStruct(PackedStruct));
|
||||
}
|
||||
|
||||
test "readStructEndian writeStructEndian round-trip with packed structs" {
|
||||
var buf: [8]u8 = undefined;
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const reader = fis.reader();
|
||||
const writer = fis.writer();
|
||||
|
||||
const PackedStruct = packed struct(u64) {
|
||||
a: u13 = 123,
|
||||
b: i7 = -24,
|
||||
c: u20 = 83,
|
||||
d: enum(i24) { val = 3452 } = .val,
|
||||
};
|
||||
|
||||
const expected_packed_struct = PackedStruct{};
|
||||
// round-trip little endian
|
||||
try writer.writeStructEndian(expected_packed_struct, .big);
|
||||
fis.reset();
|
||||
try testing.expectEqual(expected_packed_struct, try reader.readStructEndian(PackedStruct, .big));
|
||||
// round-trip big endian
|
||||
fis.reset();
|
||||
try writer.writeStructEndian(expected_packed_struct, .little);
|
||||
fis.reset();
|
||||
try testing.expectEqual(expected_packed_struct, try reader.readStructEndian(PackedStruct, .little));
|
||||
}
|
||||
|
||||
test "readStruct a packed struct with endianness-affected types" {
|
||||
const buf = [4]u8{ 0x12, 0x34, 0x56, 0x78 };
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const reader = fis.reader();
|
||||
|
||||
const PackedStruct = packed struct(u32) { a: u16, b: u16 };
|
||||
|
||||
switch (native_endian) {
|
||||
.little => {
|
||||
try testing.expectEqual(
|
||||
PackedStruct{ .a = 0x3412, .b = 0x7856 },
|
||||
reader.readStruct(PackedStruct),
|
||||
);
|
||||
},
|
||||
.big => {
|
||||
try testing.expectEqual(
|
||||
PackedStruct{ .a = 0x5678, .b = 0x1234 },
|
||||
reader.readStruct(PackedStruct),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test "readStructEndian a packed struct with endianness-affected types" {
|
||||
const buf = [4]u8{ 0x12, 0x34, 0x56, 0x78 };
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const reader = fis.reader();
|
||||
|
||||
const PackedStruct = packed struct(u32) { a: u16, b: u16 };
|
||||
|
||||
try testing.expectEqual(
|
||||
PackedStruct{ .a = 0x3412, .b = 0x7856 },
|
||||
reader.readStructEndian(PackedStruct, .little),
|
||||
);
|
||||
fis.reset();
|
||||
try testing.expectEqual(
|
||||
PackedStruct{ .a = 0x5678, .b = 0x1234 },
|
||||
reader.readStructEndian(PackedStruct, .big),
|
||||
);
|
||||
}
|
||||
|
@ -54,20 +54,48 @@ pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.built
|
||||
return self.writeAll(&bytes);
|
||||
}
|
||||
|
||||
/// Write a struct to the stream.
|
||||
/// Only packed and extern structs are supported, as they have a defined in-memory layout.
|
||||
/// Packed structs must have a `@bitSizeOf` that is a multiple of eight.
|
||||
pub fn writeStruct(self: Self, value: anytype) anyerror!void {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
|
||||
return self.writeAll(mem.asBytes(&value));
|
||||
switch (@typeInfo(@TypeOf(value)).@"struct".layout) {
|
||||
.auto => @compileError("writeStruct only supports packed and extern structs, " ++
|
||||
"but the given type: " ++ @typeName(@TypeOf(value)) ++ " is a normal struct."),
|
||||
.@"extern" => {
|
||||
return try self.writeAll(mem.asBytes(&value));
|
||||
},
|
||||
.@"packed" => {
|
||||
const bytes: [@divExact(@bitSizeOf(@TypeOf(value)), 8)]u8 = @bitCast(value);
|
||||
try self.writeAll(&bytes);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a struct to the stream in the specified endianness.
|
||||
/// Only packed and extern structs are supported, as they have a defined in-memory layout.
|
||||
/// Packed structs must have a `@bitSizeOf` that is a multiple of eight.
|
||||
pub fn writeStructEndian(self: Self, value: anytype, endian: std.builtin.Endian) anyerror!void {
|
||||
// TODO: make sure this value is not a reference type
|
||||
if (native_endian == endian) {
|
||||
return self.writeStruct(value);
|
||||
} else {
|
||||
var copy = value;
|
||||
mem.byteSwapAllFields(@TypeOf(value), ©);
|
||||
return self.writeStruct(copy);
|
||||
switch (@typeInfo(@TypeOf(value)).@"struct".layout) {
|
||||
.auto => @compileError("writeStructEndian only supports packed and extern structs, " ++
|
||||
"but the given type: " ++ @typeName(@TypeOf(value)) ++ " is a normal struct."),
|
||||
.@"extern" => {
|
||||
if (native_endian == endian) {
|
||||
return try self.writeStruct(value);
|
||||
} else {
|
||||
var copy = value;
|
||||
mem.byteSwapAllFields(@TypeOf(value), ©);
|
||||
return try self.writeStruct(copy);
|
||||
}
|
||||
},
|
||||
.@"packed" => {
|
||||
var bytes: [@divExact(@bitSizeOf(@TypeOf(value)), 8)]u8 = @bitCast(value);
|
||||
if (native_endian != endian) {
|
||||
mem.reverse(u8, &bytes);
|
||||
}
|
||||
return try self.writeAll(&bytes);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,3 +109,7 @@ pub fn writeFile(self: Self, file: std.fs.File) anyerror!void {
|
||||
if (n < buf.len) return;
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
_ = @import("Writer/test.zig");
|
||||
}
|
||||
|
63
lib/std/io/Writer/test.zig
Normal file
63
lib/std/io/Writer/test.zig
Normal file
@ -0,0 +1,63 @@
|
||||
const std = @import("../../std.zig");
|
||||
const testing = std.testing;
|
||||
const native_endian = @import("builtin").target.cpu.arch.endian();
|
||||
|
||||
test "writeStruct writes packed structs without padding" {
|
||||
var buf: [3]u8 = undefined;
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const writer = fis.writer();
|
||||
|
||||
const PackedStruct = packed struct(u24) { a: u8, b: u8, c: u8 };
|
||||
|
||||
try writer.writeStruct(PackedStruct{ .a = 11, .b = 12, .c = 13 });
|
||||
switch (native_endian) {
|
||||
.little => {
|
||||
try testing.expectEqualSlices(u8, &.{ 11, 12, 13 }, &buf);
|
||||
},
|
||||
.big => {
|
||||
try testing.expectEqualSlices(u8, &.{ 13, 12, 11 }, &buf);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test "writeStructEndian writes packed structs without padding and in correct field order" {
|
||||
var buf: [3]u8 = undefined;
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const writer = fis.writer();
|
||||
|
||||
const PackedStruct = packed struct(u24) { a: u8, b: u8, c: u8 };
|
||||
|
||||
try writer.writeStructEndian(PackedStruct{ .a = 11, .b = 12, .c = 13 }, .little);
|
||||
try testing.expectEqualSlices(u8, &.{ 11, 12, 13 }, &buf);
|
||||
fis.reset();
|
||||
try writer.writeStructEndian(PackedStruct{ .a = 11, .b = 12, .c = 13 }, .big);
|
||||
try testing.expectEqualSlices(u8, &.{ 13, 12, 11 }, &buf);
|
||||
}
|
||||
|
||||
test "writeStruct a packed struct with endianness-affected types" {
|
||||
var buf: [4]u8 = undefined;
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const writer = fis.writer();
|
||||
|
||||
const PackedStruct = packed struct(u32) { a: u16, b: u16 };
|
||||
|
||||
try writer.writeStruct(PackedStruct{ .a = 0x1234, .b = 0x5678 });
|
||||
switch (native_endian) {
|
||||
.little => try testing.expectEqualSlices(u8, &.{ 0x34, 0x12, 0x78, 0x56 }, &buf),
|
||||
.big => try testing.expectEqualSlices(u8, &.{ 0x56, 0x78, 0x12, 0x34 }, &buf),
|
||||
}
|
||||
}
|
||||
|
||||
test "writeStructEndian a packed struct with endianness-affected types" {
|
||||
var buf: [4]u8 = undefined;
|
||||
var fis = std.io.fixedBufferStream(&buf);
|
||||
const writer = fis.writer();
|
||||
|
||||
const PackedStruct = packed struct(u32) { a: u16, b: u16 };
|
||||
|
||||
try writer.writeStructEndian(PackedStruct{ .a = 0x1234, .b = 0x5678 }, .little);
|
||||
try testing.expectEqualSlices(u8, &.{ 0x34, 0x12, 0x78, 0x56 }, &buf);
|
||||
fis.reset();
|
||||
try writer.writeStructEndian(PackedStruct{ .a = 0x1234, .b = 0x5678 }, .big);
|
||||
try testing.expectEqualSlices(u8, &.{ 0x56, 0x78, 0x12, 0x34 }, &buf);
|
||||
}
|
Loading…
Reference in New Issue
Block a user