From c814fdbfaf823093497b0d354de559422a3dfda5 Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Thu, 12 Nov 2020 03:13:19 +0100 Subject: [PATCH 1/3] std.mem: improve doc comments --- lib/std/mem.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 532c34e41c..a432a46746 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -13,7 +13,8 @@ const meta = std.meta; const trait = meta.trait; const testing = std.testing; -/// https://github.com/ziglang/zig/issues/2564 +/// Compile time known minimum page size. +/// https://github.com/ziglang/zig/issues/4082 pub const page_size = switch (builtin.arch) { .wasm32, .wasm64 => 64 * 1024, .aarch64 => switch (builtin.os.tag) { @@ -139,7 +140,7 @@ test "mem.Allocator basics" { /// Copy all of source into dest at position 0. /// dest.len must be >= source.len. -/// dest.ptr must be <= src.ptr. +/// If the slices overlap, dest.ptr must be <= src.ptr. pub fn copy(comptime T: type, dest: []T, source: []const T) void { // TODO instead of manually doing this check for the whole array // and turning off runtime safety, the compiler should detect loops like @@ -152,7 +153,7 @@ pub fn copy(comptime T: type, dest: []T, source: []const T) void { /// Copy all of source into dest at position 0. /// dest.len must be >= source.len. -/// dest.ptr must be >= src.ptr. +/// If the slices overlap, dest.ptr must be >= src.ptr. pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { // TODO instead of manually doing this check for the whole array // and turning off runtime safety, the compiler should detect loops like From 92e45ee3a18cd88e90aedef467a2d203d9b330c2 Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Thu, 12 Nov 2020 03:13:46 +0100 Subject: [PATCH 2/3] std.mem: make sliceAsBytes, etc. respect volatile --- lib/std/mem.zig | 116 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 32 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index a432a46746..94f1f71540 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -7,7 +7,7 @@ const std = @import("std.zig"); const debug = std.debug; const assert = debug.assert; const math = std.math; -const builtin = @import("builtin"); +const builtin = std.builtin; const mem = @This(); const meta = std.meta; const trait = meta.trait; @@ -1937,25 +1937,31 @@ pub fn nativeToBig(comptime T: type, x: T) T { }; } +fn CopyPtrAttrs(comptime source: type, size: builtin.TypeInfo.Pointer.Size, child: type) type { + const info = @typeInfo(source).Pointer; + return @Type(.{ + .Pointer = .{ + .size = size, + .is_const = info.is_const, + .is_volatile = info.is_volatile, + .is_allowzero = info.is_allowzero, + .alignment = info.alignment, + .child = child, + .sentinel = null, + }, + }); +} + fn AsBytesReturnType(comptime P: type) type { if (!trait.isSingleItemPtr(P)) @compileError("expected single item pointer, passed " ++ @typeName(P)); const size = @sizeOf(meta.Child(P)); - const alignment = meta.alignment(P); - if (alignment == 0) { - if (trait.isConstPtr(P)) - return *const [size]u8; - return *[size]u8; - } - - if (trait.isConstPtr(P)) - return *align(alignment) const [size]u8; - return *align(alignment) [size]u8; + return CopyPtrAttrs(P, .One, [size]u8); } -/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness. +/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving pointer attributes. pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) { const P = @TypeOf(ptr); return @ptrCast(AsBytesReturnType(P), ptr); @@ -1995,6 +2001,20 @@ test "asBytes" { testing.expect(eql(u8, asBytes(&zero), "")); } +test "asBytes preserves qualifiers" { + const inArr: u32 align(16) = 0xDEADBEEF; + const inPtr = @ptrCast(*align(16) const volatile u32, &inArr); + const outSlice = asBytes(inPtr); + + const in = @typeInfo(@TypeOf(inPtr)).Pointer; + const out = @typeInfo(@TypeOf(outSlice)).Pointer; + + testing.expectEqual(in.is_const, out.is_const); + testing.expectEqual(in.is_volatile, out.is_volatile); + testing.expectEqual(in.is_allowzero, out.is_allowzero); + testing.expectEqual(in.alignment, out.alignment); +} + /// Given any value, returns a copy of its bytes in an array. pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 { return asBytes(&value).*; @@ -2024,13 +2044,11 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { @compileError(std.fmt.bufPrint(&buf, "expected *[{}]u8, passed " ++ @typeName(B), .{size}) catch unreachable); } - const alignment = comptime meta.alignment(B); - - return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T; + return CopyPtrAttrs(B, .One, T); } /// Given a pointer to an array of bytes, returns a pointer to a value of the specified type -/// backed by those bytes, preserving constness. +/// backed by those bytes, preserving pointer attributes. pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) { return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes); } @@ -2072,6 +2090,20 @@ test "bytesAsValue" { testing.expect(meta.eql(inst, inst2.*)); } +test "bytesAsValue preserves qualifiers" { + const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; + const inSlice = @ptrCast(*align(16) const volatile [4]u8, &inArr)[0..]; + const outPtr = bytesAsValue(u32, inSlice); + + const in = @typeInfo(@TypeOf(inSlice)).Pointer; + const out = @typeInfo(@TypeOf(outPtr)).Pointer; + + testing.expectEqual(in.is_const, out.is_const); + testing.expectEqual(in.is_volatile, out.is_volatile); + testing.expectEqual(in.is_allowzero, out.is_allowzero); + testing.expectEqual(in.alignment, out.alignment); +} + /// Given a pointer to an array of bytes, returns a value of the specified type backed by a /// copy of those bytes. pub fn bytesToValue(comptime T: type, bytes: anytype) T { @@ -2087,9 +2119,8 @@ test "bytesToValue" { testing.expect(deadbeef == @as(u32, 0xDEADBEEF)); } -//TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues. fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { - if (!(trait.isSlice(bytesType) and meta.Child(bytesType) == u8) and !(trait.isPtrTo(.Array)(bytesType) and meta.Child(meta.Child(bytesType)) == u8)) { + if (!(trait.isSlice(bytesType) or trait.isPtrTo(.Array)(bytesType)) or meta.Elem(bytesType) != u8) { @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType)); } @@ -2097,11 +2128,11 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { @compileError("number of bytes in " ++ @typeName(bytesType) ++ " is not divisible by size of " ++ @typeName(T)); } - const alignment = meta.alignment(bytesType); - - return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T; + return CopyPtrAttrs(bytesType, .Slice, T); } +/// Given a slice of bytes, returns a slice of the specified type +/// backed by those bytes, preserving pointer attributes. pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { // let's not give an undefined pointer to @ptrCast // it may be equal to zero and fail a null check @@ -2109,10 +2140,7 @@ pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, return &[0]T{}; } - const Bytes = @TypeOf(bytes); - const alignment = comptime meta.alignment(Bytes); - - const cast_target = if (comptime trait.isConstPtr(Bytes)) [*]align(alignment) const T else [*]align(alignment) T; + const cast_target = CopyPtrAttrs(@TypeOf(bytes), .Many, T); return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))]; } @@ -2170,17 +2198,29 @@ test "bytesAsSlice with specified alignment" { testing.expect(slice[0] == 0x33333333); } -//TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues. +test "bytesAsSlice preserves qualifiers" { + const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; + const inSlice = @ptrCast(*align(16) const volatile [4]u8, &inArr)[0..]; + const outSlice = bytesAsSlice(u16, inSlice); + + const in = @typeInfo(@TypeOf(inSlice)).Pointer; + const out = @typeInfo(@TypeOf(outSlice)).Pointer; + + testing.expectEqual(in.is_const, out.is_const); + testing.expectEqual(in.is_volatile, out.is_volatile); + testing.expectEqual(in.is_allowzero, out.is_allowzero); + testing.expectEqual(in.alignment, out.alignment); +} + fn SliceAsBytesReturnType(comptime sliceType: type) type { if (!trait.isSlice(sliceType) and !trait.isPtrTo(.Array)(sliceType)) { @compileError("expected []T or *[_]T, passed " ++ @typeName(sliceType)); } - const alignment = meta.alignment(sliceType); - - return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8; + return CopyPtrAttrs(sliceType, .Slice, u8); } +/// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes. pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { const Slice = @TypeOf(slice); @@ -2190,9 +2230,7 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { return &[0]u8{}; } - const alignment = comptime meta.alignment(Slice); - - const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8; + const cast_target = CopyPtrAttrs(Slice, .Many, u8); return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))]; } @@ -2264,6 +2302,20 @@ test "sliceAsBytes and bytesAsSlice back" { testing.expect(bytes[11] == math.maxInt(u8)); } +test "sliceAsBytes preserves qualifiers" { + const inArr align(16) = [2]u16{ 0xDEAD, 0xBEEF }; + const inSlice = @ptrCast(*align(16) const volatile [2]u16, &inArr)[0..]; + const outSlice = sliceAsBytes(inSlice); + + const in = @typeInfo(@TypeOf(inSlice)).Pointer; + const out = @typeInfo(@TypeOf(outSlice)).Pointer; + + testing.expectEqual(in.is_const, out.is_const); + testing.expectEqual(in.is_volatile, out.is_volatile); + testing.expectEqual(in.is_allowzero, out.is_allowzero); + testing.expectEqual(in.alignment, out.alignment); +} + /// Round an address up to the nearest aligned address /// The alignment must be a power of 2 and greater than 0. pub fn alignForward(addr: usize, alignment: usize) usize { From 109a3ebcca2928b1d5f40accab9a8b6a03ea750b Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Mon, 16 Nov 2020 12:50:05 +0100 Subject: [PATCH 3/3] std.mem: make args comptime, fix test names --- lib/std/mem.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 94f1f71540..fecce180fc 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1937,7 +1937,7 @@ pub fn nativeToBig(comptime T: type, x: T) T { }; } -fn CopyPtrAttrs(comptime source: type, size: builtin.TypeInfo.Pointer.Size, child: type) type { +fn CopyPtrAttrs(comptime source: type, comptime size: builtin.TypeInfo.Pointer.Size, comptime child: type) type { const info = @typeInfo(source).Pointer; return @Type(.{ .Pointer = .{ @@ -2001,7 +2001,7 @@ test "asBytes" { testing.expect(eql(u8, asBytes(&zero), "")); } -test "asBytes preserves qualifiers" { +test "asBytes preserves pointer attributes" { const inArr: u32 align(16) = 0xDEADBEEF; const inPtr = @ptrCast(*align(16) const volatile u32, &inArr); const outSlice = asBytes(inPtr); @@ -2090,7 +2090,7 @@ test "bytesAsValue" { testing.expect(meta.eql(inst, inst2.*)); } -test "bytesAsValue preserves qualifiers" { +test "bytesAsValue preserves pointer attributes" { const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; const inSlice = @ptrCast(*align(16) const volatile [4]u8, &inArr)[0..]; const outPtr = bytesAsValue(u32, inSlice); @@ -2198,7 +2198,7 @@ test "bytesAsSlice with specified alignment" { testing.expect(slice[0] == 0x33333333); } -test "bytesAsSlice preserves qualifiers" { +test "bytesAsSlice preserves pointer attributes" { const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; const inSlice = @ptrCast(*align(16) const volatile [4]u8, &inArr)[0..]; const outSlice = bytesAsSlice(u16, inSlice); @@ -2302,7 +2302,7 @@ test "sliceAsBytes and bytesAsSlice back" { testing.expect(bytes[11] == math.maxInt(u8)); } -test "sliceAsBytes preserves qualifiers" { +test "sliceAsBytes preserves pointer attributes" { const inArr align(16) = [2]u16{ 0xDEAD, 0xBEEF }; const inSlice = @ptrCast(*align(16) const volatile [2]u16, &inArr)[0..]; const outSlice = sliceAsBytes(inSlice);