// RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test // // Test corner cases specific to UnsafeRawBufferPointer. // General Collection behavior tests are in // validation-test/stdlib/UnsafeBufferPointer.swift. // FIXME: The optimized-build behavior of UnsafeBufferPointer bounds/overflow // checking cannot be tested. The standard library always compiles with debug // checking enabled, so the behavior of the optimized test depends on whether // the inlining heuristics decide to inline these methods. To fix this, we need // a way to force @inlinable UnsafeBufferPointer methods to be emitted inside // the client code, and thereby subject the stdlib implementation to the test // case's compile options. // // REQUIRES: swift_test_mode_optimize_none import StdlibUnittest var UnsafeRawBufferPointerTestSuite = TestSuite("UnsafeRawBufferPointer") // View an in-memory value's bytes. // Use copyBytes to overwrite the value's bytes. UnsafeRawBufferPointerTestSuite.test("initFromValue") { var value1: Int32 = -1 var value2: Int32 = 0 // Immutable view of value1's bytes. withUnsafeBytes(of: &value1) { bytes1 in expectEqual(bytes1.count, 4) for b in bytes1 { expectEqual(b, 0xFF) } // Mutable view of value2's bytes. withUnsafeMutableBytes(of: &value2) { bytes2 in expectEqual(bytes1.count, bytes2.count) bytes2.copyMemory(from: bytes1) } } expectEqual(value2, value1) } UnsafeRawBufferPointerTestSuite.test("initFromNilBuffer") { let urbp1 = UnsafeRawBufferPointer(UnsafeBufferPointer(start: nil, count: 0)) expectEqual(urbp1.baseAddress, nil) let urbp2 = UnsafeRawBufferPointer(UnsafeMutableBufferPointer(start: nil, count: 0)) expectEqual(urbp2.baseAddress, nil) let umrbp = UnsafeMutableRawBufferPointer( UnsafeMutableBufferPointer(start: nil, count: 0)) expectEqual(umrbp.baseAddress, nil) } UnsafeRawBufferPointerTestSuite.test("initFromNilSlice") { let urbp1 = UnsafeRawBufferPointer( rebasing: UnsafeRawBufferPointer(start: nil, count: 0)[...]) expectEqual(urbp1.baseAddress, nil) let urbp2 = UnsafeRawBufferPointer( rebasing: UnsafeMutableRawBufferPointer(start: nil, count: 0)[...]) expectEqual(urbp2.baseAddress, nil) let umrbp = UnsafeMutableRawBufferPointer( rebasing: UnsafeMutableRawBufferPointer(start: nil, count: 0)[...]) expectEqual(umrbp.baseAddress, nil) } // Test mutability and subscript getter/setters. UnsafeRawBufferPointerTestSuite.test("nonmutating_subscript_setter") { var value1: Int32 = -1 var value2: Int32 = 0 withUnsafeMutableBytes(of: &value1) { bytes1 in withUnsafeMutableBytes(of: &value2) { bytes2 in bytes2[0...alignment ) defer { buffer.deallocate() } buffer.copyBytes(from: [0, 1, 2, 3] as [UInt8]) let leftBytes = buffer[0..<2] // Subscript assign. var rightBytes = buffer[2..<4] buffer[2..<4] = leftBytes expectEqualSequence(leftBytes, rightBytes) // Subscript assign into a `var` mutable slice. buffer.copyBytes(from: [0, 1, 2, 3] as [UInt8]) rightBytes[2..<4] = leftBytes expectEqualSequence(leftBytes, rightBytes) } // View an array's elements as bytes. // Use copyBytes to overwrite the array element's bytes. UnsafeRawBufferPointerTestSuite.test("initFromArray") { let array1: [Int32] = [0, 1, 2, 3] var array2 = [Int32](repeating: 0, count: 4) // Immutable view of array1's bytes. array1.withUnsafeBytes { bytes1 in expectEqual(bytes1.count, 16) for (i, b) in bytes1.enumerated() { var num = i #if _endian(big) num = num + 1 #endif if num % 4 == 0 { expectEqual(Int(b), i / 4) } else { expectEqual(b, 0) } } // Mutable view of array2's bytes. array2.withUnsafeMutableBytes { bytes2 in expectEqual(bytes1.count, bytes2.count) bytes2.copyMemory(from: bytes1) } } expectEqual(array2, array1) } UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).underflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 30, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let source = stride(from: 5 as Int64, to: 0, by: -1) if _isDebugAssertConfiguration() { expectCrashLater() } var (it, bound) = buffer.initializeMemory(as: Int64.self, from: source) let idx = bound.endIndex * MemoryLayout.stride expectEqual(it.next()!, 2) expectEqual(idx, 24) let expected: [Int64] = [5,4,3] expected.withUnsafeBytes { expectEqualSequence($0,buffer[0...alignment ) defer { buffer.deallocate() } let source: [Int64] = [5, 4, 3, 2, 1] if _isDebugAssertConfiguration() { expectCrashLater() } var (it,bound) = buffer.initializeMemory(as: Int64.self, from: source) let idx = bound.endIndex * MemoryLayout.stride expectEqual(it.next()!, 2) expectEqual(idx, 24) let expected: [Int64] = [5,4,3] expected.withUnsafeBytes { expectEqualSequence($0,buffer[0...stride, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let source: [Int64] = [5, 4, 3] var (it,bound) = buffer.initializeMemory(as: Int64.self, from: source) let idx = bound.endIndex * MemoryLayout.stride expectNil(it.next()) expectEqual(idx, buffer.endIndex) source.withUnsafeBytes { expectEqualSequence($0,buffer) } expectEqualSequence([5, 4, 3],bound) } UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).invalidNilPtr") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer(start: nil, count: 0) let source: [Int64] = [5, 4, 3, 2, 1] expectCrashLater() _ = buffer.initializeMemory(as: Int64.self, from: source) } UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).validNilPtr") { let buffer = UnsafeMutableRawBufferPointer(start: nil, count: 0) let source: [Int64] = [] var (it, bound) = buffer.initializeMemory(as: Int64.self, from: source) let idx = bound.endIndex * MemoryLayout.stride expectNil(it.next()) expectEqual(idx, source.endIndex) } // Directly test the byte Sequence produced by withUnsafeBytes. UnsafeRawBufferPointerTestSuite.test("withUnsafeBytes.Sequence") { let array1: [Int32] = [0, 1, 2, 3] array1.withUnsafeBytes { bytes1 in // Initialize an array from a sequence of bytes. let byteArray = [UInt8](bytes1) for (b1, b2) in zip(byteArray, bytes1) { expectEqual(b1, b2) } } } // Test the empty buffer. UnsafeRawBufferPointerTestSuite.test("empty") { let emptyBytes = UnsafeRawBufferPointer(start: nil, count: 0) for _ in emptyBytes { expectUnreachable() } let emptyMutableBytes = UnsafeMutableRawBufferPointer.allocate( byteCount: 0, alignment: MemoryLayout.alignment ) for _ in emptyMutableBytes { expectUnreachable() } emptyMutableBytes.deallocate() } // Store a sequence of integers to raw memory, and reload them as structs. // Store structs to raw memory, and reload them as integers. UnsafeRawBufferPointerTestSuite.test("reinterpret") { struct Pair { var x: Int32 var y: Int32 } let numPairs = 2 let bytes = UnsafeMutableRawBufferPointer.allocate( byteCount: MemoryLayout.stride * numPairs, alignment: MemoryLayout.alignment ) defer { bytes.deallocate() } for i in 0..<(numPairs * 2) { bytes.storeBytes(of: Int32(i), toByteOffset: i * MemoryLayout.stride, as: Int32.self) } let pair1 = bytes.load(as: Pair.self) let pair2 = bytes.load(fromByteOffset: MemoryLayout.stride, as: Pair.self) expectEqual(0, pair1.x) expectEqual(1, pair1.y) expectEqual(2, pair2.x) expectEqual(3, pair2.y) bytes.storeBytes(of: Pair(x: -1, y: 0), as: Pair.self) for i in 0...stride { expectEqual(0xFF, bytes[i]) } let bytes2 = UnsafeMutableRawBufferPointer( rebasing: bytes[MemoryLayout.stride...stride { expectEqual(0, bytes2[i]) } } // Store, load, subscript, and slice at all valid byte offsets. UnsafeRawBufferPointerTestSuite.test("inBounds") { let numInts = 4 let bytes = UnsafeMutableRawBufferPointer.allocate( byteCount: MemoryLayout.stride * numInts, alignment: MemoryLayout.alignment ) defer { bytes.deallocate() } for i in 0...stride, as: Int.self) } for i in 0...stride, as: Int.self) expectEqual(x, i) } for i in 0...stride + offset], byte) } } } let median = (numInts/2 * MemoryLayout.stride) var firstHalf = bytes[0...alignment ) defer { buffer.deallocate() } let bytes = UnsafeRawBufferPointer(rebasing: buffer[1..<2]) if _isDebugAssertConfiguration() { expectCrashLater() } // Accesses a valid buffer location but triggers a debug bounds check. _ = bytes[-1] } UnsafeRawBufferPointerTestSuite.test("subscript.get.overflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 2, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = UnsafeRawBufferPointer(rebasing: buffer[0..<1]) if _isDebugAssertConfiguration() { expectCrashLater() } // Accesses a valid buffer location but triggers a debug bounds check. _ = bytes[1] } UnsafeRawBufferPointerTestSuite.test("subscript.set.underflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 2, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[1..<2]) if _isDebugAssertConfiguration() { expectCrashLater() } // Accesses a valid buffer location but triggers a debug bounds check. bytes[-1] = 0 } UnsafeRawBufferPointerTestSuite.test("subscript.set.overflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 2, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[0..<1]) if _isDebugAssertConfiguration() { expectCrashLater() } // Accesses a valid buffer location but triggers a debug bounds check. bytes[1] = 0 } UnsafeRawBufferPointerTestSuite.test("subscript.range.get.underflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 3, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = UnsafeRawBufferPointer(rebasing: buffer[1..<3]) if _isDebugAssertConfiguration() { expectCrashLater() } // Accesses a valid buffer location but triggers a debug bounds check. _ = bytes[-1..<1] } UnsafeRawBufferPointerTestSuite.test("subscript.range.get.overflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 3, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = UnsafeRawBufferPointer(rebasing: buffer[0..<2]) if _isDebugAssertConfiguration() { expectCrashLater() } // Accesses a valid buffer location but triggers a debug bounds check. _ = bytes[1..<3] } UnsafeRawBufferPointerTestSuite.test("subscript.range.set.underflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 3, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[1..<3]) if _isDebugAssertConfiguration() { expectCrashLater() } // Accesses a valid buffer location but triggers a debug bounds check. bytes[-1..<1] = bytes[0..<2] } UnsafeRawBufferPointerTestSuite.test("subscript.range.set.overflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 3, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[0..<2]) if _isDebugAssertConfiguration() { expectCrashLater() } // Performs a valid byte-wise copy but triggers a debug bounds check. bytes[1..<3] = bytes[0..<2] } UnsafeRawBufferPointerTestSuite.test("subscript.range.narrow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 3, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } if _isDebugAssertConfiguration() { expectCrashLater() } // Performs a valid byte-wise copy but triggers a debug bounds check. buffer[0..<3] = buffer[0..<2] } UnsafeRawBufferPointerTestSuite.test("subscript.range.wide") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 3, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } if _isDebugAssertConfiguration() { expectCrashLater() } // Performs a valid byte-wise copy but triggers a debug bounds check. buffer[0..<2] = buffer[0..<3] } UnsafeRawBufferPointerTestSuite.test("_copyContents") { let a = Array(0..<20) let b = UnsafeMutableBufferPointer.allocate(capacity: 10*a.count) defer { b.deallocate() } var (unwritten, written) = a.withUnsafeBytes { bytes in bytes._copyContents(initializing: b) } expectNil(unwritten.next()) expectEqual(written, a.count) } UnsafeRawBufferPointerTestSuite.test("copyMemory.overflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 3, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = buffer[0..<2] if _isDebugAssertConfiguration() { expectCrashLater() } // Performs a valid byte-wise copy but triggers a debug range size check. UnsafeMutableRawBufferPointer(rebasing: bytes).copyMemory( from: UnsafeRawBufferPointer(buffer)) } // Use copyBytes without contiguous storage UnsafeRawBufferPointerTestSuite.test("copyBytes.withoutContiguousStorage") { let ranges: [Range] = [0..<2, 1..<3, 2..<4, 3..<5] var array = [UInt8](repeating: 0, count: 2) for range in ranges { array.withUnsafeMutableBytes { byte in byte.copyBytes(from: range) } expectEqual(array.count, range.count) expectEqual(array, Array(range)) } } UnsafeRawBufferPointerTestSuite.test("copyBytes.sequence.overflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")).code { let buffer = UnsafeMutableRawBufferPointer.allocate( byteCount: 3, alignment: MemoryLayout.alignment ) defer { buffer.deallocate() } let bytes = buffer[0..<2] if _isDebugAssertConfiguration() { expectCrashLater() } // Performs a valid byte-wise copy but triggers a debug range size check. UnsafeMutableRawBufferPointer(rebasing: bytes).copyBytes( from: [0, 1, 2] as [UInt8]) } UnsafeRawBufferPointerTestSuite.test("load.before") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition.")) .code { expectCrashLater() var x: Int32 = 0 withUnsafeBytes(of: &x) { _ = $0.load(fromByteOffset: -1, as: UInt8.self) } } UnsafeRawBufferPointerTestSuite.test("load.after") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition..")) .code { expectCrashLater() var x: Int32 = 0 withUnsafeBytes(of: &x) { _ = $0.load(as: UInt64.self) } } UnsafeRawBufferPointerTestSuite.test("load.aligned") { var data: [UInt8] = [0, 0, 0, 0, .max, .max, .max, .max] data.withUnsafeBytes { let x = $0.load(fromByteOffset: 4, as: UInt32.self) expectEqual(x, .max) } data.withUnsafeMutableBytes { let x = $0.load(fromByteOffset: 0, as: UInt32.self) expectEqual(x, 0) } } UnsafeRawBufferPointerTestSuite.test("load.invalid") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom({ !_isDebugAssertConfiguration() }, // require debugAssert reason: "This tests a debug precondition..")) .code { let data: [UInt8] = [0, 0, 0, .max, .max, .max, .max, 0] let i = data.firstIndex(of: .max)! expectCrashLater() _ = data.withUnsafeBytes { $0.load(fromByteOffset: i, as: UInt32.self) } } UnsafeRawBufferPointerTestSuite.test("load.unaligned") .require(.stdlib_5_7) .code { guard #available(SwiftStdlib 5.7, *) else { return } var data: [UInt8] = [0, 0, 0, .max, .max, .max, .max, 0] let i = data.firstIndex(of: .max)! data.withUnsafeBytes { let x = $0.loadUnaligned(fromByteOffset: i, as: UInt32.self) expectEqual(x, .max) } data.withUnsafeMutableBytes { let x = $0.loadUnaligned(fromByteOffset: i-1, as: UInt32.self) expectEqual(UInt32(littleEndian: x), 0xffffff00) } } UnsafeRawBufferPointerTestSuite.test("store.before") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition..")) .code { expectCrashLater() var x: Int32 = 0 withUnsafeMutableBytes(of: &x) { $0.storeBytes(of: UInt8(0), toByteOffset: -1, as: UInt8.self) } } UnsafeRawBufferPointerTestSuite.test("store.after") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition..")) .code { expectCrashLater() var x: Int32 = 0 withUnsafeMutableBytes(of: &x) { $0.storeBytes(of: UInt64(0), as: UInt64.self) } } UnsafeRawBufferPointerTestSuite.test("store.unaligned") .require(.stdlib_5_7) .code { let count = MemoryLayout.stride * 2 let p1 = UnsafeMutableRawBufferPointer.allocate( byteCount: count, alignment: MemoryLayout.alignment ) defer { p1.deallocate() } p1.copyBytes(from: repeatElement(UInt8.zero, count: count)) let value = UInt.max let offset = 3 p1.storeBytes(of: value, toByteOffset: offset, as: UInt.self) expectEqual(p1.load(fromByteOffset: offset-1, as: UInt8.self), 0) expectEqual(p1.load(fromByteOffset: offset, as: UInt8.self), .max) let storedLength = MemoryLayout.size expectEqual(p1.load(fromByteOffset: offset-1+storedLength, as: UInt8.self), .max) expectEqual(p1.load(fromByteOffset: offset+storedLength, as: UInt8.self), 0) } UnsafeRawBufferPointerTestSuite.test("store.invalid") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom({ !_isDebugAssertConfiguration() }, // require debugAssert reason: "This tests a debug precondition..")) .require(.stdlib_5_7) .code { let t = "Text that is longer than fits in a small String." let p1 = UnsafeMutableRawPointer.allocate( byteCount: MemoryLayout.size, alignment: MemoryLayout.alignment ) defer { p1.deallocate() } expectCrashLater() p1.storeBytes(of: t, as: String.self) expectUnreachable() } UnsafeRawBufferPointerTestSuite.test("store.valid") { let value32 = UInt32.max var value64 = Int64.zero withUnsafeMutableBytes(of: &value64) { $0.storeBytes(of: value32, toByteOffset: MemoryLayout.stride, as: UInt32.self) } expectEqual(value64, 0xffffffff << 32) } UnsafeRawBufferPointerTestSuite.test("copy.bytes.overflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition..")) .code { expectCrashLater() var x: Int64 = 0 var y: Int32 = 0 withUnsafeBytes(of: &x) { srcBytes in withUnsafeMutableBytes(of: &y) { destBytes in destBytes.copyMemory( from: srcBytes) } } } UnsafeRawBufferPointerTestSuite.test("copy.sequence.overflow") .skip(.wasiAny(reason: "Trap tests aren't supported on WASI.")) .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition..")) .code { expectCrashLater() var x: Int64 = 0 var y: Int32 = 0 withUnsafeBytes(of: &x) { srcBytes in withUnsafeMutableBytes(of: &y) { destBytes in destBytes.copyMemory(from: srcBytes) } } } UnsafeRawBufferPointerTestSuite.test("copy.overlap") { let bytes = UnsafeMutableRawBufferPointer.allocate( byteCount: 4, alignment: MemoryLayout.alignment ) defer { bytes.deallocate() } // Right Overlap bytes[0] = 1 bytes[1] = 2 bytes[1..<3] = bytes[0..<2] expectEqual(1, bytes[1]) expectEqual(2, bytes[2]) // Left Overlap bytes[1] = 2 bytes[2] = 3 bytes[0..<2] = bytes[1..<3] expectEqual(2, bytes[0]) expectEqual(3, bytes[1]) // Disjoint bytes[2] = 2 bytes[3] = 3 bytes[0..<2] = bytes[2..<4] expectEqual(2, bytes[0]) expectEqual(3, bytes[1]) bytes[0] = 0 bytes[1] = 1 bytes[2..<4] = bytes[0..<2] expectEqual(0, bytes[2]) expectEqual(1, bytes[3]) } runAllTests()