//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2024 - 2025 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 // //===----------------------------------------------------------------------===// /// A fixed-size array. /// /// An `InlineArray` is a specialized container that doesn't use a separate /// memory allocation just to store its elements. When a value is copied, all of /// its elements are copied eagerly, like those of a tuple. Use an `InlineArray` /// when you have a fixed number of elements and need to avoid a separate heap /// allocation. /// /// Initializing a Value /// -------------------- /// /// When initializing a new `InlineArray` value, you must initialize all of its /// elements. You can use an array literal just as with `Array`, rely on type /// inference for the `count` and `Element` type, and spell the type with the /// shorthand `[count of Element]`. /// /// let a: InlineArray<3, Int> = [1, 2, 3] /// let b: InlineArray<_, Int> = [1, 2, 3] /// let c: InlineArray<3, _> = [1, 2, 3] /// let d: InlineArray = [1, 2, 3] /// /// let e: [3 of Int] = [1, 2, 3] /// let f: [_ of Int] = [1, 2, 3] /// let g: [3 of _] = [1, 2, 3] /// let h: [_ of _] = [1, 2, 3] /// /// You can also use one of the type's initializers to create a new value. /// /// Accessing Elements /// ------------------ /// /// Just as with `Array`, you can read and modify an element in an `InlineArray` /// using a subscript. Unless you use the memory-unsafe `unchecked` subscript, /// any index you provide is subject to bounds checking; an invalid index /// triggers a runtime error in your program. /// /// var values: [3 of Double] = [1, 1.5, 2] /// print(values[0]) // Prints "1.0" /// values[1] -= 0.25 /// print(values[1]) // Prints "1.25" /// values[3] = 42.0 // Fatal error: Index out of bounds /// /// You can use the `indices` property to iterate over all elements in order. /// /// for index in values.indices { /// print(values[index]) /// } /// /// Working with Noncopyable Elements /// --------------------------------- /// /// An `InlineArray` can store elements of potentially noncopyable type. When /// `Element` isn't copyable, the `InlineArray` itself also isn't copyable. You /// must then explicitly move or consume the value if you want to transfer /// ownership. /// /// Memory Layout /// ------------- /// /// An `InlineArray` stores its elements contiguously. If an `InlineArray` is a /// stored property of a class, then it's allocated on the heap along with the /// other stored properties of the class. Otherwise, in general, an /// `InlineArray` is allocated on the stack. /// /// A *non-empty* `InlineArray`'s size and stride are both found by multiplying /// the `count` of elements by the `Element`'s stride. Its alignment is equal to /// the `Element`'s alignment. /// /// struct Record { /// let x: UInt32 /// let y: Bool /// } /// MemoryLayout.size // 5 /// MemoryLayout.stride // 8 /// MemoryLayout.alignment // 4 /// MemoryLayout<[2 of Record]>.size // 16 /// MemoryLayout<[2 of Record]>.stride // 16 /// MemoryLayout<[2 of Record]>.alignment // 4 /// MemoryLayout<(Record, Record)>.size // 13 /// MemoryLayout<(Record, Record)>.stride // 16 /// MemoryLayout<(Record, Record)>.alignment // 4 /// /// An *empty* `InlineArray`'s size is zero. Its stride and alignment are both /// one byte. @available(SwiftStdlib 6.2, *) @frozen @safe @_addressableForDependencies public struct InlineArray: ~Copyable { @usableFromInline internal var _storage: Builtin.FixedArray } @available(SwiftStdlib 6.2, *) extension InlineArray: Copyable where Element: Copyable {} @available(SwiftStdlib 6.2, *) extension InlineArray: BitwiseCopyable where Element: BitwiseCopyable {} @available(SwiftStdlib 6.2, *) extension InlineArray: @unchecked Sendable where Element: Sendable & ~Copyable {} //===----------------------------------------------------------------------===// // MARK: - Address & Buffer //===----------------------------------------------------------------------===// @available(SwiftStdlib 6.2, *) extension InlineArray where Element: ~Copyable { /// Returns a pointer to the first element in the array. @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent internal var _address: UnsafePointer { #if $AddressOfProperty2 unsafe UnsafePointer(Builtin.unprotectedAddressOfBorrow(_storage)) #else unsafe UnsafePointer(Builtin.unprotectedAddressOfBorrow(self)) #endif } /// Returns a buffer pointer over the entire array. @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent internal var _buffer: UnsafeBufferPointer { unsafe UnsafeBufferPointer(start: _address, count: count) } /// Returns a pointer to the first element in the array while performing stack /// checking. /// /// Use this when the value of the pointer could potentially be directly used /// by users (e.g. through the use of span or the unchecked subscript). @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent internal var _protectedAddress: UnsafePointer { #if $AddressOfProperty2 unsafe UnsafePointer(Builtin.addressOfBorrow(_storage)) #else unsafe UnsafePointer(Builtin.addressOfBorrow(self)) #endif } /// Returns a buffer pointer over the entire array while performing stack /// checking. /// /// Use this when the value of the pointer could potentially be directly used /// by users (e.g. through the use of span or the unchecked subscript). @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent internal var _protectedBuffer: UnsafeBufferPointer { unsafe UnsafeBufferPointer(start: _protectedAddress, count: count) } /// Returns a mutable pointer to the first element in the array. @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent internal var _mutableAddress: UnsafeMutablePointer { mutating get { #if $AddressOfProperty2 unsafe UnsafeMutablePointer(Builtin.unprotectedAddressOf(&_storage)) #else unsafe UnsafeMutablePointer(Builtin.unprotectedAddressOf(&self)) #endif } } /// Returns a mutable buffer pointer over the entire array. @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent internal var _mutableBuffer: UnsafeMutableBufferPointer { mutating get { unsafe UnsafeMutableBufferPointer( start: _mutableAddress, count: count ) } } /// Returns a mutable pointer to the first element in the array while /// performing stack checking. /// /// Use this when the value of the pointer could potentially be directly used /// by users (e.g. through the use of span or the unchecked subscript). @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent internal var _protectedMutableAddress: UnsafeMutablePointer { mutating get { #if $AddressOfProperty2 unsafe UnsafeMutablePointer(Builtin.addressof(&_storage)) #else unsafe UnsafeMutablePointer(Builtin.addressof(&self)) #endif } } /// Returns a mutable buffer pointer over the entire array while performing /// stack checking. /// /// Use this when the value of the pointer could potentially be directly used /// by users (e.g. through the use of span or the unchecked subscript). @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent internal var _protectedMutableBuffer: UnsafeMutableBufferPointer { mutating get { unsafe UnsafeMutableBufferPointer( start: _protectedMutableAddress, count: count ) } } /// Converts the given raw pointer, which points at an uninitialized array /// instance, to a mutable buffer suitable for initialization. @available(SwiftStdlib 6.2, *) @unsafe @_alwaysEmitIntoClient @_transparent internal static func _initializationBuffer( start: Builtin.RawPointer ) -> UnsafeMutableBufferPointer { unsafe UnsafeMutableBufferPointer( start: UnsafeMutablePointer(start), count: count ) } } //===----------------------------------------------------------------------===// // MARK: - Initialization APIs //===----------------------------------------------------------------------===// @available(SwiftStdlib 6.2, *) extension InlineArray where Element: ~Copyable { /// Initializes every element in this array, by calling the given closure /// with each index. /// /// This will call the closure `count` times, where `count` is the static /// count of the array, to initialize every element by passing the closure /// the index of the current element being initialized. /// /// InlineArray<4, Int> { $0 * 2 } // [0, 2, 4, 6] /// /// The closure is allowed to throw an error at any point during /// initialization at which point the array will stop initialization, /// deinitialize every currently initialized element, and throw the given /// error back out to the caller. /// /// - Parameter body: A closure that returns an owned `Element` to emplace at /// the passed in index. @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient public init(_ body: (Index) throws(E) -> Element) throws(E) { _storage = try Builtin.emplace { (rawPtr) throws(E) -> () in let buffer = unsafe Self._initializationBuffer(start: rawPtr) for i in 0 ..< count { do throws(E) { try unsafe buffer.initializeElement(at: i, to: body(i)) } catch { // The closure threw an error. We need to deinitialize every element // we've initialized up to this point. for j in 0 ..< i { unsafe buffer.deinitializeElement(at: j) } // Throw the error we were given back out to the caller. throw error } } } } /// Initializes every element in this array, by calling the given closure /// with each preceding element. /// /// This will call the closure `count - 1` times, where `count` is the static /// count of the array, to initialize every element by passing the closure an /// immutable borrow reference to the preceding element. /// /// InlineArray<4, Int>(first: 1) { $0 * 2 } // [1, 2, 4, 8] /// /// The closure is allowed to throw an error at any point during /// initialization at which point the array will stop initialization, /// deinitialize every currently initialized element, and throw the given /// error back out to the caller. /// /// - Parameters: /// - first: The first value to emplace into the array. /// - next: A closure that takes an immutable borrow reference to the /// preceding element, and returns an owned `Element` instance to emplace /// into the array. @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient public init( first: consuming Element, next: (borrowing Element) throws(E) -> Element ) throws(E) { // FIXME: We should be able to mark 'Builtin.emplace' as '@once' or something // to give the compiler enough information to know we will only run // it once so it can consume the capture. For now, we use an optional // and take the underlying value within the closure. var o: Element? = first _storage = try Builtin.emplace { (rawPtr) throws(E) -> () in let buffer = unsafe Self._initializationBuffer(start: rawPtr) guard Self.count > 0 else { return } unsafe buffer.initializeElement( at: 0, to: o.take()._consumingUncheckedUnwrapped() ) for i in 1 ..< count { do throws(E) { try unsafe buffer.initializeElement(at: i, to: next(buffer[i &- 1])) } catch { // The closure threw an error. We need to deinitialize every element // we've initialized up to this point. for j in 0 ..< i { unsafe buffer.deinitializeElement(at: j) } // Throw the error we were given back out to the caller. throw error } } } } @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient public init( initializingWith initializer: (inout OutputSpan) throws(E) -> Void ) throws(E) { _storage = try Builtin.emplace { (rawPtr) throws(E) -> () in let buffer = unsafe Self._initializationBuffer(start: rawPtr) _internalInvariant(Self.count == buffer.count) var output = unsafe OutputSpan(buffer: buffer, initializedCount: 0) try initializer(&output) let initialized = unsafe output.finalize(for: buffer) _precondition(count == initialized, "InlineArray initialization underflow") } } } @available(SwiftStdlib 6.2, *) extension InlineArray where Element: Copyable { /// Initializes every element in this array to a copy of the given value. /// /// - Parameter value: The instance to initialize this array with. @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient public init(repeating value: Element) { _storage = Builtin.emplace { let buffer = unsafe Self._initializationBuffer(start: $0) unsafe buffer.initialize(repeating: value) } } } //===----------------------------------------------------------------------===// // MARK: - Collection APIs //===----------------------------------------------------------------------===// @available(SwiftStdlib 6.2, *) extension InlineArray where Element: ~Copyable { /// The type of the array's elements. @available(SwiftStdlib 6.2, *) public typealias Element = Element /// A type that represents a position in the array. /// /// Valid indices consist of the position of every element and a /// "past the end" position that's not valid for use as a subscript /// argument. @available(SwiftStdlib 6.2, *) public typealias Index = Int /// The number of elements in the array. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_semantics("fixed_storage.get_count") @inline(__always) public var count: Int { count } /// A Boolean value indicating whether the array is empty. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent public var isEmpty: Bool { count == 0 } /// The position of the first element in a nonempty array. /// /// If the array is empty, `startIndex` is equal to `endIndex`. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent public var startIndex: Index { 0 } /// The array's "past the end" position---that is, the position one greater /// than the last valid subscript argument. /// /// If the array is empty, `endIndex` is equal to `startIndex`. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent public var endIndex: Index { count } /// The indices that are valid for subscripting the array, in ascending order. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent public var indices: Range { unsafe Range(_uncheckedBounds: (0, count)) } /// Returns the position immediately after the given index. /// /// - Parameter i: A valid index of the array. `i` must be less than /// `endIndex`. /// - Returns: The index immediately after `i`. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent public borrowing func index(after i: Index) -> Index { i &+ 1 } /// Returns the position immediately before the given index. /// /// - Parameter i: A valid index of the array. `i` must be greater than /// `startIndex`. /// - Returns: The index value immediately before `i`. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @_transparent public borrowing func index(before i: Index) -> Index { i &- 1 } @_alwaysEmitIntoClient @_semantics("fixed_storage.check_index") @inline(__always) internal func _checkIndex(_ i: Index) { _precondition(indices.contains(i), "Index out of bounds") } /// Accesses the element at the specified position. /// /// - Parameter i: The position of the element to access. `i` must be a valid /// index of the array that is not equal to the `endIndex` property. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_addressableSelf @_alwaysEmitIntoClient public subscript(_ i: Index) -> Element { @_transparent unsafeAddress { _checkIndex(i) return unsafe _address + i } @_transparent unsafeMutableAddress { _checkIndex(i) return unsafe _mutableAddress + i } } /// Accesses the element at the specified position. /// /// - Warning: This subscript trades safety for performance. Using an invalid /// index results in undefined behavior. /// /// - Parameter i: The position of the element to access. `i` must be a valid /// index of the array that is not equal to the `endIndex` property. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_addressableSelf @_alwaysEmitIntoClient @unsafe public subscript(unchecked i: Index) -> Element { @_transparent unsafeAddress { unsafe _protectedAddress + i } @_transparent unsafeMutableAddress { unsafe _protectedMutableAddress + i } } } //===----------------------------------------------------------------------===// // MARK: - MutableCollection APIs //===----------------------------------------------------------------------===// @available(SwiftStdlib 6.2, *) extension InlineArray where Element: ~Copyable { /// Exchanges the values at the specified indices of the array. /// /// Both parameters must be valid indices of the array and not equal to /// `endIndex`. Passing the same index as both `i` and `j` has no effect. /// /// - Parameters: /// - i: The index of the first value to swap. /// - j: The index of the second value to swap. /// /// - Complexity: O(1) @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient public mutating func swapAt( _ i: Index, _ j: Index ) { guard i != j else { return } _checkIndex(i) _checkIndex(j) let ithElement = unsafe _mutableBuffer.moveElement(from: i) let jthElement = unsafe _mutableBuffer.moveElement(from: j) unsafe _mutableBuffer.initializeElement(at: i, to: jthElement) unsafe _mutableBuffer.initializeElement(at: j, to: ithElement) } } //===----------------------------------------------------------------------===// // MARK: - Span APIs //===----------------------------------------------------------------------===// @available(SwiftStdlib 6.2, *) extension InlineArray where Element: ~Copyable { @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient public var span: Span { @lifetime(borrow self) @_transparent borrowing get { guard count > 0 else { let span = Span() return unsafe _overrideLifetime(span, borrowing: self) } let span = unsafe Span(_unsafeStart: _protectedAddress, count: count) return unsafe _overrideLifetime(span, borrowing: self) } } @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient public var mutableSpan: MutableSpan { @lifetime(&self) @_transparent mutating get { guard count > 0 else { let span = MutableSpan() return unsafe _overrideLifetime(span, mutating: &self) } let span = unsafe MutableSpan( _unsafeStart: _protectedMutableAddress, count: count ) return unsafe _overrideLifetime(span, mutating: &self) } } }