//===--- ArrayBufferProtocol.swift ----------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// The underlying buffer for an ArrayType conforms to /// `_ArrayBufferProtocol`. This buffer does not provide value semantics. @usableFromInline internal protocol _ArrayBufferProtocol : MutableCollection, RandomAccessCollection where Indices == Range { /// Create an empty buffer. init() /// Adopt the entire buffer, presenting it at the provided `startIndex`. init(_buffer: _ContiguousArrayBuffer, shiftedToStartIndex: Int) init(copying buffer: Self) /// Copy the elements in `bounds` from this buffer into uninitialized /// memory starting at `target`. Return a pointer "past the end" of the /// just-initialized memory. @discardableResult __consuming func _copyContents( subRange bounds: Range, initializing target: UnsafeMutablePointer ) -> UnsafeMutablePointer /// If this buffer is backed by a uniquely-referenced mutable /// `_ContiguousArrayBuffer` that can be grown in-place to allow the `self` /// buffer store `minimumCapacity` elements, returns that buffer. /// Otherwise, returns `nil`. /// /// - Note: The result's firstElementAddress may not match ours, if we are a /// _SliceBuffer. /// /// - Note: This function must remain mutating; otherwise the buffer /// may acquire spurious extra references, which will cause /// unnecessary reallocation. mutating func requestUniqueMutableBackingBuffer( minimumCapacity: Int ) -> _ContiguousArrayBuffer? /// Returns `true` if this buffer is backed by a uniquely-referenced mutable /// _ContiguousArrayBuffer; otherwise, returns `false`. /// /// - Note: This function must remain mutating; otherwise the buffer /// may acquire spurious extra references, which will cause /// unnecessary reallocation. mutating func isMutableAndUniquelyReferenced() -> Bool /// If this buffer is backed by a `_ContiguousArrayBuffer` /// containing the same number of elements as `self`, return it. /// Otherwise, return `nil`. func requestNativeBuffer() -> _ContiguousArrayBuffer? /// Replace the given `subRange` with the first `newCount` elements of /// the given collection. /// /// - Precondition: This buffer is backed by a uniquely-referenced /// `_ContiguousArrayBuffer`. mutating func replaceSubrange( _ subrange: Range, with newCount: Int, elementsOf newValues: __owned C ) where C: Collection, C.Element == Element /// Returns a `_SliceBuffer` containing the elements in `bounds`. subscript(bounds: Range) -> _SliceBuffer { get } /// Call `body(p)`, where `p` is an `UnsafeBufferPointer` over the /// underlying contiguous storage. If no such storage exists, it is /// created on-demand. func withUnsafeBufferPointer( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R /// Call `body(p)`, where `p` is an `UnsafeMutableBufferPointer` /// over the underlying contiguous storage. /// /// - Precondition: Such contiguous storage exists or the buffer is empty. mutating func withUnsafeMutableBufferPointer( _ body: (UnsafeMutableBufferPointer) throws -> R ) rethrows -> R /// The number of elements the buffer stores. override var count: Int { get set } /// The number of elements the buffer can store without reallocation. var capacity: Int { get } /// An object that keeps the elements stored in this buffer alive. var owner: AnyObject { get } /// A pointer to the first element. /// /// - Precondition: The elements are known to be stored contiguously. var firstElementAddress: UnsafeMutablePointer { get } /// If the elements are stored contiguously, a pointer to the first /// element. Otherwise, `nil`. var firstElementAddressIfContiguous: UnsafeMutablePointer? { get } /// Returns a base address to which you can add an index `i` to get the /// address of the corresponding element at `i`. var subscriptBaseAddress: UnsafeMutablePointer { get } /// A value that identifies the storage used by the buffer. Two /// buffers address the same elements when they have the same /// identity and count. var identity: UnsafeRawPointer { get } } extension _ArrayBufferProtocol where Indices == Range{ @inlinable internal var subscriptBaseAddress: UnsafeMutablePointer { return firstElementAddress } // Make sure the compiler does not inline _copyBuffer to reduce code size. @inline(never) @inlinable // This code should be specializable such that copying an array is // fast and does not end up in an unspecialized entry point. internal init(copying buffer: Self) { let newBuffer = _ContiguousArrayBuffer( _uninitializedCount: buffer.count, minimumCapacity: buffer.count) buffer._copyContents( subRange: buffer.indices, initializing: newBuffer.firstElementAddress) self = Self( _buffer: newBuffer, shiftedToStartIndex: buffer.startIndex) } @inlinable internal mutating func replaceSubrange( _ subrange: Range, with newCount: Int, elementsOf newValues: __owned C ) where C: Collection, C.Element == Element { _internalInvariant(startIndex == 0, "_SliceBuffer should override this function.") let oldCount = self.count let eraseCount = subrange.count let growth = newCount - eraseCount // This check will prevent storing a 0 count to the empty array singleton. if growth != 0 { self.count = oldCount + growth } let elements = self.subscriptBaseAddress let oldTailIndex = subrange.upperBound let oldTailStart = elements + oldTailIndex let newTailIndex = oldTailIndex + growth let newTailStart = oldTailStart + growth let tailCount = oldCount - subrange.upperBound if growth > 0 { // Slide the tail part of the buffer forwards, in reverse order // so as not to self-clobber. newTailStart.moveInitialize(from: oldTailStart, count: tailCount) // Assign over the original subrange var i = newValues.startIndex for j in subrange { elements[j] = newValues[i] newValues.formIndex(after: &i) } // Initialize the hole left by sliding the tail forward for j in oldTailIndex.. shrinkage { // If the tail length exceeds the shrinkage // Assign over the rest of the replaced range with the first // part of the tail. newTailStart.moveAssign(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) // Destroy elements remaining after the tail in subrange (newTailStart + tailCount).deinitialize( count: shrinkage - tailCount) } } } }