authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-11-18 13:06:35+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-11-18 17:31:11-07:00
log1add7c616f2e8fed2329f282b634c51dd6250b66
treee62777abe95269bba7e1278169a8a2e9f660fdf8
parent5f9a664de958cd17fd5d5e0f3f8fa797bb4f25de

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
...@@ -1936,25 +1937,31 @@ pub fn nativeToBig(comptime T: type, x: T) T {...@@ -1936,25 +1937,31 @@ pub fn nativeToBig(comptime T: type, x: T) T {
1936 };1937 };
1937}1938}
19381939
1940fn CopyPtrAttrs(comptime source: type, comptime size: builtin.TypeInfo.Pointer.Size, comptime 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
1939fn AsBytesReturnType(comptime P: type) type {1955fn AsBytesReturnType(comptime P: type) type {
1940 if (!trait.isSingleItemPtr(P))1956 if (!trait.isSingleItemPtr(P))
1941 @compileError("expected single item pointer, passed " ++ @typeName(P));1957 @compileError("expected single item pointer, passed " ++ @typeName(P));
19421958
1943 const size = @sizeOf(meta.Child(P));1959 const size = @sizeOf(meta.Child(P));
1944 const alignment = meta.alignment(P);
19451960
1946 if (alignment == 0) {1961 return CopyPtrAttrs(P, .One, [size]u8);
1947 if (trait.isConstPtr(P))
1948 return *const [size]u8;
1949 return *[size]u8;
1950 }
1951
1952 if (trait.isConstPtr(P))
1953 return *align(alignment) const [size]u8;
1954 return *align(alignment) [size]u8;
1955}1962}
19561963
1957/// 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.
1958pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) {1965pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) {
1959 const P = @TypeOf(ptr);1966 const P = @TypeOf(ptr);
1960 return @ptrCast(AsBytesReturnType(P), ptr);1967 return @ptrCast(AsBytesReturnType(P), ptr);
...@@ -1994,6 +2001,20 @@ test "asBytes" {...@@ -1994,6 +2001,20 @@ test "asBytes" {
1994 testing.expect(eql(u8, asBytes(&zero), ""));2001 testing.expect(eql(u8, asBytes(&zero), ""));
1995}2002}
19962003
2004test "asBytes preserves pointer attributes" {
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
1997/// Given any value, returns a copy of its bytes in an array.2018/// Given any value, returns a copy of its bytes in an array.
1998pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 {2019pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 {
1999 return asBytes(&value).*;2020 return asBytes(&value).*;
...@@ -2023,13 +2044,11 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {...@@ -2023,13 +2044,11 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
2023 @compileError(std.fmt.bufPrint(&buf, "expected *[{}]u8, passed " ++ @typeName(B), .{size}) catch unreachable);2044 @compileError(std.fmt.bufPrint(&buf, "expected *[{}]u8, passed " ++ @typeName(B), .{size}) catch unreachable);
2024 }2045 }
20252046
2026 const alignment = comptime meta.alignment(B);2047 return CopyPtrAttrs(B, .One, T);
2027
2028 return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T;
2029}2048}
20302049
2031/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type2050/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type
2032/// backed by those bytes, preserving constness.2051/// backed by those bytes, preserving pointer attributes.
2033pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {2052pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
2034 return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes);2053 return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes);
2035}2054}
...@@ -2071,6 +2090,20 @@ test "bytesAsValue" {...@@ -2071,6 +2090,20 @@ test "bytesAsValue" {
2071 testing.expect(meta.eql(inst, inst2.*));2090 testing.expect(meta.eql(inst, inst2.*));
2072}2091}
20732092
2093test "bytesAsValue preserves pointer attributes" {
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
2074/// Given a pointer to an array of bytes, returns a value of the specified type backed by a2107/// Given a pointer to an array of bytes, returns a value of the specified type backed by a
2075/// copy of those bytes.2108/// copy of those bytes.
2076pub fn bytesToValue(comptime T: type, bytes: anytype) T {2109pub fn bytesToValue(comptime T: type, bytes: anytype) T {
...@@ -2086,9 +2119,8 @@ test "bytesToValue" {...@@ -2086,9 +2119,8 @@ test "bytesToValue" {
2086 testing.expect(deadbeef == @as(u32, 0xDEADBEEF));2119 testing.expect(deadbeef == @as(u32, 0xDEADBEEF));
2087}2120}
20882121
2089//TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues.
2090fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {2122fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
2091 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) {
2092 @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType));2124 @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType));
2093 }2125 }
20942126
...@@ -2096,11 +2128,11 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {...@@ -2096,11 +2128,11 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
2096 @compileError("number of bytes in " ++ @typeName(bytesType) ++ " is not divisible by size of " ++ @typeName(T));2128 @compileError("number of bytes in " ++ @typeName(bytesType) ++ " is not divisible by size of " ++ @typeName(T));
2097 }2129 }
20982130
2099 const alignment = meta.alignment(bytesType);2131 return CopyPtrAttrs(bytesType, .Slice, T);
2100
2101 return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T;
2102}2132}
21032133
2134/// Given a slice of bytes, returns a slice of the specified type
2135/// backed by those bytes, preserving pointer attributes.
2104pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) {2136pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
2105 // let's not give an undefined pointer to @ptrCast2137 // let's not give an undefined pointer to @ptrCast
2106 // it may be equal to zero and fail a null check2138 // it may be equal to zero and fail a null check
...@@ -2108,10 +2140,7 @@ pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T,...@@ -2108,10 +2140,7 @@ pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T,
2108 return &[0]T{};2140 return &[0]T{};
2109 }2141 }
21102142
2111 const Bytes = @TypeOf(bytes);2143 const cast_target = CopyPtrAttrs(@TypeOf(bytes), .Many, T);
2112 const alignment = comptime meta.alignment(Bytes);
2113
2114 const cast_target = if (comptime trait.isConstPtr(Bytes)) [*]align(alignment) const T else [*]align(alignment) T;
21152144
2116 return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))];2145 return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))];
2117}2146}
...@@ -2169,17 +2198,29 @@ test "bytesAsSlice with specified alignment" {...@@ -2169,17 +2198,29 @@ test "bytesAsSlice with specified alignment" {
2169 testing.expect(slice[0] == 0x33333333);2198 testing.expect(slice[0] == 0x33333333);
2170}2199}
21712200
2172//TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues.2201test "bytesAsSlice preserves pointer attributes" {
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
2173fn SliceAsBytesReturnType(comptime sliceType: type) type {2215fn SliceAsBytesReturnType(comptime sliceType: type) type {
2174 if (!trait.isSlice(sliceType) and !trait.isPtrTo(.Array)(sliceType)) {2216 if (!trait.isSlice(sliceType) and !trait.isPtrTo(.Array)(sliceType)) {
2175 @compileError("expected []T or *[_]T, passed " ++ @typeName(sliceType));2217 @compileError("expected []T or *[_]T, passed " ++ @typeName(sliceType));
2176 }2218 }
21772219
2178 const alignment = meta.alignment(sliceType);2220 return CopyPtrAttrs(sliceType, .Slice, u8);
2179
2180 return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8;
2181}2221}
21822222
2223/// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes.
2183pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {2224pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
2184 const Slice = @TypeOf(slice);2225 const Slice = @TypeOf(slice);
21852226
...@@ -2189,9 +2230,7 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {...@@ -2189,9 +2230,7 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
2189 return &[0]u8{};2230 return &[0]u8{};
2190 }2231 }
21912232
2192 const alignment = comptime meta.alignment(Slice);2233 const cast_target = CopyPtrAttrs(Slice, .Many, u8);
2193
2194 const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8;
21952234
2196 return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))];2235 return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))];
2197}2236}
...@@ -2263,6 +2302,20 @@ test "sliceAsBytes and bytesAsSlice back" {...@@ -2263,6 +2302,20 @@ test "sliceAsBytes and bytesAsSlice back" {
2263 testing.expect(bytes[11] == math.maxInt(u8));2302 testing.expect(bytes[11] == math.maxInt(u8));
2264}2303}
22652304
2305test "sliceAsBytes preserves pointer attributes" {
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
2266/// Round an address up to the nearest aligned address2319/// Round an address up to the nearest aligned address
2267/// The alignment must be a power of 2 and greater than 0.2320/// The alignment must be a power of 2 and greater than 0.
2268pub fn alignForward(addr: usize, alignment: usize) usize {2321pub fn alignForward(addr: usize, alignment: usize) usize {