| ... | ... | @@ -7,13 +7,14 @@ const std = @import("std.zig"); |
| 7 | 7 | const debug = std.debug; |
| 8 | 8 | const assert = debug.assert; |
| 9 | 9 | const math = std.math; |
| 10 | | const builtin = @import("builtin"); |
| 10 | const builtin = std.builtin; |
| 11 | 11 | const mem = @This(); |
| 12 | 12 | const meta = std.meta; |
| 13 | 13 | const trait = meta.trait; |
| 14 | 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 | 18 | pub const page_size = switch (builtin.arch) { |
| 18 | 19 | .wasm32, .wasm64 => 64 * 1024, |
| 19 | 20 | .aarch64 => switch (builtin.os.tag) { |
| ... | ... | @@ -139,7 +140,7 @@ test "mem.Allocator basics" { |
| 139 | 140 | |
| 140 | 141 | /// Copy all of source into dest at position 0. |
| 141 | 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 | 144 | pub fn copy(comptime T: type, dest: []T, source: []const T) void { |
| 144 | 145 | // TODO instead of manually doing this check for the whole array |
| 145 | 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 | 153 | |
| 153 | 154 | /// Copy all of source into dest at position 0. |
| 154 | 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 | 157 | pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { |
| 157 | 158 | // TODO instead of manually doing this check for the whole array |
| 158 | 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 | 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 | 1955 | fn AsBytesReturnType(comptime P: type) type { |
| 1940 | 1956 | if (!trait.isSingleItemPtr(P)) |
| 1941 | 1957 | @compileError("expected single item pointer, passed " ++ @typeName(P)); |
| 1942 | 1958 | |
| 1943 | 1959 | const size = @sizeOf(meta.Child(P)); |
| 1944 | | const alignment = meta.alignment(P); |
| 1945 | 1960 | |
| 1946 | | if (alignment == 0) { |
| 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; |
| 1961 | return CopyPtrAttrs(P, .One, [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 | 1965 | pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) { |
| 1959 | 1966 | const P = @TypeOf(ptr); |
| 1960 | 1967 | return @ptrCast(AsBytesReturnType(P), ptr); |
| ... | ... | @@ -1994,6 +2001,20 @@ test "asBytes" { |
| 1994 | 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 | 2018 | /// Given any value, returns a copy of its bytes in an array. |
| 1998 | 2019 | pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 { |
| 1999 | 2020 | return asBytes(&value).*; |
| ... | ... | @@ -2023,13 +2044,11 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { |
| 2023 | 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); |
| 2027 | | |
| 2028 | | return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T; |
| 2047 | return CopyPtrAttrs(B, .One, T); |
| 2029 | 2048 | } |
| 2030 | 2049 | |
| 2031 | 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 | 2052 | pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) { |
| 2034 | 2053 | return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes); |
| 2035 | 2054 | } |
| ... | ... | @@ -2071,6 +2090,20 @@ test "bytesAsValue" { |
| 2071 | 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 | 2107 | /// Given a pointer to an array of bytes, returns a value of the specified type backed by a |
| 2075 | 2108 | /// copy of those bytes. |
| 2076 | 2109 | pub fn bytesToValue(comptime T: type, bytes: anytype) T { |
| ... | ... | @@ -2086,9 +2119,8 @@ test "bytesToValue" { |
| 2086 | 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 | 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 | 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 | 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); |
| 2100 | | |
| 2101 | | return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T; |
| 2131 | return CopyPtrAttrs(bytesType, .Slice, 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 | 2136 | pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { |
| 2105 | 2137 | // let's not give an undefined pointer to @ptrCast |
| 2106 | 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 | 2140 | return &[0]T{}; |
| 2109 | 2141 | } |
| 2110 | 2142 | |
| 2111 | | const Bytes = @TypeOf(bytes); |
| 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; |
| 2143 | const cast_target = CopyPtrAttrs(@TypeOf(bytes), .Many, T); |
| 2115 | 2144 | |
| 2116 | 2145 | return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))]; |
| 2117 | 2146 | } |
| ... | ... | @@ -2169,17 +2198,29 @@ test "bytesAsSlice with specified alignment" { |
| 2169 | 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 | 2215 | fn SliceAsBytesReturnType(comptime sliceType: type) type { |
| 2174 | 2216 | if (!trait.isSlice(sliceType) and !trait.isPtrTo(.Array)(sliceType)) { |
| 2175 | 2217 | @compileError("expected []T or *[_]T, passed " ++ @typeName(sliceType)); |
| 2176 | 2218 | } |
| 2177 | 2219 | |
| 2178 | | const alignment = meta.alignment(sliceType); |
| 2179 | | |
| 2180 | | return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8; |
| 2220 | return CopyPtrAttrs(sliceType, .Slice, u8); |
| 2181 | 2221 | } |
| 2182 | 2222 | |
| 2223 | /// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes. |
| 2183 | 2224 | pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { |
| 2184 | 2225 | const Slice = @TypeOf(slice); |
| 2185 | 2226 | |
| ... | ... | @@ -2189,9 +2230,7 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { |
| 2189 | 2230 | return &[0]u8{}; |
| 2190 | 2231 | } |
| 2191 | 2232 | |
| 2192 | | const alignment = comptime meta.alignment(Slice); |
| 2193 | | |
| 2194 | | const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8; |
| 2233 | const cast_target = CopyPtrAttrs(Slice, .Many, u8); |
| 2195 | 2234 | |
| 2196 | 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 | 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 | 2319 | /// Round an address up to the nearest aligned address |
| 2267 | 2320 | /// The alignment must be a power of 2 and greater than 0. |
| 2268 | 2321 | pub fn alignForward(addr: usize, alignment: usize) usize { |