| ... | @@ -7,13 +7,14 @@ const std = @import("std.zig"); | ... | @@ -7,13 +7,14 @@ const std = @import("std.zig"); |
| 7 | const debug = std.debug; | 7 | const debug = std.debug; |
| 8 | const assert = debug.assert; | 8 | const assert = debug.assert; |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const builtin = @import("builtin"); | 10 | const builtin = std.builtin; |
| 11 | const mem = @This(); | 11 | const mem = @This(); |
| 12 | const meta = std.meta; | 12 | const meta = std.meta; |
| 13 | const trait = meta.trait; | 13 | const trait = meta.trait; |
| 14 | const testing = std.testing; | 14 | const testing = std.testing; |
| 15 | | 15 | |
| 16 | /// https://github.com/ziglang/zig/issues/2564 | 16 | /// Compile time known minimum page size. |
| | 17 | /// https://github.com/ziglang/zig/issues/4082 |
| 17 | pub const page_size = switch (builtin.arch) { | 18 | pub 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" { |
| 139 | | 140 | |
| 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. |
| 143 | pub fn copy(comptime T: type, dest: []T, source: []const T) void { | 144 | pub fn copy(comptime T: type, dest: []T, source: []const T) void { |
| 144 | // TODO instead of manually doing this check for the whole array | 145 | // TODO instead of manually doing this check for the whole array |
| 145 | // and turning off runtime safety, the compiler should detect loops like | 146 | // 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 { |
| 152 | | 153 | |
| 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. |
| 156 | pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { | 157 | pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { |
| 157 | // TODO instead of manually doing this check for the whole array | 158 | // TODO instead of manually doing this check for the whole array |
| 158 | // and turning off runtime safety, the compiler should detect loops like | 159 | // 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 | } |
| 1938 | | 1939 | |
| | 1940 | fn 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 | |
| 1939 | fn AsBytesReturnType(comptime P: type) type { | 1955 | fn 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)); |
| 1942 | | 1958 | |
| 1943 | const size = @sizeOf(meta.Child(P)); | 1959 | const size = @sizeOf(meta.Child(P)); |
| 1944 | const alignment = meta.alignment(P); | | |
| 1945 | | 1960 | |
| 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 | } |
| 1956 | | 1963 | |
| 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. |
| 1958 | pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) { | 1965 | pub 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 | } |
| 1996 | | 2003 | |
| | 2004 | test "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. |
| 1998 | pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 { | 2019 | pub 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 | } |
| 2025 | | 2046 | |
| 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 | } |
| 2030 | | 2049 | |
| 2031 | /// Given a pointer to an array of bytes, returns a pointer to a value of the specified type | 2050 | /// 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. |
| 2033 | pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) { | 2052 | pub 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 | } |
| 2073 | | 2092 | |
| | 2093 | test "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 a | 2107 | /// 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. |
| 2076 | pub fn bytesToValue(comptime T: type, bytes: anytype) T { | 2109 | pub 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 | } |
| 2088 | | 2121 | |
| 2089 | //TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues. | | |
| 2090 | fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { | 2122 | fn 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 | } |
| 2094 | | 2126 | |
| ... | @@ -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 | } |
| 2098 | | 2130 | |
| 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 | } |
| 2103 | | 2133 | |
| | 2134 | /// Given a slice of bytes, returns a slice of the specified type |
| | 2135 | /// backed by those bytes, preserving pointer attributes. |
| 2104 | pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { | 2136 | pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { |
| 2105 | // let's not give an undefined pointer to @ptrCast | 2137 | // let's not give an undefined pointer to @ptrCast |
| 2106 | // it may be equal to zero and fail a null check | 2138 | // 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 | } |
| 2110 | | 2142 | |
| 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; | | |
| 2115 | | 2144 | |
| 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 | } |
| 2171 | | 2200 | |
| 2172 | //TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues. | 2201 | test "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 | |
| 2173 | fn SliceAsBytesReturnType(comptime sliceType: type) type { | 2215 | fn 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 | } |
| 2177 | | 2219 | |
| 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 | } |
| 2182 | | 2222 | |
| | 2223 | /// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes. |
| 2183 | pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { | 2224 | pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { |
| 2184 | const Slice = @TypeOf(slice); | 2225 | const Slice = @TypeOf(slice); |
| 2185 | | 2226 | |
| ... | @@ -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 | } |
| 2191 | | 2232 | |
| 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; | | |
| 2195 | | 2234 | |
| 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 | } |
| 2265 | | 2304 | |
| | 2305 | test "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 address | 2319 | /// 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. |
| 2268 | pub fn alignForward(addr: usize, alignment: usize) usize { | 2321 | pub fn alignForward(addr: usize, alignment: usize) usize { |