Skip to content

Commit 2324d69

Browse files
authored
Merge pull request #97 from gourlaysama/topic/move-paged-seq
Move PagedSeq from scala-library to parser-combinators
2 parents 8d2dab9 + 75f75ec commit 2324d69

File tree

6 files changed

+267
-6
lines changed

6 files changed

+267
-6
lines changed

Diff for: shared/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ package util.parsing.combinator
1313
import java.util.regex.Pattern
1414
import scala.util.matching.Regex
1515
import scala.util.parsing.input._
16-
import scala.collection.immutable.PagedSeq
1716
import scala.language.implicitConversions
1817

1918
/** The ''most important'' differences between `RegexParsers` and
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
/* __ *\
2+
** ________ ___ / / ___ Scala API **
3+
** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL **
4+
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
5+
** /____/\___/_/ |_/____/_/ | | **
6+
** |/ **
7+
\* */
8+
9+
10+
11+
package scala
12+
package util.parsing.input
13+
14+
import java.io.{File, FileReader, Reader => JReader}
15+
import scala.reflect.ClassTag
16+
17+
/** The `PagedSeq` object defines a lazy implementations of
18+
* a random access sequence.
19+
*
20+
* Provides utility methods that return instances of `PagedSeq[Char]`.
21+
* `fromIterator` and `fromIterable` provide generalised instances of `PagedSeq`
22+
*/
23+
object PagedSeq {
24+
final val UndeterminedEnd = Int.MaxValue
25+
26+
/** Constructs a paged sequence from an iterator */
27+
def fromIterator[T: ClassTag](source: Iterator[T]): PagedSeq[T] =
28+
new PagedSeq[T]((data: Array[T], start: Int, len: Int) => {
29+
var i = 0
30+
while (i < len && source.hasNext) {
31+
data(start + i) = source.next()
32+
i += 1
33+
}
34+
if (i == 0) -1 else i
35+
})
36+
37+
/** Constructs a paged sequence from an iterable */
38+
def fromIterable[T: ClassTag](source: Iterable[T]): PagedSeq[T] =
39+
fromIterator(source.iterator)
40+
41+
/** Constructs a paged character sequence from a string iterator */
42+
def fromStrings(source: Iterator[String]): PagedSeq[Char] = {
43+
var current: String = ""
44+
def more(data: Array[Char], start: Int, len: Int): Int =
45+
if (current.length != 0) {
46+
val cnt = current.length min len
47+
current.getChars(0, cnt, data, start)
48+
current = current.substring(cnt)
49+
if (cnt == len) cnt
50+
else (more(data, start + cnt, len - cnt) max 0) + cnt
51+
} else if (source.hasNext) {
52+
current = source.next()
53+
more(data, start, len)
54+
} else -1
55+
new PagedSeq(more(_: Array[Char], _: Int, _: Int))
56+
}
57+
58+
/** Constructs a paged character sequence from a string iterable */
59+
def fromStrings(source: Iterable[String]): PagedSeq[Char] =
60+
fromStrings(source.iterator)
61+
62+
/** Constructs a paged character sequence from a line iterator
63+
* Lines do not contain trailing `\n` characters; The method inserts
64+
* a line separator `\n` between any two lines in the sequence.
65+
*/
66+
def fromLines(source: Iterator[String]): PagedSeq[Char] = {
67+
var isFirst = true
68+
fromStrings(source map { line =>
69+
if (isFirst) {
70+
isFirst = false
71+
line
72+
} else "\n"+line
73+
})
74+
}
75+
76+
/** Constructs a paged character sequence from a line iterable
77+
* Lines do not contain trailing `\n` characters; The method inserts
78+
* a line separator `\n` between any two lines in the sequence.
79+
*/
80+
def fromLines(source: Iterable[String]): PagedSeq[Char] =
81+
fromLines(source.iterator)
82+
83+
/** Constructs a paged character sequence from an input reader
84+
*/
85+
def fromReader(source: JReader): PagedSeq[Char] =
86+
new PagedSeq(source.read(_: Array[Char], _: Int, _: Int))
87+
88+
/** Constructs a paged character sequence from an input file
89+
*/
90+
def fromFile(source: File): PagedSeq[Char] =
91+
fromReader(new FileReader(source))
92+
93+
/** Constructs a paged character sequence from a file with given name
94+
*/
95+
def fromFile(source: String): PagedSeq[Char] =
96+
fromFile(new File(source))
97+
98+
/** Constructs a paged character sequence from a scala.io.Source value
99+
*/
100+
def fromSource(source: scala.io.Source) =
101+
fromLines(source.getLines())
102+
}
103+
104+
105+
import PagedSeq._
106+
107+
/** An implementation of lazily computed sequences, where elements are stored
108+
* in "pages", i.e. arrays of fixed size.
109+
*
110+
* A paged sequence is constructed from a function that produces more elements when asked.
111+
* The producer function - `more`, is similar to the read method in java.io.Reader.
112+
* The `more` function takes three parameters: an array of elements, a start index, and an end index.
113+
* It should try to fill the array between start and end indices (excluding end index).
114+
* It returns the number of elements produced, or -1 if end of logical input stream was reached
115+
* before reading any element.
116+
*
117+
* @tparam T the type of the elements contained in this paged sequence, with an `ClassTag` context bound.
118+
*
119+
* @author Martin Odersky
120+
* @define Coll `PagedSeq`
121+
* @define coll paged sequence
122+
* @define mayNotTerminateInf
123+
* @define willNotTerminateInf
124+
*/
125+
class PagedSeq[T: ClassTag] protected(
126+
more: (Array[T], Int, Int) => Int,
127+
first1: Page[T],
128+
start: Int,
129+
end: Int)
130+
extends scala.collection.AbstractSeq[T]
131+
with scala.collection.IndexedSeq[T]
132+
{
133+
def this(more: (Array[T], Int, Int) => Int) = this(more, new Page[T](0), 0, UndeterminedEnd)
134+
135+
private var current: Page[T] = first1
136+
137+
private def latest = first1.latest
138+
139+
private def addMore() = latest.addMore(more)
140+
141+
private def page(absindex: Int) = {
142+
if (absindex < current.start)
143+
current = first1
144+
while (absindex >= current.end && current.next != null)
145+
current = current.next
146+
while (absindex >= current.end && !current.isLast) {
147+
current = addMore()
148+
}
149+
current
150+
}
151+
152+
/** The length of the paged sequence
153+
* @note Calling this method will force the entire sequence to be read.
154+
*/
155+
def length: Int = {
156+
while (!latest.isLast && latest.end < end) addMore()
157+
(latest.end min end) - start
158+
}
159+
160+
/** The element at position `index`.
161+
*/
162+
def apply(index: Int) =
163+
if (isDefinedAt(index)) page(index + start)(index + start)
164+
else throw new IndexOutOfBoundsException(index.toString)
165+
166+
/** Predicate method to check if an element is defined
167+
* at position `index` of the current sequence.
168+
* Unlike `length` this operation does not force reading
169+
* a lazy sequence to the end.
170+
*/
171+
override def isDefinedAt(index: Int) =
172+
index >= 0 && index < end - start && {
173+
val absidx = index + start
174+
absidx >= 0 && absidx < page(absidx).end
175+
}
176+
177+
/** The subsequence from index `start` up to `end -1` if `end`
178+
* is lesser than the length of the current sequence and up to
179+
* length of the sequence otherwise. This is limited up to the length
180+
* of the current sequence if `end` is larger than its length.
181+
*/
182+
override def slice(_start: Int, _end: Int): PagedSeq[T] = {
183+
page(start)
184+
val s = start + _start
185+
val e = if (_end == UndeterminedEnd) _end else start + _end
186+
var f = first1
187+
while (f.end <= s && !f.isLast) {
188+
if (f.next eq null) f = f.addMore(more)
189+
else f = f.next
190+
}
191+
// Warning -- not refining `more` means that slices can freely request and obtain
192+
// data outside of their slice. This is part of the design of PagedSeq
193+
// (to read pages!) but can be surprising.
194+
new PagedSeq(more, f, s, e)
195+
}
196+
197+
/** The subsequence from index `start` up to
198+
* the length of the current sequence.
199+
*/
200+
def slice(start: Int): PagedSeq[T] = slice(start, UndeterminedEnd)
201+
202+
/** Convert sequence to string */
203+
override def toString = {
204+
val buf = new StringBuilder
205+
for (ch <- PagedSeq.this.iterator) buf append ch
206+
buf.toString
207+
}
208+
}
209+
210+
211+
/** Page containing up to PageSize characters of the input sequence.
212+
*/
213+
private class Page[T: ClassTag](val num: Int) {
214+
215+
private final val PageSize = 4096
216+
217+
/** The next page in the sequence */
218+
var next : Page[T] = null
219+
220+
/** A later page in the sequence, serves a cache for pointing to last page */
221+
var later : Page[T] = this
222+
223+
/** The number of elements read into this page */
224+
var filled: Int = 0
225+
226+
/** Set true if the current page is the last in the sequence or if
227+
* the `more` function returned -1 signalling end of input. */
228+
var isLast: Boolean = false
229+
230+
/** The element array */
231+
final val data = new Array[T](PageSize)
232+
233+
/** The index of the first element in this page relative to the whole sequence */
234+
final def start = num * PageSize
235+
236+
/** The index of the element following the last element in this page relative
237+
* to the whole sequence */
238+
final def end = start + filled
239+
240+
/** The last page as currently present in the sequence; This can change as more
241+
* elements get appended to the sequence. */
242+
final def latest: Page[T] = {
243+
if (later.next != null) later = later.next.latest
244+
later
245+
}
246+
247+
/** The element at the given sequence index.
248+
* That index is relative to the whole sequence, not the page. */
249+
def apply(index: Int) = {
250+
if (index < start || index - start >= filled) throw new IndexOutOfBoundsException(index.toString)
251+
data(index - start)
252+
}
253+
254+
/** Produces more elements by calling `more` and adds them on the current page,
255+
* or fills a subsequent page if current page is full.
256+
* @note If current page is full, it is the last one in the sequence. */
257+
final def addMore(more: (Array[T], Int, Int) => Int): Page[T] =
258+
if (filled == PageSize) {
259+
next = new Page[T](num + 1)
260+
next.addMore(more)
261+
} else {
262+
val count = more(data, filled, PageSize - filled)
263+
if (count < 0) isLast = true
264+
else filled += count
265+
this
266+
}
267+
}

Diff for: shared/src/main/scala/scala/util/parsing/input/PagedSeqReader.scala

-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
package scala
1111
package util.parsing.input
1212

13-
import scala.collection.immutable.PagedSeq
14-
1513
/** An object encapsulating basic character constants.
1614
*
1715
* @author Martin Odersky

Diff for: shared/src/main/scala/scala/util/parsing/input/StreamReader.scala

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ package scala
1010
package util.parsing.input
1111

1212
import java.io.BufferedReader
13-
import scala.collection.immutable.PagedSeq
1413

1514
/** An object to create a `StreamReader` from a `java.io.Reader`.
1615
*

Diff for: shared/src/test/scala/scala/util/parsing/combinator/gh45.scala

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package scala.util.parsing.combinator
22

33
import scala.util.parsing.input._
4-
import scala.collection.immutable.PagedSeq
54

65
import org.junit.Test
76
import org.junit.Assert.assertTrue

Diff for: shared/src/test/scala/scala/util/parsing/combinator/t8879.scala

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import scala.util.parsing.input._
2-
import scala.collection.immutable.PagedSeq
32

43
import org.junit.Test
54
import org.junit.Assert.fail

0 commit comments

Comments
 (0)