| ... | @@ -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 |
| ... | @@ -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 | } |
| 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 | fn AsBytesReturnType(comptime P: type) type { | 1935 | fn 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)); |
| 1922 | | 1938 | |
| 1923 | const size = @sizeOf(meta.Child(P)); | 1939 | const size = @sizeOf(meta.Child(P)); |
| 1924 | const alignment = meta.alignment(P); | | |
| 1925 | | 1940 | |
| 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 | } |
| 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 | pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) { | 1945 | pub 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 | } |
| 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 | /// 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. |
| 1978 | pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 { | 1999 | pub 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 | } |
| 2005 | | 2026 | |
| 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 | } |
| 2010 | | 2029 | |
| 2011 | /// Given a pointer to an array of bytes, returns a pointer to a value of the specified type | 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 | pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) { | 2032 | pub 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 | } |
| 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 | /// Given a pointer to an array of bytes, returns a value of the specified type backed by a | 2087 | /// 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. |
| 2056 | pub fn bytesToValue(comptime T: type, bytes: anytype) T { | 2089 | pub 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 | } |
| 2068 | | 2101 | |
| 2069 | //TODO copy also is_volatile, etc. I tried to use @typeInfo, modify child type, use @Type, but ran into issues. | | |
| 2070 | fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { | 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 | @compileError("expected []u8 or *[_]u8, passed " ++ @typeName(bytesType)); | 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,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 | } |
| 2078 | | 2110 | |
| 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 | } |
| 2083 | | 2113 | |
| | 2114 | /// Given a slice of bytes, returns a slice of the specified type |
| | 2115 | /// backed by those bytes, preserving pointer attributes. |
| 2084 | pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { | 2116 | pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { |
| 2085 | // let's not give an undefined pointer to @ptrCast | 2117 | // let's not give an undefined pointer to @ptrCast |
| 2086 | // it may be equal to zero and fail a null check | 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,10 +2120,7 @@ pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, |
| 2088 | return &[0]T{}; | 2120 | return &[0]T{}; |
| 2089 | } | 2121 | } |
| 2090 | | 2122 | |
| 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; | | |
| 2095 | | 2124 | |
| 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 | } |
| 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 | fn SliceAsBytesReturnType(comptime sliceType: type) type { | 2195 | fn 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 | } |
| 2157 | | 2199 | |
| 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 | } |
| 2162 | | 2202 | |
| | 2203 | /// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes. |
| 2163 | pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { | 2204 | pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { |
| 2164 | const Slice = @TypeOf(slice); | 2205 | const Slice = @TypeOf(slice); |
| 2165 | | 2206 | |
| ... | @@ -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 | } |
| 2171 | | 2212 | |
| 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; | | |
| 2175 | | 2214 | |
| 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 | } |
| 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 | /// Round an address up to the nearest aligned address | 2299 | /// 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. |
| 2248 | pub fn alignForward(addr: usize, alignment: usize) usize { | 2301 | pub fn alignForward(addr: usize, alignment: usize) usize { |