@@ -146,18 +146,19 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
146146 }
147147
148148 /** 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
150150 * strategy for dealing with elements which don't fit evenly.
151151 *
152152 * Typical uses can be achieved via methods `grouped` and `sliding`.
153153 */
154154 class GroupedIterator [B >: A ](self : Iterator [B ], size : Int , step : Int ) extends AbstractIterator [immutable.Seq [B ]] {
155+
155156 require(size >= 1 && step >= 1 , f " size= $size%d and step= $step%d, but both must be positive " )
156157
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
161162
162163 /** Public functions which can be used to configure the iterator before use.
163164 *
@@ -170,10 +171,9 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
170171 * @note This method is mutually exclusive with `withPartial(true)`.
171172 */
172173 def withPadding (x : => B ): this .type = {
173- pad = ( ) => x
174+ pad = Some (( ) => x)
174175 this
175176 }
176-
177177 /** Public functions which can be used to configure the iterator before use.
178178 *
179179 * 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
186186 * @note This method is mutually exclusive with `withPadding`.
187187 */
188188 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+
192193 this
193194 }
194195
@@ -199,8 +200,8 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
199200 * so a subsequent self.hasNext would not test self after the
200201 * group was consumed.
201202 */
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 ]
204205 var i = 0
205206 // The order of terms in the following condition is important
206207 // here as self.hasNext could be blocking
@@ -211,55 +212,66 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite
211212 buf
212213 }
213214
215+ private def padding (x : Int ) = immutable.ArraySeq .untagged.fill(x)(pad.get())
214216 private def gap = (step - size) max 0
215217
216218 private def go (count : Int ) = {
217- val prevSize = group .size
219+ val prevSize = buffer .size
218220 def isFirst = prevSize == 0
219- val extension = takeDestructively(count)
220221 // If there is padding defined we insert it immediately
221222 // 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
226229 }
230+ lazy val len = xs.length
231+ lazy val incomplete = len < count
227232
228- val extSize = extension.size
229233 // if 0 elements are requested, or if the number of newly obtained
230234 // 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)
236245 filled = true
237246 true
238247 }
248+ }
239249
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
244254 else deliver(step min size) // the typical case
245255 }
246256
247257 // fill() returns false if no more sequences can be produced
248258 private def fill (): Boolean = {
249259 if (! self.hasNext) false
250260 // 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)
252262 else go(step)
253263 }
254264
255- def hasNext : Boolean = filled || fill()
256-
265+ def hasNext = filled || fill()
257266 @ throws[NoSuchElementException ]
258267 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" )
261273 filled = false
262- immutable.ArraySeq .unsafeWrapArray(group .toArray[Any ]).asInstanceOf [immutable.ArraySeq [B ]]
274+ immutable.ArraySeq .unsafeWrapArray(buffer .toArray[Any ]).asInstanceOf [immutable.ArraySeq [B ]]
263275 }
264276 }
265277
0 commit comments