@@ -35,8 +35,19 @@ struct Stack<Element> : CollectionLikeSequence {
35
35
BridgedPassContext . Slab. getCapacity ( ) / MemoryLayout< Element> . stride
36
36
}
37
37
38
- private static func bind( _ slab: BridgedPassContext . Slab ) -> UnsafeMutablePointer < Element > {
39
- return UnsafeMutableRawPointer ( slab. data!) . bindMemory ( to: Element . self, capacity: Stack . slabCapacity)
38
+ private func allocate( after lastSlab: BridgedPassContext . Slab ? = nil ) -> BridgedPassContext . Slab {
39
+ let lastSlab = lastSlab ?? BridgedPassContext . Slab ( nil )
40
+ let newSlab = bridgedContext. allocSlab ( lastSlab)
41
+ UnsafeMutableRawPointer ( newSlab. data!) . bindMemory ( to: Element . self, capacity: Stack . slabCapacity)
42
+ return newSlab
43
+ }
44
+
45
+ private static func element( in slab: BridgedPassContext . Slab , at index: Int ) -> Element {
46
+ return pointer ( in: slab, at: index) . pointee
47
+ }
48
+
49
+ private static func pointer( in slab: BridgedPassContext . Slab , at index: Int ) -> UnsafeMutablePointer < Element > {
50
+ return UnsafeMutableRawPointer ( slab. data!) . assumingMemoryBound ( to: Element . self) + index
40
51
}
41
52
42
53
struct Iterator : IteratorProtocol {
@@ -50,7 +61,7 @@ struct Stack<Element> : CollectionLikeSequence {
50
61
51
62
guard index < end else { return nil }
52
63
53
- let elem = Stack . bind ( slab) [ index]
64
+ let elem = Stack . element ( in : slab, at : index)
54
65
index += 1
55
66
56
67
if index >= end && slab. data != lastSlab. data {
@@ -68,23 +79,23 @@ struct Stack<Element> : CollectionLikeSequence {
68
79
}
69
80
70
81
var first : Element ? {
71
- isEmpty ? nil : Stack . bind ( firstSlab) [ 0 ]
82
+ isEmpty ? nil : Stack . element ( in : firstSlab, at : 0 )
72
83
}
73
84
74
85
var last : Element ? {
75
- isEmpty ? nil : Stack . bind ( lastSlab) [ endIndex &- 1 ]
86
+ isEmpty ? nil : Stack . element ( in : lastSlab, at : endIndex &- 1 )
76
87
}
77
88
78
89
mutating func push( _ element: Element ) {
79
90
if endIndex >= Stack . slabCapacity {
80
- lastSlab = bridgedContext . allocSlab ( lastSlab)
91
+ lastSlab = allocate ( after : lastSlab)
81
92
endIndex = 0
82
93
} else if firstSlab. data == nil {
83
94
assert ( endIndex == 0 )
84
- firstSlab = bridgedContext . allocSlab ( lastSlab )
95
+ firstSlab = allocate ( )
85
96
lastSlab = firstSlab
86
97
}
87
- ( Stack . bind ( lastSlab) + endIndex) . initialize ( to: element)
98
+ Stack . pointer ( in : lastSlab, at : endIndex) . initialize ( to: element)
88
99
endIndex += 1
89
100
}
90
101
@@ -105,7 +116,7 @@ struct Stack<Element> : CollectionLikeSequence {
105
116
}
106
117
assert ( endIndex > 0 )
107
118
endIndex -= 1
108
- let elem = ( Stack . bind ( lastSlab) + endIndex) . move ( )
119
+ let elem = Stack . pointer ( in : lastSlab, at : endIndex) . move ( )
109
120
110
121
if endIndex == 0 {
111
122
if lastSlab. data == firstSlab. data {
@@ -129,3 +140,83 @@ struct Stack<Element> : CollectionLikeSequence {
129
140
/// TODO: once we have move-only types, make this a real deinit.
130
141
mutating func deinitialize( ) { removeAll ( ) }
131
142
}
143
+
144
+ extension Stack {
145
+ /// Mark a stack location for future iteration.
146
+ ///
147
+ /// TODO: Marker should be ~Escapable.
148
+ struct Marker {
149
+ let slab : BridgedPassContext . Slab
150
+ let index : Int
151
+ }
152
+
153
+ var top : Marker { Marker ( slab: lastSlab, index: endIndex) }
154
+
155
+ struct Segment : CollectionLikeSequence {
156
+ let low : Marker
157
+ let high : Marker
158
+
159
+ init ( in stack: Stack , low: Marker , high: Marker ) {
160
+ if low. slab. data == nil {
161
+ assert ( low. index == 0 , " invalid empty stack marker " )
162
+ // `low == nil` and `high == nil` is a valid empty segment,
163
+ // even though `assertValid(marker:)` would return false.
164
+ if high. slab. data != nil {
165
+ stack. assertValid ( marker: high)
166
+ }
167
+ self . low = Marker ( slab: stack. firstSlab, index: 0 )
168
+ self . high = high
169
+ return
170
+ }
171
+ stack. assertValid ( marker: low)
172
+ stack. assertValid ( marker: high)
173
+ self . low = low
174
+ self . high = high
175
+ }
176
+
177
+ func makeIterator( ) -> Stack . Iterator {
178
+ return Iterator ( slab: low. slab, index: low. index,
179
+ lastSlab: high. slab, endIndex: high. index)
180
+ }
181
+ }
182
+
183
+ /// Assert that `marker` is valid based on the current `top`.
184
+ ///
185
+ /// This is an assert rather than a query because slabs can reuse
186
+ /// memory leading to a stale marker that appears valid.
187
+ func assertValid( marker: Marker ) {
188
+ var currentSlab = lastSlab
189
+ var currentIndex = endIndex
190
+ while currentSlab. data != marker. slab. data {
191
+ assert ( currentSlab. data != firstSlab. data, " Invalid stack marker " )
192
+ currentSlab = currentSlab. getPrevious ( )
193
+ currentIndex = Stack . slabCapacity
194
+ }
195
+ assert ( marker. index <= currentIndex, " Invalid stack marker " )
196
+ }
197
+
198
+ /// Execute the `body` closure, passing it `self` for further
199
+ /// mutation of the stack and passing `marker` to mark the stack
200
+ /// position prior to executing `body`. `marker` must not escape the
201
+ /// `body` closure.
202
+ mutating func withMarker< R> (
203
+ _ body: ( inout Stack < Element > , Marker ) throws -> R ) rethrows -> R {
204
+ return try body ( & self , top)
205
+ }
206
+
207
+ /// Record a stack marker, execute a `body` closure, then execute a
208
+ /// `handleNewElements` closure with the Segment that contains all
209
+ /// elements that remain on the stack after being pushed on the
210
+ /// stack while executing `body`. `body` must push more elements
211
+ /// than it pops.
212
+ mutating func withMarker< R> (
213
+ pushElements body: ( inout Stack ) throws -> R ,
214
+ withNewElements handleNewElements: ( ( Segment ) -> ( ) )
215
+ ) rethrows -> R {
216
+ return try withMarker { ( stack: inout Stack < Element > , marker: Marker ) in
217
+ let result = try body ( & stack)
218
+ handleNewElements ( Segment ( in: stack, low: marker, high: stack. top) )
219
+ return result
220
+ }
221
+ }
222
+ }
0 commit comments