authorgravatar for 14938807+xackus@users.noreply.github.comxackus <14938807+xackus@users.noreply.github.com> 2020-11-12 03:13:46+01:00
committergravatar for 14938807+xackus@users.noreply.github.comxackus <14938807+xackus@users.noreply.github.com> 2020-11-14 13:59:29+01:00
log92e45ee3a18cd88e90aedef467a2d203d9b330c2
tree5181463219ba0d5771e263d145fd509fd763d749
parentc814fdbfaf823093497b0d354de559422a3dfda5

std.mem: make sliceAsBytes, etc. respect volatile


1 files changed, 84 insertions(+), 32 deletions(-)

lib/std/mem.zig+84-32
......@@ -7,7 +7,7 @@ const std = @import("std.zig");
77const debug = std.debug;
88const assert = debug.assert;
99const math = std.math;
10const builtin = @import("builtin");
10const builtin = std.builtin;
1111const mem = @This();
1212const meta = std.meta;
1313const trait = meta.trait;
......@@ -1937,25 +1937,31 @@ pub fn nativeToBig(comptime T: type, x: T) T {
19371937 };
19381938}
19391939
1940fn CopyPtrAttrs(comptime source: type, size: builtin.TypeInfo.Pointer.Size, child: type) type {
1941 const info = @typeInfo(source).Pointer;
1942 return @Type(.{
1943 .Pointer = .{
1944 .size = size,
1945 .is_const = info.is_const,
1946 .is_volatile = info.is_volatile,
1947 .is_allowzero = info.is_allowzero,
1948 .alignment = info.alignment,
1949 .child = child,
1950 .sentinel = null,
1951 },
1952 });
1953}
1954
19401955fn AsBytesReturnType(comptime P: type) type {
19411956 if (!trait.isSingleItemPtr(P))
19421957 @compileError("expected single item pointer, passed " ++ @typeName(P));
19431958
19441959 const size = @sizeOf(meta.Child(P));
1945 const alignment = meta.alignment(P);
19461960
1947 if (alignment == 0) {
1948 if (trait.isConstPtr(P))
1949 return *const [size]u8;
1950 return *[size]u8;
1951 }
1952
1953 if (trait.isConstPtr(P))
1954 return *align(alignment) const [size]u8;
1955 return *align(alignment) [size]u8;
1961 return CopyPtrAttrs(P, .One, [size]u8);
19561962}
19571963
1958/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
1964/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving pointer attributes.
19591965pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) {
19601966 const P = @TypeOf(ptr);
19611967 return @ptrCast(AsBytesReturnType(P), ptr);
......@@ -1995,6 +2001,20 @@ test "asBytes" {
19952001 testing.expect(eql(u8, asBytes(&zero), ""));
19962002}
19972003
2004test "asBytes preserves qualifiers" {
2005 const inArr: u32 align(16) = 0xDEADBEEF;
2006 const inPtr = @ptrCast(*align(16) const volatile u32, &inArr);
2007 const outSlice = asBytes(inPtr);
2008
2009 const in = @typeInfo(@TypeOf(inPtr)).Pointer;
2010 const out = @typeInfo(@TypeOf(outSlice)).Pointer;
2011
2012 testing.expectEqual(in.is_const, out.is_const);
2013 testing.expectEqual(in.is_volatile, out.is_volatile);
2014 testing.expectEqual(in.is_allowzero, out.is_allowzero);
2015 testing.expectEqual(in.alignment, out.alignment);
2016}
2017
19982018/// Given any value, returns a copy of its bytes in an array.
19992019pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 {
20002020 return asBytes(&value).*;
......@@ -2024,13 +2044,11 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
20242044 @compileError(std.fmt.bufPrint(&buf, "expected *[{}]u8, passed " ++ @typeName(B), .{size}) catch unreachable);
20252045 }
20262046
2027 const alignment = comptime meta.alignment(B);
2028
2029 return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T;
2047 return CopyPtrAttrs(B, .One, T);
20302048}
20312049
20322050/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type
2033/// backed by those bytes, preserving constness.
2051/// backed by those bytes, preserving pointer attributes.
20342052pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
20352053 return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes);
20362054}
......@@ -2072,6 +2090,20 @@ test "bytesAsValue" {
20722090 testing.expect(meta.eql(inst, inst2.*));
20732091}
20742092
2093test "bytesAsValue preserves qualifiers" {
2094 const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
2095 const inSlice = @ptrCast(*align(16) const volatile [4]u8, &inArr)[0..];
2096 const outPtr = bytesAsValue(u32, inSlice);
2097
2098 const in = @typeInfo(@TypeOf(inSlice)).Pointer;
2099 const out = @typeInfo(@TypeOf(outPtr)).Pointer;
2100
2101 testing.expectEqual(in.is_const, out.is_const);
2102 testing.expectEqual(in.is_volatile, out.is_volatile);
2103 testing.expectEqual(in.is_allowzero, out.is_allowzero);
2104 testing.expectEqual(in.alignment, out.alignment);
2105}
2106
20752107/// Given a pointer to an array of bytes, returns a value of the specified type backed by a
20762108/// copy of those bytes.
20772109pub fn bytesToValue(comptime T: type, bytes: anytype) T {
......@@ -2087,9 +2119,8 @@ test "bytesToValue" {
20872119 testing.expect(deadbeef == @as(u32, 0xDEADBEEF));
20882120}
20892121
2090//TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues.
20912122fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
2092 if (!(trait.isSlice(bytesType) and meta.Child(bytesType) == u8) and !(trait.isPtrTo(.Array)(bytesType) and meta.Child(meta.Child(bytesType)) == u8)) {
2123 if (!(trait.isSlice(bytesType) or trait.isPtrTo(.Array)(bytesType)) or meta.Elem(bytesType) != u8) {
20932124 @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType));
20942125 }
20952126
......@@ -2097,11 +2128,11 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
20972128 @compileError("number of bytes in " ++ @typeName(bytesType) ++ " is not divisible by size of " ++ @typeName(T));
20982129 }
20992130
2100 const alignment = meta.alignment(bytesType);
2101
2102 return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T;
2131 return CopyPtrAttrs(bytesType, .Slice, T);
21032132}
21042133
2134/// Given a slice of bytes, returns a slice of the specified type
2135/// backed by those bytes, preserving pointer attributes.
21052136pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
21062137 // let's not give an undefined pointer to @ptrCast
21072138 // 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,
21092140 return &[0]T{};
21102141 }
21112142
2112 const Bytes = @TypeOf(bytes);
2113 const alignment = comptime meta.alignment(Bytes);
2114
2115 const cast_target = if (comptime trait.isConstPtr(Bytes)) [*]align(alignment) const T else [*]align(alignment) T;
2143 const cast_target = CopyPtrAttrs(@TypeOf(bytes), .Many, T);
21162144
21172145 return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))];
21182146}
......@@ -2170,17 +2198,29 @@ test "bytesAsSlice with specified alignment" {
21702198 testing.expect(slice[0] == 0x33333333);
21712199}
21722200
2173//TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues.
2201test "bytesAsSlice preserves qualifiers" {
2202 const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
2203 const inSlice = @ptrCast(*align(16) const volatile [4]u8, &inArr)[0..];
2204 const outSlice = bytesAsSlice(u16, inSlice);
2205
2206 const in = @typeInfo(@TypeOf(inSlice)).Pointer;
2207 const out = @typeInfo(@TypeOf(outSlice)).Pointer;
2208
2209 testing.expectEqual(in.is_const, out.is_const);
2210 testing.expectEqual(in.is_volatile, out.is_volatile);
2211 testing.expectEqual(in.is_allowzero, out.is_allowzero);
2212 testing.expectEqual(in.alignment, out.alignment);
2213}
2214
21742215fn SliceAsBytesReturnType(comptime sliceType: type) type {
21752216 if (!trait.isSlice(sliceType) and !trait.isPtrTo(.Array)(sliceType)) {
21762217 @compileError("expected []T or *[_]T, passed " ++ @typeName(sliceType));
21772218 }
21782219
2179 const alignment = meta.alignment(sliceType);
2180
2181 return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8;
2220 return CopyPtrAttrs(sliceType, .Slice, u8);
21822221}
21832222
2223/// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes.
21842224pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
21852225 const Slice = @TypeOf(slice);
21862226
......@@ -2190,9 +2230,7 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
21902230 return &[0]u8{};
21912231 }
21922232
2193 const alignment = comptime meta.alignment(Slice);
2194
2195 const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8;
2233 const cast_target = CopyPtrAttrs(Slice, .Many, u8);
21962234
21972235 return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))];
21982236}
......@@ -2264,6 +2302,20 @@ test "sliceAsBytes and bytesAsSlice back" {
22642302 testing.expect(bytes[11] == math.maxInt(u8));
22652303}
22662304
2305test "sliceAsBytes preserves qualifiers" {
2306 const inArr align(16) = [2]u16{ 0xDEAD, 0xBEEF };
2307 const inSlice = @ptrCast(*align(16) const volatile [2]u16, &inArr)[0..];
2308 const outSlice = sliceAsBytes(inSlice);
2309
2310 const in = @typeInfo(@TypeOf(inSlice)).Pointer;
2311 const out = @typeInfo(@TypeOf(outSlice)).Pointer;
2312
2313 testing.expectEqual(in.is_const, out.is_const);
2314 testing.expectEqual(in.is_volatile, out.is_volatile);
2315 testing.expectEqual(in.is_allowzero, out.is_allowzero);
2316 testing.expectEqual(in.alignment, out.alignment);
2317}
2318
22672319/// Round an address up to the nearest aligned address
22682320/// The alignment must be a power of 2 and greater than 0.
22692321pub fn alignForward(addr: usize, alignment: usize) usize {