forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCOWLoggingArray.swift
123 lines (104 loc) · 3.35 KB
/
COWLoggingArray.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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)
}
}