Skip to content

Commit 5d3120d

Browse files
committed
Revert "GroupedIterator improvements"
This reverts commit ed9356d. Reverts PR scala/scala#9818; see scala/community-build#1519 for details on the community build failures
1 parent dfbc217 commit 5d3120d

File tree

1 file changed

+46
-34
lines changed

1 file changed

+46
-34
lines changed

library/src/scala/collection/Iterator.scala

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)