authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-11-18 13:06:35+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-11-18 13:06:35+02:00
log1e1a490600346490fabb69f67964e1ef62ee5e5f
treef39b759f5ab49d69f0e15cd21b022e670e735d12
parent27b73cc3958a5960691fd020551012bb6e3108d5
parent109a3ebcca2928b1d5f40accab9a8b6a03ea750b
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7084 from xackus/mem-volatile

std.mem: make sliceAsBytes, etc. respect volatile

1 files changed, 88 insertions(+), 35 deletions(-)

lib/std/mem.zig+88-35
...@@ -7,13 +7,14 @@ const std = @import("std.zig");...@@ -7,13 +7,14 @@ const std = @import("std.zig");
7const debug = std.debug;7const debug = std.debug;
8const assert = debug.assert;8const assert = debug.assert;
9const math = std.math;9const math = std.math;
10const builtin = @import("builtin");10const builtin = std.builtin;
11const mem = @This();11const mem = @This();
12const meta = std.meta;12const meta = std.meta;
13const trait = meta.trait;13const trait = meta.trait;
14const testing = std.testing;14const testing = std.testing;
1515
16/// https://github.com/ziglang/zig/issues/256416/// Compile time known minimum page size.
17/// https://github.com/ziglang/zig/issues/4082
17pub const page_size = switch (builtin.arch) {18pub const page_size = switch (builtin.arch) {
18 .wasm32, .wasm64 => 64 * 1024,19 .wasm32, .wasm64 => 64 * 1024,
19 .aarch64 => switch (builtin.os.tag) {20 .aarch64 => switch (builtin.os.tag) {
...@@ -139,7 +140,7 @@ test "mem.Allocator basics" {...@@ -139,7 +140,7 @@ test "mem.Allocator basics" {
139140
140/// Copy all of source into dest at position 0.141/// Copy all of source into dest at position 0.
141/// dest.len must be >= source.len.142/// dest.len must be >= source.len.
142/// dest.ptr must be <= src.ptr.143/// If the slices overlap, dest.ptr must be <= src.ptr.
143pub fn copy(comptime T: type, dest: []T, source: []const T) void {144pub fn copy(comptime T: type, dest: []T, source: []const T) void {
144 // TODO instead of manually doing this check for the whole array145 // TODO instead of manually doing this check for the whole array
145 // and turning off runtime safety, the compiler should detect loops like146 // 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 {...@@ -152,7 +153,7 @@ pub fn copy(comptime T: type, dest: []T, source: []const T) void {
152153
153/// Copy all of source into dest at position 0.154/// Copy all of source into dest at position 0.
154/// dest.len must be >= source.len.155/// dest.len must be >= source.len.
155/// dest.ptr must be >= src.ptr.156/// If the slices overlap, dest.ptr must be >= src.ptr.
156pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {157pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
157 // TODO instead of manually doing this check for the whole array158 // TODO instead of manually doing this check for the whole array
158 // and turning off runtime safety, the compiler should detect loops like159 // and turning off runtime safety, the compiler should detect loops like
...@@ -1916,25 +1917,31 @@ pub fn nativeToBig(comptime T: type, x: T) T {...@@ -1916,25 +1917,31 @@ pub fn nativeToBig(comptime T: type, x: T) T {
1916 };1917 };
1917}1918}
19181919
1920fn CopyPtrAttrs(comptime source: type, comptime size: builtin.TypeInfo.Pointer.Size, comptime child: type) type {
1921 const info = @typeInfo(source).Pointer;
1922 return @Type(.{
1923 .Pointer = .{
1924 .size = size,
1925 .is_const = info.is_const,
1926 .is_volatile = info.is_volatile,
1927 .is_allowzero = info.is_allowzero,
1928 .alignment = info.alignment,
1929 .child = child,
1930 .sentinel = null,
1931 },
1932 });
1933}
1934
1919fn AsBytesReturnType(comptime P: type) type {1935fn AsBytesReturnType(comptime P: type) type {
1920 if (!trait.isSingleItemPtr(P))1936 if (!trait.isSingleItemPtr(P))
1921 @compileError("expected single item pointer, passed " ++ @typeName(P));1937 @compileError("expected single item pointer, passed " ++ @typeName(P));
19221938
1923 const size = @sizeOf(meta.Child(P));1939 const size = @sizeOf(meta.Child(P));
1924 const alignment = meta.alignment(P);
19251940
1926 if (alignment == 0) {1941 return CopyPtrAttrs(P, .One, [size]u8);
1927 if (trait.isConstPtr(P))
1928 return *const [size]u8;
1929 return *[size]u8;
1930 }
1931
1932 if (trait.isConstPtr(P))
1933 return *align(alignment) const [size]u8;
1934 return *align(alignment) [size]u8;
1935}1942}
19361943
1937/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.1944/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving pointer attributes.
1938pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) {1945pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) {
1939 const P = @TypeOf(ptr);1946 const P = @TypeOf(ptr);
1940 return @ptrCast(AsBytesReturnType(P), ptr);1947 return @ptrCast(AsBytesReturnType(P), ptr);
...@@ -1974,6 +1981,20 @@ test "asBytes" {...@@ -1974,6 +1981,20 @@ test "asBytes" {
1974 testing.expect(eql(u8, asBytes(&zero), ""));1981 testing.expect(eql(u8, asBytes(&zero), ""));
1975}1982}
19761983
1984test "asBytes preserves pointer attributes" {
1985 const inArr: u32 align(16) = 0xDEADBEEF;
1986 const inPtr = @ptrCast(*align(16) const volatile u32, &inArr);
1987 const outSlice = asBytes(inPtr);
1988
1989 const in = @typeInfo(@TypeOf(inPtr)).Pointer;
1990 const out = @typeInfo(@TypeOf(outSlice)).Pointer;
1991
1992 testing.expectEqual(in.is_const, out.is_const);
1993 testing.expectEqual(in.is_volatile, out.is_volatile);
1994 testing.expectEqual(in.is_allowzero, out.is_allowzero);
1995 testing.expectEqual(in.alignment, out.alignment);
1996}
1997
1977/// Given any value, returns a copy of its bytes in an array.1998/// Given any value, returns a copy of its bytes in an array.
1978pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 {1999pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 {
1979 return asBytes(&value).*;2000 return asBytes(&value).*;
...@@ -2003,13 +2024,11 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {...@@ -2003,13 +2024,11 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
2003 @compileError(std.fmt.bufPrint(&buf, "expected *[{}]u8, passed " ++ @typeName(B), .{size}) catch unreachable);2024 @compileError(std.fmt.bufPrint(&buf, "expected *[{}]u8, passed " ++ @typeName(B), .{size}) catch unreachable);
2004 }2025 }
20052026
2006 const alignment = comptime meta.alignment(B);2027 return CopyPtrAttrs(B, .One, T);
2007
2008 return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T;
2009}2028}
20102029
2011/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type2030/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type
2012/// backed by those bytes, preserving constness.2031/// backed by those bytes, preserving pointer attributes.
2013pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {2032pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
2014 return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes);2033 return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes);
2015}2034}
...@@ -2051,6 +2070,20 @@ test "bytesAsValue" {...@@ -2051,6 +2070,20 @@ test "bytesAsValue" {
2051 testing.expect(meta.eql(inst, inst2.*));2070 testing.expect(meta.eql(inst, inst2.*));
2052}2071}
20532072
2073test "bytesAsValue preserves pointer attributes" {
2074 const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
2075 const inSlice = @ptrCast(*align(16) const volatile [4]u8, &inArr)[0..];
2076 const outPtr = bytesAsValue(u32, inSlice);
2077
2078 const in = @typeInfo(@TypeOf(inSlice)).Pointer;
2079 const out = @typeInfo(@TypeOf(outPtr)).Pointer;
2080
2081 testing.expectEqual(in.is_const, out.is_const);
2082 testing.expectEqual(in.is_volatile, out.is_volatile);
2083 testing.expectEqual(in.is_allowzero, out.is_allowzero);
2084 testing.expectEqual(in.alignment, out.alignment);
2085}
2086
2054/// Given a pointer to an array of bytes, returns a value of the specified type backed by a2087/// Given a pointer to an array of bytes, returns a value of the specified type backed by a
2055/// copy of those bytes.2088/// copy of those bytes.
2056pub fn bytesToValue(comptime T: type, bytes: anytype) T {2089pub fn bytesToValue(comptime T: type, bytes: anytype) T {
...@@ -2066,9 +2099,8 @@ test "bytesToValue" {...@@ -2066,9 +2099,8 @@ test "bytesToValue" {
2066 testing.expect(deadbeef == @as(u32, 0xDEADBEEF));2099 testing.expect(deadbeef == @as(u32, 0xDEADBEEF));
2067}2100}
20682101
2069//TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues.
2070fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {2102fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
2071 if (!(trait.isSlice(bytesType) and meta.Child(bytesType) == u8) and !(trait.isPtrTo(.Array)(bytesType) and meta.Child(meta.Child(bytesType)) == u8)) {2103 if (!(trait.isSlice(bytesType) or trait.isPtrTo(.Array)(bytesType)) or meta.Elem(bytesType) != u8) {
2072 @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType));2104 @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType));
2073 }2105 }
20742106
...@@ -2076,11 +2108,11 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {...@@ -2076,11 +2108,11 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
2076 @compileError("number of bytes in " ++ @typeName(bytesType) ++ " is not divisible by size of " ++ @typeName(T));2108 @compileError("number of bytes in " ++ @typeName(bytesType) ++ " is not divisible by size of " ++ @typeName(T));
2077 }2109 }
20782110
2079 const alignment = meta.alignment(bytesType);2111 return CopyPtrAttrs(bytesType, .Slice, T);
2080
2081 return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T;
2082}2112}
20832113
2114/// Given a slice of bytes, returns a slice of the specified type
2115/// backed by those bytes, preserving pointer attributes.
2084pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) {2116pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
2085 // let's not give an undefined pointer to @ptrCast2117 // let's not give an undefined pointer to @ptrCast
2086 // it may be equal to zero and fail a null check2118 // it may be equal to zero and fail a null check
...@@ -2088,10 +2120,7 @@ pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T,...@@ -2088,10 +2120,7 @@ pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T,
2088 return &[0]T{};2120 return &[0]T{};
2089 }2121 }
20902122
2091 const Bytes = @TypeOf(bytes);2123 const cast_target = CopyPtrAttrs(@TypeOf(bytes), .Many, T);
2092 const alignment = comptime meta.alignment(Bytes);
2093
2094 const cast_target = if (comptime trait.isConstPtr(Bytes)) [*]align(alignment) const T else [*]align(alignment) T;
20952124
2096 return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))];2125 return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))];
2097}2126}
...@@ -2149,17 +2178,29 @@ test "bytesAsSlice with specified alignment" {...@@ -2149,17 +2178,29 @@ test "bytesAsSlice with specified alignment" {
2149 testing.expect(slice[0] == 0x33333333);2178 testing.expect(slice[0] == 0x33333333);
2150}2179}
21512180
2152//TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues.2181test "bytesAsSlice preserves pointer attributes" {
2182 const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
2183 const inSlice = @ptrCast(*align(16) const volatile [4]u8, &inArr)[0..];
2184 const outSlice = bytesAsSlice(u16, inSlice);
2185
2186 const in = @typeInfo(@TypeOf(inSlice)).Pointer;
2187 const out = @typeInfo(@TypeOf(outSlice)).Pointer;
2188
2189 testing.expectEqual(in.is_const, out.is_const);
2190 testing.expectEqual(in.is_volatile, out.is_volatile);
2191 testing.expectEqual(in.is_allowzero, out.is_allowzero);
2192 testing.expectEqual(in.alignment, out.alignment);
2193}
2194
2153fn SliceAsBytesReturnType(comptime sliceType: type) type {2195fn SliceAsBytesReturnType(comptime sliceType: type) type {
2154 if (!trait.isSlice(sliceType) and !trait.isPtrTo(.Array)(sliceType)) {2196 if (!trait.isSlice(sliceType) and !trait.isPtrTo(.Array)(sliceType)) {
2155 @compileError("expected []T or *[_]T, passed " ++ @typeName(sliceType));2197 @compileError("expected []T or *[_]T, passed " ++ @typeName(sliceType));
2156 }2198 }
21572199
2158 const alignment = meta.alignment(sliceType);2200 return CopyPtrAttrs(sliceType, .Slice, u8);
2159
2160 return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8;
2161}2201}
21622202
2203/// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes.
2163pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {2204pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
2164 const Slice = @TypeOf(slice);2205 const Slice = @TypeOf(slice);
21652206
...@@ -2169,9 +2210,7 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {...@@ -2169,9 +2210,7 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
2169 return &[0]u8{};2210 return &[0]u8{};
2170 }2211 }
21712212
2172 const alignment = comptime meta.alignment(Slice);2213 const cast_target = CopyPtrAttrs(Slice, .Many, u8);
2173
2174 const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8;
21752214
2176 return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))];2215 return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))];
2177}2216}
...@@ -2243,6 +2282,20 @@ test "sliceAsBytes and bytesAsSlice back" {...@@ -2243,6 +2282,20 @@ test "sliceAsBytes and bytesAsSlice back" {
2243 testing.expect(bytes[11] == math.maxInt(u8));2282 testing.expect(bytes[11] == math.maxInt(u8));
2244}2283}
22452284
2285test "sliceAsBytes preserves pointer attributes" {
2286 const inArr align(16) = [2]u16{ 0xDEAD, 0xBEEF };
2287 const inSlice = @ptrCast(*align(16) const volatile [2]u16, &inArr)[0..];
2288 const outSlice = sliceAsBytes(inSlice);
2289
2290 const in = @typeInfo(@TypeOf(inSlice)).Pointer;
2291 const out = @typeInfo(@TypeOf(outSlice)).Pointer;
2292
2293 testing.expectEqual(in.is_const, out.is_const);
2294 testing.expectEqual(in.is_volatile, out.is_volatile);
2295 testing.expectEqual(in.is_allowzero, out.is_allowzero);
2296 testing.expectEqual(in.alignment, out.alignment);
2297}
2298
2246/// Round an address up to the nearest aligned address2299/// Round an address up to the nearest aligned address
2247/// The alignment must be a power of 2 and greater than 0.2300/// The alignment must be a power of 2 and greater than 0.
2248pub fn alignForward(addr: usize, alignment: usize) usize {2301pub fn alignForward(addr: usize, alignment: usize) usize {