Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2018-2024 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio
import java.nio._
import java.nio.charset.Charset
import java.util.concurrent.atomic.AtomicInteger
import scala.annotation.tailrec
import scala.collection.mutable.Builder
import scala.math.log
import scala.reflect.{ClassTag, classTag}
/**
* A `Chunk[A]` represents a chunk of values of type `A`. Chunks are designed
* are usually backed by arrays, but expose a purely functional, safe interface
* to the underlying elements, and they become lazy on operations that would be
* costly with arrays, such as repeated concatenation.
*
* The implementation of balanced concatenation is based on the one for
* Conc-Trees in "Conc-Trees for Functional and Parallel Programming" by
* Aleksandar Prokopec and Martin Odersky.
* [[http://aleksandar-prokopec.com/resources/docs/lcpc-conc-trees.pdf]]
*
* NOTE: For performance reasons `Chunk` does not box primitive types. As a
* result, it is not safe to construct chunks from heterogeneous primitive
* types.
*/
sealed abstract class Chunk[+A] extends ChunkLike[A] with Serializable { self =>
def chunkIterator: Chunk.ChunkIterator[A]
/**
* Returns the concatenation of this chunk with the specified chunk.
*/
final def ++[A1 >: A](that: Chunk[A1]): Chunk[A1] =
(self, that) match {
case (Chunk.AppendN(start, buffer, bufferUsed, _), that) =>
val chunk = Chunk.fromArray(buffer.asInstanceOf[Array[A1]]).take(bufferUsed)
start ++ chunk ++ that
case (self, Chunk.PrependN(end, buffer, bufferUsed, _)) =>
val chunk = Chunk.fromArray(buffer.asInstanceOf[Array[A1]]).takeRight(bufferUsed)
self ++ chunk ++ end
case (self, Chunk.Empty) => self
case (Chunk.Empty, that) => that
case (self, that) =>
val diff = that.concatDepth - self.concatDepth
if (math.abs(diff) <= 1) Chunk.Concat(self, that)
else if (diff < -1) {
if (self.left.concatDepth >= self.right.concatDepth) {
val nr = self.right ++ that
Chunk.Concat(self.left, nr)
} else {
val nrr = self.right.right ++ that
if (nrr.concatDepth == self.concatDepth - 3) {
val nr = Chunk.Concat(self.right.left, nrr)
Chunk.Concat(self.left, nr)
} else {
val nl = Chunk.Concat(self.left, self.right.left)
Chunk.Concat(nl, nrr)
}
}
} else {
if (that.right.concatDepth >= that.left.concatDepth) {
val nl = self ++ that.left
Chunk.Concat(nl, that.right)
} else {
val nll = self ++ that.left.left
if (nll.concatDepth == that.concatDepth - 3) {
val nl = Chunk.Concat(nll, that.left.right)
Chunk.Concat(nl, that.right)
} else {
val nr = Chunk.Concat(that.left.right, that.right)
Chunk.Concat(nll, nr)
}
}
}
}
/**
* Returns the concatenation of this chunk with the specified chunk.
*/
final def ++[A1 >: A](that: NonEmptyChunk[A1]): NonEmptyChunk[A1] =
that.prepend(self)
/**
* Returns the bitwise AND of this chunk and the specified chunk.
*/
def &(that: Chunk[Boolean])(implicit ev: A <:< Boolean): Chunk.BitChunkByte =
Chunk.bitwise(self.asInstanceOf[Chunk[Boolean]], that, _ & _)
/**
* Returns the bitwise OR of this chunk and the specified chunk.
*/
def |(that: Chunk[Boolean])(implicit ev: A <:< Boolean): Chunk.BitChunkByte =
Chunk.bitwise(self.asInstanceOf[Chunk[Boolean]], that, _ | _)
/**
* Returns the bitwise XOR of this chunk and the specified chunk.
*/
def ^(that: Chunk[Boolean])(implicit ev: A <:< Boolean): Chunk.BitChunkByte =
Chunk.bitwise(self.asInstanceOf[Chunk[Boolean]], that, _ ^ _)
/**
* Returns the bitwise NOT of this chunk.
*/
def negate(implicit ev: A <:< Boolean): Chunk.BitChunkByte = {
val bits = self.length
val fullBytes = bits >> 3
val remBytes = bits & 7
val arr = Array.ofDim[Byte](fullBytes + (if (remBytes == 0) 0 else 1))
var i = 0
var mask = 128
while (i < fullBytes) {
var byte = 0
mask = 128
(0 until 8).foreach { k =>
byte = byte | (if (!ev(self(i * 8 + k))) mask else 0)
mask >>= 1
}
arr(i) = byte.asInstanceOf[Byte]
i += 1
}
if (remBytes != 0) {
var byte = 0
mask = 128
(0 until remBytes).foreach { k =>
byte = byte | (if (!ev(self(fullBytes * 8 + k))) mask else 0)
mask >>= 1
}
arr(fullBytes) = byte.asInstanceOf[Byte]
}
Chunk.BitChunkByte(Chunk.fromArray(arr), 0, bits)
}
/**
* Crates a base64 encoded string based on the chunk's data.
*/
def asBase64String(implicit ev: Chunk.IsText[A]): String = {
val encoder = java.util.Base64.getEncoder
ev match {
case Chunk.IsText.byteIsText =>
encoder.encodeToString(self.asInstanceOf[Chunk[Byte]].toArray)
case Chunk.IsText.charIsText =>
encoder.encodeToString(self.asInstanceOf[Chunk[Char]].toArray.map(_.toByte))
case Chunk.IsText.strIsText =>
encoder.encodeToString(ev.convert(self).getBytes)
}
}
/**
* Converts a chunk of ints to a chunk of bits.
*/
final def asBitsInt(endianness: Chunk.BitChunk.Endianness)(implicit ev: A <:< Int): Chunk[Boolean] =
if (self.isEmpty) Chunk.empty
else Chunk.BitChunkInt(self.asInstanceOf[Chunk[Int]], endianness, 0, length << 5)
/**
* Converts a chunk of longs to a chunk of bits.
*/
final def asBitsLong(endianness: Chunk.BitChunk.Endianness)(implicit ev: A <:< Long): Chunk[Boolean] =
if (self.isEmpty) Chunk.empty
else Chunk.BitChunkLong(self.asInstanceOf[Chunk[Long]], endianness, 0, length << 6)
/**
* Converts a chunk of bytes to a chunk of bits.
*/
final def asBitsByte(implicit ev: A <:< Byte): Chunk[Boolean] =
if (self.isEmpty) Chunk.empty
else Chunk.BitChunkByte(self.map(ev), 0, length << 3)
def toPackedByte(implicit ev: A <:< Boolean): Chunk[Byte] =
if (self.isEmpty) Chunk.empty
else Chunk.ChunkPackedBoolean[Byte](self.asInstanceOf[Chunk[Boolean]], 8, Chunk.BitChunk.Endianness.BigEndian)
def toPackedInt(endianness: Chunk.BitChunk.Endianness)(implicit ev: A <:< Boolean): Chunk[Int] =
if (self.isEmpty) Chunk.empty
else Chunk.ChunkPackedBoolean[Int](self.asInstanceOf[Chunk[Boolean]], 32, endianness)
def toPackedLong(endianness: Chunk.BitChunk.Endianness)(implicit ev: A <:< Boolean): Chunk[Long] =
if (self.isEmpty) Chunk.empty
else Chunk.ChunkPackedBoolean[Long](self.asInstanceOf[Chunk[Boolean]], 64, endianness)
/**
* Crates a new String based on this chunks data.
*/
final def asString(implicit ev: Chunk.IsText[A]): String = ev.convert(self)
/**
* Crates a new String based on this chunk of bytes and using the given
* charset.
*/
final def asString(charset: Charset)(implicit ev: A <:< Byte): String = {
implicit val cls: ClassTag[A] = classTag[Byte].asInstanceOf[ClassTag[A]]
new String(self.toArray.asInstanceOf[Array[Byte]], charset)
}
/**
* Get the element at the specified index.
*/
def boolean(index: Int)(implicit ev: A <:< Boolean): Boolean =
ev(apply(index))
/**
* Get the element at the specified index.
*/
def byte(index: Int)(implicit ev: A <:< Byte): Byte =
ev(apply(index))
/**
* Get the element at the specified index.
*/
def char(index: Int)(implicit ev: A <:< Char): Char =
ev(apply(index))
/**
* Transforms all elements of the chunk for as long as the specified partial
* function is defined.
*/
def collectWhile[B](pf: PartialFunction[A, B]): Chunk[B] =
if (isEmpty) Chunk.empty else self.materialize.collectWhile(pf)
def collectWhileZIO[R, E, B](pf: PartialFunction[A, ZIO[R, E, B]])(implicit
trace: Trace
): ZIO[R, E, Chunk[B]] =
if (isEmpty) ZIO.succeed(Chunk.empty) else self.materialize.collectWhileZIO(pf)
/**
* Returns a filtered, mapped subset of the elements of this chunk based on a
* .
*/
def collectZIO[R, E, B](pf: PartialFunction[A, ZIO[R, E, B]])(implicit trace: Trace): ZIO[R, E, Chunk[B]] =
if (isEmpty) ZIO.succeed(Chunk.empty) else self.materialize.collectZIO(pf)
/**
* Determines whether this chunk and the specified chunk have the same length
* and every pair of corresponding elements of this chunk and the specified
* chunk satisfy the specified predicate.
*/
final def corresponds[B](that: Chunk[B])(f: (A, B) => Boolean): Boolean =
if (self.length != that.length) false
else {
val leftIterator = self.chunkIterator
val rightIterator = that.chunkIterator
var index = 0
var equal = true
while (equal && leftIterator.hasNextAt(index) && rightIterator.hasNextAt(index)) {
val a = leftIterator.nextAt(index)
val b = rightIterator.nextAt(index)
index += 1
equal = f(a, b)
}
equal
}
/**
* Deduplicates adjacent elements that are identical.
*/
def dedupe: Chunk[A] = {
val builder = ChunkBuilder.make[A]()
var lastA = null.asInstanceOf[A]
foreach { a =>
if (a != lastA) builder += a
lastA = a
}
builder.result()
}
/**
* Get the element at the specified index.
*/
def double(index: Int)(implicit ev: A <:< Double): Double =
ev(apply(index))
/**
* Drops the first `n` elements of the chunk.
*/
override def drop(n: Int): Chunk[A] = {
val len = self.length
if (n <= 0) self
else if (n >= len) Chunk.empty
else
self match {
case Chunk.Slice(c, o, l) => Chunk.Slice(c, o + n, l - n)
case Chunk.Concat(l, r) =>
if (n > l.length) r.drop(n - l.length)
else Chunk.Concat(l.drop(n), r)
case _ =>
if (depth >= Chunk.MaxDepthBeforeMaterialize) Chunk.Slice(self.materialize, n, len - n)
else Chunk.Slice(self, n, len - n)
}
}
/**
* Drops the last `n` elements of the chunk.
*/
override def dropRight(n: Int): Chunk[A] = {
val len = self.length
if (n <= 0) self
else if (n >= len) Chunk.empty
else
self match {
case Chunk.Slice(c, o, l) => Chunk.Slice(c, o, l - n)
case Chunk.Concat(l, r) =>
if (n > r.length) l.dropRight(n - r.length)
else Chunk.Concat(l, r.dropRight(n))
case _ =>
if (depth >= Chunk.MaxDepthBeforeMaterialize) Chunk.Slice(self.materialize, 0, len - n)
else Chunk.Slice(self, 0, len - n)
}
}
/**
* Drops all elements until the predicate returns true.
*/
def dropUntil(f: A => Boolean): Chunk[A] = {
val iterator = self.chunkIterator
var continue = true
var i = 0
while (continue && iterator.hasNextAt(i)) {
val a = iterator.nextAt(i)
if (f(a)) continue = false
i += 1
}
drop(i)
}
/**
* Drops all elements until the effectful predicate returns true.
*/
def dropUntilZIO[R, E](p: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Chunk[A]] =
ZIO.suspendSucceed {
val iterator = self.chunkIterator
def loop(index: Int): ZIO[R, E, Chunk[A]] =
if (iterator.hasNextAt(index))
p(iterator.nextAt(index)).flatMap(b => if (b) ZIO.succeed(drop(index + 1)) else loop(index + 1))
else
ZIO.succeed(Chunk.empty)
loop(0)
}
/**
* Drops all elements so long as the predicate returns true.
*/
override def dropWhile(f: A => Boolean): Chunk[A] = {
val iterator = self.chunkIterator
var continue = true
var i = 0
while (continue && iterator.hasNextAt(i)) {
val a = iterator.nextAt(i)
if (f(a)) {
i += 1
} else {
continue = false
}
}
drop(i)
}
/**
* Drops all elements so long as the effectful predicate returns true.
*/
def dropWhileZIO[R, E](p: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Chunk[A]] =
ZIO.suspendSucceed {
val iterator = self.chunkIterator
def loop(index: Int): ZIO[R, E, Chunk[A]] =
if (iterator.hasNextAt(index))
p(iterator.nextAt(index)).flatMap(b => if (b) loop(index + 1) else ZIO.succeed(drop(index)))
else
ZIO.succeed(Chunk.empty)
loop(0)
}
override def equals(that: Any): Boolean =
(self eq that.asInstanceOf[AnyRef]) || (that match {
case that: Seq[_] => self.corresponds(that)(_ == _)
case _ => false
})
/**
* Determines whether a predicate is satisfied for at least one element of
* this chunk.
*/
override final def exists(f: A => Boolean): Boolean = {
val iterator = self.chunkIterator
var index = 0
var exists = false
while (!exists && iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
exists = f(a)
}
exists
}
/**
* Returns a filtered subset of this chunk.
*/
override def filter(f: A => Boolean): Chunk[A] = {
val iterator = self.chunkIterator
var index = 0
val builder = ChunkBuilder.make[A]()
builder.sizeHint(length)
while (iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
if (f(a)) {
builder += a
}
}
builder.result()
}
/**
* Filters this chunk by the specified effectful predicate, retaining all
* elements for which the predicate evaluates to true.
*/
final def filterZIO[R, E](f: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Chunk[A]] =
ZIO.filter(self)(f)
/**
* Returns the first element that satisfies the predicate.
*/
override final def find(f: A => Boolean): Option[A] = {
val iterator = self.chunkIterator
var index = 0
var result: Option[A] = None
while (result.isEmpty && iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
if (f(a)) {
result = Some(a)
}
}
result
}
/**
* Returns the first element that satisfies the effectful predicate.
*/
final def findZIO[R, E](f: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Option[A]] =
ZIO.suspendSucceed {
val iterator = self.chunkIterator
var index = 0
def loop(iterator: Chunk.ChunkIterator[A]): ZIO[R, E, Option[A]] =
if (iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
f(a).flatMap {
if (_) ZIO.succeed(Some(a))
else loop(iterator)
}
} else {
ZIO.succeed(None)
}
loop(iterator)
}
/**
* Get the element at the specified index.
*/
def float(index: Int)(implicit ev: A <:< Float): Float =
ev(apply(index))
/**
* Folds over the elements in this chunk from the left.
*/
override def foldLeft[S](s0: S)(f: (S, A) => S): S = {
val iterator = self.chunkIterator
var index = 0
var s = s0
while (iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
s = f(s, a)
}
s
}
/**
* Effectfully folds over the elements in this chunk from the left.
*/
final def foldZIO[R, E, S](s: S)(f: (S, A) => ZIO[R, E, S])(implicit trace: Trace): ZIO[R, E, S] =
ZIO.foldLeft(self)(s)(f)
/**
* Folds over the elements in this chunk from the right.
*/
override def foldRight[S](s0: S)(f: (A, S) => S): S = {
val iterator = self.reverseIterator
var s = s0
while (iterator.hasNext) {
val a = iterator.next()
s = f(a, s)
}
s
}
/**
* Folds over the elements in this chunk from the left. Stops the fold early
* when the condition is not fulfilled.
*/
final def foldWhile[S](s0: S)(pred: S => Boolean)(f: (S, A) => S): S = {
val iterator = self.chunkIterator
var index = 0
var s = s0
var continue = pred(s)
while (continue && iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
s = f(s, a)
continue = pred(s)
}
s
}
final def foldWhileZIO[R, E, S](
z: S
)(pred: S => Boolean)(f: (S, A) => ZIO[R, E, S])(implicit trace: Trace): ZIO[R, E, S] = {
val iterator = self.chunkIterator
def loop(s: S, iterator: Chunk.ChunkIterator[A], index: Int): ZIO[R, E, S] =
if (iterator.hasNextAt(index)) {
if (pred(s)) f(s, iterator.nextAt(index)).flatMap(loop(_, iterator, index + 1))
else ZIO.succeed(s)
} else {
ZIO.succeed(s)
}
loop(z, iterator, 0)
}
/**
* Determines whether a predicate is satisfied for all elements of this chunk.
*/
override final def forall(f: A => Boolean): Boolean = {
val iterator = self.chunkIterator
var index = 0
var exists = true
while (exists && iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
exists = f(a)
}
exists
}
override final def hashCode: Int = toArrayOption match {
case None => Seq.empty[A].hashCode
case Some(array) => array.toSeq.hashCode
}
/**
* Returns the first element of this chunk. Note that this method is partial
* in that it will throw an exception if the chunk is empty. Consider using
* `headOption` to explicitly handle the possibility that the chunk is empty
* or iterating over the elements of the chunk in lower level, performance
* sensitive code unless you really only need the first element of the chunk.
*/
override def head: A =
self(0)
/**
* Returns the first element of this chunk if it exists.
*/
override final def headOption: Option[A] =
if (isEmpty) None else Some(self(0))
/**
* Returns the first index for which the given predicate is satisfied after or
* at some given index.
*/
override final def indexWhere(f: A => Boolean, from: Int): Int = {
val iterator = self.chunkIterator
var i = 0
var result = -1
while (result < 0 && iterator.hasNextAt(i)) {
val a = iterator.nextAt(i)
if (i >= from && f(a)) {
result = i
}
i += 1
}
result
}
/**
* Get the element at the specified index.
*/
def int(index: Int)(implicit ev: A <:< Int): Int =
ev(apply(index))
/**
* Determines if the chunk is empty.
*/
override final def isEmpty: Boolean =
length == 0
/**
* Returns the last element of this chunk if it exists.
*/
override final def lastOption: Option[A] =
if (isEmpty) None else Some(self(self.length - 1))
/**
* Get the element at the specified index.
*/
def long(index: Int)(implicit ev: A <:< Long): Long =
ev(apply(index))
/**
* Statefully maps over the chunk, producing new elements of type `B`.
*/
final def mapAccum[S1, B](s1: S1)(f1: (S1, A) => (S1, B)): (S1, Chunk[B]) = {
val iterator = self.chunkIterator
var index = 0
val builder = ChunkBuilder.make[B]()
builder.sizeHint(length)
var s = s1
while (iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
val tuple = f1(s, a)
s = tuple._1
builder += tuple._2
}
(s, builder.result())
}
/**
* Statefully and effectfully maps over the elements of this chunk to produce
* new elements.
*/
final def mapAccumZIO[R, E, S1, B](
s1: S1
)(f1: (S1, A) => ZIO[R, E, (S1, B)])(implicit trace: Trace): ZIO[R, E, (S1, Chunk[B])] =
ZIO.suspendSucceed {
val iterator = self.chunkIterator
val builder = ChunkBuilder.make[B]()
builder.sizeHint(length)
def loop(s1: S1, index: Int): ZIO[R, E, (S1, Chunk[B])] =
if (iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
f1(s1, a).flatMap { case (s1, b) =>
builder += b
loop(s1, index + 1)
}
} else {
ZIO.succeed((s1, builder.result()))
}
loop(s1, 0)
}
/**
* Effectfully maps the elements of this chunk.
*/
final def mapZIO[R, E, B](f: A => ZIO[R, E, B])(implicit trace: Trace): ZIO[R, E, Chunk[B]] =
ZIO.foreach(self)(f)
/**
* Effectfully maps the elements of this chunk purely for the effects.
*/
final def mapZIODiscard[R, E](f: A => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
ZIO.foreachDiscard(self)(f)
/**
* Effectfully maps the elements of this chunk in parallel.
*/
final def mapZIOPar[R, E, B](f: A => ZIO[R, E, B])(implicit trace: Trace): ZIO[R, E, Chunk[B]] =
ZIO.foreachPar(self)(f)
/**
* Effectfully maps the elements of this chunk in parallel purely for the
* effects.
*/
final def mapZIOParDiscard[R, E](f: A => ZIO[R, E, Any])(implicit trace: Trace): ZIO[R, E, Unit] =
ZIO.foreachParDiscard(self)(f)
/**
* Materializes a chunk into a chunk backed by an array. This method can
* improve the performance of bulk operations.
*/
def materialize[A1 >: A]: Chunk[A1] =
self.toArrayOption[A1] match {
case None => Chunk.Empty
case Some(array) => Chunk.fromArray(array)
}
/**
* Runs `fn` if a `chunk` is not empty or returns default value
*/
def nonEmptyOrElse[B](ifEmpty: => B)(fn: NonEmptyChunk[A] => B): B =
if (isEmpty) ifEmpty else fn(NonEmptyChunk.nonEmpty(self))
/**
* Partitions the elements of this chunk into two chunks using the specified
* function.
*/
override final def partitionMap[B, C](f: A => Either[B, C]): (Chunk[B], Chunk[C]) = {
val bs = ChunkBuilder.make[B]()
val cs = ChunkBuilder.make[C]()
foreach { a =>
f(a) match {
case Left(b) => bs += b
case Right(c) => cs += c
}
}
(bs.result(), cs.result())
}
/**
* Get the element at the specified index.
*/
def short(index: Int)(implicit ev: A <:< Short): Short =
ev(apply(index))
override def slice(from: Int, until: Int): Chunk[A] = {
val start = if (from < 0) 0 else if (from > length) length else from
val end = if (until < start) start else if (until > length) length else until
if (depth > Chunk.MaxDepthBeforeMaterialize) Chunk.Slice(self.materialize, start, end - start)
else Chunk.Slice(self, start, end - start)
}
override def span(f: A => Boolean): (Chunk[A], Chunk[A]) =
splitWhere(!f(_))
/**
* Splits this chunk into `n` equally sized chunks.
*/
final def split(n: Int): Chunk[Chunk[A]] = {
val length = self.length
val quotient = length / n
val remainder = length % n
val iterator = self.chunkIterator
var index = 0
val chunks = ChunkBuilder.make[Chunk[A]]()
var i = 0
while (i < remainder) {
val chunk = ChunkBuilder.make[A]()
var j = 0
while (j <= quotient) {
chunk += iterator.nextAt(index)
index += 1
j += 1
}
chunks += chunk.result()
i += 1
}
if (quotient > 0) {
while (i < n) {
val chunk = ChunkBuilder.make[A]()
var j = 0
while (j < quotient) {
chunk += iterator.nextAt(index)
index += 1
j += 1
}
chunks += chunk.result()
i += 1
}
}
chunks.result()
}
/**
* Returns two splits of this chunk at the specified index.
*/
override final def splitAt(n: Int): (Chunk[A], Chunk[A]) =
(take(n), drop(n))
/**
* Splits this chunk on the first element that matches this predicate.
*/
final def splitWhere(f: A => Boolean): (Chunk[A], Chunk[A]) = {
val iterator = self.chunkIterator
var continue = true
var i = 0
while (continue && iterator.hasNextAt(i)) {
val a = iterator.nextAt(i)
if (f(a)) {
continue = false
} else {
i += 1
}
}
splitAt(i)
}
/**
* Takes the first `n` elements of the chunk.
*/
override def take(n: Int): Chunk[A] =
if (n <= 0) Chunk.Empty
else if (n >= length) this
else
self match {
case Chunk.Slice(c, o, _) => Chunk.Slice(c, o, n)
case Chunk.Concat(l, r) =>
if (n > l.length) Chunk.Concat(l, r.take(n - l.length))
else l.take(n)
case _ =>
if (depth >= Chunk.MaxDepthBeforeMaterialize) Chunk.Slice(self.materialize, 0, n)
else Chunk.Slice(self, 0, n)
}
/**
* Takes the last `n` elements of the chunk.
*/
override def takeRight(n: Int): Chunk[A] =
if (n <= 0) Chunk.Empty
else if (n >= length) this
else
self match {
case Chunk.Slice(c, o, l) => Chunk.Slice(c, o + l - n, n)
case Chunk.Concat(l, r) =>
if (n > r.length) Chunk.Concat(l.takeRight(n - r.length), r)
else r.takeRight(n)
case _ =>
if (depth >= Chunk.MaxDepthBeforeMaterialize) Chunk.Slice(self.materialize, length - n, n)
else Chunk.Slice(self, length - n, n)
}
/**
* Takes all elements so long as the predicate returns true.
*/
override def takeWhile(f: A => Boolean): Chunk[A] = {
val iterator = self.chunkIterator
var continue = true
var i = 0
while (continue && iterator.hasNextAt(i)) {
val a = iterator.nextAt(i)
if (!f(a)) {
continue = false
} else {
i += 1
}
}
take(i)
}
/**
* Takes all elements so long as the effectual predicate returns true.
*/
def takeWhileZIO[R, E](p: A => ZIO[R, E, Boolean])(implicit trace: Trace): ZIO[R, E, Chunk[A]] =
ZIO.suspendSucceed {
val iterator = self.chunkIterator
def loop(index: Int): ZIO[R, E, Chunk[A]] =
if (iterator.hasNextAt(index))
p(iterator.nextAt(index)).flatMap(b => if (b) loop(index + 1) else ZIO.succeed(take(index)))
else
ZIO.succeed(take(index))
loop(0)
}
/**
* Converts the chunk into an array.
*/
override def toArray[A1 >: A: ClassTag]: Array[A1] = {
val dest = Array.ofDim[A1](self.length)
try {
self.toArray(0, dest)
dest
} catch {
case _: ClassCastException =>
val dest = Array.ofDim[AnyRef](self.length).asInstanceOf[Array[A1]]
self.toArray(0, dest)
dest
}
}
/**
* Renders this chunk of bits as a binary string.
*/
final def toBinaryString(implicit ev: A <:< Boolean): String = {
val bits = self.asInstanceOf[Chunk[Boolean]]
val builder = new scala.collection.mutable.StringBuilder
bits.foreach(bit => if (bit) builder.append("1") else builder.append("0"))
builder.toString
}
override final def toList: List[A] = {
val listBuilder = List.newBuilder[A]
fromBuilder(listBuilder)
}
override final def toVector: Vector[A] = {
val vectorBuilder = Vector.newBuilder[A]
fromBuilder(vectorBuilder)
}
override final def toString: String =
toArrayOption.fold("Chunk()")(_.mkString("Chunk(", ",", ")"))
/**
* Zips this chunk with the specified chunk to produce a new chunk with pairs
* of elements from each chunk. The returned chunk will have the length of the
* shorter chunk.
*/
final def zip[B](that: Chunk[B])(implicit zippable: Zippable[A, B]): Chunk[zippable.Out] =
zipWith(that)(zippable.zip(_, _))
/**
* Zips this chunk with the specified chunk to produce a new chunk with pairs
* of elements from each chunk, filling in missing values from the shorter
* chunk with `None`. The returned chunk will have the length of the longer
* chunk.
*/
final def zipAll[B](that: Chunk[B]): Chunk[(Option[A], Option[B])] =
zipAllWith(that)(a => (Some(a), None), b => (None, Some(b)))((a, b) => (Some(a), Some(b)))
/**
* Zips with chunk with the specified chunk to produce a new chunk with pairs
* of elements from each chunk combined using the specified function `both`.
* If one chunk is shorter than the other uses the specified function `left`
* or `right` to map the element that does exist to the result type.
*/
final def zipAllWith[B, C](
that: Chunk[B]
)(left: A => C, right: B => C)(both: (A, B) => C): Chunk[C] = {
val length = self.length.max(that.length)
if (length == 0) Chunk.empty
else {
val leftIterator = self.chunkIterator
val rightIterator = that.chunkIterator
var index = 0
val builder = ChunkBuilder.make[C]()
builder.sizeHint(length)
while (leftIterator.hasNextAt(index) && rightIterator.hasNextAt(index)) {
val a = leftIterator.nextAt(index)
val b = rightIterator.nextAt(index)
index += 1
val c = both(a, b)
builder += c
}
while (leftIterator.hasNextAt(index)) {
val a = leftIterator.nextAt(index)
index += 1
val c = left(a)
builder += c
}
while (rightIterator.hasNextAt(index)) {
val b = rightIterator.nextAt(index)
index += 1
val c = right(b)
builder += c
}
builder.result()
}
}
/**
* Zips this chunk with the specified chunk using the specified combiner.
*/
final def zipWith[B, C](that: Chunk[B])(f: (A, B) => C): Chunk[C] = {
val length = self.length.min(that.length)
if (length == 0) Chunk.empty
else {
val leftIterator = self.chunkIterator
val rightIterator = that.chunkIterator
var index = 0
val builder = ChunkBuilder.make[C]()
builder.sizeHint(length)
while (leftIterator.hasNextAt(index) && rightIterator.hasNextAt(index)) {
val a = leftIterator.nextAt(index)
val b = rightIterator.nextAt(index)
index += 1
val c = f(a, b)
builder += c
}
builder.result()
}
}
/**
* Zips this chunk with the index of every element, starting from the initial
* index value.
*/
final def zipWithIndexFrom(indexOffset: Int): Chunk[(A, Int)] = {
val iterator = self.chunkIterator
var index = 0
val builder = ChunkBuilder.make[(A, Int)]()
builder.sizeHint(length)
var i = indexOffset
while (iterator.hasNextAt(index)) {
val a = iterator.nextAt(index)
index += 1
builder += ((a, i))
i += 1
}
builder.result()
}
//noinspection AccessorLikeMethodIsUnit
protected[zio] final def toArray[A1 >: A](n: Int, dest: Array[A1]): Unit =
toArray(0, dest, n, length)
protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit =
if (isEmpty) () else materialize.toArray(srcPos, dest, destPos, length)
/**
* Appends an element to the chunk.
*/
protected def append[A1 >: A](a1: A1): Chunk[A1] = {
val buffer = Array.ofDim[AnyRef](Chunk.BufferSize)
buffer(0) = a1.asInstanceOf[AnyRef]
if (depth >= Chunk.MaxDepthBeforeMaterialize) Chunk.AppendN(self.materialize, buffer, 1, new AtomicInteger(1))
else Chunk.AppendN(self, buffer, 1, new AtomicInteger(1))
}
/**
* Returns a filtered, mapped subset of the elements of this chunk.
*/
protected def collectChunk[B](pf: PartialFunction[A, B]): Chunk[B] =
if (isEmpty) Chunk.empty else self.materialize.collectChunk(pf)
protected def concatDepth: Int =
0
protected def depth: Int =
0
protected def left: Chunk[A] =
Chunk.empty
/**
* Returns a chunk with the elements mapped by the specified function.
*/
protected def mapChunk[B](f: A => B): Chunk[B] = {
val iter = self.chunkIterator
val newArr = Array.ofDim[AnyRef](self.length).asInstanceOf[Array[B]]
var i = 0
while (iter.hasNextAt(i)) {
newArr(i) = f(iter.nextAt(i))
i += 1
}
Chunk.fromArray(newArr)
}
/**
* Prepends an element to the chunk.
*/
protected def prepend[A1 >: A](a1: A1): Chunk[A1] = {
val buffer = Array.ofDim[AnyRef](Chunk.BufferSize)
buffer(Chunk.BufferSize - 1) = a1.asInstanceOf[AnyRef]
if (depth >= Chunk.MaxDepthBeforeMaterialize) Chunk.PrependN(self.materialize, buffer, 1, new AtomicInteger(1))
else Chunk.PrependN(self, buffer, 1, new AtomicInteger(1))
}
protected def right: Chunk[A] =
Chunk.empty
/**
* Updates an element at the specified index of the chunk.
*/
protected def update[A1 >: A](index: Int, a1: A1): Chunk[A1] =
if (index < 0 || index >= length) throw new IndexOutOfBoundsException(s"Update chunk access to $index")
else {
val bufferIndices = Array.ofDim[Int](Chunk.UpdateBufferSize)
val bufferValues = Array.ofDim[AnyRef](Chunk.UpdateBufferSize)
bufferIndices(0) = index
bufferValues(0) = a1.asInstanceOf[AnyRef]
if (depth >= Chunk.MaxDepthBeforeMaterialize)
Chunk.Update(self.materialize, bufferIndices, bufferValues, 1, new AtomicInteger(1))
else Chunk.Update(self, bufferIndices, bufferValues, 1, new AtomicInteger(1))
}
private final def fromBuilder[A1 >: A, B[_]](builder: Builder[A1, B[A1]]): B[A1] = {
val c = materialize
var i = 0
val len = c.length
builder.sizeHint(len)
while (i < len) {
builder += c(i)
i += 1
}
builder.result()
}
/**
* A helper function that converts the chunk into an array if it is not empty.
*/
private final def toArrayOption[A1 >: A]: Option[Array[A1]] =
self match {
case Chunk.Empty => None
case chunk => Some(chunk.toArray(Chunk.classTagOf(self)))
}
}
object Chunk extends ChunkFactory with ChunkPlatformSpecific {
/**
* Returns a chunk from a number of values.
*/
override def apply[A](as: A*): Chunk[A] =
if (as.size == 1) single(as.head) else fromIterable(as)
/*
* Performs bitwise operations on boolean chunks returning a Chunk.BitChunk
*/
private def bitwise(
left: Chunk[Boolean],
right: Chunk[Boolean],
op: (Boolean, Boolean) => Boolean
): Chunk.BitChunkByte = {
val bits = left.length min right.length
val fullBytes = bits >> 3
val remBits = bits & 7
val arr = Array.ofDim[Byte](
if (remBits == 0) fullBytes else fullBytes + 1
)
var i = 0
var mask = 128
while (i < fullBytes) {
var byte = 0
mask = 128
(0 until 8).foreach { k =>
byte = byte | (if (op(left(i * 8 + k), right(i * 8 + k))) mask else 0)
mask >>= 1
}
arr(i) = byte.toByte
i += 1
}
if (remBits != 0) {
val offset = fullBytes * 8
var byte = 0
mask = 128
(0 until remBits).foreach { k =>
byte = byte | (if (op(left(offset + k), right(offset + k))) mask else 0)
mask >>= 1
}
arr(fullBytes) = byte.toByte
}
Chunk.BitChunkByte(Chunk.fromArray(arr), 0, bits)
}
/**
* Returns the empty chunk.
*/
override def empty[A]: Chunk[A] =
Empty
/**
* Returns a chunk backed by an array.
*
* WARNING: The array must not be mutated after creating the chunk.
*/
def fromArray[A](array: Array[A]): Chunk[A] =
(if (array.isEmpty) Empty
else
(array.asInstanceOf[AnyRef]: @unchecked) match {
case x: Array[AnyRef] => AnyRefArray(x, 0, array.length)
case x: Array[Int] => IntArray(x, 0, array.length)
case x: Array[Double] => DoubleArray(x, 0, array.length)
case x: Array[Long] => LongArray(x, 0, array.length)
case x: Array[Float] => FloatArray(x, 0, array.length)
case x: Array[Char] => CharArray(x, 0, array.length)
case x: Array[Byte] => ByteArray(x, 0, array.length)
case x: Array[Short] => ShortArray(x, 0, array.length)
case x: Array[Boolean] => BooleanArray(x, 0, array.length)
}).asInstanceOf[Chunk[A]]
/**
* Returns a chunk backed by a [[java.nio.ByteBuffer]].
*/
def fromByteBuffer(buffer: ByteBuffer): Chunk[Byte] = {
val dest = Array.ofDim[Byte](buffer.remaining())
val pos = buffer.position()
buffer.get(dest)
buffer.position(pos)
Chunk.fromArray(dest)
}
/**
* Returns a chunk backed by a [[java.nio.CharBuffer]].
*/
def fromCharBuffer(buffer: CharBuffer): Chunk[Char] = {
val dest = Array.ofDim[Char](buffer.remaining())
val pos = buffer.position()
buffer.get(dest)
buffer.position(pos)
Chunk.fromArray(dest)
}
/**
* Returns a chunk backed by a [[java.nio.DoubleBuffer]].
*/
def fromDoubleBuffer(buffer: DoubleBuffer): Chunk[Double] = {
val dest = Array.ofDim[Double](buffer.remaining())
val pos = buffer.position()
buffer.get(dest)
buffer.position(pos)
Chunk.fromArray(dest)
}
/**
* Returns a chunk backed by a [[java.nio.FloatBuffer]].
*/
def fromFloatBuffer(buffer: FloatBuffer): Chunk[Float] = {
val dest = Array.ofDim[Float](buffer.remaining())
val pos = buffer.position()
buffer.get(dest)
buffer.position(pos)
Chunk.fromArray(dest)
}
/**
* Returns a chunk backed by a [[java.nio.IntBuffer]].
*/
def fromIntBuffer(buffer: IntBuffer): Chunk[Int] = {
val dest = Array.ofDim[Int](buffer.remaining())
val pos = buffer.position()
buffer.get(dest)
buffer.position(pos)
Chunk.fromArray(dest)
}
/**
* Returns a chunk backed by a [[java.nio.LongBuffer]].
*/
def fromLongBuffer(buffer: LongBuffer): Chunk[Long] = {
val dest = Array.ofDim[Long](buffer.remaining())
val pos = buffer.position()
buffer.get(dest)
buffer.position(pos)
Chunk.fromArray(dest)
}
/**
* Returns a chunk backed by a [[java.nio.ShortBuffer]].
*/
def fromShortBuffer(buffer: ShortBuffer): Chunk[Short] = {
val dest = Array.ofDim[Short](buffer.remaining())
val pos = buffer.position()
buffer.get(dest)
buffer.position(pos)
Chunk.fromArray(dest)
}
/**
* Returns a chunk backed by an iterable.
*/
def fromIterable[A](it: Iterable[A]): Chunk[A] =
it match {
case chunk: Chunk[A] => chunk
case iterable if iterable.isEmpty => Empty
case vector: Vector[A] => VectorChunk(vector)
case iterable =>
val builder = ChunkBuilder.make[A]()
builder.sizeHint(iterable)
builder ++= iterable
builder.result()
}
/**
* Creates a chunk from an iterator.
*/
def fromIterator[A](iterator: Iterator[A]): Chunk[A] =
if (iterator.hasNext) {
val builder = ChunkBuilder.make[A]()
builder ++= iterator
builder.result()
} else Empty
/**
* Returns a chunk backed by a Java iterable.
*/
def fromJavaIterable[A](iterable: java.lang.Iterable[A]): Chunk[A] = {
val builder = ChunkBuilder.make[A]()
if (iterable.isInstanceOf[java.util.Collection[_]]) {
builder.sizeHint(iterable.asInstanceOf[java.util.Collection[_]].size())
}
val iterator = iterable.iterator()
while (iterator.hasNext()) {
builder += iterator.next()
}
builder.result()
}
/**
* Creates a chunk from a Java iterator.
*/
def fromJavaIterator[A](iterator: java.util.Iterator[A]): Chunk[A] = {
val builder = ChunkBuilder.make[A]()
while (iterator.hasNext()) {
val a = iterator.next()
builder += a
}
builder.result()
}
override def fill[A](n: Int)(elem: => A): Chunk[A] =
if (n <= 0) Chunk.empty
else {
val builder = ChunkBuilder.make[A]()
builder.sizeHint(n)
var i = 0
while (i < n) {
builder += elem
i += 1
}
builder.result()
}
override def iterate[A](start: A, len: Int)(f: A => A): Chunk[A] =
if (len <= 0) Chunk.empty
else {
val builder = ChunkBuilder.make[A]()
builder.sizeHint(len)
var i = 0
var a = start
while (i < len) {
builder += a
a = f(a)
i += 1
}
builder.result()
}
def newBuilder[A]: ChunkBuilder[A] =
ChunkBuilder.make()
/**
* Returns a singleton chunk, eagerly evaluated.
*/
def single[A](a: A): Chunk[A] =
Singleton(a)
/**
* Alias for [[Chunk.single]].
*/
def succeed[A](a: A): Chunk[A] =
single(a)
/**
* Constructs a `Chunk` by repeatedly applying the function `f` as long as it
* returns `Some`.
*/
def unfold[S, A](s: S)(f: S => Option[(A, S)]): Chunk[A] = {
@tailrec
def go(s: S, builder: ChunkBuilder[A]): Chunk[A] =
f(s) match {
case Some((a, s)) => go(s, builder += a)
case None => builder.result()
}
go(s, ChunkBuilder.make[A]())
}
/**
* Constructs a `Chunk` by repeatedly applying the effectual function `f` as
* long as it returns `Some`.
*/
def unfoldZIO[R, E, A, S](
s: S
)(f: S => ZIO[R, E, Option[(A, S)]])(implicit trace: Trace): ZIO[R, E, Chunk[A]] =
ZIO.suspendSucceed {
def go(s: S, builder: ChunkBuilder[A]): ZIO[R, E, Chunk[A]] =
f(s).flatMap {
case Some((a, s)) => go(s, builder += a)
case None => ZIO.succeed(builder.result())
}
go(s, ChunkBuilder.make[A]())
}
/**
* The unit chunk
*/
val unit: Chunk[Unit] = single(())
/**
* Returns the `ClassTag` for the element type of the chunk.
*/
private[zio] def classTagOf[A](chunk: Chunk[A]): ClassTag[A] =
chunk match {
case x: AppendN[_] => x.classTag.asInstanceOf[ClassTag[A]]
case x: Arr[_] => x.classTag.asInstanceOf[ClassTag[A]]
case x: Concat[_] => x.classTag.asInstanceOf[ClassTag[A]]
case Empty => classTag[java.lang.Object].asInstanceOf[ClassTag[A]]
case x: PrependN[_] => x.classTag.asInstanceOf[ClassTag[A]]
case x: Singleton[_] => x.classTag.asInstanceOf[ClassTag[A]]
case x: Slice[_] => x.classTag.asInstanceOf[ClassTag[A]]
case x: Update[_] => x.classTag.asInstanceOf[ClassTag[A]]
case x: VectorChunk[_] => x.classTag.asInstanceOf[ClassTag[A]]
case x: ChunkPackedBoolean[_] => x.classTag.asInstanceOf[ClassTag[A]]
case _: BitChunk[_] => ClassTag.Boolean.asInstanceOf[ClassTag[A]]
}
sealed trait IsText[-T] {
def convert(chunk: Chunk[T]): String
}
object IsText {
implicit val byteIsText: IsText[Byte] =
new IsText[Byte] { def convert(chunk: Chunk[Byte]): String = new String(chunk.toArray) }
implicit val charIsText: IsText[Char] =
new IsText[Char] { def convert(chunk: Chunk[Char]): String = new String(chunk.toArray) }
implicit val strIsText: IsText[String] =
new IsText[String] { def convert(chunk: Chunk[String]): String = chunk.toArray.mkString }
}
/**
* The maximum number of elements in the buffer for fast append.
*/
private val BufferSize: Int =
64
/**
* The maximum depth of elements in the chunk before it is materialized.
*/
private val MaxDepthBeforeMaterialize: Int =
128
/**
* The maximum number of elements in the buffer for fast update.
*/
private val UpdateBufferSize: Int =
256
private final case class AppendN[A](start: Chunk[A], buffer: Array[AnyRef], bufferUsed: Int, chain: AtomicInteger)
extends Chunk[A] { self =>
def chunkIterator: ChunkIterator[A] =
start.chunkIterator ++ ChunkIterator.fromArray(buffer.asInstanceOf[Array[A]]).sliceIterator(0, bufferUsed)
implicit val classTag: ClassTag[A] = classTagOf(start)
override val depth: Int =
start.depth + 1
val length: Int =
start.length + bufferUsed
override protected def append[A1 >: A](a1: A1): Chunk[A1] =
if (bufferUsed < buffer.length && chain.compareAndSet(bufferUsed, bufferUsed + 1)) {
buffer(bufferUsed) = a1.asInstanceOf[AnyRef]
AppendN(start, buffer, bufferUsed + 1, chain)
} else {
val buffer = Array.ofDim[AnyRef](BufferSize)
buffer(0) = a1.asInstanceOf[AnyRef]
val chunk = Chunk.fromArray(self.buffer.asInstanceOf[Array[A1]]).take(bufferUsed)
AppendN(start ++ chunk, buffer, 1, new AtomicInteger(1))
}
def apply(n: Int): A =
if (n < 0 || n >= length) throw new IndexOutOfBoundsException(s"Append chunk access to $n")
else if (n < start.length) start(n)
else buffer(n - start.length).asInstanceOf[A]
override protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit = {
val n = math.max(math.min(math.min(length, start.length - srcPos), dest.length - destPos), 0)
start.toArray(math.min(start.length, srcPos), dest, destPos, n)
Array.copy(buffer, math.max(srcPos - start.length, 0), dest, destPos + n, length - n)
}
}
private final case class PrependN[A](end: Chunk[A], buffer: Array[AnyRef], bufferUsed: Int, chain: AtomicInteger)
extends Chunk[A] { self =>
def chunkIterator: ChunkIterator[A] =
ChunkIterator
.fromArray(buffer.asInstanceOf[Array[A]])
.sliceIterator(BufferSize - bufferUsed, bufferUsed) ++ end.chunkIterator
implicit val classTag: ClassTag[A] = classTagOf(end)
override val depth: Int =
end.depth + 1
val length: Int =
end.length + bufferUsed
override protected def prepend[A1 >: A](a1: A1): Chunk[A1] =
if (bufferUsed < buffer.length && chain.compareAndSet(bufferUsed, bufferUsed + 1)) {
buffer(BufferSize - bufferUsed - 1) = a1.asInstanceOf[AnyRef]
PrependN(end, buffer, bufferUsed + 1, chain)
} else {
val buffer = Array.ofDim[AnyRef](BufferSize)
buffer(BufferSize - 1) = a1.asInstanceOf[AnyRef]
val chunk = Chunk.fromArray(self.buffer.asInstanceOf[Array[A1]]).takeRight(bufferUsed)
PrependN(chunk ++ end, buffer, 1, new AtomicInteger(1))
}
def apply(n: Int): A =
if (n < 0 || n >= length) throw new IndexOutOfBoundsException(s"Prepend chunk access to $n")
else if (n < bufferUsed) buffer(BufferSize - bufferUsed + n).asInstanceOf[A]
else end(n - bufferUsed)
override protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit = {
val n = math.max(math.min(math.min(length, bufferUsed - srcPos), dest.length - destPos), 0)
Array.copy(buffer, math.min(BufferSize, BufferSize - bufferUsed + srcPos), dest, destPos, n)
end.toArray(math.max(srcPos - bufferUsed, 0), dest, destPos + n, length - n)
}
}
private final case class Update[A](
chunk: Chunk[A],
bufferIndices: Array[Int],
bufferValues: Array[AnyRef],
used: Int,
chain: AtomicInteger
) extends Chunk[A] { self =>
def chunkIterator: ChunkIterator[A] =
ChunkIterator.fromArray(self.toArray)
implicit val classTag: ClassTag[A] = Chunk.classTagOf(chunk)
override val depth: Int =
chunk.depth + 1
val length: Int =
chunk.length
def apply(i: Int): A = {
var j = used - 1
var a = null.asInstanceOf[A]
while (j >= 0) {
if (bufferIndices(j) == i) {
a = bufferValues(j).asInstanceOf[A]
j = -1
} else {
j -= 1
}
}
if (a != null) a else chunk(i)
}
override protected def update[A1 >: A](i: Int, a: A1): Chunk[A1] =
if (i < 0 || i >= length) throw new IndexOutOfBoundsException(s"Update chunk access to $i")
else if (used < UpdateBufferSize && chain.compareAndSet(used, used + 1)) {
bufferIndices(used) = i
bufferValues(used) = a.asInstanceOf[AnyRef]
Update(chunk, bufferIndices, bufferValues, used + 1, chain)
} else {
val bufferIndices = Array.ofDim[Int](UpdateBufferSize)
val bufferValues = Array.ofDim[AnyRef](UpdateBufferSize)
bufferIndices(0) = i
bufferValues(0) = a.asInstanceOf[AnyRef]
val array = self.asInstanceOf[Chunk[AnyRef]].toArray
Update(Chunk.fromArray(array.asInstanceOf[Array[A1]]), bufferIndices, bufferValues, 1, new AtomicInteger(1))
}
override protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit = {
chunk.toArray(srcPos, dest, destPos, length)
var i = 0
while (i < used) {
val index = bufferIndices(i)
val value = self.bufferValues(i)
if (index >= srcPos && index < srcPos + length)
dest(index + destPos) = value.asInstanceOf[A1]
i += 1
}
}
}
private[zio] sealed abstract class Arr[A] extends Chunk[A] with Serializable { self =>
val array: Array[A]
implicit val classTag: ClassTag[A] =
ClassTag(array.getClass.getComponentType)
override def collectZIO[R, E, B](
pf: PartialFunction[A, ZIO[R, E, B]]
)(implicit trace: Trace): ZIO[R, E, Chunk[B]] = ZIO.suspendSucceed {
val builder = ChunkBuilder.make[B]()
builder.sizeHint(length)
val orElse = (_: A) => ZIO.succeed(null.asInstanceOf[B])
def loop(index: Int): ZIO[R, E, Chunk[B]] =
if (index < length) {
val a = self(index)
pf.applyOrElse(a, orElse).flatMap { b =>
if (b != null) builder += b
loop(index + 1)
}
} else ZIO.succeed(builder.result())
loop(0)
}
override def collectWhile[B](pf: PartialFunction[A, B]): Chunk[B] = {
val self = array
val len = self.length
val builder = ChunkBuilder.make[B]()
builder.sizeHint(len)
var i = 0
var done = false
while (!done && i < len) {
val b = pf.applyOrElse(self(i), (_: A) => null.asInstanceOf[B])
if (b != null) {
builder += b
} else {
done = true
}
i += 1
}
builder.result()
}
override def collectWhileZIO[R, E, B](
pf: PartialFunction[A, ZIO[R, E, B]]
)(implicit trace: Trace): ZIO[R, E, Chunk[B]] =
ZIO.suspendSucceed {
val builder = ChunkBuilder.make[B]()
builder.sizeHint(length)
val orElse = (_: A) => ZIO.succeed(null.asInstanceOf[B])
def loop(index: Int): ZIO[R, E, Chunk[B]] =
if (index < length) {
val a = self(index)
pf.applyOrElse(a, orElse).flatMap { b =>
if (b != null) {
builder += b
loop(index + 1)
} else {
ZIO.succeed(builder.result())
}
}
} else ZIO.succeed(builder.result())
loop(0)
}
override def dropWhile(f: A => Boolean): Chunk[A] = {
val self = array
val len = self.length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
drop(i)
}
override def filter(f: A => Boolean): Chunk[A] = {
val len = self.length
val builder = ChunkBuilder.make[A]()
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder += elem
}
i += 1
}
builder.result()
}
override def foldLeft[S](s0: S)(f: (S, A) => S): S = {
val len = self.length
var s = s0
var i = 0
while (i < len) {
s = f(s, self(i))
i += 1
}
s
}
override def foldRight[S](s0: S)(f: (A, S) => S): S = {
val self = array
val len = self.length
var s = s0
var i = len - 1
while (i >= 0) {
s = f(self(i), s)
i -= 1
}
s
}
override def foreach[B](f: A => B): Unit =
array.foreach(f)
override def iterator: Iterator[A] =
array.iterator
override def materialize[A1 >: A]: Chunk[A1] =
self
/**
* Takes all elements so long as the predicate returns true.
*/
override def takeWhile(f: A => Boolean): Chunk[A] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
override protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit =
Array.copy(array, srcPos, dest, destPos, length)
override protected def collectChunk[B](pf: PartialFunction[A, B]): Chunk[B] = {
val len = self.length
val builder = ChunkBuilder.make[B]()
builder.sizeHint(len)
var i = 0
while (i < len) {
val b = pf.applyOrElse(self(i), (_: A) => null.asInstanceOf[B])
if (b != null) {
builder += b
}
i += 1
}
builder.result()
}
override protected def mapChunk[B](f: A => B): Chunk[B] = {
implicit val ct: ClassTag[B] = ClassTag.AnyRef.asInstanceOf[ClassTag[B]]
Chunk.fromArray(self.array.map(f))
}
}
private final case class Concat[A](override protected val left: Chunk[A], override protected val right: Chunk[A])
extends Chunk[A] {
self =>
def chunkIterator: ChunkIterator[A] =
left.chunkIterator ++ right.chunkIterator
implicit val classTag: ClassTag[A] = {
val lct = classTagOf(left)
val rct = classTagOf(right)
if (left eq Empty) lct
else if (right eq Empty) rct
else if (lct == rct) lct
else ClassTag.AnyRef.asInstanceOf[ClassTag[A]]
}
override val concatDepth: Int =
1 + math.max(left.concatDepth, right.concatDepth)
override val depth: Int =
1 + math.max(left.depth, right.depth)
override val length: Int =
left.length + right.length
override def apply(n: Int): A =
if (n < left.length) left(n) else right(n - left.length)
override def foreach[B](f: A => B): Unit = {
left.foreach(f)
right.foreach(f)
}
override def iterator: Iterator[A] =
left.iterator ++ right.iterator
override protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit = {
val n = math.max(math.min(math.min(length, left.length - srcPos), dest.length - destPos), 0)
left.toArray(math.min(left.length, srcPos), dest, destPos, n)
right.toArray(math.max(srcPos - left.length, 0), dest, destPos + n, length - n)
}
}
private final case class Singleton[A](a: A) extends Chunk[A] with ChunkIterator[A] { self =>
implicit val classTag: ClassTag[A] =
Tags.fromValue(a)
override val length =
1
override def apply(n: Int): A =
if (n == 0) a
else throw new ArrayIndexOutOfBoundsException(s"Singleton chunk access to $n")
override def foreach[B](f: A => B): Unit = {
val _ = f(a)
}
override protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit =
dest(destPos) = a
def chunkIterator: ChunkIterator[A] =
self
def hasNextAt(index: Int): Boolean =
index == 0
def nextAt(index: Int): A =
if (index == 0) a
else throw new ArrayIndexOutOfBoundsException(s"Singleton chunk access to $index")
def sliceIterator(offset: Int, length: Int): ChunkIterator[A] =
if (offset <= 0 && length >= 1) self
else ChunkIterator.empty
}
private final case class Slice[A](private val chunk: Chunk[A], offset: Int, l: Int) extends Chunk[A] {
def chunkIterator: ChunkIterator[A] =
chunk.chunkIterator.sliceIterator(offset, l)
implicit val classTag: ClassTag[A] =
classTagOf(chunk)
override val depth: Int =
chunk.depth + 1
override val length: Int =
l
override def apply(n: Int): A =
chunk.apply(offset + n)
override def foreach[B](f: A => B): Unit = {
var i = 0
while (i < length) {
f(apply(i))
i += 1
}
}
override def iterator: Iterator[A] =
chunk.iterator.slice(offset, offset + l)
override protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit =
chunk.toArray(srcPos + offset, dest, destPos, math.min(length, l - srcPos))
}
private final case class VectorChunk[A](private val vector: Vector[A]) extends Chunk[A] {
def chunkIterator: ChunkIterator[A] =
ChunkIterator.fromVector(vector)
implicit val classTag: ClassTag[A] =
Tags.fromValue(vector(0))
override val length: Int =
vector.length
override def apply(n: Int): A =
vector(n)
override def foreach[B](f: A => B): Unit =
vector.foreach(f)
override protected[zio] def toArray[A1 >: A](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit =
vector.drop(srcPos).copyToArray(dest, destPos, length)
}
private[zio] trait BitOps[T] {
def zero: T
def one: T
def reverse(a: T): T
def <<(x: T, n: Int): T
def >>(x: T, n: Int): T
def |(x: T, y: T): T
def &(x: T, y: T): T
def ^(x: T, y: T): T
def invert(x: T): T
def classTag: ClassTag[T]
}
private[zio] object BitOps {
def apply[T](implicit ops: BitOps[T]): BitOps[T] = ops
implicit val ByteOps: BitOps[Byte] = new BitOps[Byte] {
def zero: Byte = 0
def one: Byte = 1
def reverse(a: Byte): Byte = throw new UnsupportedOperationException
def <<(x: Byte, n: Int): Byte = (x << n).toByte
def >>(x: Byte, n: Int): Byte = (x >> n).toByte
def |(x: Byte, y: Byte): Byte = (x | y).toByte
def &(x: Byte, y: Byte): Byte = (x & y).toByte
def ^(x: Byte, y: Byte): Byte = (x ^ y).toByte
def invert(x: Byte): Byte = (~x).toByte
def classTag: ClassTag[Byte] = ClassTag.Byte
}
implicit val IntOps: BitOps[Int] = new BitOps[Int] {
def zero: Int = 0
def one: Int = 1
def reverse(a: Int): Int = Integer.reverse(a)
def <<(x: Int, n: Int): Int = x << n
def >>(x: Int, n: Int): Int = x >> n
def |(x: Int, y: Int): Int = x | y
def &(x: Int, y: Int): Int = x & y
def ^(x: Int, y: Int): Int = x ^ y
def invert(x: Int): Int = ~x
def classTag: ClassTag[Int] = ClassTag.Int
}
implicit val LongOps: BitOps[Long] = new BitOps[Long] {
def zero: Long = 0L
def one: Long = 1L
def reverse(a: Long): Long = java.lang.Long.reverse(a)
def <<(x: Long, n: Int): Long = x << n
def >>(x: Long, n: Int): Long = x >> n
def |(x: Long, y: Long): Long = x | y
def &(x: Long, y: Long): Long = x & y
def ^(x: Long, y: Long): Long = x ^ y
def invert(x: Long): Long = ~x
def classTag: ClassTag[Long] = ClassTag.Long
}
}
private[zio] sealed abstract class BitChunk[T](
chunk: Chunk[T],
val bits: Int
) extends Chunk[Boolean]
with ChunkIterator[Boolean] {
self =>
protected val minBitIndex: Int
protected val maxBitIndex: Int
protected val bitsLog2: Int = (log(bits.toDouble) / log(2d)).toInt
val length: Int =
maxBitIndex - minBitIndex
protected def elementAt(n: Int): T
protected def newBitChunk(chunk: Chunk[T], min: Int, max: Int): BitChunk[T]
override def drop(n: Int): Chunk[Boolean] = {
val index = (minBitIndex + n) min maxBitIndex
val toDrop = index >> bitsLog2
val min = index & bits - 1
val max = maxBitIndex - index + min
newBitChunk(chunk.drop(toDrop), min, max)
}
override def take(n: Int): Chunk[Boolean] = {
val index = (minBitIndex + n) min maxBitIndex
val toTake = (index + bits - 1) >> bitsLog2
newBitChunk(chunk.take(toTake), minBitIndex, index)
}
override def foreach[A](f: Boolean => A): Unit = {
val minLongIndex = (minBitIndex + bits - 1) >> bitsLog2
val maxLongIndex = maxBitIndex >> bitsLog2
val minFullBitIndex = (minLongIndex << bitsLog2) min maxBitIndex
val maxFullBitIndex = (maxLongIndex << bitsLog2) max minFullBitIndex
var i = minBitIndex
while (i < minFullBitIndex) {
f(self.apply(i))
i += 1
}
i = minLongIndex
while (i < maxLongIndex) {
foreachElement(f, elementAt(i))
i += 1
}
i = maxFullBitIndex
while (i < maxBitIndex) {
f(self.apply(i))
i += 1
}
}
protected def foreachElement[A](f: Boolean => A, elem: T): Unit
override protected[zio] def toArray[A1 >: Boolean](
srcPos: Int,
dest: Array[A1],
destPos: Int,
length: Int
): Unit = {
var i = 0
while (i < length) {
dest(i + destPos) = self.apply(i + srcPos)
i += 1
}
}
def chunkIterator: ChunkIterator[Boolean] =
self
def hasNextAt(index: Int): Boolean =
index < length
def nextAt(index: Int): Boolean =
self(index)
override def slice(from: Int, until: Int): BitChunk[T] = {
val lo = from max 0
if (lo <= 0 && until >= self.length) self
else if (lo >= self.length || until <= lo) newBitChunk(Chunk.empty, 0, 0)
else newBitChunk(chunk, self.minBitIndex + lo, self.minBitIndex + until min self.maxBitIndex)
}
def sliceIterator(offset: Int, length: Int): ChunkIterator[Boolean] = {
val lo = offset max 0
if (lo <= 0 && length >= self.length) self
else if (lo >= self.length || length <= 0) ChunkIterator.empty
else newBitChunk(chunk, self.minBitIndex + lo, self.minBitIndex + lo + length min self.maxBitIndex)
}
}
object BitChunk {
sealed trait Endianness
object Endianness {
case object BigEndian extends Endianness
case object LittleEndian extends Endianness
}
}
private[zio] final case class BitChunkByte(bytes: Chunk[Byte], minBitIndex: Int, maxBitIndex: Int)
extends BitChunk[Byte](bytes, 8) {
self =>
override val length: Int =
maxBitIndex - minBitIndex
override def apply(n: Int): Boolean =
(bytes(n >> bitsLog2) & (1 << (bits - 1 - (n & bits - 1)))) != 0
override protected def elementAt(n: Int): Byte = bytes(n)
override protected def newBitChunk(bytes: Chunk[Byte], min: Int, max: Int): BitChunk[Byte] =
BitChunkByte(bytes, min, max)
override protected def foreachElement[A](f: Boolean => A, byte: Byte): Unit = {
f((byte & 128) != 0)
f((byte & 64) != 0)
f((byte & 32) != 0)
f((byte & 16) != 0)
f((byte & 8) != 0)
f((byte & 4) != 0)
f((byte & 2) != 0)
f((byte & 1) != 0)
()
}
override def toPackedByte(implicit ev: Boolean <:< Boolean): Chunk[Byte] =
if (minBitIndex == maxBitIndex) Chunk.empty
else if ((minBitIndex & bits - 1) != 0) ChunkPackedBoolean[Byte](self, bits, BitChunk.Endianness.BigEndian)
else if ((maxBitIndex & bits - 1) != 0) {
val minByteIndex = minBitIndex >> bitsLog2
val maxByteIndex = maxBitIndex >> bitsLog2
val maxByte = bytes(maxByteIndex)
val maxByteValue = (maxByte & 0xff) >> bits - (maxBitIndex & bits - 1)
bytes.slice(minByteIndex, maxByteIndex) :+ maxByteValue.toByte
} else bytes
private def nthByte(n: Int): Byte = {
val offset = minBitIndex & 7
val startByte = minBitIndex >> 3
if (offset == 0) {
bytes(n + startByte)
} else {
val leftover = minBitIndex + (n + 1) * 8 - maxBitIndex
if (leftover <= 0) {
val index = n + startByte
val first = ((255 >> offset) & bytes(index)) << offset
val second = (255 << (8 - offset) & 255 & bytes(index + 1)) >> (8 - offset)
(first | second).asInstanceOf[Byte]
} else {
throw new ArrayIndexOutOfBoundsException(s"There are only $leftover bits left.")
}
}
}
private def bitwise(that: BitChunkByte, f: (Byte, Byte) => Byte, g: (Boolean, Boolean) => Boolean): BitChunkByte = {
val bits = self.length min that.length
val bytes = bits >> 3
val leftovers = bits - bytes * 8
val arr = Array.ofDim[Byte](
if (leftovers == 0) bytes else bytes + 1
)
(0 until bytes).foreach { n =>
arr(n) = f(self.nthByte(n), that.nthByte(n))
}
if (leftovers != 0) {
val offset = bytes * 8
var last: Byte = null.asInstanceOf[Byte]
var mask = 128
var i = 0
while (i < leftovers) {
if (g(self.apply(offset + self.minBitIndex + i), that.apply(offset + that.minBitIndex + i)))
last = (last | mask).asInstanceOf[Byte]
i += 1
mask >>= 1
}
arr(bytes) = last
}
BitChunkByte(Chunk.fromArray(arr), 0, bits)
}
def and(that: BitChunkByte): BitChunkByte =
bitwise(that, (l, r) => (l & r).asInstanceOf[Byte], _ && _)
def &(that: BitChunkByte): BitChunkByte =
bitwise(that, (l, r) => (l & r).asInstanceOf[Byte], _ && _)
def or(that: BitChunkByte): BitChunkByte =
bitwise(that, (l, r) => (l | r).asInstanceOf[Byte], _ || _)
def |(that: BitChunkByte): BitChunkByte =
bitwise(that, (l, r) => (l | r).asInstanceOf[Byte], _ || _)
def xor(that: BitChunkByte): BitChunkByte =
bitwise(that, (l, r) => (l ^ r).asInstanceOf[Byte], _ ^ _)
def ^(that: BitChunkByte): BitChunkByte =
bitwise(that, (l, r) => (l ^ r).asInstanceOf[Byte], _ ^ _)
def negate: BitChunkByte = {
val bits = self.length
val bytes = bits >> 3
val leftovers = bits - bytes * 8
val arr = Array.ofDim[Byte](
if (leftovers == 0) bytes else bytes + 1
)
(0 until bytes).foreach { n =>
arr(n) = (~self.nthByte(n)).asInstanceOf[Byte]
}
if (leftovers != 0) {
val offset = bytes * 8 + self.minBitIndex
var last: Byte = null.asInstanceOf[Byte]
var mask = 128
var i = 0
while (i < leftovers) {
if (!self.apply(offset + i))
last = (last | mask).asInstanceOf[Byte]
i += 1
mask >>= 1
}
arr(bytes) = last
}
BitChunkByte(Chunk.fromArray(arr), 0, bits)
}
}
private[zio] final case class BitChunkInt(
ints: Chunk[Int],
endianness: BitChunk.Endianness,
minBitIndex: Int,
maxBitIndex: Int
) extends BitChunk[Int](ints, 32) {
self =>
override val length: Int =
maxBitIndex - minBitIndex
override protected def elementAt(n: Int): Int =
respectEndian(endianness, ints(n))
override def apply(n: Int): Boolean =
(elementAt(n >> bitsLog2) & (1 << (bits - 1 - (n & bits - 1)))) != 0
override protected def newBitChunk(chunk: Chunk[Int], min: Int, max: Int): BitChunk[Int] =
BitChunkInt(chunk, endianness, min, max)
override protected def foreachElement[A](f: Boolean => A, int: Int): Unit = {
f((int & 0x80000000) != 0)
f((int & 0x40000000) != 0)
f((int & 0x20000000) != 0)
f((int & 0x10000000) != 0)
f((int & 0x8000000) != 0)
f((int & 0x4000000) != 0)
f((int & 0x2000000) != 0)
f((int & 0x1000000) != 0)
f((int & 0x800000) != 0)
f((int & 0x400000) != 0)
f((int & 0x200000) != 0)
f((int & 0x100000) != 0)
f((int & 0x80000) != 0)
f((int & 0x40000) != 0)
f((int & 0x20000) != 0)
f((int & 0x10000) != 0)
f((int & 0x8000) != 0)
f((int & 0x4000) != 0)
f((int & 0x2000) != 0)
f((int & 0x1000) != 0)
f((int & 0x800) != 0)
f((int & 0x400) != 0)
f((int & 0x200) != 0)
f((int & 0x100) != 0)
f((int & 0x80) != 0)
f((int & 0x40) != 0)
f((int & 0x20) != 0)
f((int & 0x10) != 0)
f((int & 0x8) != 0)
f((int & 0x4) != 0)
f((int & 0x2) != 0)
f((int & 0x1) != 0)
()
}
override def toPackedInt(endianness: BitChunk.Endianness)(implicit ev: Boolean <:< Boolean): Chunk[Int] =
if (minBitIndex == maxBitIndex) Chunk.empty
else if ((minBitIndex & bits - 1) != 0) ChunkPackedBoolean[Int](self, bits, endianness)
else if ((maxBitIndex & bits - 1) != 0) {
val minIntIndex = minBitIndex >> bitsLog2
val maxIntIndex = maxBitIndex >> bitsLog2
val maxInt = elementAt(maxIntIndex)
val maxIntValue = maxInt >>> bits - (maxBitIndex & bits - 1)
val fullInts = ints.slice(minIntIndex, maxIntIndex)
if (self.endianness == endianness) fullInts :+ respectEndian(endianness, maxIntValue)
else fullInts.map(Integer.reverse) :+ respectEndian(endianness, maxIntValue)
} else if (self.endianness == endianness) ints
else ints.map(Integer.reverse)
private def respectEndian(endianness: BitChunk.Endianness, bigEndianValue: Int) =
if (endianness == BitChunk.Endianness.BigEndian) bigEndianValue
else Integer.reverse(bigEndianValue)
}
private[zio] final case class BitChunkLong(
longs: Chunk[Long],
endianness: BitChunk.Endianness,
minBitIndex: Int,
maxBitIndex: Int
) extends BitChunk[Long](longs, 64) {
self =>
override protected def elementAt(n: Int): Long =
if (endianness == BitChunk.Endianness.BigEndian) longs(n) else java.lang.Long.reverse(longs(n))
def apply(n: Int): Boolean =
(elementAt(n >> bitsLog2) & (1L << (bits - 1 - (n & bits - 1)))) != 0
override protected def newBitChunk(longs: Chunk[Long], min: Int, max: Int): BitChunk[Long] =
BitChunkLong(longs, endianness, min, max)
override protected def foreachElement[A](f: Boolean => A, long: Long): Unit = {
f((long & 0x8000000000000000L) != 0)
f((long & 0x4000000000000000L) != 0)
f((long & 0x2000000000000000L) != 0)
f((long & 0x1000000000000000L) != 0)
f((long & 0x800000000000000L) != 0)
f((long & 0x400000000000000L) != 0)
f((long & 0x200000000000000L) != 0)
f((long & 0x100000000000000L) != 0)
f((long & 0x80000000000000L) != 0)
f((long & 0x40000000000000L) != 0)
f((long & 0x20000000000000L) != 0)
f((long & 0x10000000000000L) != 0)
f((long & 0x8000000000000L) != 0)
f((long & 0x4000000000000L) != 0)
f((long & 0x2000000000000L) != 0)
f((long & 0x1000000000000L) != 0)
f((long & 0x800000000000L) != 0)
f((long & 0x400000000000L) != 0)
f((long & 0x200000000000L) != 0)
f((long & 0x100000000000L) != 0)
f((long & 0x80000000000L) != 0)
f((long & 0x40000000000L) != 0)
f((long & 0x20000000000L) != 0)
f((long & 0x10000000000L) != 0)
f((long & 0x8000000000L) != 0)
f((long & 0x4000000000L) != 0)
f((long & 0x2000000000L) != 0)
f((long & 0x1000000000L) != 0)
f((long & 0x800000000L) != 0)
f((long & 0x400000000L) != 0)
f((long & 0x200000000L) != 0)
f((long & 0x100000000L) != 0)
f((long & 0x80000000L) != 0)
f((long & 0x40000000L) != 0)
f((long & 0x20000000L) != 0)
f((long & 0x10000000L) != 0)
f((long & 0x8000000L) != 0)
f((long & 0x4000000L) != 0)
f((long & 0x2000000L) != 0)
f((long & 0x1000000L) != 0)
f((long & 0x800000L) != 0)
f((long & 0x400000L) != 0)
f((long & 0x200000L) != 0)
f((long & 0x100000L) != 0)
f((long & 0x80000L) != 0)
f((long & 0x40000L) != 0)
f((long & 0x20000L) != 0)
f((long & 0x10000L) != 0)
f((long & 0x8000L) != 0)
f((long & 0x4000L) != 0)
f((long & 0x2000L) != 0)
f((long & 0x1000L) != 0)
f((long & 0x800L) != 0)
f((long & 0x400L) != 0)
f((long & 0x200L) != 0)
f((long & 0x100L) != 0)
f((long & 0x80L) != 0)
f((long & 0x40L) != 0)
f((long & 0x20L) != 0)
f((long & 0x10L) != 0)
f((long & 0x8L) != 0)
f((long & 0x4L) != 0)
f((long & 0x2L) != 0)
f((long & 0x1L) != 0)
()
}
override def toPackedLong(endianness: BitChunk.Endianness)(implicit ev: Boolean <:< Boolean): Chunk[Long] =
if (minBitIndex == maxBitIndex) Chunk.empty
else if ((minBitIndex & bits - 1) != 0) ChunkPackedBoolean[Long](self, bits, endianness)
else if ((maxBitIndex & bits - 1) != 0) {
val minLongIndex = minBitIndex >> bitsLog2
val maxLongIndex = maxBitIndex >> bitsLog2
val maxLong = elementAt(maxLongIndex)
val maxLongValue = maxLong >>> bits - (maxBitIndex & bits - 1)
val fullLongs = longs.slice(minLongIndex, maxLongIndex)
if (self.endianness == endianness) fullLongs :+ respectEndian(endianness, maxLongValue)
else fullLongs.map(java.lang.Long.reverse) :+ respectEndian(endianness, maxLongValue)
} else if (self.endianness == endianness) longs
else longs.map(java.lang.Long.reverse)
private def respectEndian(endianness: BitChunk.Endianness, bigEndianValue: Long) =
if (endianness == BitChunk.Endianness.BigEndian) bigEndianValue
else java.lang.Long.reverse(bigEndianValue)
}
private[zio] final case class ChunkPackedBoolean[T](
unpacked: Chunk[Boolean],
bits: Int,
endianness: Chunk.BitChunk.Endianness
)(implicit val ops: BitOps[T])
extends Chunk[T]
with ChunkIterator[T] {
self =>
import ops._
override val length: Int = unpacked.length / bits + (if (unpacked.length % bits == 0) 0 else 1)
private def bitOr0(index: Int) = if (index < unpacked.length && unpacked(index)) one else zero
override def apply(n: Int): T =
if (n < 0 || n >= length)
throw new IndexOutOfBoundsException(s"Packed boolean chunk index $n out of bounds [0, $length)")
else {
val offset = n * bits
val bitsToRead = if ((n + 1) * bits > unpacked.length) unpacked.length % bits else bits
var elem = zero
var i = bitsToRead - 1
while (i >= 0) {
val shiftBy = bitsToRead - 1 - i
val index = offset + i
val shifted = <<(bitOr0(index), shiftBy)
elem = ops.|(elem, shifted)
i -= 1
}
if (endianness == BitChunk.Endianness.BigEndian) elem else ops.reverse(elem)
}
override protected[zio] def toArray[A1 >: T](srcPos: Int, dest: Array[A1], destPos: Int, length: Int): Unit = {
var i = 0
while (i < length) {
dest(i + destPos) = self.apply(i + srcPos)
i += 1
}
}
override def chunkIterator: ChunkIterator[T] =
self
implicit val classTag: ClassTag[T] =
ops.classTag
def hasNextAt(index: Int): Boolean =
index < length
def nextAt(index: Int): T =
self(index)
def sliceIterator(offset: Int, length: Int): ChunkIterator[T] =
if (offset < 0 && offset >= this.length) self
else ChunkPackedBoolean(unpacked.slice(offset * bits, length * bits), bits, endianness)
}
private case object Empty extends Chunk[Nothing] { self =>
def chunkIterator: ChunkIterator[Nothing] =
ChunkIterator.empty
override val length: Int =
0
override def apply(n: Int): Nothing =
throw new ArrayIndexOutOfBoundsException(s"Empty chunk access to $n")
override def equals(that: Any): Boolean =
that match {
case seq: Seq[_] => seq.isEmpty
case _ => false
}
override def foreach[B](f: Nothing => B): Unit = {
val _ = f
}
/**
* Materializes a chunk into a chunk backed by an array. This method can
* improve the performance of bulk operations.
*/
override def materialize[A1]: Chunk[A1] =
Empty
override def toArray[A1: ClassTag]: Array[A1] =
Array.empty
}
final case class AnyRefArray[A <: AnyRef](array: Array[A], offset: Int, override val length: Int)
extends Arr[A]
with ChunkIterator[A] { self =>
def apply(index: Int): A =
array(index + offset)
def chunkIterator: ChunkIterator[A] =
self
def hasNextAt(index: Int): Boolean =
index < length
def nextAt(index: Int): A =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[A] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else AnyRefArray(array, self.offset + offset, self.length - offset min length)
}
final case class ByteArray(array: Array[Byte], offset: Int, override val length: Int)
extends Arr[Byte]
with ChunkIterator[Byte] { self =>
def apply(index: Int): Byte =
array(index + offset)
override def byte(index: Int)(implicit ev: Byte <:< Byte): Byte =
array(index + offset)
def chunkIterator: ChunkIterator[Byte] =
self
override def filter(f: Byte => Boolean): Chunk[Byte] = {
val len = self.length
val builder = new ChunkBuilder.Byte
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder.addOne(elem)
}
i += 1
}
builder.result()
}
def hasNextAt(index: Int): Boolean =
index < length
override protected def mapChunk[B](f: Byte => B): Chunk[B] = {
val len = self.length
if (len == 0) Empty
else {
val oldArr = array
val head = f(oldArr(0))
var newArr = Array.ofDim[B](len)(Chunk.Tags.fromValue(head))
var i = 1
newArr(0) = head
while (i < len) {
val newVal = f(oldArr(i))
try {
newArr(i) = newVal
} catch {
case _: ClassCastException =>
val newArr2 = Array.ofDim[AnyRef](len)
var ii = 0
while (ii < i) {
newArr2(ii) = newArr(ii).asInstanceOf[AnyRef]
ii += 1
}
newArr = newArr2.asInstanceOf[Array[B]]
newArr(i) = newVal
}
i += 1
}
Chunk.fromArray(newArr)
}
}
def nextAt(index: Int): Byte =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[Byte] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else ByteArray(array, self.offset + offset, self.length - offset min length)
override def takeWhile(f: Byte => Boolean): Chunk[Byte] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
}
final case class CharArray(array: Array[Char], offset: Int, override val length: Int)
extends Arr[Char]
with ChunkIterator[Char] { self =>
def apply(index: Int): Char =
array(index + offset)
override def char(index: Int)(implicit ev: Char <:< Char): Char =
array(index + offset)
def chunkIterator: ChunkIterator[Char] =
self
override def filter(f: Char => Boolean): Chunk[Char] = {
val len = self.length
val builder = new ChunkBuilder.Char
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder.addOne(elem)
}
i += 1
}
builder.result()
}
def hasNextAt(index: Int): Boolean =
index < length
override protected def mapChunk[B](f: Char => B): Chunk[B] = {
val len = self.length
if (len == 0) Empty
else {
val oldArr = array
val head = f(oldArr(0))
var newArr = Array.ofDim[B](len)(Chunk.Tags.fromValue(head))
var i = 1
newArr(0) = head
while (i < len) {
val newVal = f(oldArr(i))
try {
newArr(i) = newVal
} catch {
case _: ClassCastException =>
val newArr2 = Array.ofDim[AnyRef](len)
var ii = 0
while (ii < i) {
newArr2(ii) = newArr(ii).asInstanceOf[AnyRef]
ii += 1
}
newArr = newArr2.asInstanceOf[Array[B]]
newArr(i) = newVal
}
i += 1
}
Chunk.fromArray(newArr)
}
}
def nextAt(index: Int): Char =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[Char] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else CharArray(array, self.offset + offset, self.length - offset min length)
override def takeWhile(f: Char => Boolean): Chunk[Char] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
}
final case class IntArray(array: Array[Int], offset: Int, override val length: Int)
extends Arr[Int]
with ChunkIterator[Int] { self =>
def apply(index: Int): Int =
array(index + offset)
def chunkIterator: ChunkIterator[Int] =
self
override def filter(f: Int => Boolean): Chunk[Int] = {
val len = self.length
val builder = new ChunkBuilder.Int
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder.addOne(elem)
}
i += 1
}
builder.result()
}
def hasNextAt(index: Int): Boolean =
index < length
override def int(index: Int)(implicit ev: Int <:< Int): Int =
array(index + offset)
override protected def mapChunk[B](f: Int => B): Chunk[B] = {
val len = self.length
if (len == 0) Empty
else {
val oldArr = array
val head = f(oldArr(0))
var newArr = Array.ofDim[B](len)(Chunk.Tags.fromValue(head))
var i = 1
newArr(0) = head
while (i < len) {
val newVal = f(oldArr(i))
try {
newArr(i) = newVal
} catch {
case _: ClassCastException =>
val newArr2 = Array.ofDim[AnyRef](len)
var ii = 0
while (ii < i) {
newArr2(ii) = newArr(ii).asInstanceOf[AnyRef]
ii += 1
}
newArr = newArr2.asInstanceOf[Array[B]]
newArr(i) = newVal
}
i += 1
}
Chunk.fromArray(newArr)
}
}
def nextAt(index: Int): Int =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[Int] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else IntArray(array, self.offset + offset, self.length - offset min length)
override def takeWhile(f: Int => Boolean): Chunk[Int] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
}
final case class LongArray(array: Array[Long], offset: Int, override val length: Int)
extends Arr[Long]
with ChunkIterator[Long] { self =>
def apply(index: Int): Long =
array(index + offset)
def chunkIterator: ChunkIterator[Long] =
self
override def filter(f: Long => Boolean): Chunk[Long] = {
val len = self.length
val builder = new ChunkBuilder.Long
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder.addOne(elem)
}
i += 1
}
builder.result()
}
def hasNextAt(index: Int): Boolean =
index < length
override def long(index: Int)(implicit ev: Long <:< Long): Long =
array(index + offset)
override protected def mapChunk[B](f: Long => B): Chunk[B] = {
val len = self.length
if (len == 0) Empty
else {
val oldArr = array
val head = f(oldArr(0))
var newArr = Array.ofDim[B](len)(Chunk.Tags.fromValue(head))
var i = 1
newArr(0) = head
while (i < len) {
val newVal = f(oldArr(i))
try {
newArr(i) = newVal
} catch {
case _: ClassCastException =>
val newArr2 = Array.ofDim[AnyRef](len)
var ii = 0
while (ii < i) {
newArr2(ii) = newArr(ii).asInstanceOf[AnyRef]
ii += 1
}
newArr = newArr2.asInstanceOf[Array[B]]
newArr(i) = newVal
}
i += 1
}
Chunk.fromArray(newArr)
}
}
def nextAt(index: Int): Long =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[Long] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else LongArray(array, self.offset + offset, self.length - offset min length)
override def takeWhile(f: Long => Boolean): Chunk[Long] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
}
final case class DoubleArray(array: Array[Double], offset: Int, override val length: Int)
extends Arr[Double]
with ChunkIterator[Double] { self =>
def apply(index: Int): Double =
array(index + offset)
def chunkIterator: ChunkIterator[Double] =
self
override def double(index: Int)(implicit ev: Double <:< Double): Double =
array(index + offset)
override def filter(f: Double => Boolean): Chunk[Double] = {
val len = self.length
val builder = new ChunkBuilder.Double
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder.addOne(elem)
}
i += 1
}
builder.result()
}
def hasNextAt(index: Int): Boolean =
index < length
override protected def mapChunk[B](f: Double => B): Chunk[B] = {
val len = self.length
if (len == 0) Empty
else {
val oldArr = array
val head = f(oldArr(0))
var newArr = Array.ofDim[B](len)(Chunk.Tags.fromValue(head))
var i = 1
newArr(0) = head
while (i < len) {
val newVal = f(oldArr(i))
try {
newArr(i) = newVal
} catch {
case _: ClassCastException =>
val newArr2 = Array.ofDim[AnyRef](len)
var ii = 0
while (ii < i) {
newArr2(ii) = newArr(ii).asInstanceOf[AnyRef]
ii += 1
}
newArr = newArr2.asInstanceOf[Array[B]]
newArr(i) = newVal
}
i += 1
}
Chunk.fromArray(newArr)
}
}
def nextAt(index: Int): Double =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[Double] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else DoubleArray(array, self.offset + offset, self.length - offset min length)
override def takeWhile(f: Double => Boolean): Chunk[Double] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
}
final case class FloatArray(array: Array[Float], offset: Int, override val length: Int)
extends Arr[Float]
with ChunkIterator[Float] { self =>
def apply(index: Int): Float =
array(index + offset)
def chunkIterator: ChunkIterator[Float] =
self
override def filter(f: Float => Boolean): Chunk[Float] = {
val len = self.length
val builder = new ChunkBuilder.Float
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder.addOne(elem)
}
i += 1
}
builder.result()
}
override def float(index: Int)(implicit ev: Float <:< Float): Float =
array(index + offset)
def hasNextAt(index: Int): Boolean =
index < length
override protected def mapChunk[B](f: Float => B): Chunk[B] = {
val len = self.length
if (len == 0) Empty
else {
val oldArr = array
val head = f(oldArr(0))
var newArr = Array.ofDim[B](len)(Chunk.Tags.fromValue(head))
var i = 1
newArr(0) = head
while (i < len) {
val newVal = f(oldArr(i))
try {
newArr(i) = newVal
} catch {
case _: ClassCastException =>
val newArr2 = Array.ofDim[AnyRef](len)
var ii = 0
while (ii < i) {
newArr2(ii) = newArr(ii).asInstanceOf[AnyRef]
ii += 1
}
newArr = newArr2.asInstanceOf[Array[B]]
newArr(i) = newVal
}
i += 1
}
Chunk.fromArray(newArr)
}
}
def nextAt(index: Int): Float =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[Float] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else FloatArray(array, self.offset + offset, self.length - offset min length)
override def takeWhile(f: Float => Boolean): Chunk[Float] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
}
final case class ShortArray(array: Array[Short], offset: Int, override val length: Int)
extends Arr[Short]
with ChunkIterator[Short] { self =>
def apply(index: Int): Short =
array(index + offset)
def chunkIterator: ChunkIterator[Short] =
self
override def filter(f: Short => Boolean): Chunk[Short] = {
val len = self.length
val builder = new ChunkBuilder.Short
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder.addOne(elem)
}
i += 1
}
builder.result()
}
def hasNextAt(index: Int): Boolean =
index < length
override protected def mapChunk[B](f: Short => B): Chunk[B] = {
val len = self.length
if (len == 0) Empty
else {
val oldArr = array
val head = f(oldArr(0))
var newArr = Array.ofDim[B](len)(Chunk.Tags.fromValue(head))
var i = 1
newArr(0) = head
while (i < len) {
val newVal = f(oldArr(i))
try {
newArr(i) = newVal
} catch {
case _: ClassCastException =>
val newArr2 = Array.ofDim[AnyRef](len)
var ii = 0
while (ii < i) {
newArr2(ii) = newArr(ii).asInstanceOf[AnyRef]
ii += 1
}
newArr = newArr2.asInstanceOf[Array[B]]
newArr(i) = newVal
}
i += 1
}
Chunk.fromArray(newArr)
}
}
def nextAt(index: Int): Short =
array(index + offset)
override def short(index: Int)(implicit ev: Short <:< Short): Short =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[Short] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else ShortArray(array, self.offset + offset, self.length - offset min length)
override def takeWhile(f: Short => Boolean): Chunk[Short] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
}
final case class BooleanArray(array: Array[Boolean], offset: Int, length: Int)
extends Arr[Boolean]
with ChunkIterator[Boolean] { self =>
def apply(index: Int): Boolean =
array(index + offset)
override def boolean(index: Int)(implicit ev: Boolean <:< Boolean): Boolean =
array(index + offset)
def chunkIterator: ChunkIterator[Boolean] =
self
override def filter(f: Boolean => Boolean): Chunk[Boolean] = {
val len = self.length
val builder = new ChunkBuilder.Boolean
builder.sizeHint(len)
var i = 0
while (i < len) {
val elem = self(i)
if (f(elem)) {
builder.addOne(elem)
}
i += 1
}
builder.result()
}
def hasNextAt(index: Int): Boolean =
index < length
override protected def mapChunk[B](f: Boolean => B): Chunk[B] = {
val len = self.length
if (len == 0) Empty
else {
val oldArr = array
val head = f(oldArr(0))
var newArr = Array.ofDim[B](len)(Chunk.Tags.fromValue(head))
var i = 1
newArr(0) = head
while (i < len) {
val newVal = f(oldArr(i))
try {
newArr(i) = newVal
} catch {
case _: ClassCastException =>
val newArr2 = Array.ofDim[AnyRef](len)
var ii = 0
while (ii < i) {
newArr2(ii) = newArr(ii).asInstanceOf[AnyRef]
ii += 1
}
newArr = newArr2.asInstanceOf[Array[B]]
newArr(i) = newVal
}
i += 1
}
Chunk.fromArray(newArr)
}
}
def nextAt(index: Int): Boolean =
array(index + offset)
def sliceIterator(offset: Int, length: Int): ChunkIterator[Boolean] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) ChunkIterator.empty
else BooleanArray(array, self.offset + offset, self.length - offset min length)
override def takeWhile(f: Boolean => Boolean): Chunk[Boolean] = {
val self = array
val len = length
var i = 0
while (i < len && f(self(i))) {
i += 1
}
take(i)
}
}
/**
* A `ChunkIterator` is a specialized iterator that supports efficient
* iteration over chunks. Unlike a normal iterator, the caller is responsible
* for providing an `index` with each call to `hasNextAt` and `nextAt`. By
* contract this should be `0` initially and incremented by `1` each time
* `nextAt` is called. This allows the caller to maintain the current index in
* local memory rather than the iterator having to do it on the heap for array
* backed chunks.
*/
sealed trait ChunkIterator[+A] { self =>
/**
* Checks if the chunk iterator has another element.
*/
def hasNextAt(index: Int): Boolean
/**
* The length of the iterator.
*/
def length: Int
/**
* Gets the next element from the chunk iterator.
*/
def nextAt(index: Int): A
/**
* Returns a new iterator that is a slice of this iterator.
*/
def sliceIterator(offset: Int, length: Int): ChunkIterator[A]
/**
* Concatenates this chunk iterator with the specified chunk iterator.
*/
final def ++[A1 >: A](that: ChunkIterator[A1]): ChunkIterator[A1] =
ChunkIterator.Concat(self, that)
}
object ChunkIterator {
/**
* The empty iterator.
*/
val empty: ChunkIterator[Nothing] =
Empty
/**
* Constructs an iterator from an array of arbitrary values.
*/
def fromArray[A](array: Array[A]): ChunkIterator[A] =
array match {
case array: Array[AnyRef] => Chunk.AnyRefArray(array, 0, array.length)
case array: Array[Boolean] => Chunk.BooleanArray(array, 0, array.length)
case array: Array[Byte] => Chunk.ByteArray(array, 0, array.length)
case array: Array[Char] => Chunk.CharArray(array, 0, array.length)
case array: Array[Double] => Chunk.DoubleArray(array, 0, array.length)
case array: Array[Float] => Chunk.FloatArray(array, 0, array.length)
case array: Array[Int] => Chunk.IntArray(array, 0, array.length)
case array: Array[Long] => Chunk.LongArray(array, 0, array.length)
case array: Array[Short] => Chunk.ShortArray(array, 0, array.length)
}
/**
* Constructs an iterator from a `Vector`.
*/
def fromVector[A](vector: Vector[A]): ChunkIterator[A] =
if (vector.length <= 0) Empty
else Iterator(vector.iterator, vector.length)
private final case class Concat[A](left: ChunkIterator[A], right: ChunkIterator[A]) extends ChunkIterator[A] {
self =>
def hasNextAt(index: Int): Boolean =
index < length
val length: Int =
left.length + right.length
def nextAt(index: Int): A =
if (left.hasNextAt(index)) left.nextAt(index)
else right.nextAt(index - left.length)
def sliceIterator(offset: Int, length: Int): ChunkIterator[A] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) Empty
else if (offset >= left.length) right.sliceIterator(offset - left.length, length)
else if (length <= left.length - offset) left.sliceIterator(offset, length)
else
Concat(
left.sliceIterator(offset, left.length - offset),
right.sliceIterator(0, offset + length - left.length)
)
}
private case object Empty extends ChunkIterator[Nothing] { self =>
def hasNextAt(index: Int): Boolean =
false
val length: Int =
0
def nextAt(index: Int): Nothing =
throw new ArrayIndexOutOfBoundsException(s"Empty chunk access to $index")
def sliceIterator(offset: Int, length: Int): ChunkIterator[Nothing] =
self
}
private final case class Iterator[A](iterator: scala.Iterator[A], length: Int) extends ChunkIterator[A] { self =>
def hasNextAt(index: Int): Boolean =
iterator.hasNext
def nextAt(index: Int): A =
iterator.next()
def sliceIterator(offset: Int, length: Int): ChunkIterator[A] =
if (offset <= 0 && length >= self.length) self
else if (offset >= self.length || length <= 0) Empty
else Iterator(iterator.slice(offset, offset + length), self.length - offset min length)
}
}
}