blob: 70d89007e3daaa245cebf44a374e3cb00323e325 [file] [log] [blame]
import StdlibUnittest
fileprivate var COWLoggingArray_CopyCount = 0
public func expectNoCopyOnWrite<T>(
_ elements: [T],
_ message: @autoclosure () -> String = "",
stackTrace: SourceLocStack = SourceLocStack(),
showFrame: Bool = true,
file: String = #file,
line: UInt = #line,
_ body: (inout COWLoggingArray<T>) -> 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<Element> {
var storage: Storage
class Storage {
var buffer: UnsafeMutableBufferPointer<Element>
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<C>(_ subrange: Range<Int>, 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)
}
}