/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2009, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id: PagedSeq.scala 14497 2008-04-04 12:09:06Z washburn $ package scala.collection.immutable import java.io._ import util.matching.Regex /** The PagedSeq object defines a lazy implementations of * a random access sequence. */ object PagedSeq { final val UndeterminedEnd = Math.MAX_INT /** Constructs a character sequence from a character iterator */ def fromIterator[T](source: Iterator[T]): PagedSeq[T] = new PagedSeq[T]((data: Array[T], start: Int, len: Int) => { var i = 0 while (i < len && source.hasNext) { data(start + i) = source.next i += 1 } if (i == 0) -1 else i }) /** Constructs a character sequence from a character iterable */ def fromIterable[T](source: Iterable[T]): PagedSeq[T] = fromIterator(source.iterator) /** Constructs a character sequence from a string iterator */ def fromStrings(source: Iterator[String]): PagedSeq[Char] = { var current: String = "" def more(data: Array[Char], start: Int, len: Int): Int = if (current.length != 0) { val cnt = current.length min len current.getChars(0, cnt, data, start) current = current.substring(cnt) if (cnt == len) cnt else (more(data, start + cnt, len - cnt) max 0) + cnt } else if (source.hasNext) { current = source.next more(data, start, len) } else -1 new PagedSeq(more(_: Array[Char], _: Int, _: Int)) } /** Constructs a character sequence from a string iterable */ def fromStrings(source: Iterable[String]): PagedSeq[Char] = fromStrings(source.iterator) /** Constructs a character sequence from a line iterator * Lines do not contain trailing `\n' characters; The method inserts * a line separator `\n' between any two lines in the sequence. */ def fromLines(source: Iterator[String]): PagedSeq[Char] = { var isFirst = true fromStrings(source map { line => if (isFirst) line else { isFirst = false "\n"+line } }) } /** Constructs a character sequence from a line iterable * Lines do not contain trailing `\n' characters; The method inserts * a line separator `\n' between any two lines in the sequence. */ def fromLines(source: Iterable[String]): PagedSeq[Char] = fromLines(source.iterator) /** Constructs a character sequence from an input reader */ def fromReader(source: Reader): PagedSeq[Char] = new PagedSeq(source.read(_: Array[Char], _: Int, _: Int)) /** Constructs a character sequence from an input file */ def fromFile(source: File): PagedSeq[Char] = fromReader(new FileReader(source)) /** Constructs a character sequence from a file with given name */ def fromFile(source: String): PagedSeq[Char] = fromFile(new File(source)) /** Constructs a character sequence from a scala.io.Source value */ def fromSource(source: io.Source) = fromLines(source.getLines()) } import PagedSeq._ /** An implementation of lazily computed sequences, where elements are stored * in ``pages'', i.e. arrays of fixed size. * * @author Martin Odersky */ class PagedSeq[T] protected( more: (Array[T], Int, Int) => Int, first1: Page[T], start: Int, end: Int) extends collection.Vector[T] { /** A paged sequence is constructed from a method that produces more characters when asked. * The producer method is analogous to the read method in java.io.Reader. * It takes three parameters: an array of characters, a start index, and an end index. * It should try to fill the array between start and end indices (not including end index). * It returns the number of characters produced, or -1 if end of logical input stream was reached * before any character was read. */ def this(more: (Array[T], Int, Int) => Int) = this(more, new Page[T](0), 0, UndeterminedEnd) private var current: Page[T] = first1 private def latest = first1.latest private def addMore() = latest.addMore(more) private def page(absindex: Int) = { if (absindex < current.start) current = first1 while (absindex >= current.end && current.next != null) current = current.next while (absindex >= current.end && !current.isLast) { current = addMore() } current } /** The length of the character sequence * Note: calling this method will force sequence to be read until the end. */ def length: Int = { while (!latest.isLast) addMore() (latest.end min end) - start } /** The character at position `index'. */ def apply(index: Int) = if (isDefinedAt(index)) page(index + start)(index + start) else throw new IndexOutOfBoundsException(index.toString) /** Is character sequence defined at `index'? * Unlike `length' this operation does not force reading * a lazy sequence to the end. */ override def isDefinedAt(index: Int) = index >= 0 && index < end - start && { val p = page(index + start); index + start < p.end } /** the subsequence from index `start' up to and excluding * the minimum of index `end' and the length of the current sequence. */ override def slice(_start: Int, _end: Int): PagedSeq[T] = { page(start) val s = start + _start val e = if (_end == UndeterminedEnd) _end else start + _end var f = first1 while (f.end <= s && !f.isLast) f = f.next new PagedSeq(more, f, s, e) } /** the subsequence from index `start' up to the * length of the current sequence. */ override def slice(start: Int) = slice(start, UndeterminedEnd) /** Convert sequence to string */ override def toString = { val buf = new StringBuilder for (ch <- PagedSeq.this.iterator) buf append ch buf.toString } } /** Page containing up to PageSize characters of the input sequence. */ private class Page[T](val num: Int) { private final val PageSize = 4096 /** The next page in the sequence */ var next : Page[T] = null /** A later page in the sequence, serves a cachae for pointing to last page */ var later : Page[T] = this /** The number of characters read into this page */ var filled: Int = 0 /** Is this page the permamnently last one in the sequence? Only true once `more' * method has returned -1 to signal end of input. */ var isLast: Boolean = false /** The character array */ final val data = new Array[T](PageSize) /** The index of the first character in this page relative to the whole sequence */ final def start = num * PageSize /** The index of the character following the last charcater in this page relative * to the whole sequence */ final def end = start + filled /** The currently last page in the sequence; might change as more charcaters are appended */ final def latest: Page[T] = { if (later.next != null) later = later.next.latest later } /** The character at given sequence index. * That index is relative to the whole sequence, not the page. */ def apply(index: Int) = { if (index < start || index - start >= filled) throw new IndexOutOfBoundsException(index.toString) data(index - start) } /** produces more characters by calling `more' and appends them on the current page, * or fills a subsequent page if current page is full * pre: if current page is full, it is the last one in the sequence. */ final def addMore(more: (Array[T], Int, Int) => Int): Page[T] = if (filled == PageSize) { next = new Page[T](num + 1) next.addMore(more) } else { val count = more(data, filled, PageSize - filled) if (count < 0) isLast = true else filled += count this } }