From 0844575edd2b45a7ffeb2049d38e7f31bfd3725e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 18 Oct 2021 21:27:07 -0600 Subject: [PATCH 1/3] prerequisite: implementation of initialization improvements pitch --- stdlib/public/core/ArrayBufferProtocol.swift | 10 +- stdlib/public/core/Bitset.swift | 2 +- stdlib/public/core/HashTable.swift | 4 +- stdlib/public/core/StringGuts.swift | 2 +- .../public/core/UnsafeBufferPointer.swift.gyb | 348 +++++++++++++++--- stdlib/public/core/UnsafePointer.swift | 99 +++-- .../core/UnsafeRawBufferPointer.swift.gyb | 135 ++++++- stdlib/public/core/UnsafeRawPointer.swift | 39 ++ 8 files changed, 555 insertions(+), 84 deletions(-) diff --git a/stdlib/public/core/ArrayBufferProtocol.swift b/stdlib/public/core/ArrayBufferProtocol.swift index 3dd88825b5a0a..cbf69f384ff09 100644 --- a/stdlib/public/core/ArrayBufferProtocol.swift +++ b/stdlib/public/core/ArrayBufferProtocol.swift @@ -167,7 +167,7 @@ extension _ArrayBufferProtocol where Indices == Range{ // so as not to self-clobber. newTailStart.moveInitialize(from: oldTailStart, count: tailCount) - // Assign over the original subrange + // Update the original subrange var i = newValues.startIndex for j in subrange { elements[j] = newValues[i] @@ -200,17 +200,17 @@ extension _ArrayBufferProtocol where Indices == Range{ let shrinkage = -growth if tailCount > shrinkage { // If the tail length exceeds the shrinkage - // Assign over the rest of the replaced range with the first + // Update the rest of the replaced range with the first // part of the tail. - newTailStart.moveAssign(from: oldTailStart, count: shrinkage) + newTailStart.moveUpdate(from: oldTailStart, count: shrinkage) // Slide the rest of the tail back oldTailStart.moveInitialize( from: oldTailStart + shrinkage, count: tailCount - shrinkage) } else { // Tail fits within erased elements - // Assign over the start of the replaced range with the tail - newTailStart.moveAssign(from: oldTailStart, count: tailCount) + // Update the start of the replaced range with the tail + newTailStart.moveUpdate(from: oldTailStart, count: tailCount) // Destroy elements remaining after the tail in subrange (newTailStart + tailCount).deinitialize( diff --git a/stdlib/public/core/Bitset.swift b/stdlib/public/core/Bitset.swift index 38ecbe7a9860d..7723a98f9e0ae 100644 --- a/stdlib/public/core/Bitset.swift +++ b/stdlib/public/core/Bitset.swift @@ -118,7 +118,7 @@ extension _UnsafeBitset { @inlinable @inline(__always) internal func clear() { - words.assign(repeating: .empty, count: wordCount) + words.update(repeating: .empty, count: wordCount) } } diff --git a/stdlib/public/core/HashTable.swift b/stdlib/public/core/HashTable.swift index 46f8dadecb565..922613e546ffa 100644 --- a/stdlib/public/core/HashTable.swift +++ b/stdlib/public/core/HashTable.swift @@ -416,7 +416,7 @@ extension _HashTable { @_effects(releasenone) internal func copyContents(of other: _HashTable) { _internalInvariant(bucketCount == other.bucketCount) - self.words.assign(from: other.words, count: wordCount) + self.words.update(from: other.words, count: wordCount) } /// Insert a new entry with the specified hash value into the table. @@ -446,7 +446,7 @@ extension _HashTable { // without a special case. words[0] = Word.allBits.subtracting(elementsBelow: bucketCount) } else { - words.assign(repeating: .empty, count: wordCount) + words.update(repeating: .empty, count: wordCount) } } diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 163b06d3b626c..2b740e898c987 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -329,7 +329,7 @@ func _persistCString(_ p: UnsafePointer?) -> [CChar]? { guard let s = p else { return nil } let bytesToCopy = UTF8._nullCodeUnitOffset(in: s) + 1 // +1 for the terminating NUL let result = [CChar](unsafeUninitializedCapacity: bytesToCopy) { buf, initedCount in - buf.baseAddress!.assign(from: s, count: bytesToCopy) + buf.baseAddress!.update(from: s, count: bytesToCopy) initedCount = bytesToCopy } return result diff --git a/stdlib/public/core/UnsafeBufferPointer.swift.gyb b/stdlib/public/core/UnsafeBufferPointer.swift.gyb index fd1ae2eda8c35..13247d398a508 100644 --- a/stdlib/public/core/UnsafeBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeBufferPointer.swift.gyb @@ -423,7 +423,7 @@ extension Unsafe${Mutable}BufferPointer: ${Mutable}Collection, RandomAccessColle // FIXME: swift-3-indexing-model: tests. if !newValue.isEmpty { - (_position! + bounds.lowerBound).assign( + (_position! + bounds.lowerBound).update( from: newValue.base._position! + newValue.startIndex, count: newValue.count) } @@ -621,27 +621,321 @@ extension Unsafe${Mutable}BufferPointer { dstBase.initialize(repeating: repeatedValue, count: count) } - - /// Assigns every element in this buffer's memory to a copy of the given value. + + /// Initializes the buffer's memory with the given elements. /// - /// The buffer’s memory must be initialized or the buffer's `Element` - /// must be a trivial type. + /// Prior to calling the `initialize(from:)` method on a buffer, + /// the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. + /// The buffer must contain sufficient memory to accommodate + /// `source.underestimatedCount`. /// - /// - Parameters: - /// - repeatedValue: The instance to assign this buffer's memory to. + /// The returned index is the position of the next uninitialized element + /// in the buffer, which is one past the last element written. + /// If `source` contains no elements, the returned index is equal to + /// the buffer's `startIndex`. If `source` contains an equal or greater number + /// of elements than the buffer can hold, the returned index is equal to + /// the buffer's `endIndex`. /// - /// Warning: All buffer elements must be initialized before calling this. - /// Assigning to part of the buffer must be done using the `assign(repeating:count:)` - /// method on the buffer’s `baseAddress`. + /// - Parameter source: A sequence of elements with which to initialize the + /// buffer. + /// - Returns: An iterator to any elements of `source` that didn't fit in the + /// buffer, and an index to the next uninitialized element in the buffer. @inlinable // unsafe-performance - public func assign(repeating repeatedValue: Element) { + public func initialize(from source: S) -> (S.Iterator, Index) + where S.Element == Element { + return source._copyContents(initializing: self) + } + + /// Initializes the buffer's memory with the given elements. + /// + /// Prior to calling the `initialize(fromElements:)` method on a buffer, + /// the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer, which is one past the last element written. + /// If `fromElements` contains no elements, the returned index is equal to + /// the buffer's `startIndex`. If `fromElements` contains an equal or greater + /// number of elements than the buffer can hold, the returned index is equal + /// to the buffer's `endIndex`. + /// + /// - Parameter fromElements: A collection of elements to be used to + /// initialize the buffer's storage. + /// - Returns: An index to the next uninitialized element in the buffer, + /// or `endIndex`. + @discardableResult + public func initialize( + fromElements source: C + ) -> Index where C.Element == Element { + let count = Swift.min(self.count, source.count) + guard count > 0, let base = baseAddress else { return startIndex } + + if let _ = source.withContiguousStorageIfAvailable({ + base.initialize( + from: $0.baseAddress._unsafelyUnwrappedUnchecked, + count: count + ) + }) { + return startIndex.advanced(by: count) + } + + for (p, e) in zip(base..( + from source: S + ) -> (unwritten: S.Iterator, updated: Index) where S.Element == Element { + var iterator = source.makeIterator() + guard var pointer = baseAddress else { return (iterator, startIndex) } + for index in indices { + guard let element = iterator.next() else { + return (iterator, index) + } + pointer.pointee = element + pointer += 1 + } + return (iterator, endIndex) + } + + /// Updates the buffer's initialized memory with the given elements. + /// + /// The buffer’s memory must be initialized or its `Element` type + /// must be a trivial type. + /// + /// - Parameter fromElements: A collection of elements to be used to update + /// the buffer's contents. + /// - Returns: An index one past the last updated element in the buffer, + /// or `endIndex`. + @discardableResult + public func update( + fromElements source: C + ) -> Index where C.Element == Element { + let count = Swift.min(source.count, self.count) + guard count > 0, let base = baseAddress else { return startIndex } + + if let _ = source.withContiguousStorageIfAvailable({ + base.update(from: $0.baseAddress._unsafelyUnwrappedUnchecked, + count: count) + }) { + return startIndex.advanced(by: count) + } + + for (p, e) in zip(base.. Index { + guard let pointer = source.baseAddress, source.count > 0 else { return 0 } + _debugPrecondition(count >= source.count) + baseAddress._unsafelyUnwrappedUnchecked.moveInitialize(from: pointer, + count: source.count) + return startIndex.advanced(by: source.count) + } + + /// Moves every element of an initialized source buffer slice into the + /// uninitialized memory referenced by this buffer, leaving the + /// source memory uninitialized and this buffer's memory initialized. + /// + /// The region of memory starting at the beginning of this buffer and + /// covering `source.count` instances of its `Element` type must be + /// uninitialized, or `Element` must be a trivial type. After calling + /// `moveInitialize(fromElements:)`, the region is initialized and + /// the region of memory underlying `source` is uninitialized. + /// + /// - Parameter source: A buffer containing the values to copy. The memory + /// region underlying `source` must be initialized. The memory regions + /// referenced by `source` and this buffer may overlap. + /// - Returns: An index one past the last replaced element in the buffer, + /// or `endIndex`. + @discardableResult + public func moveInitialize(fromElements source: Slice) -> Index { + return moveInitialize(fromElements: Self(rebasing: source)) + } + + /// Updates this buffer's initialized memory initialized memory by + /// moving every element from the source buffer, + /// leaving the source memory uninitialized. + /// + /// The region of memory starting at the beginning of this buffer and + /// covering `fromElements.count` instances of its `Element` type must be + /// initialized, or `Element` must be a trivial type. + /// After calling `moveUpdate(fromElements:)`, + /// the region of memory underlying `source` is uninitialized. + /// + /// - Parameter source: A buffer containing the values to move. + /// The memory region underlying `source` must be initialized. The + /// memory regions referenced by `source` and this pointer must not overlap. + /// - Returns: An index one past the last updated element in the buffer, + /// or `endIndex`. + @discardableResult + public func moveUpdate(fromElements source: Self) -> Index { + guard let pointer = source.baseAddress, source.count > 0 else { return 0 } + _debugPrecondition(count >= source.count) + baseAddress._unsafelyUnwrappedUnchecked.moveUpdate(from: pointer, + count: source.count) + return startIndex.advanced(by: source.count) + } + + /// Updates this buffer's initialized memory initialized memory by + /// moving every element from the source buffer slice, + /// leaving the source memory uninitialized. + /// + /// The region of memory starting at the beginning of this buffer and + /// covering `fromElements.count` instances of its `Element` type must be + /// initialized, or `Element` must be a trivial type. + /// After calling `moveUpdate(fromElements:)`, + /// the region of memory underlying `source` is uninitialized. + /// + /// - Parameter source: A buffer containing the values to move. + /// The memory region underlying `source` must be initialized. The + /// memory regions referenced by `source` and this pointer must not overlap. + /// - Returns: An index one past the last updated element in the buffer, + /// or `endIndex`. + @discardableResult + public func moveUpdate(fromElements source: Slice) -> Index { + return moveUpdate(fromElements: Self(rebasing: source)) + } + + /// Deinitializes every instance in this buffer. + /// + /// The region of memory underlying this buffer must be fully initialized. + /// After calling `deinitialize(count:)`, the memory is uninitialized, + /// but still bound to the `Element` type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Returns: A raw buffer to the same range of memory as this buffer. + /// The range of memory is still bound to `Element`. + @discardableResult + public func deinitialize() -> UnsafeMutableRawBufferPointer { + guard let rawValue = baseAddress?._rawValue + else { return .init(start: nil, count: 0) } + Builtin.destroyArray(Element.self, rawValue, count._builtinWordValue) + return .init(start: UnsafeMutableRawPointer(rawValue), + count: count*MemoryLayout.stride) + } + + /// Initializes the element at `index` to the given value. + /// + /// The memory underlying the destination element must be uninitialized, + /// or `Element` must be a trivial type. After a call to `initialize(to:)`, + /// the memory underlying this element of the buffer is initialized. + /// + /// - Parameters: + /// - value: The value used to initialize the buffer element's memory. + /// - index: The index of the element to initialize + public func initializeElement(at index: Index, to value: Element) { + precondition(startIndex <= index && index < endIndex) + let p = baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index) + p.initialize(to: value) + } + + /// Updates the initialized element at `index` to the given value. + /// + /// The memory underlying the destination element must be initialized, + /// or `Element` must be a trivial type. This method is equivalent to: + /// + /// self[index] = value + /// + /// - Parameters: + /// - value: The value used to update the buffer element's memory. + /// - index: The index of the element to update + public func updateElement(at index: Index, to value: Element) { + precondition(startIndex <= index && index < endIndex) + let p = baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index) + p.pointee = value + } + + /// Retrieves and returns the element at `index`, + /// leaving that element's underlying memory uninitialized. + /// + /// The memory underlying the element at `index` must be initialized. + /// After calling `moveElement(from:)`, the memory underlying this element + /// of the buffer is uninitialized, and still bound to type `Element`. + /// + /// - Parameters: + /// - index: The index of the buffer element to retrieve and deinitialize. + /// - Returns: The instance referenced by this index in this buffer. + public func moveElement(from index: Index) -> Element { + precondition(startIndex <= index && index < endIndex) + return baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index).move() + } + + /// Deinitializes the memory underlying the element at `index`. + /// + /// The memory underlying the element at `index` must be initialized. + /// After calling `deinitializeElement()`, the memory underlying this element + /// of the buffer is uninitialized, and still bound to type `Element`. + /// + /// - Parameters: + /// - index: The index of the buffer element to deinitialize. + public func deinitializeElement(at index: Index) { + precondition(startIndex <= index && index < endIndex) + let p = baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index) + p.deinitialize(count: 1) + } + % end /// Executes the given closure while temporarily binding the memory referenced @@ -721,34 +1015,6 @@ extension Unsafe${Mutable}BufferPointer: CustomDebugStringConvertible { %end -extension UnsafeMutableBufferPointer { - /// Initializes the buffer's memory with the given elements. - /// - /// When calling the `initialize(from:)` method on a buffer `b`, the memory - /// referenced by `b` must be uninitialized or the `Element` type must be a - /// trivial type. After the call, the memory referenced by this buffer up - /// to, but not including, the returned index is initialized. The buffer - /// must contain sufficient memory to accommodate - /// `source.underestimatedCount`. - /// - /// The returned index is the position of the element in the buffer one past - /// the last element written. If `source` contains no elements, the returned - /// index is equal to the buffer's `startIndex`. If `source` contains an - /// equal or greater number of elements than the buffer can hold, the - /// returned index is equal to the buffer's `endIndex`. - /// - /// - Parameter source: A sequence of elements with which to initializer the - /// buffer. - /// - Returns: An iterator to any elements of `source` that didn't fit in the - /// buffer, and an index to the point in the buffer one past the last - /// element written. - @inlinable // unsafe-performance - public func initialize(from source: S) -> (S.Iterator, Index) - where S.Element == Element { - return source._copyContents(initializing: self) - } -} - extension UnsafeBufferPointer: Sendable { } extension UnsafeBufferPointer.Iterator: Sendable { } extension UnsafeMutableBufferPointer: Sendable { } diff --git a/stdlib/public/core/UnsafePointer.swift b/stdlib/public/core/UnsafePointer.swift index 03ecd957f6f83..29b1c764b2b03 100644 --- a/stdlib/public/core/UnsafePointer.swift +++ b/stdlib/public/core/UnsafePointer.swift @@ -626,14 +626,14 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { Builtin.deallocRaw(_rawValue, (-1)._builtinWordValue, (0)._builtinWordValue) } - /// Accesses the instance referenced by this pointer. + /// Reads or updates the instance referenced by this pointer. /// /// When reading from the `pointee` property, the instance referenced by this /// pointer must already be initialized. When `pointee` is used as the left - /// side of an assignment, the instance must be initialized or this - /// pointer's `Pointee` type must be a trivial type. + /// side of an assignment, the instance is updated. The instance must + /// be initialized or this pointer's `Pointee` type must be a trivial type. /// - /// Do not assign an instance of a nontrivial type through `pointee` to + /// Do not update an instance of a nontrivial type through `pointee` to /// uninitialized memory. Instead, use an initializing method, such as /// `initialize(repeating:count:)`. @inlinable // unsafe-performance @@ -705,33 +705,55 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { return Builtin.take(_rawValue) } - /// Replaces this pointer's memory with the specified number of + /// Update this pointer's initialized memory with the specified number of /// consecutive copies of the given value. /// /// The region of memory starting at this pointer and covering `count` /// instances of the pointer's `Pointee` type must be initialized or /// `Pointee` must be a trivial type. After calling - /// `assign(repeating:count:)`, the region is initialized. + /// `update(repeating:count:)`, the region is initialized. /// /// - Parameters: - /// - repeatedValue: The instance to assign this pointer's memory to. - /// - count: The number of consecutive copies of `newValue` to assign. - /// `count` must not be negative. + /// - repeatedValue: The value used when updating this pointer's memory. + /// - count: The number of consecutive elements to update. + /// `count` must not be negative. @inlinable - public func assign(repeating repeatedValue: Pointee, count: Int) { + @_silgen_name("$sSp6assign9repeating5countyx_SitF") + public func update(repeating repeatedValue: Pointee, count: Int) { _debugPrecondition(count >= 0, "UnsafeMutablePointer.assign(repeating:count:) with negative count") for i in 0..: _Pointer, Sendable { /// - count: The number of instances to copy from the memory referenced by /// `source` to this pointer's memory. `count` must not be negative. @inlinable - public func assign(from source: UnsafePointer, count: Int) { + @_silgen_name("$sSp6assign4from5countySPyxG_SitF") + public func update(from source: UnsafePointer, count: Int) { _debugPrecondition( - count >= 0, "UnsafeMutablePointer.assign with negative count") + count >= 0, "UnsafeMutablePointer.update with negative count") if UnsafePointer(self) < source || UnsafePointer(self) >= source + count { // assign forward from a disjoint or following overlapping range. Builtin.assignCopyArrayFrontToBack( @@ -767,6 +790,13 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { } } + @_alwaysEmitIntoClient + @available(*, deprecated, renamed: "update(from:count:)") + @_silgen_name("_deprecated_assign_from_count") + public func assign(from source: UnsafePointer, count: Int) { + update(from: source, count: count) + } + /// Moves instances from initialized source memory into the uninitialized /// memory referenced by this pointer, leaving the source memory /// uninitialized and the memory referenced by this pointer initialized. @@ -819,6 +849,8 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { /// `Pointee` must be a trivial type. After calling /// `initialize(from:count:)`, the region is initialized. /// + /// - Note: The source and destination memory regions must not overlap. + /// /// - Parameters: /// - source: A pointer to the values to copy. The memory region /// `source..<(source + count)` must be initialized. The memory regions @@ -841,30 +873,34 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { // } } - /// Replaces the memory referenced by this pointer with the values - /// starting at the given pointer, leaving the source memory uninitialized. + /// Update this pointer's initialized memory by moving the specified number + /// of instances the source pointer's memory, leaving the source memory + /// uninitialized. /// /// The region of memory starting at this pointer and covering `count` /// instances of the pointer's `Pointee` type must be initialized or /// `Pointee` must be a trivial type. After calling - /// `moveAssign(from:count:)`, the region is initialized and the memory + /// `moveUpdate(from:count:)`, the region is initialized and the memory /// region `source..<(source + count)` is uninitialized. /// + /// - Note: The source and destination memory regions must not overlap. + /// /// - Parameters: - /// - source: A pointer to the values to copy. The memory region + /// - source: A pointer to the values to be moved. The memory region /// `source..<(source + count)` must be initialized. The memory regions /// referenced by `source` and this pointer must not overlap. /// - count: The number of instances to move from `source` to this /// pointer's memory. `count` must not be negative. @inlinable - public func moveAssign( + @_silgen_name("$sSp10moveAssign4from5countySpyxG_SitF") + public func moveUpdate( @_nonEphemeral from source: UnsafeMutablePointer, count: Int ) { _debugPrecondition( - count >= 0, "UnsafeMutablePointer.moveAssign(from:) with negative count") + count >= 0, "UnsafeMutablePointer.moveUpdate(from:) with negative count") _debugPrecondition( self + count <= source || source + count <= self, - "moveAssign overlapping range") + "moveUpdate overlapping range") Builtin.assignTakeArray( Pointee.self, self._rawValue, source._rawValue, count._builtinWordValue) // These builtins are equivalent to: @@ -872,7 +908,16 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { // self[i] = (source + i).move() // } } - + + @_alwaysEmitIntoClient + @available(*, deprecated, renamed: "moveUpdate(from:count:)") + @_silgen_name("_deprecated_moveAssign_from_count") + public func moveAssign( + @_nonEphemeral from source: UnsafeMutablePointer, count: Int + ) { + moveUpdate(from: source, count: count) + } + /// Deinitializes the specified number of values starting at this pointer. /// /// The region of memory starting at this pointer and covering `count` @@ -952,14 +997,14 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { return try body(UnsafeMutablePointer(_rawValue)) } - /// Accesses the pointee at the specified offset from this pointer. + /// Reads or updates the pointee at the specified offset from this pointer. /// /// For a pointer `p`, the memory at `p + i` must be initialized when reading /// the value by using the subscript. When the subscript is used as the left - /// side of an assignment, the memory at `p + i` must be initialized or - /// the pointer's `Pointee` type must be a trivial type. + /// side of an assignment, the memory at `p + i` is updated. The memory must + /// be initialized or the pointer's `Pointee` type must be a trivial type. /// - /// Do not assign an instance of a nontrivial type through the subscript to + /// Do not update an instance of a nontrivial type through the subscript to /// uninitialized memory. Instead, use an initializing method, such as /// `initialize(repeating:count:)`. /// diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index d08e488661e0d..e41ad1c547427 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -428,8 +428,8 @@ extension Unsafe${Mutable}RawBufferPointer { /// memory referenced by this buffer are initialized to raw bytes. If the /// memory is bound to type `T`, then it contains values of type `T`. /// - /// - Parameter source: A buffer of raw bytes from which to copy. - /// `source.count` must be less than or equal to this buffer's `count`. + /// - Parameter source: A buffer of raw bytes. `source.count` must + /// be less than or equal to this buffer's `count`. @inlinable public func copyMemory(from source: UnsafeRawBufferPointer) { _debugPrecondition(source.count <= self.count, @@ -444,7 +444,7 @@ extension Unsafe${Mutable}RawBufferPointer { /// must be properly aligned for accessing `T`, and `source.count` must be a /// multiple of `MemoryLayout.stride`. /// - /// After calling `copyBytes(from:)`, the `source.count` bytes of memory + /// After calling `copyBytes(from:)`, the first `source.count` bytes of memory /// referenced by this buffer are initialized to raw bytes. If the memory is /// bound to type `T`, then it contains values of type `T`. /// @@ -631,9 +631,10 @@ extension Unsafe${Mutable}RawBufferPointer { return UnsafeMutableBufferPointer(start: nil, count: 0) } - let count = (_end! - base) / MemoryLayout.stride - let typed = base.initializeMemory( - as: type, repeating: repeatedValue, count: count) + let count = (_end._unsafelyUnwrappedUnchecked - base)/MemoryLayout.stride + let typed = base.initializeMemory(as: type, + repeating: repeatedValue, + count: count) return UnsafeMutableBufferPointer(start: typed, count: count) } @@ -654,7 +655,7 @@ extension Unsafe${Mutable}RawBufferPointer { /// `S.Element`. /// /// - Parameters: - /// - type: The type of the elements to bind the buffer's memory to. + /// - type: The type of element to which this buffer's memory will be bound. /// - source: A sequence of elements with which to initialize the buffer. /// - Returns: An iterator to any elements of `source` that didn't fit in the /// buffer, and a typed buffer of the written elements. The returned @@ -696,6 +697,126 @@ extension Unsafe${Mutable}RawBufferPointer { start: base.assumingMemoryBound(to: S.Element.self), count: idx / elementStride)) } + + /// Initializes the buffer's memory with the given elements, binding the + /// initialized memory to the elements' type. + /// + /// When calling the `initializeMemory(as:fromElements:)` method on a buffer + /// `b`, the memory referenced by `b` must be uninitialized, or initialized + /// to a trivial type. `b` must be properly aligned for accessing `C.Element`. + /// + /// This method initializes the buffer with the contents of `fromElements` + /// until `fromElements` is exhausted or the buffer runs out of available + /// space. After calling `initializeMemory(as:fromElements:)`, the memory + /// referenced by the returned `UnsafeMutableBufferPointer` instance is bound + /// and initialized to type `C.Element`. This method does not change + /// the binding state of the unused portion of `b`, if any. + /// + /// - Parameters: + /// - type: The type of element to which this buffer's memory will be bound. + /// - fromElements: A collection of elements to be used to + /// initialize the buffer's storage. + /// - Returns: A typed buffer of the initialized elements. The returned + /// buffer references memory starting at the same base address as this + /// buffer, and its count indicates the number of elements copied from + /// the collection `elements`. + @discardableResult + public func initializeMemory( + as type: C.Element.Type, + fromElements source: C + ) -> UnsafeMutableBufferPointer { + var count = source.count + guard let base = _position, let end = _end, end > base, count > 0 else { + return UnsafeMutableBufferPointer(start: nil, count: 0) + } + count = Swift.min((end - base)/MemoryLayout.stride, count) + + if let start = source.withContiguousStorageIfAvailable({ + base.initializeMemory( + as: C.Element.self, + from: $0.baseAddress._unsafelyUnwrappedUnchecked, + count: count + ) + }) { + return UnsafeMutableBufferPointer(start: start, count: count) + } + + let start = base.bindMemory(to: C.Element.self, capacity: count) + for (pointer, element) in zip(start..( + as type: T.Type, + fromElements source: UnsafeMutableBufferPointer + ) -> UnsafeMutableBufferPointer { + guard let sourceBase = source.baseAddress + else { return UnsafeMutableBufferPointer(start: nil, count: 0) } + + let neededBytes = source.count*MemoryLayout.stride + guard let base = _position, count >= neededBytes + else { _debugPreconditionFailure("destination has too few bytes") } + + let initialized = base.moveInitializeMemory(as: T.self, + from: sourceBase, + count: source.count) + return UnsafeMutableBufferPointer(start: initialized, count: source.count) + } + + /// Moves every instance of an initialized source buffer slice into the + /// uninitialized memory referenced by this buffer, leaving the source memory + /// uninitialized and this buffer's memory initialized. + /// + /// The region of memory starting at this pointer and covering + /// `fromElements.count` instances of the buffer's `Element` type + /// must be uninitialized, or `Element` must be a trivial type. After + /// calling `moveInitialize(as:from:)`, the region is initialized and the + /// memory region underlying `source[..( + as type: T.Type, + fromElements source: Slice> + ) -> UnsafeMutableBufferPointer { + let rebased = UnsafeMutableBufferPointer(rebasing: source) + return moveInitializeMemory(as: T.self, fromElements: rebased) + } + % end # mutable /// Binds this buffer’s memory to the specified type and returns a typed buffer diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index 8bb87ea5ed6e4..22e23cf48d58e 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -700,6 +700,45 @@ public struct UnsafeMutableRawPointer: _Pointer, Sendable { return UnsafeMutablePointer(_rawValue) } + /// Initializes the memory referenced by this pointer with the given value, + /// binds the memory to the value's type, and returns a typed pointer to the + /// initialized memory. + /// + /// The memory referenced by this pointer must be uninitialized or + /// initialized to a trivial type, and must be properly aligned for + /// accessing `T`. + /// + /// The following example allocates raw memory for one instance of `UInt`, + /// and then uses the `initializeMemory(as:to:)` method + /// to initialize the allocated memory. + /// + /// let bytePointer = UnsafeMutableRawPointer.allocate( + /// byteCount: MemoryLayout.stride, + /// alignment: MemoryLayout.alignment) + /// let int8Pointer = bytePointer.initializeMemory(as: UInt.self, to: 0) + /// + /// // After using 'int8Pointer': + /// int8Pointer.deallocate() + /// + /// After calling this method on a raw pointer `p`, the region starting at + /// `self` and continuing up to `p + MemoryLayout.stride` is bound + /// to type `T` and initialized. If `T` is a nontrivial type, you must + /// eventually deinitialize the memory in this region to avoid memory leaks. + /// + /// - Parameters: + /// - type: The type to which this memory will be bound. + /// - value: The value used to initialize this memory. + /// - Returns: A typed pointer to the memory referenced by this raw pointer. + @inlinable + @discardableResult + public func initializeMemory( + as type: T.Type, to value: T + ) -> UnsafeMutablePointer { + Builtin.bindMemory(_rawValue, (1)._builtinWordValue, type) + Builtin.initialize(value, _rawValue) + return UnsafeMutablePointer(_rawValue) + } + /// Initializes the memory referenced by this pointer with the given value, /// binds the memory to the value's type, and returns a typed pointer to the /// initialized memory. From 32102f45814f746076a86b1a70d6b91708853ae5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 30 Nov 2021 11:15:00 -0500 Subject: [PATCH 2/3] prerequisite: implementation of withMemoryRebound proposal --- .../public/core/UnsafeBufferPointer.swift.gyb | 72 +++++++--- stdlib/public/core/UnsafePointer.swift | 117 ++++++++++------ .../core/UnsafeRawBufferPointer.swift.gyb | 89 ++++++++++++ stdlib/public/core/UnsafeRawPointer.swift | 127 ++++++++++++++++++ 4 files changed, 343 insertions(+), 62 deletions(-) diff --git a/stdlib/public/core/UnsafeBufferPointer.swift.gyb b/stdlib/public/core/UnsafeBufferPointer.swift.gyb index 13247d398a508..84bf422ebc6a9 100644 --- a/stdlib/public/core/UnsafeBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeBufferPointer.swift.gyb @@ -948,8 +948,18 @@ extension Unsafe${Mutable}BufferPointer { /// the same memory as an unrelated type without first rebinding the memory /// is undefined. /// - /// The entire region of memory referenced by this buffer must be initialized. + /// The number of instances of `T` referenced by the rebound buffer may be + /// different than the number of instances of `Element` referenced by the + /// original buffer. The number of instances of `T` will be calculated + /// at runtime. /// + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. Every instance of `Pointee` overlapping with a given + /// instance of `T` should have the same initialization state (i.e. + /// initialized or uninitialized.) Accessing a `T` whose underlying + /// `Pointee` storage is in a mixed initialization state shall be + /// undefined behaviour. + /// /// Because this buffer's memory is no longer bound to its `Element` type /// while the `body` closure executes, do not access memory using the /// original buffer from within `body`. Instead, use the `body` closure's @@ -960,39 +970,57 @@ extension Unsafe${Mutable}BufferPointer { /// `Element` type. /// /// - Note: Only use this method to rebind the buffer's memory to a type - /// with the same size and stride as the currently bound `Element` type. - /// To bind a region of memory to a type that is a different size, convert - /// the buffer to a raw buffer and use the `bindMemory(to:)` method. + /// that is layout compatible with the currently bound `Element` type. + /// The stride of the temporary type (`T`) may be an integer multiple + /// or a whole fraction of `Element`'s stride. + /// To bind a region of memory to a type that does not match these + /// requirements, convert the buffer to a raw buffer and use the + /// `bindMemory(to:)` method. + /// If `T` and `Element` have different alignments, this buffer's + /// `baseAddress` must be aligned with the larger of the two alignments. /// /// - Parameters: /// - type: The type to temporarily bind the memory referenced by this - /// buffer. The type `T` must have the same size and be layout compatible + /// buffer. The type `T` must be layout compatible /// with the pointer's `Element` type. /// - body: A closure that takes a ${Mutable.lower()} typed buffer to the - /// same memory as this buffer, only bound to type `T`. The buffer argument - /// contains the same number of complete instances of `T` as the original - /// buffer’s `count`. The closure's buffer argument is valid only for the - /// duration of the closure's execution. If `body` has a return value, that - /// value is also used as the return value for the `withMemoryRebound(to:_:)` + /// same memory as this buffer, only bound to type `T`. The buffer + /// parameter contains a number of complete instances of `T` based + /// on the capacity of the original buffer and the stride of `Element`. + /// The closure's buffer argument is valid only for the duration of the + /// closure's execution. If `body` has a return value, that value + /// is also used as the return value for the `withMemoryRebound(to:_:)` /// method. + /// - buffer: The buffer temporarily bound to `T`. /// - Returns: The return value, if any, of the `body` closure parameter. @inlinable // unsafe-performance + @_alwaysEmitIntoClient public func withMemoryRebound( - to type: T.Type, _ body: (${Self}) throws -> Result + to type: T.Type, + _ body: (_ buffer: ${Self}) throws -> Result ) rethrows -> Result { - if let base = _position { - _debugPrecondition(MemoryLayout.stride == MemoryLayout.stride) - Builtin.bindMemory(base._rawValue, count._builtinWordValue, T.self) - defer { - Builtin.bindMemory(base._rawValue, count._builtinWordValue, Element.self) - } - - return try body(${Self}( - start: Unsafe${Mutable}Pointer(base._rawValue), count: count)) + guard let base = _position?._rawValue else { + return try body(.init(start: nil, count: 0)) } - else { - return try body(${Self}(start: nil, count: 0)) + + let newCount: Int + if MemoryLayout.stride == MemoryLayout.stride { + newCount = count + _debugPrecondition( + MemoryLayout.alignment == MemoryLayout.alignment + ) + } else { + newCount = count * MemoryLayout.stride / MemoryLayout.stride + _debugPrecondition( + Int(bitPattern: .init(base)) & (MemoryLayout.alignment-1) == 0 && + MemoryLayout.stride > MemoryLayout.stride + ? MemoryLayout.stride % MemoryLayout.stride == 0 + : MemoryLayout.stride % MemoryLayout.stride == 0 + ) } + let binding = Builtin.bindMemory(base, newCount._builtinWordValue, T.self) + defer { Builtin.rebindMemory(base, binding) } + return try body(.init(start: .init(base), count: newCount)) } /// A pointer to the first element of the buffer. diff --git a/stdlib/public/core/UnsafePointer.swift b/stdlib/public/core/UnsafePointer.swift index 29b1c764b2b03..29975fded8f2a 100644 --- a/stdlib/public/core/UnsafePointer.swift +++ b/stdlib/public/core/UnsafePointer.swift @@ -243,8 +243,8 @@ public struct UnsafePointer: _Pointer, Sendable { } } - /// Executes the given closure while temporarily binding the specified number - /// of instances to the given type. + /// Executes the given closure while temporarily binding memory to + /// the specified number of instances of type `T`. /// /// Use this method when you have a pointer to memory bound to one type and /// you need to access that memory as instances of another type. Accessing @@ -253,15 +253,22 @@ public struct UnsafePointer: _Pointer, Sendable { /// the same memory as an unrelated type without first rebinding the memory /// is undefined. /// - /// The region of memory starting at this pointer and covering `count` - /// instances of the pointer's `Pointee` type must be initialized. + /// The region of memory that starts at this pointer and covers `count` + /// strides of `T` instances must be bound to `Pointee`. + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. Every instance of `Pointee` overlapping with a given + /// instance of `T` should have the same initialization state (i.e. + /// initialized or uninitialized.) Accessing a `T` whose underlying + /// `Pointee` storage is in a mixed initialization state shall be + /// undefined behaviour. /// /// The following example temporarily rebinds the memory of a `UInt64` /// pointer to `Int64`, then accesses a property on the signed integer. /// /// let uint64Pointer: UnsafePointer = fetchValue() - /// let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in - /// return ptr.pointee < 0 + /// let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, + /// capacity: 1) { + /// return $0.pointee < 0 /// } /// /// Because this pointer's memory is no longer bound to its `Pointee` type @@ -274,31 +281,43 @@ public struct UnsafePointer: _Pointer, Sendable { /// `Pointee` type. /// /// - Note: Only use this method to rebind the pointer's memory to a type - /// with the same size and stride as the currently bound `Pointee` type. - /// To bind a region of memory to a type that is a different size, convert - /// the pointer to a raw pointer and use the `bindMemory(to:capacity:)` - /// method. + /// that is layout compatible with the `Pointee` type. The stride of the + /// temporary type (`T`) may be an integer multiple or a whole fraction + /// of `Pointee`'s stride, for example to point to one element of + /// an aggregate. + /// To bind a region of memory to a type that does not match these + /// requirements, convert the pointer to a raw pointer and use the + /// `bindMemory(to:)` method. + /// If `T` and `Pointee` have different alignments, this pointer + /// must be aligned with the larger of the two alignments. /// /// - Parameters: /// - type: The type to temporarily bind the memory referenced by this - /// pointer. The type `T` must be the same size and be layout compatible + /// pointer. The type `T` must be layout compatible /// with the pointer's `Pointee` type. - /// - count: The number of instances of `Pointee` to bind to `type`. - /// - body: A closure that takes a typed pointer to the + /// - count: The number of instances of `T` in the re-bound region. + /// - body: A closure that takes a typed pointer to the /// same memory as this pointer, only bound to type `T`. The closure's /// pointer argument is valid only for the duration of the closure's /// execution. If `body` has a return value, that value is also used as /// the return value for the `withMemoryRebound(to:capacity:_:)` method. + /// - pointer: The pointer temporarily bound to `T`. /// - Returns: The return value, if any, of the `body` closure parameter. @inlinable - public func withMemoryRebound(to type: T.Type, capacity count: Int, - _ body: (UnsafePointer) throws -> Result + @_alwaysEmitIntoClient + public func withMemoryRebound( + to type: T.Type, capacity count: Int, + _ body: (_ pointer: UnsafePointer) throws -> Result ) rethrows -> Result { - Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self) - defer { - Builtin.bindMemory(_rawValue, count._builtinWordValue, Pointee.self) - } - return try body(UnsafePointer(_rawValue)) + _debugPrecondition( + Int(bitPattern: .init(_rawValue)) & (MemoryLayout.alignment-1) == 0 && + MemoryLayout.stride > MemoryLayout.stride + ? MemoryLayout.stride % MemoryLayout.stride == 0 + : MemoryLayout.stride % MemoryLayout.stride == 0 + ) + let binding = Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self) + defer { Builtin.rebindMemory(_rawValue, binding) } + return try body(.init(_rawValue)) } /// Accesses the pointee at the specified offset from this pointer. @@ -939,8 +958,8 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { return UnsafeMutableRawPointer(self) } - /// Executes the given closure while temporarily binding the specified number - /// of instances to the given type. + /// Executes the given closure while temporarily binding memory to + /// the specified number of instances of the given type. /// /// Use this method when you have a pointer to memory bound to one type and /// you need to access that memory as instances of another type. Accessing @@ -949,15 +968,21 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { /// the same memory as an unrelated type without first rebinding the memory /// is undefined. /// - /// The region of memory starting at this pointer and covering `count` - /// instances of the pointer's `Pointee` type must be initialized. + /// The region of memory that starts at this pointer and covers `count` + /// strides of `T` instances must be bound to `Pointee`. + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. Every instance of `Pointee` overlapping with a given + /// instance of `T` should have the same initialization state (i.e. + /// initialized or uninitialized.) Accessing a `T` whose underlying + /// `Pointee` storage is in a mixed initialization state shall be + /// undefined behaviour. /// /// The following example temporarily rebinds the memory of a `UInt64` - /// pointer to `Int64`, then accesses a property on the signed integer. + /// pointer to `Int64`, then modifies the signed integer. /// /// let uint64Pointer: UnsafeMutablePointer = fetchValue() - /// let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in - /// return ptr.pointee < 0 + /// uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in + /// ptr.pointee.negate() /// } /// /// Because this pointer's memory is no longer bound to its `Pointee` type @@ -970,31 +995,43 @@ public struct UnsafeMutablePointer: _Pointer, Sendable { /// `Pointee` type. /// /// - Note: Only use this method to rebind the pointer's memory to a type - /// with the same size and stride as the currently bound `Pointee` type. - /// To bind a region of memory to a type that is a different size, convert - /// the pointer to a raw pointer and use the `bindMemory(to:capacity:)` - /// method. + /// that is layout compatible with the `Pointee` type. The stride of the + /// temporary type (`T`) may be an integer multiple or a whole fraction + /// of `Pointee`'s stride, for example to point to one element of + /// an aggregate. + /// To bind a region of memory to a type that does not match these + /// requirements, convert the pointer to a raw pointer and use the + /// `bindMemory(to:)` method. + /// If `T` and `Pointee` have different alignments, this pointer + /// must be aligned with the larger of the two alignments. /// /// - Parameters: /// - type: The type to temporarily bind the memory referenced by this - /// pointer. The type `T` must be the same size and be layout compatible + /// pointer. The type `T` must be layout compatible /// with the pointer's `Pointee` type. - /// - count: The number of instances of `Pointee` to bind to `type`. + /// - count: The number of instances of `T` in the re-bound region. /// - body: A closure that takes a mutable typed pointer to the /// same memory as this pointer, only bound to type `T`. The closure's /// pointer argument is valid only for the duration of the closure's /// execution. If `body` has a return value, that value is also used as /// the return value for the `withMemoryRebound(to:capacity:_:)` method. + /// - pointer: The pointer temporarily bound to `T`. /// - Returns: The return value, if any, of the `body` closure parameter. @inlinable - public func withMemoryRebound(to type: T.Type, capacity count: Int, - _ body: (UnsafeMutablePointer) throws -> Result + @_alwaysEmitIntoClient + public func withMemoryRebound( + to type: T.Type, capacity count: Int, + _ body: (_ pointer: UnsafeMutablePointer) throws -> Result ) rethrows -> Result { - Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self) - defer { - Builtin.bindMemory(_rawValue, count._builtinWordValue, Pointee.self) - } - return try body(UnsafeMutablePointer(_rawValue)) + _debugPrecondition( + Int(bitPattern: .init(_rawValue)) & (MemoryLayout.alignment-1) == 0 && + MemoryLayout.stride > MemoryLayout.stride + ? MemoryLayout.stride % MemoryLayout.stride == 0 + : MemoryLayout.stride % MemoryLayout.stride == 0 + ) + let binding = Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self) + defer { Builtin.rebindMemory(_rawValue, binding) } + return try body(.init(_rawValue)) } /// Reads or updates the pointee at the specified offset from this pointer. diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index e41ad1c547427..33f758e7c6729 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -850,6 +850,95 @@ extension Unsafe${Mutable}RawBufferPointer { return Unsafe${Mutable}BufferPointer( start: Unsafe${Mutable}Pointer(base._rawValue), count: capacity) } + + /// Executes the given closure while temporarily binding the buffer to + /// instances of type `T`. + /// + /// Use this method when you have a buffer to raw memory and you need + /// to access that memory as instances of a given type `T`. Accessing + /// memory as a type `T` requires that the memory be bound to that type. + /// A memory location may only be bound to one type at a time, so accessing + /// the same memory as an unrelated type without first rebinding the memory + /// is undefined. + /// + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. The memory underlying any individual instance of `T` + /// must have the same initialization state (i.e. initialized or + /// uninitialized.) Accessing a `T` whose underlying memory + /// is in a mixed initialization state shall be undefined behaviour. + /// + /// If the byte count of the original buffer is not a multiple of + /// the stride of `T`, then the re-bound buffer is shorter + /// than the original buffer. + /// + /// After executing `body`, this method rebinds memory back to its original + /// binding state. This can be unbound memory, or bound to a different type. + /// + /// - Note: The buffer's base address must match the + /// alignment of `T` (as reported by `MemoryLayout.alignment`). + /// That is, `Int(bitPattern: self.baseAddress) % MemoryLayout.alignment` + /// must equal zero. + /// + /// - Note: A raw buffer may represent memory that has been bound to a type. + /// If that is the case, then `T` must be layout compatible with the + /// type to which the memory has been bound. This requirement does not + /// apply if the raw buffer represents memory that has not been bound + /// to any type. + /// + /// - Parameters: + /// - type: The type to temporarily bind the memory referenced by this + /// pointer. This pointer must be a multiple of this type's alignment. + /// - body: A closure that takes a typed pointer to the + /// same memory as this pointer, only bound to type `T`. The closure's + /// pointer argument is valid only for the duration of the closure's + /// execution. If `body` has a return value, that value is also used as + /// the return value for the `withMemoryRebound(to:capacity:_:)` method. + /// - buffer: The buffer temporarily bound to instances of `T`. + /// - Returns: The return value, if any, of the `body` closure parameter. + @inlinable + public func withMemoryRebound( + to type: T.Type, + _ body: (_ buffer: Unsafe${Mutable}BufferPointer) throws -> Result + ) rethrows -> Result { + guard let s = _position, let e = _end else { + return try body(.init(start: nil, count: 0)) + } + _debugPrecondition( + Int(bitPattern: s) & (MemoryLayout.alignment-1) == 0 + ) + let c = s.distance(to: e) / MemoryLayout.stride + let binding = Builtin.bindMemory(s._rawValue, c._builtinWordValue, T.self) + defer { Builtin.rebindMemory(s._rawValue, binding) } + return try body(.init(start: .init(s._rawValue), count: c)) + } + + /// Returns a typed buffer to the memory referenced by this buffer, + /// assuming that the memory is already bound to the specified type. + /// + /// Use this method when you have a raw buffer to memory that has already + /// been bound to the specified type. The memory starting at this pointer + /// must be bound to the type `T`. Accessing memory through the returned + /// pointer is undefined if the memory has not been bound to `T`. To bind + /// memory to `T`, use `bindMemory(to:capacity:)` instead of this method. + /// + /// - Note: The buffer's base address must match the + /// alignment of `T` (as reported by `MemoryLayout.alignment`). + /// That is, `Int(bitPattern: self.baseAddress) % MemoryLayout.alignment` + /// must equal zero. + /// + /// - Parameter to: The type `T` that the memory has already been bound to. + /// - Returns: A typed pointer to the same memory as this raw pointer. + @_transparent + public func assumingMemoryBound( + to: T.Type + ) -> Unsafe${Mutable}BufferPointer { + guard let base = _position else { + return .init(start: nil, count: 0) + } + + let c = count / MemoryLayout.stride + return .init(start: .init(base._rawValue), count: c) + } } extension Unsafe${Mutable}RawBufferPointer: CustomDebugStringConvertible { diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index 22e23cf48d58e..8cae989ebf3b0 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -319,6 +319,70 @@ public struct UnsafeRawPointer: _Pointer, Sendable { return UnsafePointer(_rawValue) } + /// Executes the given closure while temporarily binding memory to + /// the specified number of instances of type `T`. + /// + /// Use this method when you have a pointer to raw memory and you need + /// to access that memory as instances of a given type `T`. Accessing + /// memory as a type `T` requires that the memory be bound to that type. A + /// memory location may only be bound to one type at a time, so accessing + /// the same memory as an unrelated type without first rebinding the memory + /// is undefined. + /// + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. The memory underlying any individual instance of `T` + /// must have the same initialization state (i.e. initialized or + /// uninitialized.) Accessing a `T` whose underlying memory + /// is in a mixed initialization state shall be undefined behaviour. + /// + /// The following example temporarily rebinds a raw memory pointer + /// to `Int64`, then accesses a property on the signed integer. + /// + /// let pointer: UnsafeRawPointer = fetchValue() + /// let isNegative = pointer.withMemoryRebound(to: Int64.self, + /// capacity: 1) { + /// return $0.pointee < 0 + /// } + /// + /// After executing `body`, this method rebinds memory back to its original + /// binding state. This can be unbound memory, or bound to a different type. + /// + /// - Note: The region of memory starting at this pointer must match the + /// alignment of `T` (as reported by `MemoryLayout.alignment`). + /// That is, `Int(bitPattern: self) % MemoryLayout.alignment` + /// must equal zero. + /// + /// - Note: The region of memory starting at this pointer may have been + /// bound to a type. If that is the case, then `T` must be + /// layout compatible with the type to which the memory has been bound. + /// This requirement does not apply if the region of memory + /// has not been bound to any type. + /// + /// - Parameters: + /// - type: The type to temporarily bind the memory referenced by this + /// pointer. This pointer must be a multiple of this type's alignment. + /// - count: The number of instances of `T` in the re-bound region. + /// - body: A closure that takes a typed pointer to the + /// same memory as this pointer, only bound to type `T`. The closure's + /// pointer argument is valid only for the duration of the closure's + /// execution. If `body` has a return value, that value is also used as + /// the return value for the `withMemoryRebound(to:capacity:_:)` method. + /// - pointer: The pointer temporarily bound to `T`. + /// - Returns: The return value, if any, of the `body` closure parameter. + @inlinable + public func withMemoryRebound( + to type: T.Type, + capacity count: Int, + _ body: (_ pointer: UnsafePointer) throws -> Result + ) rethrows -> Result { + _debugPrecondition( + Int(bitPattern: self) & (MemoryLayout.alignment-1) == 0 + ) + let binding = Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self) + defer { Builtin.rebindMemory(_rawValue, binding) } + return try body(.init(_rawValue)) + } + /// Returns a typed pointer to the memory referenced by this pointer, /// assuming that the memory is already bound to the specified type. /// @@ -684,6 +748,69 @@ public struct UnsafeMutableRawPointer: _Pointer, Sendable { return UnsafeMutablePointer(_rawValue) } + /// Executes the given closure while temporarily binding memory to + /// the specified number of instances of type `T`. + /// + /// Use this method when you have a pointer to raw memory and you need + /// to access that memory as instances of a given type `T`. Accessing + /// memory as a type `T` requires that the memory be bound to that type. A + /// memory location may only be bound to one type at a time, so accessing + /// the same memory as an unrelated type without first rebinding the memory + /// is undefined. + /// + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. The memory underlying any individual instance of `T` + /// must have the same initialization state (i.e. initialized or + /// uninitialized.) Accessing a `T` whose underlying memory + /// is in a mixed initialization state shall be undefined behaviour. + /// + /// The following example temporarily rebinds a raw memory pointer + /// to `Int64`, then modifies the signed integer. + /// + /// let pointer: UnsafeMutableRawPointer = fetchValue() + /// pointer.withMemoryRebound(to: Int64.self, capacity: 1) { + /// ptr.pointee.negate() + /// } + /// + /// After executing `body`, this method rebinds memory back to its original + /// binding state. This can be unbound memory, or bound to a different type. + /// + /// - Note: The region of memory starting at this pointer must match the + /// alignment of `T` (as reported by `MemoryLayout.alignment`). + /// That is, `Int(bitPattern: self) % MemoryLayout.alignment` + /// must equal zero. + /// + /// - Note: The region of memory starting at this pointer may have been + /// bound to a type. If that is the case, then `T` must be + /// layout compatible with the type to which the memory has been bound. + /// This requirement does not apply if the region of memory + /// has not been bound to any type. + /// + /// - Parameters: + /// - type: The type to temporarily bind the memory referenced by this + /// pointer. This pointer must be a multiple of this type's alignment. + /// - count: The number of instances of `T` in the re-bound region. + /// - body: A closure that takes a typed pointer to the + /// same memory as this pointer, only bound to type `T`. The closure's + /// pointer argument is valid only for the duration of the closure's + /// execution. If `body` has a return value, that value is also used as + /// the return value for the `withMemoryRebound(to:capacity:_:)` method. + /// - pointer: The pointer temporarily bound to `T`. + /// - Returns: The return value, if any, of the `body` closure parameter. + @inlinable + public func withMemoryRebound( + to type: T.Type, + capacity count: Int, + _ body: (_ pointer: UnsafeMutablePointer) throws -> Result + ) rethrows -> Result { + _debugPrecondition( + Int(bitPattern: self) & (MemoryLayout.alignment-1) == 0 + ) + let binding = Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self) + defer { Builtin.rebindMemory(_rawValue, binding) } + return try body(.init(_rawValue)) + } + /// Returns a typed pointer to the memory referenced by this pointer, /// assuming that the memory is already bound to the specified type. /// From 9e3afe7671bf2ed1dd3eb044ed8dd7985f703e70 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 30 Nov 2021 14:14:59 -0500 Subject: [PATCH 3/3] [stdlib] partial buffer initialization (better buffer slices) --- stdlib/public/core/CMakeLists.txt | 2 + stdlib/public/core/GroupInfo.json | 2 + .../core/UnsafeBufferPointerProtocols.swift | 82 ++ .../core/UnsafeBufferPointerSlice.swift | 847 ++++++++++++++++++ 4 files changed, 933 insertions(+) create mode 100644 stdlib/public/core/UnsafeBufferPointerProtocols.swift create mode 100644 stdlib/public/core/UnsafeBufferPointerSlice.swift diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index db963a4f46728..298270d38dae5 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -222,6 +222,8 @@ set(SWIFTLIB_SOURCES SliceBuffer.swift SIMDVector.swift UnfoldSequence.swift + UnsafeBufferPointerSlice.swift + UnsafeBufferPointerProtocols.swift VarArgs.swift Zip.swift "${SWIFT_SOURCE_DIR}/stdlib/linker-support/magic-symbols-for-install-name.c" diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index 70be153291477..9bd8d4f0518bf 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -185,6 +185,8 @@ "UnsafePointer.swift", "UnsafeRawPointer.swift", "UnsafeBufferPointer.swift", + "UnsafeBufferPointerSlice.swift", + "UnsafeBufferPointerProtocols.swift", "UnsafeRawBufferPointer.swift" ], "Protocols": [ diff --git a/stdlib/public/core/UnsafeBufferPointerProtocols.swift b/stdlib/public/core/UnsafeBufferPointerProtocols.swift new file mode 100644 index 0000000000000..8d689112c062d --- /dev/null +++ b/stdlib/public/core/UnsafeBufferPointerProtocols.swift @@ -0,0 +1,82 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +public protocol _RebasableCollection: Collection { + init(rebasing slice: SubSequence) +} + +extension UnsafeBufferPointer: _RebasableCollection {} +extension UnsafeMutableBufferPointer: _RebasableCollection {} + + +public protocol _BufferProtocol: Collection where Index == Int { + associatedtype Element + + func withMemoryRebound( + to type: T.Type, _ body: (UnsafeBufferPointer) throws -> Result + ) rethrows -> Result +} + +extension UnsafeBufferPointer: _BufferProtocol {} + + +public protocol _MutableBaseAddressProtocol: MutableCollection { + var baseAddress: UnsafeMutablePointer? { get } +} + +extension UnsafeMutableBufferPointer: _MutableBaseAddressProtocol {} + + +public protocol _MutableBufferProtocol: MutableCollection where Index == Int { + associatedtype Element + + func initialize(repeating repeatedValue: Element) + + func initialize(from source: S) -> (S.Iterator, Index) + where S.Element == Element + + func initialize(fromElements: C) -> Index + where C.Element == Element + + func update(repeating repeatedValue: Element) + + func update( + from source: S + ) -> (unwritten: S.Iterator, updated: Index) where S.Element == Element + + func update( + fromElements: C + ) -> Index where C.Element == Element + + func moveInitialize(fromElements source: UnsafeMutableBufferPointer) -> Index + + func moveInitialize(fromElements source: Slice>) -> Index + + func moveUpdate(fromElements source: UnsafeMutableBufferPointer) -> Index + + func moveUpdate(fromElements source: Slice>) -> Index + + func deinitialize() -> UnsafeMutableRawBufferPointer + + func initializeElement(at index: Index, to value: Element) + + func updateElement(at index: Index, to value: Element) + + func moveElement(from index: Index) -> Element + + func deinitializeElement(at index: Index) + + func withMemoryRebound( + to type: T.Type, _ body: (UnsafeMutableBufferPointer) throws -> Result + ) rethrows -> Result +} + +extension UnsafeMutableBufferPointer: _MutableBufferProtocol {} diff --git a/stdlib/public/core/UnsafeBufferPointerSlice.swift b/stdlib/public/core/UnsafeBufferPointerSlice.swift new file mode 100644 index 0000000000000..6726e2f9228e2 --- /dev/null +++ b/stdlib/public/core/UnsafeBufferPointerSlice.swift @@ -0,0 +1,847 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +extension Slice where Base == UnsafeMutableRawBufferPointer { + + /// Copies the bytes from the given buffer to this buffer slice's memory. + /// + /// If the `source.count` bytes of memory referenced by this buffer are bound + /// to a type `T`, then `T` must be a trivial type, the underlying pointer + /// must be properly aligned for accessing `T`, and `source.count` must be a + /// multiple of `MemoryLayout.stride`. + /// + /// The memory referenced by `source` may overlap with the memory referenced + /// by this buffer. + /// + /// After calling `copyMemory(from:)`, the first `source.count` bytes of + /// memory referenced by this buffer are initialized to raw bytes. If the + /// memory is bound to type `T`, then it contains values of type `T`. + /// + /// - Parameter source: A buffer of raw bytes. `source.count` must + /// be less than or equal to this buffer slice's `count`. + @inlinable + func copyMemory(from source: UnsafeRawBufferPointer) { + let buffer = Base(rebasing: self) + buffer.copyMemory(from: source) + } + + /// Copies from a collection of `UInt8` into this buffer slice's memory. + /// + /// If the `source.count` bytes of memory referenced by this buffer are bound + /// to a type `T`, then `T` must be a trivial type, the underlying pointer + /// must be properly aligned for accessing `T`, and `source.count` must be a + /// multiple of `MemoryLayout.stride`. + /// + /// After calling `copyBytes(from:)`, the first `source.count` bytes of memory + /// referenced by this buffer are initialized to raw bytes. If the memory is + /// bound to type `T`, then it contains values of type `T`. + /// + /// - Parameter source: A collection of `UInt8` elements. `source.count` must + /// be less than or equal to this buffer slice's `count`. + @inlinable + public func copyBytes( + from source: C + ) where C.Element == UInt8 { + let buffer = Base(rebasing: self) + buffer.copyBytes(from: source) + } + + /// Initializes the memory referenced by this buffer with the given value, + /// binds the memory to the value's type, and returns a typed buffer of the + /// initialized memory. + /// + /// The memory referenced by this buffer must be uninitialized or + /// initialized to a trivial type, and must be properly aligned for + /// accessing `T`. + /// + /// After calling this method on a raw buffer with non-nil `baseAddress` `b`, + /// the region starting at `b` and continuing up to + /// `b + self.count - self.count % MemoryLayout.stride` is bound to type `T` and + /// initialized. If `T` is a nontrivial type, you must eventually deinitialize + /// or move the values in this region to avoid leaks. If `baseAddress` is + /// `nil`, this function does nothing and returns an empty buffer pointer. + /// + /// - Parameters: + /// - type: The type to bind this buffer’s memory to. + /// - repeatedValue: The instance to copy into memory. + /// - Returns: A typed buffer of the memory referenced by this raw buffer. + /// The typed buffer contains `self.count / MemoryLayout.stride` + /// instances of `T`. + @inlinable + @discardableResult + func initializeMemory( + as type: T.Type, repeating repeatedValue: T + ) -> UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + return buffer.initializeMemory(as: T.self, repeating: repeatedValue) + } + + /// Initializes the buffer's memory with the given elements, binding the + /// initialized memory to the elements' type. + /// + /// When calling the `initializeMemory(as:from:)` method on a buffer `b`, + /// the memory referenced by `b` must be uninitialized or initialized to a + /// trivial type, and must be properly aligned for accessing `S.Element`. + /// The buffer must contain sufficient memory to accommodate + /// `source.underestimatedCount`. + /// + /// This method initializes the buffer with elements from `source` until + /// `source` is exhausted or, if `source` is a sequence but not a + /// collection, the buffer has no more room for its elements. After calling + /// `initializeMemory(as:from:)`, the memory referenced by the returned + /// `UnsafeMutableBufferPointer` instance is bound and initialized to type + /// `S.Element`. + /// + /// - Parameters: + /// - type: The type of element to which this buffer's memory will be bound. + /// - source: A sequence of elements with which to initialize the buffer. + /// - Returns: An iterator to any elements of `source` that didn't fit in the + /// buffer, and a typed buffer of the written elements. The returned + /// buffer references memory starting at the same base address as this + /// buffer. + @inlinable + public func initializeMemory( + as type: S.Element.Type, from source: S + ) -> (unwritten: S.Iterator, initialized: UnsafeMutableBufferPointer) { + let buffer = Base(rebasing: self) + return buffer.initializeMemory(as: S.Element.self, from: source) + } + + /// Initializes the buffer's memory with the given elements, binding the + /// initialized memory to the elements' type. + /// + /// When calling the `initializeMemory(as:fromElements:)` method on a buffer + /// `b`, the memory referenced by `b` must be uninitialized, or initialized + /// to a trivial type. `b` must be properly aligned for accessing `C.Element`. + /// + /// This method initializes the buffer with the contents of `fromElements` + /// until `fromElements` is exhausted or the buffer runs out of available + /// space. After calling `initializeMemory(as:fromElements:)`, the memory + /// referenced by the returned `UnsafeMutableBufferPointer` instance is bound + /// and initialized to type `C.Element`. This method does not change + /// the binding state of the unused portion of `b`, if any. + /// + /// - Parameters: + /// - type: The type of element to which this buffer's memory will be bound. + /// - fromElements: A collection of elements to be used to + /// initialize the buffer's storage. + /// - Returns: A typed buffer of the initialized elements. The returned + /// buffer references memory starting at the same base address as this + /// buffer, and its count indicates the number of elements copied from + /// the collection `elements`. + @discardableResult + func initializeMemory( + as type: C.Element.Type, + fromElements source: C + ) -> UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + return buffer.initializeMemory(as: C.Element.self, fromElements: source) + } + + /// Moves instances from an initialized source buffer into the + /// uninitialized memory referenced by this buffer, leaving the source memory + /// uninitialized and this buffer's memory initialized. + /// + /// When calling the `moveInitializeMemory(as:from:)` method on a buffer `b`, + /// the memory referenced by `b` must be uninitialized, or initialized to a + /// trivial type. `b` must be properly aligned for accessing `C.Element`. + /// + /// The region of memory starting at this pointer and covering + /// `fromElements.count` instances of the buffer's `Element` type + /// must be uninitialized, or `Element` must be a trivial type. After + /// calling `moveInitialize(as:from:)`, the region is initialized and the + /// memory region underlying `source` is uninitialized. + /// + /// - Parameters: + /// - type: The type of element to which this buffer's memory will be bound. + /// - fromElements: A buffer containing the values to copy. + /// The memory region underlying `source` must be initialized. + /// The memory regions referenced by `source` and this buffer may overlap. + /// - Returns: A typed buffer of the initialized elements. The returned + /// buffer references memory starting at the same base address as this + /// buffer, and its count indicates the number of elements copied from + /// `source`. + @discardableResult + func moveInitializeMemory( + as type: T.Type, + fromElements source: UnsafeMutableBufferPointer + ) -> UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + return buffer.moveInitializeMemory(as: T.self, fromElements: source) + } + + /// Moves instances from an initialized source buffer slice into the + /// uninitialized memory referenced by this buffer, leaving the source memory + /// uninitialized and this buffer's memory initialized. + /// + /// The region of memory starting at this pointer and covering + /// `fromElements.count` instances of the buffer's `Element` type + /// must be uninitialized, or `Element` must be a trivial type. After + /// calling `moveInitialize(as:from:)`, the region is initialized and the + /// memory region underlying `source[..( + as type: T.Type, + fromElements source: Slice> + ) -> UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + return buffer.moveInitializeMemory(as: T.self, fromElements: source) + } + + /// Binds this buffer’s memory to the specified type and returns a typed buffer + /// of the bound memory. + /// + /// Use the `bindMemory(to:)` method to bind the memory referenced + /// by this buffer to the type `T`. The memory must be uninitialized or + /// initialized to a type that is layout compatible with `T`. If the memory + /// is uninitialized, it is still uninitialized after being bound to `T`. + /// + /// - Warning: A memory location may only be bound to one type at a time. The + /// behavior of accessing memory as a type unrelated to its bound type is + /// undefined. + /// + /// - Parameters: + /// - type: The type `T` to bind the memory to. + /// - Returns: A typed buffer of the newly bound memory. The memory in this + /// region is bound to `T`, but has not been modified in any other way. + /// The typed buffer references `self.count / MemoryLayout.stride` instances of `T`. + @discardableResult + public func bindMemory(to type: T.Type) -> UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + return buffer.bindMemory(to: T.self) + } + + /// Executes the given closure while temporarily binding the buffer to + /// instances of type `T`. + /// + /// Use this method when you have a buffer to raw memory and you need + /// to access that memory as instances of a given type `T`. Accessing + /// memory as a type `T` requires that the memory be bound to that type. + /// A memory location may only be bound to one type at a time, so accessing + /// the same memory as an unrelated type without first rebinding the memory + /// is undefined. + /// + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. The memory underlying any individual instance of `T` + /// must have the same initialization state (i.e. initialized or + /// uninitialized.) Accessing a `T` whose underlying memory + /// is in a mixed initialization state shall be undefined behaviour. + /// + /// If the byte count of the original buffer is not a multiple of + /// the stride of `T`, then the re-bound buffer is shorter + /// than the original buffer. + /// + /// After executing `body`, this method rebinds memory back to its original + /// binding state. This can be unbound memory, or bound to a different type. + /// + /// - Note: The buffer's base address must match the + /// alignment of `T` (as reported by `MemoryLayout.alignment`). + /// That is, `Int(bitPattern: self.baseAddress) % MemoryLayout.alignment` + /// must equal zero. + /// + /// - Note: A raw buffer may represent memory that has been bound to a type. + /// If that is the case, then `T` must be layout compatible with the + /// type to which the memory has been bound. This requirement does not + /// apply if the raw buffer represents memory that has not been bound + /// to any type. + /// + /// - Parameters: + /// - type: The type to temporarily bind the memory referenced by this + /// pointer. This pointer must be a multiple of this type's alignment. + /// - body: A closure that takes a typed pointer to the + /// same memory as this pointer, only bound to type `T`. The closure's + /// pointer argument is valid only for the duration of the closure's + /// execution. If `body` has a return value, that value is also used as + /// the return value for the `withMemoryRebound(to:capacity:_:)` method. + /// - buffer: The buffer temporarily bound to instances of `T`. + /// - Returns: The return value, if any, of the `body` closure parameter. + func withMemoryRebound( + to type: T.Type, _ body: (UnsafeMutableBufferPointer) throws -> Result + ) rethrows -> Result { + let buffer = Base(rebasing: self) + return try buffer.withMemoryRebound(to: T.self, body) + } + + /// Returns a typed buffer to the memory referenced by this buffer, + /// assuming that the memory is already bound to the specified type. + /// + /// Use this method when you have a raw buffer to memory that has already + /// been bound to the specified type. The memory starting at this pointer + /// must be bound to the type `T`. Accessing memory through the returned + /// pointer is undefined if the memory has not been bound to `T`. To bind + /// memory to `T`, use `bindMemory(to:capacity:)` instead of this method. + /// + /// - Note: The buffer's base address must match the + /// alignment of `T` (as reported by `MemoryLayout.alignment`). + /// That is, `Int(bitPattern: self.baseAddress) % MemoryLayout.alignment` + /// must equal zero. + /// + /// - Parameter to: The type `T` that the memory has already been bound to. + /// - Returns: A typed pointer to the same memory as this raw pointer. + func assumingMemoryBound(to type: T.Type) -> UnsafeMutableBufferPointer { + let buffer = Base(rebasing: self) + return buffer.assumingMemoryBound(to: T.self) + } +} + +extension Slice where Base == UnsafeRawBufferPointer { + + /// Binds this buffer’s memory to the specified type and returns a typed buffer + /// of the bound memory. + /// + /// Use the `bindMemory(to:)` method to bind the memory referenced + /// by this buffer to the type `T`. The memory must be uninitialized or + /// initialized to a type that is layout compatible with `T`. If the memory + /// is uninitialized, it is still uninitialized after being bound to `T`. + /// + /// - Warning: A memory location may only be bound to one type at a time. The + /// behavior of accessing memory as a type unrelated to its bound type is + /// undefined. + /// + /// - Parameters: + /// - type: The type `T` to bind the memory to. + /// - Returns: A typed buffer of the newly bound memory. The memory in this + /// region is bound to `T`, but has not been modified in any other way. + /// The typed buffer references `self.count / MemoryLayout.stride` instances of `T`. + @discardableResult + public func bindMemory(to type: T.Type) -> UnsafeBufferPointer { + let buffer = Base(rebasing: self) + return buffer.bindMemory(to: T.self) + } + + /// Executes the given closure while temporarily binding the buffer to + /// instances of type `T`. + /// + /// Use this method when you have a buffer to raw memory and you need + /// to access that memory as instances of a given type `T`. Accessing + /// memory as a type `T` requires that the memory be bound to that type. + /// A memory location may only be bound to one type at a time, so accessing + /// the same memory as an unrelated type without first rebinding the memory + /// is undefined. + /// + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. The memory underlying any individual instance of `T` + /// must have the same initialization state (i.e. initialized or + /// uninitialized.) Accessing a `T` whose underlying memory + /// is in a mixed initialization state shall be undefined behaviour. + /// + /// If the byte count of the original buffer is not a multiple of + /// the stride of `T`, then the re-bound buffer is shorter + /// than the original buffer. + /// + /// After executing `body`, this method rebinds memory back to its original + /// binding state. This can be unbound memory, or bound to a different type. + /// + /// - Note: The buffer's base address must match the + /// alignment of `T` (as reported by `MemoryLayout.alignment`). + /// That is, `Int(bitPattern: self.baseAddress) % MemoryLayout.alignment` + /// must equal zero. + /// + /// - Note: A raw buffer may represent memory that has been bound to a type. + /// If that is the case, then `T` must be layout compatible with the + /// type to which the memory has been bound. This requirement does not + /// apply if the raw buffer represents memory that has not been bound + /// to any type. + /// + /// - Parameters: + /// - type: The type to temporarily bind the memory referenced by this + /// pointer. This pointer must be a multiple of this type's alignment. + /// - body: A closure that takes a typed pointer to the + /// same memory as this pointer, only bound to type `T`. The closure's + /// pointer argument is valid only for the duration of the closure's + /// execution. If `body` has a return value, that value is also used as + /// the return value for the `withMemoryRebound(to:capacity:_:)` method. + /// - buffer: The buffer temporarily bound to instances of `T`. + /// - Returns: The return value, if any, of the `body` closure parameter. + func withMemoryRebound( + to type: T.Type, _ body: (UnsafeBufferPointer) throws -> Result + ) rethrows -> Result { + let buffer = Base(rebasing: self) + return try buffer.withMemoryRebound(to: T.self, body) + } + + /// Returns a typed buffer to the memory referenced by this buffer, + /// assuming that the memory is already bound to the specified type. + /// + /// Use this method when you have a raw buffer to memory that has already + /// been bound to the specified type. The memory starting at this pointer + /// must be bound to the type `T`. Accessing memory through the returned + /// pointer is undefined if the memory has not been bound to `T`. To bind + /// memory to `T`, use `bindMemory(to:capacity:)` instead of this method. + /// + /// - Note: The buffer's base address must match the + /// alignment of `T` (as reported by `MemoryLayout.alignment`). + /// That is, `Int(bitPattern: self.baseAddress) % MemoryLayout.alignment` + /// must equal zero. + /// + /// - Parameter to: The type `T` that the memory has already been bound to. + /// - Returns: A typed pointer to the same memory as this raw pointer. + func assumingMemoryBound(to type: T.Type) -> UnsafeBufferPointer { + let buffer = Base(rebasing: self) + return buffer.assumingMemoryBound(to: T.self) + } +} + +extension Slice: _BufferProtocol + where Base: _BufferProtocol & _RebasableCollection, Base.SubSequence == Self { + + /// Executes the given closure while temporarily binding the memory referenced + /// by this buffer slice to the given type. + /// + /// Use this method when you have a buffer of memory bound to one type and + /// you need to access that memory as a buffer of another type. Accessing + /// memory as type `T` requires that the memory be bound to that type. A + /// memory location may only be bound to one type at a time, so accessing + /// the same memory as an unrelated type without first rebinding the memory + /// is undefined. + /// + /// The number of instances of `T` referenced by the rebound buffer may be + /// different than the number of instances of `Element` referenced by the + /// original buffer slice. The number of instances of `T` will be calculated + /// at runtime. + /// + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. Every instance of `Pointee` overlapping with a given + /// instance of `T` should have the same initialization state (i.e. + /// initialized or uninitialized.) Accessing a `T` whose underlying + /// `Pointee` storage is in a mixed initialization state shall be + /// undefined behaviour. + /// + /// Because this range of memory is no longer bound to its `Element` type + /// while the `body` closure executes, do not access memory using the + /// original buffer slice from within `body`. Instead, + /// use the `body` closure's buffer argument to access the values + /// in memory as instances of type `T`. + /// + /// After executing `body`, this method rebinds memory back to the original + /// `Element` type. + /// + /// - Note: Only use this method to rebind the buffer's memory to a type + /// that is layout compatible with the currently bound `Element` type. + /// The stride of the temporary type (`T`) may be an integer multiple + /// or a whole fraction of `Element`'s stride. + /// To bind a region of memory to a type that does not match these + /// requirements, convert the buffer to a raw buffer and use the + /// `bindMemory(to:)` method. + /// If `T` and `Element` have different alignments, this buffer's + /// `baseAddress` must be aligned with the larger of the two alignments. + /// + /// - Parameters: + /// - type: The type to temporarily bind the memory referenced by this + /// buffer. The type `T` must be layout compatible + /// with the pointer's `Element` type. + /// - body: A closure that takes a typed buffer to the + /// same memory as this buffer, only bound to type `T`. The buffer + /// parameter contains a number of complete instances of `T` based + /// on the capacity of the original buffer and the stride of `Element`. + /// The closure's buffer argument is valid only for the duration of the + /// closure's execution. If `body` has a return value, that value + /// is also used as the return value for the `withMemoryRebound(to:_:)` + /// method. + /// - buffer: The buffer temporarily bound to `T`. + /// - Returns: The return value, if any, of the `body` closure parameter. + @inlinable + public func withMemoryRebound( + to type: T.Type, _ body: (UnsafeBufferPointer) throws -> Result + ) rethrows -> Result { + let buffer = Base(rebasing: self) + return try buffer.withMemoryRebound(to: T.self, body) + } +} + +extension Slice: _MutableBufferProtocol +where Base: _MutableBufferProtocol & _RebasableCollection & _MutableBaseAddressProtocol, + Base.SubSequence == Self { + + /// Initializes every element in this buffer slice's memory to + /// a copy of the given value. + /// + /// The destination memory must be uninitialized or the buffer's `Element` + /// must be a trivial type. After a call to `initialize(repeating:)`, the + /// entire region of memory referenced by this buffer slice is initialized. + /// + /// - Parameter repeatedValue: The value with which to initialize this + /// buffer slice's memory. + public func initialize(repeating repeatedValue: Base.Element) { + Base(rebasing: self).initialize(repeating: repeatedValue) + } + + /// Initializes the buffer slice's memory with the given elements. + /// + /// Prior to calling the `initialize(from:)` method on a buffer slice, + /// the memory it references must be uninitialized, + /// or the `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. + /// The buffer must contain sufficient memory to accommodate + /// `source.underestimatedCount`. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer slice, which is one past the last element written. + /// If `source` contains no elements, the returned index is equal to + /// the buffer's `startIndex`. If `source` contains an equal or greater number + /// of elements than the buffer slice can hold, the returned index is equal to + /// the buffer's `endIndex`. + /// + /// - Parameter source: A sequence of elements with which to initialize the + /// buffer. + /// - Returns: An iterator to any elements of `source` that didn't fit in the + /// buffer, and an index to the next uninitialized element in the buffer. + public func initialize( + from source: S + ) -> (S.Iterator, Index) where S: Sequence, Base.Element == S.Element { + let buffer = Base(rebasing: self) + let (iterator, index) = buffer.initialize(from: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return (iterator, startIndex.advanced(by: distance)) + } + + /// Initializes the buffer slice's memory with the given elements. + /// + /// Prior to calling the `initialize(fromElements:)` method on a buffer slice, + /// the memory it references must be uninitialized, + /// or the `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer slice, which is one past the last element written. + /// If `fromElements` contains no elements, the returned index is equal to + /// the buffer's `startIndex`. If `fromElements` contains an equal or greater + /// of elements than the buffer slice can hold, the returned index is equal to + /// to the buffer's `endIndex`. + /// + /// - Parameter fromElements: A collection of elements to be used to + /// initialize the buffer's storage. + /// - Returns: An index to the next uninitialized element in the buffer, + /// or `endIndex`. + @discardableResult + public func initialize( + fromElements source: C + ) -> Int where C : Collection, Base.Element == C.Element { + let buffer = Base(rebasing: self) + let index = buffer.initialize(fromElements: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Updates every element of this buffer slice's initialized memory. + /// + /// The buffer slice’s memory must be initialized or its `Element` + /// must be a trivial type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Parameters: + /// - repeatedValue: The value used when updating this pointer's memory. + public func update(repeating repeatedValue: Base.Element) { + Base(rebasing: self).update(repeating: repeatedValue) + } + + /// Updates the buffer slice's initialized memory with the given elements. + /// + /// The buffer slice's memory must be initialized or its `Element` type + /// must be a trivial type. + /// + /// - Parameter source: A sequence of elements to be used to update + /// the buffer's contents. + /// - Returns: An iterator to any elements of `source` that didn't fit in the + /// buffer, and the index one past the last updated element in the buffer. + public func update( + from source: S + ) -> (unwritten: S.Iterator, updated: Index) where S.Element == Element { + let buffer = Base(rebasing: self) + let (iterator, index) = buffer.update(from: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return (iterator, startIndex.advanced(by: distance)) + } + + /// Updates the buffer slice's initialized memory with the given elements. + /// + /// The buffer slice's memory must be initialized or the buffer's `Element` type + /// must be a trivial type. + /// + /// - Parameter fromElements: A collection of elements to be used to update + /// the buffer's contents. + /// - Returns: An index one past the last updated element in the buffer, + /// or `endIndex`. + @discardableResult + public func update( + fromElements source: C + ) -> Index where Base.Element == C.Element { + let buffer = Base(rebasing: self) + let index = buffer.update(fromElements: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Moves every element of an initialized source buffer into the + /// uninitialized memory referenced by this buffer slice, leaving the + /// source memory uninitialized and this buffer slice's memory initialized. + /// + /// The region of memory starting at the beginning of this buffer and + /// covering `source.count` instances of its `Element` type must be + /// uninitialized, or `Element` must be a trivial type. After calling + /// `moveInitialize(fromElements:)`, the region is initialized and + /// the region of memory underlying `source` is uninitialized. + /// + /// - Parameter source: A buffer containing the values to copy. The memory + /// region underlying `source` must be initialized. The memory regions + /// referenced by `source` and this buffer may overlap. + /// - Returns: An index to the next uninitialized element in the buffer, + /// or `endIndex`. + @discardableResult + public func moveInitialize( + fromElements source: UnsafeMutableBufferPointer + ) -> Index { + let buffer = Base(rebasing: self) + let index = buffer.moveInitialize(fromElements: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Moves every element of an initialized source buffer slice into the + /// uninitialized memory referenced by this buffer slice, leaving the + /// source memory uninitialized and this buffer slice's memory initialized. + /// + /// The region of memory starting at the beginning of this buffer slice and + /// covering `source.count` instances of its `Element` type must be + /// uninitialized, or `Element` must be a trivial type. After calling + /// `moveInitialize(fromElements:)`, the region is initialized and + /// the region of memory underlying `source` is uninitialized. + /// + /// - Parameter source: A buffer containing the values to copy. The memory + /// region underlying `source` must be initialized. The memory regions + /// referenced by `source` and this buffer may overlap. + /// - Returns: An index one past the last replaced element in the buffer, + /// or `endIndex`. + @discardableResult + public func moveInitialize( + fromElements source: Slice> + ) -> Index { + let buffer = Base(rebasing: self) + let index = buffer.moveInitialize(fromElements: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Updates this buffer slice's initialized memory initialized memory by + /// moving every element from the source buffer, + /// leaving the source memory uninitialized. + /// + /// The region of memory starting at the beginning of this buffer slice and + /// covering `fromElements.count` instances of its `Element` type must be + /// initialized, or `Element` must be a trivial type. After calling + /// `moveUpdate(fromElements:)`, + /// the region of memory underlying `source` is uninitialized. + /// + /// - Parameter source: A buffer containing the values to move. + /// The memory region underlying `source` must be initialized. The + /// memory regions referenced by `source` and this pointer must not overlap. + /// - Returns: An index one past the last updated element in the buffer, + /// or `endIndex`. + @discardableResult + public func moveUpdate( + fromElements source: UnsafeMutableBufferPointer + ) -> Index { + let buffer = Base(rebasing: self) + let index = buffer.moveUpdate(fromElements: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Updates this buffer slice's initialized memory initialized memory by + /// moving every element from the source buffer slice, + /// leaving the source memory uninitialized. + /// + /// The region of memory starting at the beginning of this buffer slice and + /// covering `fromElements.count` instances of its `Element` type must be + /// initialized, or `Element` must be a trivial type. After calling + /// `moveUpdate(fromElements:)`, + /// the region of memory underlying `source` is uninitialized. + /// + /// - Parameter source: A buffer containing the values to move. + /// The memory region underlying `source` must be initialized. The + /// memory regions referenced by `source` and this pointer must not overlap. + /// - Returns: An index one past the last updated element in the buffer, + /// or `endIndex`. + @discardableResult + public func moveUpdate( + fromElements source: Slice> + ) -> Index { + let buffer = Base(rebasing: self) + let index = buffer.moveUpdate(fromElements: source) + let distance = buffer.distance(from: buffer.startIndex, to: index) + return startIndex.advanced(by: distance) + } + + /// Deinitializes every instance in this buffer slice. + /// + /// The region of memory underlying this buffer slice must be fully + /// initialized. After calling `deinitialize(count:)`, the memory + /// is uninitialized, but still bound to the `Element` type. + /// + /// - Note: All buffer elements must already be initialized. + /// + /// - Returns: A raw buffer to the same range of memory as this buffer. + /// The range of memory is still bound to `Element`. + @discardableResult + public func deinitialize() -> UnsafeMutableRawBufferPointer { + Base(rebasing: self).deinitialize() + } + + /// Initializes the element at `index` to the given value. + /// + /// The memory underlying the destination element must be uninitialized, + /// or `Element` must be a trivial type. After a call to `initialize(to:)`, + /// the memory underlying this element of the buffer slice is initialized. + /// + /// - Parameters: + /// - value: The value used to initialize the buffer element's memory. + /// - index: The index of the element to initialize + public func initializeElement(at index: Int, to value: Element) { + assert(startIndex <= index && index < endIndex) + base.baseAddress.unsafelyUnwrapped.advanced(by: index).initialize(to: value) + } + + /// Updates the initialized element at `index` to the given value. + /// + /// The memory underlying the destination element must be initialized, + /// or `Element` must be a trivial type. This method is equivalent to: + /// + /// self[index] = value + /// + /// - Parameters: + /// - value: The value used to update the buffer element's memory. + /// - index: The index of the element to update + public func updateElement(at index: Index, to value: Element) { + assert(startIndex <= index && index < endIndex) + base.baseAddress.unsafelyUnwrapped.advanced(by: index).pointee = value + } + + /// Retrieves and returns the element at `index`, + /// leaving that element's underlying memory uninitialized. + /// + /// The memory underlying the element at `index` must be initialized. + /// After calling `moveElement(from:)`, the memory underlying this element + /// of the buffer slice is uninitialized, and still bound to type `Element`. + /// + /// - Parameters: + /// - index: The index of the buffer element to retrieve and deinitialize. + /// - Returns: The instance referenced by this index in this buffer. + public func moveElement(from index: Index) -> Element { + assert(startIndex <= index && index < endIndex) + return base.baseAddress.unsafelyUnwrapped.advanced(by: index).move() + } + + /// Deinitializes the memory underlying the element at `index`. + /// + /// The memory underlying the element at `index` must be initialized. + /// After calling `deinitializeElement()`, the memory underlying this element + /// of the buffer slice is uninitialized, and still bound to type `Element`. + /// + /// - Parameters: + /// - index: The index of the buffer element to deinitialize. + public func deinitializeElement(at index: Base.Index) { + assert(startIndex <= index && index < endIndex) + base.baseAddress.unsafelyUnwrapped.advanced(by: index).deinitialize(count: 1) + } + + /// Executes the given closure while temporarily binding the memory referenced + /// by this buffer slice to the given type. + /// + /// Use this method when you have a buffer of memory bound to one type and + /// you need to access that memory as a buffer of another type. Accessing + /// memory as type `T` requires that the memory be bound to that type. A + /// memory location may only be bound to one type at a time, so accessing + /// the same memory as an unrelated type without first rebinding the memory + /// is undefined. + /// + /// The number of instances of `T` referenced by the rebound buffer may be + /// different than the number of instances of `Element` referenced by the + /// original buffer slice. The number of instances of `T` will be calculated + /// at runtime. + /// + /// Any instance of `T` within the re-bound region may be initialized or + /// uninitialized. Every instance of `Pointee` overlapping with a given + /// instance of `T` should have the same initialization state (i.e. + /// initialized or uninitialized.) Accessing a `T` whose underlying + /// `Pointee` storage is in a mixed initialization state shall be + /// undefined behaviour. + /// + /// Because this range of memory is no longer bound to its `Element` type + /// while the `body` closure executes, do not access memory using the + /// original buffer slice from within `body`. Instead, + /// use the `body` closure's buffer argument to access the values + /// in memory as instances of type `T`. + /// + /// After executing `body`, this method rebinds memory back to the original + /// `Element` type. + /// + /// - Note: Only use this method to rebind the buffer's memory to a type + /// that is layout compatible with the currently bound `Element` type. + /// The stride of the temporary type (`T`) may be an integer multiple + /// or a whole fraction of `Element`'s stride. + /// To bind a region of memory to a type that does not match these + /// requirements, convert the buffer to a raw buffer and use the + /// `bindMemory(to:)` method. + /// If `T` and `Element` have different alignments, this buffer's + /// `baseAddress` must be aligned with the larger of the two alignments. + /// + /// - Parameters: + /// - type: The type to temporarily bind the memory referenced by this + /// buffer. The type `T` must be layout compatible + /// with the pointer's `Element` type. + /// - body: A closure that takes a ${Mutable.lower()} typed buffer to the + /// same memory as this buffer, only bound to type `T`. The buffer + /// parameter contains a number of complete instances of `T` based + /// on the capacity of the original buffer and the stride of `Element`. + /// The closure's buffer argument is valid only for the duration of the + /// closure's execution. If `body` has a return value, that value + /// is also used as the return value for the `withMemoryRebound(to:_:)` + /// method. + /// - buffer: The buffer temporarily bound to `T`. + /// - Returns: The return value, if any, of the `body` closure parameter. + @inlinable + public func withMemoryRebound( + to type: T.Type, _ body: (UnsafeMutableBufferPointer) throws -> Result + ) rethrows -> Result { + try Base(rebasing: self).withMemoryRebound(to: T.self, body) + } + + @inlinable + func withContiguousMutableStorageIfAvailable( + _ body: (_ buffer: inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R? { + try base.withContiguousStorageIfAvailable { buffer in + let start = base.baseAddress?.advanced(by: startIndex) + var slice = UnsafeMutableBufferPointer(start: start, count: count) + let (b,c) = (slice.baseAddress, slice.count) + defer { + precondition( + slice.baseAddress == b && slice.count == c, + "Slice.withContiguousMutableStorageIfAvailable: " + + "replacing the buffer is not allowed") + } + return try body(&slice) + } + } +}