// RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test // 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 @_inlineable 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 import StdlibCollectionUnittest // Tests func allocateForRawBuffer(count: Int) -> UnsafeMutableRawPointer { return UnsafeMutableRawPointer.allocate(bytes: count, alignedTo: 1) } func allocateForBuffer(count: Int) -> UnsafeMutablePointer { return UnsafeMutablePointer.allocate(capacity: count) } func deallocateForRawBuffer( _ memory: UnsafeMutableRawPointer, count: Int ) { memory.deallocate(bytes: count, alignedTo: 1) } func deallocateForBuffer( _ memory: UnsafeMutablePointer, count: Int ) { memory.deallocate(capacity: count) } // Initialize memory with arbitrary equatable, comparable values. func initUnsafeRawBufferPointer(_ b: UnsafeMutableRawBufferPointer) { _ = b.initializeMemory(as: UInt8.self, from: UInt8(0)..) { _ = b.initialize(from: (0..', 'UnsafePointer'), % ('UnsafeMutableBufferPointer', True, False, 'UnsafeMutableBufferPointer', 'UnsafeMutablePointer'), % ('UnsafeRawBufferPointer', False, True, 'UnsafeRawBufferPointer', 'UnsafeRawPointer'), % ('UnsafeMutableRawBufferPointer', True, True, 'UnsafeMutableRawBufferPointer', 'UnsafeMutableRawPointer') % ]: % Raw = 'Raw' if IsRaw else '' % Element = 'UInt8' if IsRaw else 'Float' ${SelfName}TestSuite.test("AssociatedTypes") { expectRandomAccessCollectionAssociatedTypes( collectionType: ${SelfType}.self, % if IsRaw: iteratorType: ${SelfType}.Iterator.self, % else: iteratorType: UnsafeBufferPointerIterator.self, % end subSequenceType: ${'Mutable' if IsMutable else ''}RandomAccessSlice<${SelfType}>.self, indexType: Int.self, indexDistanceType: Int.self, indicesType: CountableRange.self) expect${'Mutable' if IsMutable else ''}CollectionType(${SelfType}.self) } ${SelfName}TestSuite.test("rebasing") { let rawbuffer = UnsafeMutableRawBufferPointer.allocate(count: 4 * MemoryLayout.stride) defer { rawbuffer.deallocate() } rawbuffer.copyBytes(from: [0, 1, 2, 3]) % if IsRaw: let buffer = rawbuffer % else: let intPtr = rawbuffer.baseAddress!.bindMemory(to: Int.self, capacity: 4) let buffer = UnsafeMutableBufferPointer(start: intPtr, count: 4) % end for i in buffer.indices { let slice = buffer[i.. 1 { expectEqual(rebased[1], slice[i + 1]) // Rebase again. Tests Mutable->Immutable initializer // and an rvalue slice with literal indices. let rebased2 = ${SelfName}(rebasing: rebased[1..<2]) expectEqual(rebased2.startIndex, 0) expectEqual(rebased2.endIndex, 1) expectEqual(rebased2[0], slice[i + 1]) } } } // Allocate two buffers. Initialize one and copy it into the other. Pass those // to the specified generic collection test function `checkBuffers`. func checkWithExpectedBuffer(checkBuffers: (${SelfType}, ${SelfType}) -> Void) { // A collection just big enough to be sliced up interestingly. let elementCount = 4 % if IsRaw: var memory: UnsafeMutableRawPointer var memoryCopy: UnsafeMutableRawPointer % else: var memory: UnsafeMutablePointer var memoryCopy: UnsafeMutablePointer % end memory = allocateFor${Raw}Buffer(count: elementCount) defer { deallocateFor${Raw}Buffer( memory, count: elementCount) } memoryCopy = allocateFor${Raw}Buffer(count: elementCount) defer { deallocateFor${Raw}Buffer(memoryCopy, count: elementCount) } initUnsafe${Raw}BufferPointer( UnsafeMutable${Raw}BufferPointer(start: memory, count: elementCount)) let buffer = ${SelfType}(start: memory, count: elementCount) let expectedBuffer = UnsafeMutable${Raw}BufferPointer(start: memoryCopy, count: elementCount) % if IsRaw: expectedBuffer.copyBytes(from: buffer) % else: _ = expectedBuffer.initialize(from: buffer) % end let expected = ${SelfType}( start: expectedBuffer.baseAddress, count: expectedBuffer.count) checkBuffers(expected, buffer) } ${SelfName}TestSuite.test("RandomAccessCollection") { checkWithExpectedBuffer { (expected: ${SelfType}, buffer: ${SelfType}) in checkRandomAccessCollection(expected, buffer, sameValue: { (a: ${Element}, b: ${Element}) in a == b }) } } ${SelfName}TestSuite.test("nilBaseAddress") { let emptyBuffer = ${SelfType}(start: nil, count: 0) expectNil(emptyBuffer.baseAddress) expectEqual(0, emptyBuffer.count) expectTrue(emptyBuffer.startIndex == emptyBuffer.endIndex) var iter = emptyBuffer.makeIterator() expectNil(iter.next()) expectEqualSequence([], emptyBuffer) } ${SelfName}TestSuite.test("nonNilButEmpty") { let emptyAllocated = UnsafeMutablePointer.allocate(capacity: 0) defer { emptyAllocated.deallocate(capacity: 0) } let emptyBuffer = ${SelfType}(start: ${PointerType}(emptyAllocated), count: 0) expectEqual(emptyAllocated, emptyBuffer.baseAddress) expectEqual(0, emptyBuffer.count) expectTrue(emptyBuffer.startIndex == emptyBuffer.endIndex) var iter = emptyBuffer.makeIterator() expectNil(iter.next()) expectEqualSequence([], emptyBuffer) } % if IsRaw: ${SelfName}TestSuite.test("nonNilNonEmpty") { let count = 4 let allocated = UnsafeMutableRawPointer.allocate(bytes: count, alignedTo: 1) defer { allocated.deallocate(bytes: count, alignedTo: 1) } let uint8Ptr = allocated.initializeMemory(as: UInt8.self, count: count, to: 1) uint8Ptr[count - 1] = 2 let buffer = ${SelfType}(start: ${PointerType}(allocated), count: count - 1) expectEqual(allocated, buffer.baseAddress) expectEqual(count - 1, buffer.count) expectEqual(count - 1, buffer.endIndex - buffer.startIndex) uint8Ptr[1] = 0 expectEqual(1, buffer[0]) expectEqual(0, buffer[1]) expectEqual(1, buffer[2]) var iter = buffer.makeIterator() expectEqual(1, iter.next()) expectEqual(0, iter.next()) expectEqual(1, iter.next()) expectNil(iter.next()) expectEqualSequence([1, 0, 1], buffer.map { Int($0) }) expectEqual(2, uint8Ptr[count-1]) } % else: # !IsRaw ${SelfName}TestSuite.test("nonNilNonEmpty") { let count = 4 let allocated = UnsafeMutablePointer.allocate(capacity: count) defer { allocated.deallocate(capacity: count) } allocated.initialize(to: 1.0, count: count) allocated[count - 1] = 2.0 let buffer = ${SelfType}(start: ${PointerType}(allocated), count: count - 1) expectEqual(allocated, buffer.baseAddress) expectEqual(count - 1, buffer.count) expectEqual(count - 1, buffer.endIndex - buffer.startIndex) allocated[1] = 0.0 expectEqual(1.0, buffer[0]) expectEqual(0.0, buffer[1]) expectEqual(1.0, buffer[2]) var iter = buffer.makeIterator() expectEqual(1.0, iter.next()) expectEqual(0.0, iter.next()) expectEqual(1.0, iter.next()) expectNil(iter.next()) expectEqualSequence([1.0, 0.0, 1.0], buffer) expectEqual(2.0, allocated[count-1]) } % end # !IsRaw ${SelfName}TestSuite.test("badCount") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .code { expectCrashLater() let emptyAllocated = UnsafeMutablePointer.allocate(capacity: 0) defer { emptyAllocated.deallocate(capacity: 0) } let buffer = ${SelfType}(start: ${PointerType}(emptyAllocated), count: -1) _ = buffer } ${SelfName}TestSuite.test("badNilCount") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .code { expectCrashLater() let buffer = ${SelfType}(start: nil, count: 1) _ = buffer } % end # for SelfName, IsRaw, SelfType UnsafeMutableRawBufferPointerTestSuite.test("changeElementViaBuffer") { let count = 4 let allocated = UnsafeMutableRawPointer.allocate(bytes: count, alignedTo: 1) defer { allocated.deallocate(bytes: count, alignedTo: 1) } let uint8Ptr = allocated.initializeMemory(as: UInt8.self, count: count, to: 1) uint8Ptr[count-1] = UInt8.max var buffer = UnsafeMutableRawBufferPointer(start: allocated, count: count - 1) buffer[1] = 0 expectEqual(1, buffer[0]) expectEqual(0, buffer[1]) expectEqual(1, buffer[2]) expectEqual(1, uint8Ptr[0]) expectEqual(0, uint8Ptr[1]) expectEqual(1, uint8Ptr[2]) expectEqual(UInt8.max, uint8Ptr[count-1]) buffer.sort() expectEqual(0, buffer[0]) expectEqual(1, buffer[1]) expectEqual(1, buffer[2]) expectEqual(0, uint8Ptr[0]) expectEqual(1, uint8Ptr[1]) expectEqual(1, uint8Ptr[2]) expectEqual(UInt8.max, uint8Ptr[count-1]) } UnsafeMutableBufferPointerTestSuite.test("changeElementViaBuffer") { let count = 4 let allocated = UnsafeMutablePointer.allocate(capacity: count) defer { allocated.deallocate(capacity: count) } allocated.initialize(to: 1.0, count: count) allocated[count-1] = -1.0 var buffer = UnsafeMutableBufferPointer(start: allocated, count: count - 1) buffer[1] = 0.0 expectEqual(1.0, buffer[0]) expectEqual(0.0, buffer[1]) expectEqual(1.0, buffer[2]) expectEqual(1.0, allocated[0]) expectEqual(0.0, allocated[1]) expectEqual(1.0, allocated[2]) expectEqual(-1.0, allocated[count-1]) buffer.sort() expectEqual(0.0, buffer[0]) expectEqual(1.0, buffer[1]) expectEqual(1.0, buffer[2]) expectEqual(0.0, allocated[0]) expectEqual(1.0, allocated[1]) expectEqual(1.0, allocated[2]) expectEqual(-1.0, allocated[count-1]) } protocol SubscriptTest { static var start: Int { get } static var end: Int { get } static var elementCount: Int { get } } // Initialize memory with opaque values, for use with subscript tests. extension SubscriptTest { static var elementCount: Int { get { return end - start } } % for SelfType in ['UnsafeRawBufferPointer', 'UnsafeMutableRawBufferPointer']: /// Create and populate an `${SelfType}` for use with unit tests. /// PRECONDITION: `memory` must be allocated with space for /// `SubscriptGetTest.elementCount` bytes. static func create${SelfType}(from memory: UnsafeMutableRawPointer) -> ${SelfType} { for i in Self.start..>) -> ${SelfType}> { for i in Self.start..] let replacementClosedValues: [OpaqueValue] /// The values that should be expected by slicing the UBP, or `nil` if the /// test is expected to crash. let expectedValues: [Int]? /// Same as `expectedValues`, but for closed ranges. `nil` if no difference. let expectedClosedValues: [Int]? let loc: SourceLoc init( rangeSelection: RangeSelection, replacementValues: [Int], replacementClosedValues: [Int]? = nil, expectedValues: [Int]? = nil, expectedClosedValues: [Int]? = nil, file: String = #file, line: UInt = #line ) { self.rangeSelection = rangeSelection self.expectedValues = expectedValues self.expectedClosedValues = expectedClosedValues ?? expectedValues self.replacementValues = replacementValues.map { OpaqueValue($0) } if let replacements = replacementClosedValues { self.replacementClosedValues = replacements.map { OpaqueValue($0) } } else { self.replacementClosedValues = self.replacementValues } self.loc = SourceLoc(file, line, comment: "test data") } /// Create and populate an UnsafeMutableRawBufferPointer slice for use with /// unit tests. /// PRECONDITION: `memory` must be allocated with space for /// `replacementValues.count` bytes. func replacementValuesSlice( from memory: UnsafeMutableRawPointer, replacementValues: [OpaqueValue] ) -> UnsafeMutableRawBufferPointer.SubSequence { for (i, value) in replacementValues.enumerated() { memory.initializeMemory( as: UInt8.self, at: i, to: numericCast(value.value)) } let buffer = UnsafeMutableRawBufferPointer( start: memory, count: replacementValues.count) let fullRange = RangeSelection.full.range(in: buffer) return buffer[fullRange] } /// Create and populate an UnsafeMutableBufferPointer slice for use with unit /// tests. /// PRECONDITION: `memory` must be allocated with space for /// `replacementValues.count` elements. func replacementValuesSlice( from memory: UnsafeMutablePointer>, replacementValues: [OpaqueValue] ) -> MutableRandomAccessSlice>> { for (i, value) in replacementValues.enumerated() { memory[i] = value } let buffer = UnsafeMutableBufferPointer( start: memory, count: replacementValues.count) let fullRange = RangeSelection.full.range(in: buffer) return buffer[fullRange] } } let subscriptSetTests : [SubscriptSetTest] = [ // Valid, empty. SubscriptSetTest( rangeSelection: .emptyRange, replacementValues: [], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), // Valid, edges. SubscriptSetTest( rangeSelection: .leftEdge, replacementValues: [], replacementClosedValues: [91], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], expectedClosedValues: [91, 1, 2, 3, 4, 5, 6, 7, 8, 9]), SubscriptSetTest( rangeSelection: .rightEdge, replacementValues: [], replacementClosedValues: [91], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], expectedClosedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 91]), // Valid, internal. SubscriptSetTest( rangeSelection: .leftHalf, replacementValues: [10, 11, 12, 13, 14], expectedValues: [10, 11, 12, 13, 14, 5, 6, 7, 8, 9]), SubscriptSetTest( rangeSelection: .rightHalf, replacementValues: [10, 11, 12, 13, 14], expectedValues: [0, 1, 2, 3, 4, 10, 11, 12, 13, 14]), SubscriptSetTest( rangeSelection: .middle, replacementValues: [10, 11, 12, 13, 14, 15], expectedValues: [0, 1, 10, 11, 12, 13, 14, 15, 8, 9]), SubscriptSetTest( rangeSelection: .full, replacementValues: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], expectedValues: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]), // Invalid, range and replacement count mismatch. SubscriptSetTest( rangeSelection: .leftEdge, replacementValues: [9, 9], replacementClosedValues: [9, 9, 9]), SubscriptSetTest( rangeSelection: .offsets(1, 2), replacementValues: [], replacementClosedValues: [9]), SubscriptSetTest( rangeSelection: .offsets(1, 2), replacementValues: [9, 9], replacementClosedValues: [9, 9, 9]), SubscriptSetTest( rangeSelection: .offsets(2, 5), replacementValues: [9, 9], replacementClosedValues: [9, 9, 9]), SubscriptSetTest( rangeSelection: .offsets(2, 5), replacementValues: [9, 9, 9, 9], replacementClosedValues: [9, 9, 9, 9, 9]), // Invalid, bottom out of bounds. SubscriptSetTest( rangeSelection: .offsets(-1, -1), replacementValues: []), SubscriptSetTest( rangeSelection: .offsets(-1, 0), replacementValues: [9]), SubscriptSetTest( rangeSelection: .offsets(-3, 5), replacementValues: [9, 9, 9, 9, 9, 9, 9, 9]), // Invalid, top out of bounds. SubscriptSetTest( rangeSelection: .offsets(10, 10), replacementValues: [], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], expectedClosedValues: []), // Only crash on a closed range. SubscriptSetTest( rangeSelection: .offsets(9, 10), replacementValues: [91], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 91], expectedClosedValues: []), // Only crash on a closed range. SubscriptSetTest( rangeSelection: .offsets(11, 11), replacementValues: []), SubscriptSetTest( rangeSelection: .offsets(8, 15), replacementValues: [9, 9, 9, 9, 9, 9, 9]), // Invalid, both out of bounds. SubscriptSetTest( rangeSelection: .offsets(-1, 10), replacementValues: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]), SubscriptSetTest( rangeSelection: .offsets(-2, 11), replacementValues: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]), ] % for (SelfName, IsRaw) in [ % ('UnsafeBufferPointer', False), % ('UnsafeRawBufferPointer', True), % ('UnsafeMutableBufferPointer', False), % ('UnsafeMutableRawBufferPointer', True) % ]: % Raw = 'Raw' if IsRaw else '' % for RangeName in ['range', 'countableRange', 'closedRange', 'countableClosedRange']: ${SelfName}TestSuite.test("subscript/${RangeName}/get").forEach(in: subscriptGetTests) { (test) in let expectedValues: [Int]? % if 'closed' in RangeName.lower(): if test.rangeSelection.isEmpty { return } // Treat an empty set as nil for closed ranges. if test.expectedClosedValues?.isEmpty ?? true { expectedValues = nil } else { expectedValues = test.expectedClosedValues } % else: expectedValues = test.expectedValues % end let elementCount = SubscriptGetTest.elementCount % if IsRaw: var memory: UnsafeMutableRawPointer % else: var memory: UnsafeMutablePointer> % end memory = allocateFor${Raw}Buffer(count: elementCount) defer { deallocateFor${Raw}Buffer(memory, count: elementCount) } let buffer = SubscriptGetTest.create${SelfName}(from: memory) % if IsRaw: // A raw buffer pointer has debug bounds checks on indices, and // (currently) invokes Collection._failEarlyRangeCheck in nondebug mode. if expectedValues == nil { expectCrashLater() } % end let range = test.rangeSelection.${RangeName}(in: buffer) if _isDebugAssertConfiguration() { if expectedValues == nil { expectCrashLater() } } let slice = buffer[range] // If expectedValues is nil, we should have crashed above. Allowing the // following code to crash leads to false positives. if let expectedValues = expectedValues { expectEqual( expectedValues, % if IsRaw: slice.map { Int($0) }, % else: slice.map { $0.value }, % end stackTrace: SourceLocStack().with(test.loc) ) } } % end # RangeName % end # SelfName, IsMutable, IsRaw, SelfType, PointerType % for (SelfName, IsRaw) in [ % ('UnsafeMutableBufferPointer', False), % ('UnsafeMutableRawBufferPointer', True) % ]: % Raw = 'Raw' if IsRaw else '' % for RangeName in ['range', 'countableRange', 'closedRange', 'countableClosedRange']: UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/${RangeName}/set") % if not IsRaw: // UnsafeRawBuffer (currently) invokes Collection._failEarlyRangeCheck // in nondebug mode. .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition.")) % end .forEach(in: subscriptSetTests) { (test) in let expectedValues: [Int]? let replacementValues: [OpaqueValue] let elementCount = SubscriptSetTest.elementCount % if 'closed' in RangeName.lower(): if test.rangeSelection.isEmpty { return } // Treat an empty set as nil for closed ranges. if test.expectedClosedValues?.isEmpty ?? true { expectedValues = nil } else { expectedValues = test.expectedClosedValues } replacementValues = test.replacementClosedValues let isOutOfBounds: () -> Bool = { switch test.rangeSelection { case let .offsets(lowerBound, upperBound): return lowerBound < 0 || upperBound >= elementCount default: return false } } % else: # !closed expectedValues = test.expectedValues replacementValues = test.replacementValues let isOutOfBounds: () -> Bool = { switch test.rangeSelection { case let .offsets(lowerBound, upperBound): return lowerBound < 0 || upperBound > elementCount default: return false } } % end # !closed % if IsRaw: var memory: UnsafeMutableRawPointer var sliceMemory: UnsafeMutableRawPointer % else: var memory: UnsafeMutablePointer> var sliceMemory: UnsafeMutablePointer> % end memory = allocateFor${Raw}Buffer(count: elementCount) defer { deallocateFor${Raw}Buffer(memory, count: elementCount) } sliceMemory = allocateFor${Raw}Buffer(count: replacementValues.count) defer { deallocateFor${Raw}Buffer( sliceMemory, count: replacementValues.count) } var buffer = SubscriptSetTest.create${SelfName}(from: memory) % if IsRaw: // A raw buffer pointer has debug bounds checks on indices, and // (currently) invokes Collection._failEarlyRangeCheck in nondebug mode. if _isDebugAssertConfiguration() || isOutOfBounds() { if expectedValues == nil { expectCrashLater() } } % end let range = test.rangeSelection.${RangeName}(in: buffer) let replacementSlice = test.replacementValuesSlice( from: sliceMemory, replacementValues: replacementValues) if _isDebugAssertConfiguration() { if expectedValues == nil { expectCrashLater() } } buffer[range] = replacementSlice // If expectedValues is nil, we should have crashed above. Allowing the // following code to crash leads to false positives. if let expectedValues = expectedValues { expectEqual( expectedValues, % if IsRaw: buffer.map { Int($0) }, % else: buffer.map { $0.value }, % end stackTrace: SourceLocStack().with(test.loc) ) } } % end # RangeName % end # SelfName runAllTests()