| ... | ... | @@ -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 |
| ... | ... | @@ -1916,25 +1917,31 @@ pub fn nativeToBig(comptime T: type, x: T) T { |
| 1916 | 1917 | }; |
| 1917 | 1918 | } |
| 1918 | 1919 | |
| 1920 | fn 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 | |
| 1919 | 1935 | fn AsBytesReturnType(comptime P: type) type { |
| 1920 | 1936 | if (!trait.isSingleItemPtr(P)) |
| 1921 | 1937 | @compileError("expected single item pointer, passed " ++ @typeName(P)); |
| 1922 | 1938 | |
| 1923 | 1939 | const size = @sizeOf(meta.Child(P)); |
| 1924 | | const alignment = meta.alignment(P); |
| 1925 | 1940 | |
| 1926 | | if (alignment == 0) { |
| 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; |
| 1941 | return CopyPtrAttrs(P, .One, [size]u8); |
| 1935 | 1942 | } |
| 1936 | 1943 | |
| 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. |
| 1938 | 1945 | pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) { |
| 1939 | 1946 | const P = @TypeOf(ptr); |
| 1940 | 1947 | return @ptrCast(AsBytesReturnType(P), ptr); |
| ... | ... | @@ -1974,6 +1981,20 @@ test "asBytes" { |
| 1974 | 1981 | testing.expect(eql(u8, asBytes(&zero), "")); |
| 1975 | 1982 | } |
| 1976 | 1983 | |
| 1984 | test "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 | 1998 | /// Given any value, returns a copy of its bytes in an array. |
| 1978 | 1999 | pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 { |
| 1979 | 2000 | return asBytes(&value).*; |
| ... | ... | @@ -2003,13 +2024,11 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { |
| 2003 | 2024 | @compileError(std.fmt.bufPrint(&buf, "expected *[{}]u8, passed " ++ @typeName(B), .{size}) catch unreachable); |
| 2004 | 2025 | } |
| 2005 | 2026 | |
| 2006 | | const alignment = comptime meta.alignment(B); |
| 2007 | | |
| 2008 | | return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T; |
| 2027 | return CopyPtrAttrs(B, .One, T); |
| 2009 | 2028 | } |
| 2010 | 2029 | |
| 2011 | 2030 | /// 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. |
| 2013 | 2032 | pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) { |
| 2014 | 2033 | return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes); |
| 2015 | 2034 | } |
| ... | ... | @@ -2051,6 +2070,20 @@ test "bytesAsValue" { |
| 2051 | 2070 | testing.expect(meta.eql(inst, inst2.*)); |
| 2052 | 2071 | } |
| 2053 | 2072 | |
| 2073 | test "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 | 2087 | /// Given a pointer to an array of bytes, returns a value of the specified type backed by a |
| 2055 | 2088 | /// copy of those bytes. |
| 2056 | 2089 | pub fn bytesToValue(comptime T: type, bytes: anytype) T { |
| ... | ... | @@ -2066,9 +2099,8 @@ test "bytesToValue" { |
| 2066 | 2099 | testing.expect(deadbeef == @as(u32, 0xDEADBEEF)); |
| 2067 | 2100 | } |
| 2068 | 2101 | |
| 2069 | | //TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues. |
| 2070 | 2102 | fn 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 | 2104 | @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType)); |
| 2073 | 2105 | } |
| 2074 | 2106 | |
| ... | ... | @@ -2076,11 +2108,11 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { |
| 2076 | 2108 | @compileError("number of bytes in " ++ @typeName(bytesType) ++ " is not divisible by size of " ++ @typeName(T)); |
| 2077 | 2109 | } |
| 2078 | 2110 | |
| 2079 | | const alignment = meta.alignment(bytesType); |
| 2080 | | |
| 2081 | | return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T; |
| 2111 | return CopyPtrAttrs(bytesType, .Slice, T); |
| 2082 | 2112 | } |
| 2083 | 2113 | |
| 2114 | /// Given a slice of bytes, returns a slice of the specified type |
| 2115 | /// backed by those bytes, preserving pointer attributes. |
| 2084 | 2116 | pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { |
| 2085 | 2117 | // let's not give an undefined pointer to @ptrCast |
| 2086 | 2118 | // 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 | 2120 | return &[0]T{}; |
| 2089 | 2121 | } |
| 2090 | 2122 | |
| 2091 | | const Bytes = @TypeOf(bytes); |
| 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; |
| 2123 | const cast_target = CopyPtrAttrs(@TypeOf(bytes), .Many, T); |
| 2095 | 2124 | |
| 2096 | 2125 | return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))]; |
| 2097 | 2126 | } |
| ... | ... | @@ -2149,17 +2178,29 @@ test "bytesAsSlice with specified alignment" { |
| 2149 | 2178 | testing.expect(slice[0] == 0x33333333); |
| 2150 | 2179 | } |
| 2151 | 2180 | |
| 2152 | | //TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues. |
| 2181 | test "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 | |
| 2153 | 2195 | fn SliceAsBytesReturnType(comptime sliceType: type) type { |
| 2154 | 2196 | if (!trait.isSlice(sliceType) and !trait.isPtrTo(.Array)(sliceType)) { |
| 2155 | 2197 | @compileError("expected []T or *[_]T, passed " ++ @typeName(sliceType)); |
| 2156 | 2198 | } |
| 2157 | 2199 | |
| 2158 | | const alignment = meta.alignment(sliceType); |
| 2159 | | |
| 2160 | | return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8; |
| 2200 | return CopyPtrAttrs(sliceType, .Slice, u8); |
| 2161 | 2201 | } |
| 2162 | 2202 | |
| 2203 | /// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes. |
| 2163 | 2204 | pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { |
| 2164 | 2205 | const Slice = @TypeOf(slice); |
| 2165 | 2206 | |
| ... | ... | @@ -2169,9 +2210,7 @@ pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { |
| 2169 | 2210 | return &[0]u8{}; |
| 2170 | 2211 | } |
| 2171 | 2212 | |
| 2172 | | const alignment = comptime meta.alignment(Slice); |
| 2173 | | |
| 2174 | | const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8; |
| 2213 | const cast_target = CopyPtrAttrs(Slice, .Many, u8); |
| 2175 | 2214 | |
| 2176 | 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 | 2282 | testing.expect(bytes[11] == math.maxInt(u8)); |
| 2244 | 2283 | } |
| 2245 | 2284 | |
| 2285 | test "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 | 2299 | /// Round an address up to the nearest aligned address |
| 2247 | 2300 | /// The alignment must be a power of 2 and greater than 0. |
| 2248 | 2301 | pub fn alignForward(addr: usize, alignment: usize) usize { |