import StdlibUnittest fileprivate var COWLoggingArray_CopyCount = 0 public func expectNoCopyOnWrite( _ elements: [T], _ message: @autoclosure () -> String = "", stackTrace: SourceLocStack = SourceLocStack(), showFrame: Bool = true, file: String = #file, line: UInt = #line, _ body: (inout COWLoggingArray) -> Void ) { let copyCountBeforeBody = COWLoggingArray_CopyCount var loggingArray = COWLoggingArray(elements) body(&loggingArray) expectEqual(copyCountBeforeBody, COWLoggingArray_CopyCount, message(), stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) } public struct COWLoggingArray { var storage: Storage class Storage { var buffer: UnsafeMutableBufferPointer var count: Int var capacity: Int { buffer.count } init(capacity: Int) { self.buffer = .allocate(capacity: capacity) self.count = 0 } deinit { buffer.baseAddress!.deinitialize(count: count) buffer.deallocate() } func cloned(capacity: Int? = nil) -> Storage { let newCapacity = Swift.max(capacity ?? self.capacity, self.capacity) let newStorage = Storage(capacity: newCapacity) newStorage.buffer.baseAddress! .initialize(from: buffer.baseAddress!, count: count) newStorage.count = count return newStorage } } mutating func _makeUnique() { if !isKnownUniquelyReferenced(&storage) { storage = storage.cloned() COWLoggingArray_CopyCount += 1 } } } extension COWLoggingArray: RandomAccessCollection, RangeReplaceableCollection, MutableCollection, ExpressibleByArrayLiteral { public var count: Int { storage.count } public var startIndex: Int { 0 } public var endIndex: Int { count } public subscript(i: Int) -> Element { get { storage.buffer[i] } set { _makeUnique() storage.buffer[i] = newValue } } public init() { storage = Storage(capacity: 10) } public mutating func reserveCapacity(_ n: Int) { if !isKnownUniquelyReferenced(&storage) { COWLoggingArray_CopyCount += 1 storage = storage.cloned(capacity: n) } else if count < n { storage = storage.cloned(capacity: n) } } public mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C : Collection, Element == C.Element { _makeUnique() let newCount = (count - subrange.count) + newElements.count if newCount > storage.capacity { storage = storage.cloned(capacity: newCount) } let startOfSubrange = storage.buffer.baseAddress! + subrange.lowerBound let endOfSubrange = startOfSubrange + subrange.count let endOfNewElements = startOfSubrange + newElements.count let countAfterSubrange = count - subrange.upperBound // clear out old elements startOfSubrange.deinitialize(count: subrange.count) // move elements above subrange endOfNewElements.moveInitialize(from: endOfSubrange, count: countAfterSubrange) // assign new elements for (pointer, element) in zip(startOfSubrange..., newElements) { pointer.initialize(to: element) } // update count storage.count = newCount } public init(arrayLiteral elements: Element...) { storage = Storage(capacity: elements.count) replaceSubrange(0..<0, with: elements) } }