|  | 
|  | 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 | +} | 
0 commit comments