| ... | ... | @@ -0,0 +1,317 @@ |
| 1 | const endian = @import("builtin").cpu.arch.endian(); |
| 2 | const testing = @import("std").testing; |
| 3 | const ptr_size = @sizeOf(usize); |
| 4 | |
| 5 | test "type pun signed and unsigned as single pointer" { |
| 6 | comptime { |
| 7 | var x: u32 = 0; |
| 8 | const y = @ptrCast(*i32, &x); |
| 9 | y.* = -1; |
| 10 | try testing.expectEqual(@as(u32, 0xFFFFFFFF), x); |
| 11 | } |
| 12 | } |
| 13 | |
| 14 | test "type pun signed and unsigned as many pointer" { |
| 15 | comptime { |
| 16 | var x: u32 = 0; |
| 17 | const y = @ptrCast([*]i32, &x); |
| 18 | y[0] = -1; |
| 19 | try testing.expectEqual(@as(u32, 0xFFFFFFFF), x); |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | test "type pun signed and unsigned as array pointer" { |
| 24 | comptime { |
| 25 | var x: u32 = 0; |
| 26 | const y = @ptrCast(*[1]i32, &x); |
| 27 | y[0] = -1; |
| 28 | try testing.expectEqual(@as(u32, 0xFFFFFFFF), x); |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | test "type pun signed and unsigned as offset many pointer" { |
| 33 | comptime { |
| 34 | var x: u32 = 0; |
| 35 | var y = @ptrCast([*]i32, &x); |
| 36 | y -= 10; |
| 37 | y[10] = -1; |
| 38 | try testing.expectEqual(@as(u32, 0xFFFFFFFF), x); |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | test "type pun signed and unsigned as array pointer" { |
| 43 | comptime { |
| 44 | var x: u32 = 0; |
| 45 | const y = @ptrCast([*]i32, &x) - 10; |
| 46 | const z: *[15]i32 = y[0..15]; |
| 47 | z[10] = -1; |
| 48 | try testing.expectEqual(@as(u32, 0xFFFFFFFF), x); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | test "type pun value and struct" { |
| 53 | comptime { |
| 54 | const StructOfU32 = extern struct { x: u32 }; |
| 55 | var inst: StructOfU32 = .{ .x = 0 }; |
| 56 | @ptrCast(*i32, &inst.x).* = -1; |
| 57 | try testing.expectEqual(@as(u32, 0xFFFFFFFF), inst.x); |
| 58 | @ptrCast(*i32, &inst).* = -2; |
| 59 | try testing.expectEqual(@as(u32, 0xFFFFFFFE), inst.x); |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | fn bigToNativeEndian(comptime T: type, v: T) T { |
| 64 | return if (endian == .Big) v else @byteSwap(T, v); |
| 65 | } |
| 66 | test "type pun endianness" { |
| 67 | comptime { |
| 68 | const StructOfBytes = extern struct { x: [4]u8 }; |
| 69 | var inst: StructOfBytes = .{ .x = [4]u8{ 0, 0, 0, 0 } }; |
| 70 | const structPtr = @ptrCast(*align(1) u32, &inst); |
| 71 | const arrayPtr = @ptrCast(*align(1) u32, &inst.x); |
| 72 | inst.x[0] = 0xFE; |
| 73 | inst.x[2] = 0xBE; |
| 74 | try testing.expectEqual(bigToNativeEndian(u32, 0xFE00BE00), structPtr.*); |
| 75 | try testing.expectEqual(bigToNativeEndian(u32, 0xFE00BE00), arrayPtr.*); |
| 76 | structPtr.* = bigToNativeEndian(u32, 0xDEADF00D); |
| 77 | try testing.expectEqual(bigToNativeEndian(u32, 0xDEADF00D), structPtr.*); |
| 78 | try testing.expectEqual(bigToNativeEndian(u32, 0xDEADF00D), arrayPtr.*); |
| 79 | try testing.expectEqual(@as(u8, 0xDE), inst.x[0]); |
| 80 | try testing.expectEqual(@as(u8, 0xAD), inst.x[1]); |
| 81 | try testing.expectEqual(@as(u8, 0xF0), inst.x[2]); |
| 82 | try testing.expectEqual(@as(u8, 0x0D), inst.x[3]); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | const Bits = packed struct { |
| 87 | // Note: This struct has only single byte words so it |
| 88 | // doesn't need to be byte swapped. |
| 89 | p0: u1, |
| 90 | p1: u4, |
| 91 | p2: u3, |
| 92 | p3: u2, |
| 93 | p4: u6, |
| 94 | p5: u8, |
| 95 | p6: u7, |
| 96 | p7: u1, |
| 97 | }; |
| 98 | const ShuffledBits = packed struct { |
| 99 | p1: u4, |
| 100 | p3: u2, |
| 101 | p7: u1, |
| 102 | p0: u1, |
| 103 | p5: u8, |
| 104 | p2: u3, |
| 105 | p6: u7, |
| 106 | p4: u6, |
| 107 | }; |
| 108 | fn shuffle(ptr: usize, comptime From: type, comptime To: type) usize { |
| 109 | if (@sizeOf(From) != @sizeOf(To)) |
| 110 | @compileError("Mismatched sizes! " ++ @typeName(From) ++ " and " ++ @typeName(To) ++ " must have the same size!"); |
| 111 | const array_len = @divExact(ptr_size, @sizeOf(From)); |
| 112 | var result: usize = 0; |
| 113 | const pSource = @ptrCast(*align(1) const [array_len]From, &ptr); |
| 114 | const pResult = @ptrCast(*align(1) [array_len]To, &result); |
| 115 | var i: usize = 0; |
| 116 | while (i < array_len) : (i += 1) { |
| 117 | inline for (@typeInfo(To).Struct.fields) |f| { |
| 118 | @field(pResult[i], f.name) = @field(pSource[i], f.name); |
| 119 | } |
| 120 | } |
| 121 | return result; |
| 122 | } |
| 123 | |
| 124 | fn doTypePunBitsTest(as_bits: *Bits) !void { |
| 125 | const as_u32 = @ptrCast(*align(1) u32, as_bits); |
| 126 | const as_bytes = @ptrCast(*[4]u8, as_bits); |
| 127 | as_u32.* = bigToNativeEndian(u32, 0xB0A7DEED); |
| 128 | try testing.expectEqual(@as(u1, 0x00), as_bits.p0); |
| 129 | try testing.expectEqual(@as(u4, 0x08), as_bits.p1); |
| 130 | try testing.expectEqual(@as(u3, 0x05), as_bits.p2); |
| 131 | try testing.expectEqual(@as(u2, 0x03), as_bits.p3); |
| 132 | try testing.expectEqual(@as(u6, 0x29), as_bits.p4); |
| 133 | try testing.expectEqual(@as(u8, 0xDE), as_bits.p5); |
| 134 | try testing.expectEqual(@as(u7, 0x6D), as_bits.p6); |
| 135 | try testing.expectEqual(@as(u1, 0x01), as_bits.p7); |
| 136 | |
| 137 | as_bits.p6 = 0x2D; |
| 138 | as_bits.p1 = 0x0F; |
| 139 | try testing.expectEqual(bigToNativeEndian(u32, 0xBEA7DEAD), as_u32.*); |
| 140 | |
| 141 | // clobbering one bit doesn't clobber the word |
| 142 | as_bits.p7 = undefined; |
| 143 | try testing.expectEqual(@as(u7, 0x2D), as_bits.p6); |
| 144 | // even when read as a whole |
| 145 | const u = as_u32.*; |
| 146 | _ = u; // u is undefined |
| 147 | try testing.expectEqual(@as(u7, 0x2D), as_bits.p6); |
| 148 | // or if a field which shares the byte is modified |
| 149 | as_bits.p6 = 0x6D; |
| 150 | try testing.expectEqual(@as(u7, 0x6D), as_bits.p6); |
| 151 | |
| 152 | // but overwriting the undefined will clear it |
| 153 | as_bytes[3] = 0xAF; |
| 154 | try testing.expectEqual(bigToNativeEndian(u32, 0xBEA7DEAF), as_u32.*); |
| 155 | } |
| 156 | |
| 157 | test "type pun bits" { |
| 158 | comptime { |
| 159 | var v: u32 = undefined; |
| 160 | try doTypePunBitsTest(@ptrCast(*Bits, &v)); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | const imports = struct { |
| 165 | var global_u32: u32 = 0; |
| 166 | }; |
| 167 | |
| 168 | // Make sure lazy values work on their own, before getting into more complex tests |
| 169 | test "basic pointer preservation" { |
| 170 | comptime { |
| 171 | const lazy_address = @ptrToInt(&imports.global_u32); |
| 172 | try testing.expectEqual(@ptrToInt(&imports.global_u32), lazy_address); |
| 173 | try testing.expectEqual(&imports.global_u32, @intToPtr(*u32, lazy_address)); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | test "byte copy preserves linker value" { |
| 178 | const ct_value = comptime blk: { |
| 179 | const lazy = &imports.global_u32; |
| 180 | var result: *u32 = undefined; |
| 181 | const pSource = @ptrCast(*const [ptr_size]u8, &lazy); |
| 182 | const pResult = @ptrCast(*[ptr_size]u8, &result); |
| 183 | var i: usize = 0; |
| 184 | while (i < ptr_size) : (i += 1) { |
| 185 | pResult[i] = pSource[i]; |
| 186 | try testing.expectEqual(pSource[i], pResult[i]); |
| 187 | } |
| 188 | try testing.expectEqual(&imports.global_u32, result); |
| 189 | break :blk result; |
| 190 | }; |
| 191 | |
| 192 | try testing.expectEqual(&imports.global_u32, ct_value); |
| 193 | } |
| 194 | |
| 195 | test "unordered byte copy preserves linker value" { |
| 196 | const ct_value = comptime blk: { |
| 197 | const lazy = &imports.global_u32; |
| 198 | var result: *u32 = undefined; |
| 199 | const pSource = @ptrCast(*const [ptr_size]u8, &lazy); |
| 200 | const pResult = @ptrCast(*[ptr_size]u8, &result); |
| 201 | if (ptr_size > 8) @compileError("This array needs to be expanded for platform with very big pointers"); |
| 202 | const shuffled_indices = [_]usize{ 4, 5, 2, 6, 1, 3, 0, 7 }; |
| 203 | for (shuffled_indices) |i| { |
| 204 | pResult[i] = pSource[i]; |
| 205 | try testing.expectEqual(pSource[i], pResult[i]); |
| 206 | } |
| 207 | try testing.expectEqual(&imports.global_u32, result); |
| 208 | break :blk result; |
| 209 | }; |
| 210 | |
| 211 | try testing.expectEqual(&imports.global_u32, ct_value); |
| 212 | } |
| 213 | |
| 214 | test "shuffle chunks of linker value" { |
| 215 | const lazy_address = @ptrToInt(&imports.global_u32); |
| 216 | const shuffled1_rt = shuffle(lazy_address, Bits, ShuffledBits); |
| 217 | const unshuffled1_rt = shuffle(shuffled1_rt, ShuffledBits, Bits); |
| 218 | try testing.expectEqual(lazy_address, unshuffled1_rt); |
| 219 | const shuffled1_ct = comptime shuffle(lazy_address, Bits, ShuffledBits); |
| 220 | const shuffled1_ct_2 = comptime shuffle(lazy_address, Bits, ShuffledBits); |
| 221 | comptime try testing.expectEqual(shuffled1_ct, shuffled1_ct_2); |
| 222 | const unshuffled1_ct = comptime shuffle(shuffled1_ct, ShuffledBits, Bits); |
| 223 | comptime try testing.expectEqual(lazy_address, unshuffled1_ct); |
| 224 | try testing.expectEqual(shuffled1_ct, shuffled1_rt); |
| 225 | } |
| 226 | |
| 227 | test "dance on linker values" { |
| 228 | comptime { |
| 229 | var arr: [2]usize = undefined; |
| 230 | arr[0] = @ptrToInt(&imports.global_u32); |
| 231 | arr[1] = @ptrToInt(&imports.global_u32); |
| 232 | |
| 233 | const weird_ptr = @ptrCast([*]Bits, @ptrCast([*]u8, &arr) + @sizeOf(usize) - 3); |
| 234 | try doTypePunBitsTest(&weird_ptr[0]); |
| 235 | if (ptr_size > @sizeOf(Bits)) |
| 236 | try doTypePunBitsTest(&weird_ptr[1]); |
| 237 | |
| 238 | var arr_bytes = @ptrCast(*[2][ptr_size]u8, &arr); |
| 239 | |
| 240 | var rebuilt_bytes: [ptr_size]u8 = undefined; |
| 241 | var i: usize = 0; |
| 242 | while (i < ptr_size - 3) : (i += 1) { |
| 243 | rebuilt_bytes[i] = arr_bytes[0][i]; |
| 244 | } |
| 245 | while (i < ptr_size) : (i += 1) { |
| 246 | rebuilt_bytes[i] = arr_bytes[1][i]; |
| 247 | } |
| 248 | |
| 249 | try testing.expectEqual(&imports.global_u32, @intToPtr(*u32, @bitCast(usize, rebuilt_bytes))); |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | test "offset array ptr by element size" { |
| 254 | comptime { |
| 255 | const VirtualStruct = struct { x: u32 }; |
| 256 | var arr: [4]VirtualStruct = .{ |
| 257 | .{ .x = bigToNativeEndian(u32, 0x0004080c) }, |
| 258 | .{ .x = bigToNativeEndian(u32, 0x0105090d) }, |
| 259 | .{ .x = bigToNativeEndian(u32, 0x02060a0e) }, |
| 260 | .{ .x = bigToNativeEndian(u32, 0x03070b0f) }, |
| 261 | }; |
| 262 | |
| 263 | const address = @ptrToInt(&arr); |
| 264 | try testing.expectEqual(@ptrToInt(&arr[0]), address); |
| 265 | try testing.expectEqual(@ptrToInt(&arr[0]) + 10, address + 10); |
| 266 | try testing.expectEqual(@ptrToInt(&arr[1]), address + @sizeOf(VirtualStruct)); |
| 267 | try testing.expectEqual(@ptrToInt(&arr[2]), address + 2 * @sizeOf(VirtualStruct)); |
| 268 | try testing.expectEqual(@ptrToInt(&arr[3]), address + @sizeOf(VirtualStruct) * 3); |
| 269 | |
| 270 | const secondElement = @intToPtr(*VirtualStruct, @ptrToInt(&arr[0]) + 2 * @sizeOf(VirtualStruct)); |
| 271 | try testing.expectEqual(bigToNativeEndian(u32, 0x02060a0e), secondElement.x); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | test "offset instance by field size" { |
| 276 | comptime { |
| 277 | const VirtualStruct = struct { x: u32, y: u32, z: u32, w: u32 }; |
| 278 | var inst = VirtualStruct{ .x = 0, .y = 1, .z = 2, .w = 3 }; |
| 279 | |
| 280 | var ptr = @ptrToInt(&inst); |
| 281 | ptr -= 4; |
| 282 | ptr += @offsetOf(VirtualStruct, "x"); |
| 283 | try testing.expectEqual(@as(u32, 0), @intToPtr([*]u32, ptr)[1]); |
| 284 | ptr -= @offsetOf(VirtualStruct, "x"); |
| 285 | ptr += @offsetOf(VirtualStruct, "y"); |
| 286 | try testing.expectEqual(@as(u32, 1), @intToPtr([*]u32, ptr)[1]); |
| 287 | ptr = ptr - @offsetOf(VirtualStruct, "y") + @offsetOf(VirtualStruct, "z"); |
| 288 | try testing.expectEqual(@as(u32, 2), @intToPtr([*]u32, ptr)[1]); |
| 289 | ptr = @ptrToInt(&inst.z) - 4 - @offsetOf(VirtualStruct, "z"); |
| 290 | ptr += @offsetOf(VirtualStruct, "w"); |
| 291 | try testing.expectEqual(@as(u32, 3), @intToPtr(*u32, ptr + 4).*); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | test "offset field ptr by enclosing array element size" { |
| 296 | comptime { |
| 297 | const VirtualStruct = struct { x: u32 }; |
| 298 | var arr: [4]VirtualStruct = .{ |
| 299 | .{ .x = bigToNativeEndian(u32, 0x0004080c) }, |
| 300 | .{ .x = bigToNativeEndian(u32, 0x0105090d) }, |
| 301 | .{ .x = bigToNativeEndian(u32, 0x02060a0e) }, |
| 302 | .{ .x = bigToNativeEndian(u32, 0x03070b0f) }, |
| 303 | }; |
| 304 | |
| 305 | var i: usize = 0; |
| 306 | while (i < 4) : (i += 1) { |
| 307 | var ptr: [*]u8 = @ptrCast([*]u8, &arr[0]); |
| 308 | ptr += i; |
| 309 | ptr += @offsetOf(VirtualStruct, "x"); |
| 310 | var j: usize = 0; |
| 311 | while (j < 4) : (j += 1) { |
| 312 | const base = ptr + j * @sizeOf(VirtualStruct); |
| 313 | try testing.expectEqual(@intCast(u8, i * 4 + j), base[0]); |
| 314 | } |
| 315 | } |
| 316 | } |
| 317 | } |