@@ -146,18 +146,19 @@ 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
+
155
156
require(size >= 1 && step >= 1 , f " size= $size%d and step= $step%d, but both must be positive " )
156
157
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
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
161
162
162
163
/** Public functions which can be used to configure the iterator before use.
163
164
*
@@ -170,10 +171,9 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
170
171
* @note This method is mutually exclusive with `withPartial(true)`.
171
172
*/
172
173
def withPadding (x : => B ): this .type = {
173
- pad = ( ) => x
174
+ pad = Some (( ) => x)
174
175
this
175
176
}
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,9 +186,10 @@ 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
- // reset pad since otherwise it will take precedence
191
- if (partial) pad = null
189
+ _partial = x
190
+ if (_partial) // reset pad since otherwise it will take precedence
191
+ pad = None
192
+
192
193
this
193
194
}
194
195
@@ -199,8 +200,8 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
199
200
* so a subsequent self.hasNext would not test self after the
200
201
* group was consumed.
201
202
*/
202
- private def takeDestructively (size : Int ): ArrayBuffer [B ] = {
203
- val buf = new ArrayBuffer [B ](size)
203
+ private def takeDestructively (size : Int ): Seq [B ] = {
204
+ val buf = new ArrayBuffer [B ]
204
205
var i = 0
205
206
// The order of terms in the following condition is important
206
207
// here as self.hasNext could be blocking
@@ -211,55 +212,66 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
211
212
buf
212
213
}
213
214
215
+ private def padding (x : Int ) = immutable.ArraySeq .untagged.fill(x)(pad.get())
214
216
private def gap = (step - size) max 0
215
217
216
218
private def go (count : Int ) = {
217
- val prevSize = group .size
219
+ val prevSize = buffer .size
218
220
def isFirst = prevSize == 0
219
- val extension = takeDestructively(count)
220
221
// If there is padding defined we insert it immediately
221
222
// so the rest of the code can be oblivious
222
- var shortBy = count - extension.size
223
- if (pad != null ) while (shortBy > 0 ) {
224
- extension += pad()
225
- shortBy -= 1
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
226
229
}
230
+ lazy val len = xs.length
231
+ lazy val incomplete = len < count
227
232
228
- val extSize = extension.size
229
233
// if 0 elements are requested, or if the number of newly obtained
230
234
// elements is less than the gap between sequences, we are done.
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)
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)
236
245
filled = true
237
246
true
238
247
}
248
+ }
239
249
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
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
244
254
else deliver(step min size) // the typical case
245
255
}
246
256
247
257
// fill() returns false if no more sequences can be produced
248
258
private def fill (): Boolean = {
249
259
if (! self.hasNext) false
250
260
// the first time we grab size, but after that we grab step
251
- else if (group .isEmpty) go(size)
261
+ else if (buffer .isEmpty) go(size)
252
262
else go(step)
253
263
}
254
264
255
- def hasNext : Boolean = filled || fill()
256
-
265
+ def hasNext = filled || fill()
257
266
@ throws[NoSuchElementException ]
258
267
def next (): immutable.Seq [B ] = {
259
- if (! filled) fill()
260
- if (! filled) Iterator .empty.next()
268
+ if (! filled)
269
+ fill()
270
+
271
+ if (! filled)
272
+ throw new NoSuchElementException (" next on empty iterator" )
261
273
filled = false
262
- immutable.ArraySeq .unsafeWrapArray(group .toArray[Any ]).asInstanceOf [immutable.ArraySeq [B ]]
274
+ immutable.ArraySeq .unsafeWrapArray(buffer .toArray[Any ]).asInstanceOf [immutable.ArraySeq [B ]]
263
275
}
264
276
}
265
277
0 commit comments