@@ -146,19 +146,18 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
146
146
}
147
147
148
148
/** A flexible iterator for transforming an `Iterator[A]` into an
149
- * Iterator[Seq[A]], with configurable sequence size, step, and
149
+ * ` Iterator[Seq[A]]` , with configurable sequence size, step, and
150
150
* strategy for dealing with elements which don't fit evenly.
151
151
*
152
152
* Typical uses can be achieved via methods `grouped` and `sliding`.
153
153
*/
154
154
class GroupedIterator [B >: A ](self : Iterator [B ], size : Int , step : Int ) extends AbstractIterator [immutable.Seq [B ]] {
155
-
156
155
require(size >= 1 && step >= 1 , f " size= $size%d and step= $step%d, but both must be positive " )
157
156
158
- private [this ] var buffer : ArrayBuffer [B ] = ArrayBuffer ( ) // the buffer
159
- private [this ] var filled = false // whether the buffer is "hot"
160
- private [this ] var _partial = true // whether we deliver short sequences
161
- private [this ] var pad : Option [ () => B ] = None // what to pad short sequences with
157
+ private [this ] val group = new ArrayBuffer [B ](size ) // the group
158
+ private [this ] var filled = false // whether the group is "hot"
159
+ private [this ] var partial = true // whether we deliver short sequences
160
+ private [this ] var pad : () => B = null // what to pad short sequences with
162
161
163
162
/** Public functions which can be used to configure the iterator before use.
164
163
*
@@ -171,9 +170,10 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
171
170
* @note This method is mutually exclusive with `withPartial(true)`.
172
171
*/
173
172
def withPadding (x : => B ): this .type = {
174
- pad = Some (( ) => x)
173
+ pad = ( ) => x
175
174
this
176
175
}
176
+
177
177
/** Public functions which can be used to configure the iterator before use.
178
178
*
179
179
* Select whether the last segment may be returned with less than `size`
@@ -186,10 +186,9 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
186
186
* @note This method is mutually exclusive with `withPadding`.
187
187
*/
188
188
def withPartial (x : Boolean ): this .type = {
189
- _partial = x
190
- if (_partial) // reset pad since otherwise it will take precedence
191
- pad = None
192
-
189
+ partial = x
190
+ // reset pad since otherwise it will take precedence
191
+ if (partial) pad = null
193
192
this
194
193
}
195
194
@@ -200,8 +199,8 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
200
199
* so a subsequent self.hasNext would not test self after the
201
200
* group was consumed.
202
201
*/
203
- private def takeDestructively (size : Int ): Seq [B ] = {
204
- val buf = new ArrayBuffer [B ]
202
+ private def takeDestructively (size : Int ): ArrayBuffer [B ] = {
203
+ val buf = new ArrayBuffer [B ](size)
205
204
var i = 0
206
205
// The order of terms in the following condition is important
207
206
// here as self.hasNext could be blocking
@@ -212,66 +211,55 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
212
211
buf
213
212
}
214
213
215
- private def padding (x : Int ) = immutable.ArraySeq .untagged.fill(x)(pad.get())
216
214
private def gap = (step - size) max 0
217
215
218
216
private def go (count : Int ) = {
219
- val prevSize = buffer .size
217
+ val prevSize = group .size
220
218
def isFirst = prevSize == 0
219
+ val extension = takeDestructively(count)
221
220
// If there is padding defined we insert it immediately
222
221
// so the rest of the code can be oblivious
223
- val xs : Seq [B ] = {
224
- val res = takeDestructively(count)
225
- // was: extra checks so we don't calculate length unless there's reason
226
- // but since we took the group eagerly, just use the fast length
227
- val shortBy = count - res.length
228
- if (shortBy > 0 && pad.isDefined) res ++ padding(shortBy) else res
222
+ var shortBy = count - extension.size
223
+ if (pad != null ) while (shortBy > 0 ) {
224
+ extension += pad()
225
+ shortBy -= 1
229
226
}
230
- lazy val len = xs.length
231
- lazy val incomplete = len < count
232
227
228
+ val extSize = extension.size
233
229
// if 0 elements are requested, or if the number of newly obtained
234
230
// elements is less than the gap between sequences, we are done.
235
- def deliver (howMany : Int ) = {
236
- (howMany > 0 && (isFirst || len > gap)) && {
237
- if (! isFirst)
238
- buffer dropInPlace (step min prevSize)
239
-
240
- val available =
241
- if (isFirst) len
242
- else howMany min (len - gap)
243
-
244
- buffer ++= (xs takeRight available)
231
+ def deliver (howMany : Int ) =
232
+ (howMany > 0 && (isFirst || extSize > gap)) && {
233
+ if (! isFirst) group.dropInPlace(step min prevSize)
234
+ val available = if (isFirst) extSize else howMany min (extSize - gap)
235
+ group ++= extension.takeRightInPlace(available)
245
236
filled = true
246
237
true
247
238
}
248
- }
249
239
250
- if (xs .isEmpty) false // self ran out of elements
251
- else if (_partial ) deliver(len min size) // if _partial is true, we deliver regardless
252
- else if (incomplete ) false // !_partial && incomplete means no more seqs
253
- else if (isFirst) deliver(len) // first element
240
+ if (extension .isEmpty) false // self ran out of elements
241
+ else if (partial ) deliver(extSize min size) // if partial is true, we deliver regardless
242
+ else if (extSize < count ) false // !partial && extSize < count means no more seqs
243
+ else if (isFirst) deliver(extSize) // first element
254
244
else deliver(step min size) // the typical case
255
245
}
256
246
257
247
// fill() returns false if no more sequences can be produced
258
248
private def fill (): Boolean = {
259
249
if (! self.hasNext) false
260
250
// the first time we grab size, but after that we grab step
261
- else if (buffer .isEmpty) go(size)
251
+ else if (group .isEmpty) go(size)
262
252
else go(step)
263
253
}
264
254
265
- def hasNext = filled || fill()
255
+ def hasNext : Boolean = filled || fill()
256
+
266
257
@ throws[NoSuchElementException ]
267
258
def next (): immutable.Seq [B ] = {
268
- if (! filled)
269
- fill()
270
-
271
- if (! filled)
272
- throw new NoSuchElementException (" next on empty iterator" )
259
+ if (! filled) fill()
260
+ if (! filled) Iterator .empty.next()
273
261
filled = false
274
- immutable.ArraySeq .unsafeWrapArray(buffer .toArray[Any ]).asInstanceOf [immutable.ArraySeq [B ]]
262
+ immutable.ArraySeq .unsafeWrapArray(group .toArray[Any ]).asInstanceOf [immutable.ArraySeq [B ]]
275
263
}
276
264
}
277
265
0 commit comments