From 9076fbf26850567e14eef95edce20414ead7f373 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 1 May 2012 17:05:09 +0200 Subject: [PATCH 01/60] Optimized ByteString1.++ --- akka-actor/src/main/scala/akka/util/ByteString.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 6d869826a8..c335855f39 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -142,7 +142,11 @@ object ByteString { def ++(that: ByteString): ByteString = that match { case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) - case b: ByteString1 ⇒ ByteStrings(this, b) + case b: ByteString1 ⇒ { + if ((bytes eq b.bytes) && (startIndex + length == b.startIndex)) + new ByteString1(bytes, startIndex, length + b.length) + else ByteStrings(this, b) + } case bs: ByteStrings ⇒ ByteStrings(this, bs) } From 322edb2eca2f5b0336dc181a6cc1656e5b6f5478 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 2 May 2012 01:40:42 +0200 Subject: [PATCH 02/60] Introduced ContByteString to complement CompactByteString Many operations are necessarily more efficient on contiguous data (represented by a contiguous part of an array) than on chunked data (e.g. wrapping in a ByteBuffer, etc.). It should be possibly to check whether a ByteString fulfills this requirement and to specifically request such a ByteString for specific applications. The new abstract ContByteString, as a natural bridge between CompactByteString and ByteString, provides this. --- .../src/main/scala/akka/util/ByteString.scala | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index c335855f39..a291761d66 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -79,8 +79,6 @@ object ByteString { override def clone: ByteString1C = new ByteString1C(toArray) - def compact: ByteString1C = this - def asByteBuffer: ByteBuffer = toByteString1.asByteBuffer @@ -107,7 +105,7 @@ object ByteString { /** * An unfragmented ByteString. */ - final class ByteString1 private (private val bytes: Array[Byte], private val startIndex: Int, val length: Int) extends ByteString { + final class ByteString1 private (private val bytes: Array[Byte], private val startIndex: Int, val length: Int) extends ContByteString { private def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length) @@ -268,6 +266,7 @@ object ByteString { case bs: ByteStrings ⇒ ByteStrings(this, bs) } + def contiguous = compact def compact: CompactByteString = { val ar = new Array[Byte](length) var pos = 0 @@ -320,8 +319,14 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz def copyToBuffer(buffer: ByteBuffer): Int /** - * Create a new ByteString with all contents compacted into a single - * byte array. + * Create a new ByteString with all contents contiguous in memory + * (in a single section of a byte array). + */ + def contiguous: ContByteString + + /** + * Create a new ByteString with all contents compacted into a single, + * full byte array. */ def compact: CompactByteString @@ -426,9 +431,24 @@ object CompactByteString { } /** - * A compact, unfragmented ByteString. + * A contiguous (i.e. non-fragmented) ByteString. + * + * The ByteString is guarantieed to be contiguous in memory, making certain + * operations more efficient than on chunked ByteString instances. */ -sealed abstract class CompactByteString extends ByteString with Serializable +sealed abstract class ContByteString extends ByteString { + def contiguous = this +} + +/** + * A compact and contiguous ByteString. + * + * The ByteString is guarantied to be contiguous in memory and to blocks only + * as much memory as required for its contents. + */ +sealed abstract class CompactByteString extends ContByteString with Serializable { + def compact = this +} /** * A mutable builder for efficiently creating a [[akka.util.ByteString]]. From 3436c3b14dd17703fabc624a8e295bb7b517fe63 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 13:13:47 +0200 Subject: [PATCH 03/60] Added ByteString1.apply(bytes, startIndex, length) --- akka-actor/src/main/scala/akka/util/ByteString.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index a291761d66..4efc71a325 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -100,6 +100,7 @@ object ByteString { private[akka] object ByteString1 { def apply(bytes: Array[Byte]) = new ByteString1(bytes) + def apply(bytes: Array[Byte], startIndex: Int, length: Int) = new ByteString1(bytes, startIndex, length) } /** From 76dab6354ef191a590681365d319d1d85177741f Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 1 May 2012 18:41:04 +0200 Subject: [PATCH 04/60] Added ByteIterator --- .../test/scala/akka/util/ByteStringSpec.scala | 71 +++- .../main/scala/akka/util/ByteIterator.scala | 354 ++++++++++++++++++ .../src/main/scala/akka/util/ByteString.scala | 9 + 3 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 akka-actor/src/main/scala/akka/util/ByteIterator.scala diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 94997a49de..1a4dd40ffe 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -14,7 +14,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { def genSimpleByteString(min: Int, max: Int) = for { n ← choose(min, max) b ← Gen.containerOfN[Array, Byte](n, arbitrary[Byte]) - } yield ByteString(b) + from ← choose(0, b.length) + until ← choose(from, b.length) + } yield ByteString(b).slice(from, until) implicit val arbitraryByteString: Arbitrary[ByteString] = Arbitrary { Gen.sized { s ⇒ @@ -25,6 +27,30 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } } + type ByteStringSlice = (ByteString, Int, Int) + + implicit val arbitraryByteStringSlice: Arbitrary[ByteStringSlice] = Arbitrary { + for { + xs ← arbitraryByteString.arbitrary + from ← choose(0, xs.length) + until ← choose(from, xs.length) + } yield (xs, from, until) + } + + def likeVecIt(bs: ByteString)(body: BufferedIterator[Byte] ⇒ Any, strict: Boolean = true): Boolean = { + val bsIterator = bs.iterator + val vecIterator = Vector(bs: _*).iterator.buffered + (body(bsIterator) == body(vecIterator)) && + (!strict || (bsIterator.toSeq == vecIterator.toSeq)) + } + + def likeVecIts(a: ByteString, b: ByteString)(body: (BufferedIterator[Byte], BufferedIterator[Byte]) ⇒ Any, strict: Boolean = true): Boolean = { + val (bsAIt, bsBIt) = (a.iterator, b.iterator) + val (vecAIt, vecBIt) = (Vector(a: _*).iterator.buffered, Vector(b: _*).iterator.buffered) + (body(bsAIt, bsBIt) == body(vecAIt, vecBIt)) && + (!strict || (bsAIt.toSeq, bsBIt.toSeq) == (vecAIt.toSeq, vecBIt.toSeq)) + } + "A ByteString" must { "have correct size" when { "concatenating" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).size == a.size + b.size) } @@ -35,4 +61,47 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(a.size) == b) } } } + + "A ByteStringIterator" must { + "behave like a buffered Vector Iterator" when { + "concatenating" in { check { (a: ByteString, b: ByteString) ⇒ likeVecIts(a, b) { (a, b) ⇒ (a ++ b).toSeq } } } + + "calling head" in { check { a: ByteString ⇒ a.isEmpty || likeVecIt(a) { _.head } } } + "calling next" in { check { a: ByteString ⇒ a.isEmpty || likeVecIt(a) { _.next() } } } + "calling hasNext" in { check { a: ByteString ⇒ likeVecIt(a) { _.hasNext } } } + "calling length" in { check { a: ByteString ⇒ likeVecIt(a) { _.length } } } + "calling duplicate" in { check { a: ByteString ⇒ likeVecIt(a)({ _.duplicate match { case (a, b) ⇒ (a.toSeq, b.toSeq) } }, strict = false) } } + "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ == b) match { case (a, b) ⇒ (a.toSeq, b.toSeq) } }, strict = false) } } + "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.takeWhile(_ == b).toSeq }, strict = false) } } + "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.dropWhile(_ == b).toSeq } } } + "calling indexWhere" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexWhere(_ == b) } } } + "calling indexOf" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexOf(b) } } } + "calling toSeq" in { check { a: ByteString ⇒ likeVecIt(a) { _.toSeq } } } + "calling foreach" in { check { a: ByteString ⇒ likeVecIt(a) { it ⇒ var acc = 0; it foreach { acc += _ }; acc } } } + "calling foldLeft" in { check { a: ByteString ⇒ likeVecIt(a) { _.foldLeft(0) { _ + _ } } } } + "calling toArray" in { check { a: ByteString ⇒ likeVecIt(a) { _.toArray.toSeq } } } + + "calling slice" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVecIt(xs)({ + _.slice(from, until).toSeq + }, strict = false) + } + } + } + + "calling copyToArray" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVecIt(xs)({ it ⇒ + val array = Array.ofDim[Byte](xs.length) + it.slice(from, until).copyToArray(array, from, until) + array.toSeq + }, strict = false) + } + } + } + } + } } diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala new file mode 100644 index 0000000000..0c6146e93d --- /dev/null +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -0,0 +1,354 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ + +package akka.util + +import java.nio.ByteBuffer + +import scala.collection.IndexedSeqOptimized +import scala.collection.mutable.{ Builder, WrappedArray } +import scala.collection.immutable.{ IndexedSeq, VectorBuilder } +import scala.collection.generic.CanBuildFrom +import scala.collection.mutable.{ ListBuffer } +import scala.annotation.tailrec + +import java.nio.ByteBuffer + +abstract class ByteIterator extends BufferedIterator[Byte] { + def isIdenticalTo(that: Iterator[Byte]): Boolean + + def len: Int + + def head: Byte + + def next(): Byte + + protected def clear(): Unit + + def ++(that: TraversableOnce[Byte]): ByteIterator = + ByteArrayIterator(that.toArray) + + // *must* be overridden by derived classes + override def clone: ByteIterator = null + + final override def duplicate = (this, clone) + + // *must* be overridden by derived classes + override def take(n: Int): this.type = null + + // *must* be overridden by derived classes + override def drop(n: Int): this.type = null + + final override def slice(from: Int, until: Int): this.type = + drop(from).take(until - from) + + // *must* be overridden by derived classes + override def takeWhile(p: Byte ⇒ Boolean): this.type = null + + // *must* be overridden by derived classes + override def dropWhile(p: Byte ⇒ Boolean): this.type = null + + final override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { + val that = clone + that.takeWhile(p) + drop(that.len) + (that, this) + } + + final override def indexWhere(p: Byte ⇒ Boolean): Int = { + var index = 0 + var found = false + while (!found && hasNext) if (p(next())) { found = true } else { index += 1 } + if (found) index else -1 + } + + final def indexOf(elem: Byte): Int = { + var index = 0 + var found = false + while (!found && hasNext) if (elem == next()) { found = true } else { index += 1 } + if (found) index else -1 + } + + final override def indexOf[B >: Byte](elem: B): Int = { + var index = 0 + var found = false + while (!found && hasNext) if (elem == next()) { found = true } else { index += 1 } + if (found) index else -1 + } + + def toByteString: ByteString + + override def toSeq: ByteString = toByteString + + @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = + while (hasNext) f(next()) + + final override def foldLeft[@specialized B](z: B)(op: (B, Byte) ⇒ B): B = { + var acc = z + while (hasNext) acc = op(acc, next()) + acc + } + + final override def toArray[B >: Byte](implicit arg0: ClassManifest[B]): Array[B] = { + val target = Array.ofDim[B](len) + copyToArray(target) + target + } + + /** + * Copy as many bytes as possible to a ByteBuffer, starting from it's + * current position. This method will not overflow the buffer. + * + * @param buffer a ByteBuffer to copy bytes to + * @return the number of bytes actually copied + */ + def copyToBuffer(buffer: ByteBuffer): Int +} + +object ByteArrayIterator { + private val emptyArray = Array.ofDim[Byte](0) + + protected[akka] def apply(array: Array[Byte]): ByteArrayIterator = + new ByteArrayIterator(array, 0, array.length) + + protected[akka] def apply(array: Array[Byte], from: Int, until: Int): ByteArrayIterator = + new ByteArrayIterator(array, from, until) + + val empty: ByteArrayIterator = apply(Array.empty[Byte]) +} + +class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { + protected[util] final def internalArray = array + protected[util] final def internalFrom = from + protected[util] final def internalUntil = until + + final def isIdenticalTo(that: Iterator[Byte]) = that match { + case that: ByteArrayIterator ⇒ + ((this.array) eq (that.internalArray)) && + ((this.from) == (that.from)) && ((this.until) == (that.until)) + case _ ⇒ false + } + + @inline final def len = until - from + + @inline final def hasNext = from < until + + @inline final def head = array(from) + + final def next() = { + if (!hasNext) Iterator.empty.next + else { val i = from; from = from + 1; array(i) } + } + + def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } + + final override def length = { val l = len; drop(len); l } + + final override def ++(that: TraversableOnce[Byte]) = that match { + case that: ByteArrayIterator ⇒ { + if (this.isEmpty) that + else if ((this.array eq that.array) && (this.until == that.from)) { + this.until = that.until + that.clear() + this + } else { + val result = MultiByteArrayIterator(List(this, that)) + this.clear() + result + } + } + case that: MultiByteArrayIterator ⇒ if (this.isEmpty) that else (this +: that) + case _ ⇒ super.++(that) + } + + final override def clone = new ByteArrayIterator(array, from, until) + + final override def take(n: Int) = { + until = until min (from + (0 max n)) + this + } + + final override def drop(n: Int) = { + from = until min (from + (0 max n)) + this + } + + final override def takeWhile(p: Byte ⇒ Boolean) = { + val prev = from + dropWhile(p) + until = from; from = prev + this + } + + final override def dropWhile(p: Byte ⇒ Boolean) = { + var stop = false + while (!stop && hasNext) { + if (p(array(from))) { from = from + 1 } else { stop = true } + } + this + } + + final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { + val n = 0 max ((xs.length - start) min this.len min len) + Array.copy(this.array, from, xs, start, n) + this.drop(n) + } + + final override def toByteString = { + val result = + if ((from == 0) && (until == array.length)) ByteString.ByteString1C(array) + else ByteString.ByteString1(array, from, len) + clear() + result + } + + def copyToBuffer(buffer: ByteBuffer): Int = { + val copyLength = math.min(buffer.remaining, len) + if (copyLength > 0) { + buffer.put(array, from, copyLength) + drop(copyLength) + } + copyLength + } +} + +object MultiByteArrayIterator { + protected val clearedList = List(ByteArrayIterator.empty) + + val empty = new MultiByteArrayIterator(Nil) + + protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = + new MultiByteArrayIterator(iterators) +} + +class MultiByteArrayIterator private (private var iterators: List[ByteArrayIterator]) extends ByteIterator { + // After normalization: + // * iterators.isEmpty == false + // * (!iterator.head.isEmpty || iterators.tail.isEmpty) == true + private def normalize(): this.type = { + @tailrec def norm(xs: List[ByteArrayIterator]): List[ByteArrayIterator] = { + if (xs.isEmpty) MultiByteArrayIterator.clearedList + else if (xs.head.isEmpty) norm(xs.tail) + else xs + } + iterators = norm(iterators) + this + } + normalize() + + @inline private def current = iterators.head + @inline private def dropCurrent() { iterators = iterators.tail } + @inline def clear() { iterators = MultiByteArrayIterator.empty.iterators } + + final def isIdenticalTo(that: Iterator[Byte]) = false + + @inline final def hasNext = current.hasNext + + @inline final def head = current.head + + final def next() = { + val result = current.next() + normalize() + result + } + + final override def len = iterators.foldLeft(0) { _ + _.len } + + final override def length = { + var result = len + clear() + result + } + + def +:(that: ByteArrayIterator): this.type = { + iterators = that +: iterators + this + } + + final override def ++(that: TraversableOnce[Byte]) = that match { + case that: ByteArrayIterator ⇒ if (this.isEmpty) that else { + iterators = iterators :+ that + that.clear() + this + } + case that: MultiByteArrayIterator ⇒ if (this.isEmpty) that else { + iterators = this.iterators ++ that.iterators + that.clear() + this + } + case _ ⇒ super.++(that) + } + + final override def clone = new MultiByteArrayIterator(iterators map { _.clone }) + + final override def take(n: Int) = { + var rest = n + val builder = new ListBuffer[ByteArrayIterator] + while ((rest > 0) && !iterators.isEmpty) { + current.take(rest) + if (current.hasNext) { + rest -= current.len + builder += current + } + iterators = iterators.tail + } + iterators = builder.result + normalize() + } + + @tailrec final override def drop(n: Int) = if ((n > 0) && !isEmpty) { + val nCurrent = math.min(n, current.len) + current.drop(n) + val rest = n - nCurrent + assert(current.isEmpty || (rest == 0)) + normalize() + drop(rest) + } else this + + final override def takeWhile(p: Byte ⇒ Boolean) = { + var stop = false + var builder = new ListBuffer[ByteArrayIterator] + while (!stop && !iterators.isEmpty) { + val lastLen = current.len + current.takeWhile(p) + if (current.hasNext) builder += current + if (current.len < lastLen) stop = true + dropCurrent() + } + iterators = builder.result + normalize() + } + + @tailrec final override def dropWhile(p: Byte ⇒ Boolean) = if (!isEmpty) { + current.dropWhile(p) + val dropMore = current.isEmpty + normalize() + if (dropMore) dropWhile(p) else this + } else this + + final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { + var pos = start + var rest = len + while ((rest > 0) && !iterators.isEmpty) { + val n = 0 max ((xs.length - pos) min current.len min rest) + current.copyToArray(xs, pos, n) + pos += n + rest -= n + dropCurrent() + } + normalize() + } + + final override def toByteString = { + val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } + clear() + result + } + + def copyToBuffer(buffer: ByteBuffer): Int = { + val n = iterators.foldLeft(0) { _ + _.copyToBuffer(buffer) } + normalize() + n + } +} diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 4efc71a325..ba3cca51d6 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -73,6 +73,8 @@ object ByteString { override def length = bytes.length + override def iterator = ByteArrayIterator(bytes, 0, bytes.length) + def toArray: Array[Byte] = bytes.clone def toByteString1: ByteString1 = ByteString1(bytes) @@ -112,6 +114,8 @@ object ByteString { def apply(idx: Int): Byte = bytes(checkRangeConvert(idx)) + override def iterator = ByteArrayIterator(bytes, startIndex, startIndex + length) + private def checkRangeConvert(index: Int) = { if (0 <= index && length > index) index + startIndex @@ -224,6 +228,8 @@ object ByteString { bytestrings(pos)(idx - seen) } else throw new IndexOutOfBoundsException(idx.toString) + override def iterator = MultiByteArrayIterator(bytestrings.toList.map { _.iterator }) + override def slice(from: Int, until: Int): ByteString = { val start = math.max(from, 0) val end = math.min(until, length) @@ -305,6 +311,9 @@ object ByteString { sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] { override protected[this] def newBuilder = ByteString.newBuilder + // *must* be overridden by derived classes + override def iterator: ByteIterator = null + /** * Efficiently concatenate another ByteString. */ From c8d7c995dbfe5607e92f8f23183f267c45689772 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 1 May 2012 19:22:16 +0200 Subject: [PATCH 05/60] Enhanced ByteString implementation using ByteIterator --- .../main/scala/akka/util/ByteIterator.scala | 2 +- .../src/main/scala/akka/util/ByteString.scala | 108 +++++------------- 2 files changed, 31 insertions(+), 79 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 0c6146e93d..877dbef6a9 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -143,7 +143,7 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } - final override def length = { val l = len; drop(len); l } + final override def length = { val l = len; clear(); l } final override def ++(that: TraversableOnce[Byte]) = that match { case that: ByteArrayIterator ⇒ { diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index ba3cca51d6..e070d294a6 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -75,8 +75,6 @@ object ByteString { override def iterator = ByteArrayIterator(bytes, 0, bytes.length) - def toArray: Array[Byte] = bytes.clone - def toByteString1: ByteString1 = ByteString1(bytes) override def clone: ByteString1C = new ByteString1C(toArray) @@ -92,12 +90,6 @@ object ByteString { override def slice(from: Int, until: Int): ByteString = if ((from != 0) || (until != length)) toByteString1.slice(from, until) else this - - override def copyToArray[A >: Byte](xs: Array[A], start: Int, len: Int): Unit = - toByteString1.copyToArray(xs, start, len) - - def copyToBuffer(buffer: ByteBuffer): Int = - toByteString1.copyToBuffer(buffer) } private[akka] object ByteString1 { @@ -123,12 +115,6 @@ object ByteString { throw new IndexOutOfBoundsException(index.toString) } - def toArray: Array[Byte] = { - val ar = new Array[Byte](length) - Array.copy(bytes, startIndex, ar, 0, length) - ar - } - override def clone: CompactByteString = ByteString1C(toArray) def compact: CompactByteString = @@ -152,23 +138,6 @@ object ByteString { } case bs: ByteStrings ⇒ ByteStrings(this, bs) } - - override def slice(from: Int, until: Int): ByteString = { - val newStartIndex = math.max(from, 0) + startIndex - val newLength = math.min(until, length) - from - if (newLength <= 0) ByteString.empty - else new ByteString1(bytes, newStartIndex, newLength) - } - - override def copyToArray[A >: Byte](xs: Array[A], start: Int, len: Int): Unit = - Array.copy(bytes, startIndex, xs, start, math.min(math.min(length, len), xs.length - start)) - - def copyToBuffer(buffer: ByteBuffer): Int = { - val copyLength = math.min(buffer.remaining, length) - if (copyLength > 0) buffer.put(bytes, startIndex, copyLength) - copyLength - } - } private[akka] object ByteStrings { @@ -230,43 +199,6 @@ object ByteString { override def iterator = MultiByteArrayIterator(bytestrings.toList.map { _.iterator }) - override def slice(from: Int, until: Int): ByteString = { - val start = math.max(from, 0) - val end = math.min(until, length) - if (end <= start) - ByteString.empty - else { - val iter = bytestrings.iterator - var cur = iter.next - var pos = 0 - var seen = 0 - while (from >= seen + cur.length) { - seen += cur.length - pos += 1 - cur = iter.next - } - val startpos = pos - val startidx = start - seen - while (until > seen + cur.length) { - seen += cur.length - pos += 1 - cur = iter.next - } - val endpos = pos - val endidx = end - seen - if (startpos == endpos) - cur.slice(startidx, endidx) - else { - val first = bytestrings(startpos).drop(startidx).asInstanceOf[ByteString1] - val last = cur.take(endidx).asInstanceOf[ByteString1] - if ((endpos - startpos) == 1) - new ByteStrings(Vector(first, last), until - from) - else - new ByteStrings(first +: bytestrings.slice(startpos + 1, endpos) :+ last, until - from) - } - } - } - def ++(that: ByteString): ByteString = that match { case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) case b: ByteString1 ⇒ ByteStrings(this, b) @@ -287,15 +219,6 @@ object ByteString { def asByteBuffer: ByteBuffer = compact.asByteBuffer def decodeString(charset: String): String = compact.decodeString(charset) - - def copyToBuffer(buffer: ByteBuffer): Int = { - val copyLength = math.min(buffer.remaining, length) - val iter = bytestrings.iterator - while (iter.hasNext && buffer.hasRemaining) { - iter.next.copyToBuffer(buffer) - } - copyLength - } } } @@ -314,6 +237,35 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz // *must* be overridden by derived classes override def iterator: ByteIterator = null + @inline final override def head: Byte = this(0) + @inline final override def tail: ByteString = this.drop(1) + @inline final override def last: Byte = this(this.length - 1) + override def init: ByteString = this.take(this.length - 1) + + override def slice(from: Int, until: Int): ByteString = + if ((from == 0) && (until == length)) this + else iterator.slice(from, until).toByteString + + override def take(n: Int): ByteString = slice(0, n) + override def takeRight(n: Int): ByteString = slice(length - n, length) + override def drop(n: Int): ByteString = slice(n, length) + override def dropRight(n: Int): ByteString = slice(0, length - n) + + override def takeWhile(p: Byte ⇒ Boolean) = iterator.takeWhile(p).toByteString + override def dropWhile(p: Byte ⇒ Boolean) = iterator.dropWhile(p).toByteString + override def span(p: Byte ⇒ Boolean): (ByteString, ByteString) = + { val (a, b) = iterator.span(p); (a.toByteString, b.toByteString) } + + override def splitAt(n: Int): (ByteString, ByteString) = (take(n), drop(n)) + + override def indexWhere(p: Byte ⇒ Boolean): Int = iterator.indexWhere(p) + override def indexOf[B >: Byte](elem: B): Int = iterator.indexOf(elem) + + override def toArray[B >: Byte](implicit arg0: ClassManifest[B]) = iterator.toArray + override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int) = iterator.copyToArray(xs, start, len) + + @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = iterator foreach f + /** * Efficiently concatenate another ByteString. */ @@ -326,7 +278,7 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz * @param buffer a ByteBuffer to copy bytes to * @return the number of bytes actually copied */ - def copyToBuffer(buffer: ByteBuffer): Int + def copyToBuffer(buffer: ByteBuffer): Int = iterator.copyToBuffer(buffer) /** * Create a new ByteString with all contents contiguous in memory From 018f1c92d51d8f189e3d48ed602128be8d73d107 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 22:34:50 +0200 Subject: [PATCH 06/60] Improved implementations of ByteString.++ --- .../src/main/scala/akka/util/ByteString.scala | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index e070d294a6..091a0d728a 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -85,7 +85,9 @@ object ByteString { def decodeString(charset: String): String = new String(bytes, charset) def ++(that: ByteString): ByteString = - if (!that.isEmpty) toByteString1 ++ that else this + if (that.isEmpty) this + else if (this.isEmpty) that + else toByteString1 ++ that override def slice(from: Int, until: Int): ByteString = if ((from != 0) || (until != length)) toByteString1.slice(from, until) @@ -129,14 +131,18 @@ object ByteString { def decodeString(charset: String): String = new String(if (length == bytes.length) bytes else toArray, charset) - def ++(that: ByteString): ByteString = that match { - case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) - case b: ByteString1 ⇒ { - if ((bytes eq b.bytes) && (startIndex + length == b.startIndex)) - new ByteString1(bytes, startIndex, length + b.length) - else ByteStrings(this, b) + def ++(that: ByteString): ByteString = { + if (that.isEmpty) this + else if (this.isEmpty) that + else that match { + case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) + case b: ByteString1 ⇒ { + if ((bytes eq b.bytes) && (startIndex + length == b.startIndex)) + new ByteString1(bytes, startIndex, length + b.length) + else ByteStrings(this, b) + } + case bs: ByteStrings ⇒ ByteStrings(this, bs) } - case bs: ByteStrings ⇒ ByteStrings(this, bs) } } @@ -199,10 +205,14 @@ object ByteString { override def iterator = MultiByteArrayIterator(bytestrings.toList.map { _.iterator }) - def ++(that: ByteString): ByteString = that match { - case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) - case b: ByteString1 ⇒ ByteStrings(this, b) - case bs: ByteStrings ⇒ ByteStrings(this, bs) + def ++(that: ByteString): ByteString = { + if (that.isEmpty) this + else if (this.isEmpty) that + else that match { + case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) + case b: ByteString1 ⇒ ByteStrings(this, b) + case bs: ByteStrings ⇒ ByteStrings(this, bs) + } } def contiguous = compact From b7e2246ce86d09429ff1a3870f10e0916a7ca2d1 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 22:56:59 +0200 Subject: [PATCH 07/60] Improved implementations of ByteIterator.++ --- .../main/scala/akka/util/ByteIterator.scala | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 877dbef6a9..33440ff640 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -26,8 +26,10 @@ abstract class ByteIterator extends BufferedIterator[Byte] { protected def clear(): Unit - def ++(that: TraversableOnce[Byte]): ByteIterator = - ByteArrayIterator(that.toArray) + def ++(that: TraversableOnce[Byte]): ByteIterator = { + if (that.isEmpty) this + else ByteArrayIterator(that.toArray) + } // *must* be overridden by derived classes override def clone: ByteIterator = null @@ -146,20 +148,25 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro final override def length = { val l = len; clear(); l } final override def ++(that: TraversableOnce[Byte]) = that match { - case that: ByteArrayIterator ⇒ { - if (this.isEmpty) that - else if ((this.array eq that.array) && (this.until == that.from)) { - this.until = that.until - that.clear() - this - } else { - val result = MultiByteArrayIterator(List(this, that)) - this.clear() - result + case that: ByteIterator ⇒ { + if (that.isEmpty) this + else if (this.isEmpty) that + else that match { + case that: ByteArrayIterator ⇒ { + if ((this.array eq that.array) && (this.until == that.from)) { + this.until = that.until + that.clear() + this + } else { + val result = MultiByteArrayIterator(List(this, that)) + this.clear() + result + } + } + case that: MultiByteArrayIterator ⇒ this +: that } } - case that: MultiByteArrayIterator ⇒ if (this.isEmpty) that else (this +: that) - case _ ⇒ super.++(that) + case _ ⇒ super.++(that) } final override def clone = new ByteArrayIterator(array, from, until) @@ -267,15 +274,23 @@ class MultiByteArrayIterator private (private var iterators: List[ByteArrayItera } final override def ++(that: TraversableOnce[Byte]) = that match { - case that: ByteArrayIterator ⇒ if (this.isEmpty) that else { - iterators = iterators :+ that - that.clear() - this - } - case that: MultiByteArrayIterator ⇒ if (this.isEmpty) that else { - iterators = this.iterators ++ that.iterators - that.clear() - this + case that: ByteIterator ⇒ { + if (that.isEmpty) this + else if (this.isEmpty) that + else { + that match { + case that: ByteArrayIterator ⇒ { + iterators = this.iterators :+ that + that.clear() + this + } + case that: MultiByteArrayIterator ⇒ { + iterators = this.iterators ++ that.iterators + that.clear() + this + } + } + } } case _ ⇒ super.++(that) } From 3263f43060624e58f3c11e6d6889e25dd2e72166 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 2 May 2012 21:16:49 +0200 Subject: [PATCH 08/60] Added a companion object, length and sizeHint to class ByteStreamBuilder --- .../src/main/scala/akka/util/ByteString.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 091a0d728a..5ce80b0549 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -422,6 +422,10 @@ sealed abstract class CompactByteString extends ContByteString with Serializable def compact = this } +object ByteStringBuilder { + def apply(initialSize: Int = 0): ByteStringBuilder = new ByteStringBuilder(initialSize) +} + /** * A mutable builder for efficiently creating a [[akka.util.ByteString]]. * @@ -435,6 +439,17 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { private var _tempLength = 0 private var _tempCapacity = 0 + def this(initialSize: Int) = { + this() + if (initialSize > 0) sizeHint(initialSize) + } + + def length: Int = _length + + override def sizeHint(len: Int) { + resizeTemp(len - (_length - _tempLength)) + } + private def clearTemp() { if (_tempLength > 0) { val arr = new Array[Byte](_tempLength) From 53a01ecc125f2a0bf651a3f495b0d37a1c30aa3b Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 2 May 2012 22:19:09 +0200 Subject: [PATCH 09/60] Added a java.io.OutputStream wrapper to ByteStringBuilder --- .../src/main/scala/akka/util/ByteString.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 5ce80b0549..d75fb01374 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -424,6 +424,15 @@ sealed abstract class CompactByteString extends ContByteString with Serializable object ByteStringBuilder { def apply(initialSize: Int = 0): ByteStringBuilder = new ByteStringBuilder(initialSize) + + /** + * An OutputStream that directly wraps a ByteStringBuilder. + */ + class OutputStreamWrapper(val builder: ByteStringBuilder) extends java.io.OutputStream { + def write(b: Int) = builder += b.toByte + + override def write(b: Array[Byte], off: Int, len: Int) { builder.putBytes(b, off, len) } + } } /** @@ -526,4 +535,9 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { ByteStrings(bytestrings, _length) } + /** + * Directly wraps this ByteStringBuilder in an OutputStream. Write + * operations on the stream are forwarded to the builder. + */ + def asOutputStream = new ByteStringBuilder.OutputStreamWrapper(this) } From 4517b44e8b42f2a7b44d6b56e7198ca4b636d71c Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 2 May 2012 02:09:32 +0200 Subject: [PATCH 10/60] Added a java.io.InputStream wrapper for ByteIterator. --- .../main/scala/akka/util/ByteIterator.scala | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 33440ff640..4bb8beec74 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -15,6 +15,29 @@ import scala.annotation.tailrec import java.nio.ByteBuffer +object ByteIterator { + /** + * An InputStream that directly wraps a ByteIterator without copying + */ + final class InputStreamWrapper(val iterator: ByteIterator) extends java.io.InputStream { + override def available = iterator.len + + def read = iterator.next.toInt + + override def read(b: Array[Byte], off: Int, len: Int) = { + val nRead = math.min(iterator.len, len - off) + iterator.copyToArray(b, off, nRead) + nRead + } + + override def skip(n: Long) = { + val nSkip = math.min(iterator.len, n.toInt) + iterator.drop(nSkip) + nSkip + } + } +} + abstract class ByteIterator extends BufferedIterator[Byte] { def isIdenticalTo(that: Iterator[Byte]): Boolean @@ -106,6 +129,13 @@ abstract class ByteIterator extends BufferedIterator[Byte] { * @return the number of bytes actually copied */ def copyToBuffer(buffer: ByteBuffer): Int + + /** + * Directly wraps this ByteIterator in an InputStream without copying. + * Read and skip operations on the stream will advance the iterator + * accordingly. + */ + def asInputStream: java.io.InputStream = new ByteIterator.InputStreamWrapper(this) } object ByteArrayIterator { From f127b50ca2c99ace657f5ada58a02256b96cba28 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 13:26:18 +0200 Subject: [PATCH 11/60] Added methods putBytes, putShorts, etc. to ByteStringBuilder --- .../src/main/scala/akka/util/ByteString.scala | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index d75fb01374..baf77fba6f 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -4,7 +4,7 @@ package akka.util -import java.nio.ByteBuffer +import java.nio.{ ByteBuffer, ByteOrder } import scala.collection.IndexedSeqOptimized import scala.collection.mutable.{ Builder, WrappedArray } @@ -448,6 +448,23 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { private var _tempLength = 0 private var _tempCapacity = 0 + protected def fillArray(len: Int)(fill: (Array[Byte], Int) ⇒ Unit): this.type = { + ensureTempSize(_tempLength + len) + fill(_temp, _tempLength) + _tempLength += len + _length += len + this + } + + protected def fillByteBuffer(len: Int, byteOrder: ByteOrder)(fill: ByteBuffer ⇒ Unit): this.type = { + fillArray(len) { + case (array, start) ⇒ + val buffer = ByteBuffer.wrap(array, start, len) + buffer.order(byteOrder) + fill(buffer) + } + } + def this(initialSize: Int) = { this() if (initialSize > 0) sizeHint(initialSize) @@ -518,6 +535,42 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { this } + /** + * Add a number of Bytes from an array to this builder. + */ + def putBytes(array: Array[Byte], start: Int, len: Int): this.type = + fillArray(len) { case (target, targetOffset) ⇒ Array.copy(array, start, target, targetOffset, len) } + + /** + * Add a number of Shorts from an array to this builder. + */ + def putShorts(array: Array[Short], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 2, byteOrder) { _.asShortBuffer.put(array, start, len) } + + /** + * Add a number of Ints from an array to this builder. + */ + def putInts(array: Array[Int], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 4, byteOrder) { _.asIntBuffer.put(array, start, len) } + + /** + * Add a number of Longs from an array to this builder. + */ + def putLongs(array: Array[Long], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 8, byteOrder) { _.asLongBuffer.put(array, start, len) } + + /** + * Add a number of Floats from an array to this builder. + */ + def putFloats(array: Array[Float], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 4, byteOrder) { _.asFloatBuffer.put(array, start, len) } + + /** + * Add a number of Doubles from an array to this builder. + */ + def putDoubles(array: Array[Double], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + fillByteBuffer(len * 8, byteOrder) { _.asDoubleBuffer.put(array, start, len) } + def clear() { _builder.clear _length = 0 From f4ee17dcac02fbc0be376bdfb6742633b3993ecf Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 14:12:14 +0200 Subject: [PATCH 12/60] Added methods getBytes, getShorts, etc. to ByteIterator --- .../main/scala/akka/util/ByteIterator.scala | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 4bb8beec74..d574a6fbdd 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -4,7 +4,7 @@ package akka.util -import java.nio.ByteBuffer +import java.nio.{ ByteBuffer, ByteOrder } import scala.collection.IndexedSeqOptimized import scala.collection.mutable.{ Builder, WrappedArray } @@ -121,6 +121,37 @@ abstract class ByteIterator extends BufferedIterator[Byte] { target } + /** + * Get a specific number of Bytes from this iterator. In contrast to + * copyToArray, this method will fail if length < n or if (xs.length - offset) < n. + */ + def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type + + /** + * Get a number of Shorts from this iterator. + */ + def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + + /** + * Get a number of Ints from this iterator. + */ + def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + + /** + * Get a number of Longs from this iterator. + */ + def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + + /** + * Get a number of Floats from this iterator. + */ + def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + + /** + * Get a number of Doubles from this iterator. + */ + def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + /** * Copy as many bytes as possible to a ByteBuffer, starting from it's * current position. This method will not overflow the buffer. @@ -240,6 +271,28 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro result } + def getBytes(xs: Array[Byte], offset: Int, n: Int) = { + if (n <= this.len) { + Array.copy(this.array, this.from, xs, offset, n) + this + } else Iterator.empty.next + } + + def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } + + def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } + + def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } + + def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } + + def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } + def copyToBuffer(buffer: ByteBuffer): Int = { val copyLength = math.min(buffer.remaining, len) if (copyLength > 0) { @@ -391,6 +444,38 @@ class MultiByteArrayIterator private (private var iterators: List[ByteArrayItera result } + @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { + if (isEmpty) Iterator.empty.next + val nDone = if (current.len >= elemSize) { + val nCurrent = math.min(elemSize, current.len / elemSize) + getMult(xs, offset, nCurrent) + nCurrent + } else { + xs(offset) = getSingle + 1 + } + normalize() + getToArray(xs, offset + nDone, n - nDone, elemSize)(getSingle)(getMult) + } + + def getBytes(xs: Array[Byte], offset: Int, n: Int) = + getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } + + def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } + + def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } + + def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } + + def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } + + def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } + def copyToBuffer(buffer: ByteBuffer): Int = { val n = iterators.foldLeft(0) { _ + _.copyToBuffer(buffer) } normalize() From 15507dff858bfcbcf99a9d0ff99ea6c8b9ffa105 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 23:14:46 +0200 Subject: [PATCH 13/60] Added methods putByte, putShort, etc. to ByteStringBuilder --- .../src/main/scala/akka/util/ByteString.scala | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index baf77fba6f..3e11b1560a 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -535,6 +535,88 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { this } + /** + * Add a single Byte to this builder. + */ + def putByte(x: Byte): this.type = this += x + + /** + * Add a single Short to this builder. + */ + def putShort(x: Short, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + this += (x >>> 8).toByte + this += (x >>> 0).toByte + } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + this += (x >>> 0).toByte + this += (x >>> 8).toByte + } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + + /** + * Add a single Int to this builder. + */ + def putInt(x: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + fillArray(4) { + case (target, offset) ⇒ { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + target(offset + 0) = (x >>> 24).toByte + target(offset + 1) = (x >>> 16).toByte + target(offset + 2) = (x >>> 8).toByte + target(offset + 3) = (x >>> 0).toByte + } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + target(offset + 0) = (x >>> 0).toByte + target(offset + 1) = (x >>> 8).toByte + target(offset + 2) = (x >>> 16).toByte + target(offset + 3) = (x >>> 24).toByte + } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + } + this + } + + /** + * Add a single Long to this builder. + */ + def putLong(x: Long, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + fillArray(8) { + case (target, offset) ⇒ { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + target(offset + 0) = (x >>> 56).toByte + target(offset + 1) = (x >>> 48).toByte + target(offset + 2) = (x >>> 40).toByte + target(offset + 3) = (x >>> 32).toByte + target(offset + 4) = (x >>> 24).toByte + target(offset + 5) = (x >>> 16).toByte + target(offset + 6) = (x >>> 8).toByte + target(offset + 7) = (x >>> 0).toByte + } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + target(offset + 0) = (x >>> 0).toByte + target(offset + 1) = (x >>> 8).toByte + target(offset + 2) = (x >>> 16).toByte + target(offset + 3) = (x >>> 24).toByte + target(offset + 4) = (x >>> 32).toByte + target(offset + 5) = (x >>> 40).toByte + target(offset + 6) = (x >>> 48).toByte + target(offset + 7) = (x >>> 56).toByte + } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + } + this + } + + /** + * Add a single Float to this builder. + */ + def putFloat(x: Float, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + putInt(java.lang.Float.floatToRawIntBits(x), byteOrder) + + /** + * Add a single Double to this builder. + */ + def putDouble(x: Double, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + putLong(java.lang.Double.doubleToRawLongBits(x), byteOrder) + /** * Add a number of Bytes from an array to this builder. */ From 844f09a5c7286dc4fcf019f781ecac3deb7533b0 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 6 May 2012 20:47:14 +0200 Subject: [PATCH 14/60] Added methods getByte, getShort, etc. to ByteIterator --- .../main/scala/akka/util/ByteIterator.scala | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index d574a6fbdd..9ccef832c7 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -121,6 +121,70 @@ abstract class ByteIterator extends BufferedIterator[Byte] { target } + /** + * Get a single Byte from this iterator. Identical to next(). + */ + def getByte = next() + + /** + * Get a single Short from this iterator. + */ + def getShort(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Short = { + if (byteOrder == ByteOrder.BIG_ENDIAN) + ((next() & 0xff) << 8 | (next() & 0xff) << 0).toShort + else if (byteOrder == ByteOrder.LITTLE_ENDIAN) + ((next() & 0xff) << 0 | (next() & 0xff) << 8).toShort + else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + + /** + * Get a single Int from this iterator. + */ + def getInt(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Int = { + if (byteOrder == ByteOrder.BIG_ENDIAN) + ((next() & 0xff) << 24 + | (next() & 0xff) << 16 + | (next() & 0xff) << 8 + | (next() & 0xff) << 0) + else if (byteOrder == ByteOrder.LITTLE_ENDIAN) + ((next() & 0xff) << 0 + | (next() & 0xff) << 8 + | (next() & 0xff) << 16 + | (next() & 0xff) << 24) + else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + + /** + * Get a single Long from this iterator. + */ + def getLong(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Long = { + if (byteOrder == ByteOrder.BIG_ENDIAN) + ((next().toLong & 0xff) << 56 + | (next().toLong & 0xff) << 48 + | (next().toLong & 0xff) << 40 + | (next().toLong & 0xff) << 32 + | (next().toLong & 0xff) << 24 + | (next().toLong & 0xff) << 16 + | (next().toLong & 0xff) << 8 + | (next().toLong & 0xff) << 0) + else if (byteOrder == ByteOrder.LITTLE_ENDIAN) + ((next().toLong & 0xff) << 0 + | (next().toLong & 0xff) << 8 + | (next().toLong & 0xff) << 16 + | (next().toLong & 0xff) << 24 + | (next().toLong & 0xff) << 32 + | (next().toLong & 0xff) << 40 + | (next().toLong & 0xff) << 48 + | (next().toLong & 0xff) << 56) + else throw new IllegalArgumentException("Unknown byte order " + byteOrder) + } + + def getFloat(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Float = + java.lang.Float.intBitsToFloat(getInt(byteOrder)) + + def getDouble(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Double = + java.lang.Double.longBitsToDouble(getLong(byteOrder)) + /** * Get a specific number of Bytes from this iterator. In contrast to * copyToArray, this method will fail if length < n or if (xs.length - offset) < n. From 5d7823fd49d1f9df85b7eab13034f9fb8e0a597e Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 11 May 2012 19:36:13 +0200 Subject: [PATCH 15/60] Made byteOrder implicit in ByteStringBuilder methods --- .../src/main/scala/akka/util/ByteString.scala | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 3e11b1560a..b11a43b42e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -543,7 +543,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Short to this builder. */ - def putShort(x: Short, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putShort(x: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { if (byteOrder == ByteOrder.BIG_ENDIAN) { this += (x >>> 8).toByte this += (x >>> 0).toByte @@ -556,7 +556,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Int to this builder. */ - def putInt(x: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putInt(x: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { fillArray(4) { case (target, offset) ⇒ { if (byteOrder == ByteOrder.BIG_ENDIAN) { @@ -578,7 +578,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Long to this builder. */ - def putLong(x: Long, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putLong(x: Long)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { fillArray(8) { case (target, offset) ⇒ { if (byteOrder == ByteOrder.BIG_ENDIAN) { @@ -608,14 +608,14 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Float to this builder. */ - def putFloat(x: Float, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = - putInt(java.lang.Float.floatToRawIntBits(x), byteOrder) + def putFloat(x: Float)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + putInt(java.lang.Float.floatToRawIntBits(x))(byteOrder) /** * Add a single Double to this builder. */ - def putDouble(x: Double, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = - putLong(java.lang.Double.doubleToRawLongBits(x), byteOrder) + def putDouble(x: Double)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + putLong(java.lang.Double.doubleToRawLongBits(x))(byteOrder) /** * Add a number of Bytes from an array to this builder. @@ -626,31 +626,31 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a number of Shorts from an array to this builder. */ - def putShorts(array: Array[Short], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putShorts(array: Array[Short], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 2, byteOrder) { _.asShortBuffer.put(array, start, len) } /** * Add a number of Ints from an array to this builder. */ - def putInts(array: Array[Int], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putInts(array: Array[Int], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 4, byteOrder) { _.asIntBuffer.put(array, start, len) } /** * Add a number of Longs from an array to this builder. */ - def putLongs(array: Array[Long], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putLongs(array: Array[Long], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 8, byteOrder) { _.asLongBuffer.put(array, start, len) } /** * Add a number of Floats from an array to this builder. */ - def putFloats(array: Array[Float], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putFloats(array: Array[Float], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 4, byteOrder) { _.asFloatBuffer.put(array, start, len) } /** * Add a number of Doubles from an array to this builder. */ - def putDoubles(array: Array[Double], start: Int, len: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putDoubles(array: Array[Double], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = fillByteBuffer(len * 8, byteOrder) { _.asDoubleBuffer.put(array, start, len) } def clear() { From 18e6e3ed7a764c9026e94b1cb770575358a8c80e Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 14:13:36 +0200 Subject: [PATCH 16/60] Made byteOrder implicit in ByteIterator methods --- .../main/scala/akka/util/ByteIterator.scala | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 9ccef832c7..2b8e970d3e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -129,7 +129,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Short from this iterator. */ - def getShort(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Short = { + def getShort(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Short = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next() & 0xff) << 8 | (next() & 0xff) << 0).toShort else if (byteOrder == ByteOrder.LITTLE_ENDIAN) @@ -140,7 +140,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Int from this iterator. */ - def getInt(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Int = { + def getInt(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Int = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next() & 0xff) << 24 | (next() & 0xff) << 16 @@ -157,7 +157,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Long from this iterator. */ - def getLong(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Long = { + def getLong(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Long = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next().toLong & 0xff) << 56 | (next().toLong & 0xff) << 48 @@ -179,10 +179,10 @@ abstract class ByteIterator extends BufferedIterator[Byte] { else throw new IllegalArgumentException("Unknown byte order " + byteOrder) } - def getFloat(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Float = + def getFloat(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Float = java.lang.Float.intBitsToFloat(getInt(byteOrder)) - def getDouble(byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Double = + def getDouble(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Double = java.lang.Double.longBitsToDouble(getLong(byteOrder)) /** @@ -194,27 +194,27 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a number of Shorts from this iterator. */ - def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Get a number of Ints from this iterator. */ - def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Get a number of Longs from this iterator. */ - def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Get a number of Floats from this iterator. */ - def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Get a number of Doubles from this iterator. */ - def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type /** * Copy as many bytes as possible to a ByteBuffer, starting from it's @@ -342,19 +342,19 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro } else Iterator.empty.next } - def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } - def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } - def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } - def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } - def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } def copyToBuffer(buffer: ByteBuffer): Int = { @@ -525,19 +525,19 @@ class MultiByteArrayIterator private (private var iterators: List[ByteArrayItera def getBytes(xs: Array[Byte], offset: Int, n: Int) = getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } - def getShorts(xs: Array[Short], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } - def getInts(xs: Array[Int], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } - def getLongs(xs: Array[Long], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } - def getFloats(xs: Array[Float], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } - def getDoubles(xs: Array[Double], offset: Int, n: Int, byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } def copyToBuffer(buffer: ByteBuffer): Int = { From 266c67679aa0d354724b8410824c1c41c15b3839 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 17:52:33 +0200 Subject: [PATCH 17/60] Added methods isCompact and isContiguous to ByteString * The ability to check whether a ByteString is compact / contiguous can be very useful when optimizing user code --- .../src/main/scala/akka/util/ByteString.scala | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index b11a43b42e..94fa7a4ea3 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -119,8 +119,10 @@ object ByteString { override def clone: CompactByteString = ByteString1C(toArray) + def isCompact = (length == bytes.length) + def compact: CompactByteString = - if (length == bytes.length) ByteString1C(bytes) else clone + if (isCompact) ByteString1C(bytes) else clone def asByteBuffer: ByteBuffer = { val buffer = ByteBuffer.wrap(bytes, startIndex, length).asReadOnlyBuffer @@ -191,6 +193,7 @@ object ByteString { * A ByteString with 2 or more fragments. */ final class ByteStrings private (val bytestrings: Vector[ByteString1], val length: Int) extends ByteString { + if (bytestrings.isEmpty) throw new IllegalArgumentException("bytestrings must not be empty") def apply(idx: Int): Byte = if (0 <= idx && idx < length) { @@ -215,15 +218,29 @@ object ByteString { } } - def contiguous = compact - def compact: CompactByteString = { - val ar = new Array[Byte](length) - var pos = 0 - bytestrings foreach { b ⇒ - b.copyToArray(ar, pos, b.length) - pos += b.length + def isContiguous = (bytestrings.length == 1) + + def contiguous = { + if (isContiguous) bytestrings.head + else compact + } + + def isCompact = { + if (bytestrings.length == 1) bytestrings.head.isCompact + else false + } + + def compact = { + if (isCompact) bytestrings.head.compact + else { + val ar = new Array[Byte](length) + var pos = 0 + bytestrings foreach { b ⇒ + b.copyToArray(ar, pos, b.length) + pos += b.length + } + ByteString1C(ar) } - ByteString1C(ar) } def asByteBuffer: ByteBuffer = compact.asByteBuffer @@ -296,12 +313,24 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz */ def contiguous: ContByteString + /** + * Check whether this ByteString is contiguous in memory + * (i.e. represented by a single section of a byte array). + */ + def isContiguous: Boolean + /** * Create a new ByteString with all contents compacted into a single, * full byte array. */ def compact: CompactByteString + /** + * Check whether this ByteString is compact in memory + * (i.e. represented by a single, full byte array). + */ + def isCompact: Boolean + /** * Returns a read-only ByteBuffer that directly wraps this ByteString * if it is not fragmented. @@ -409,6 +438,7 @@ object CompactByteString { * operations more efficient than on chunked ByteString instances. */ sealed abstract class ContByteString extends ByteString { + def isContiguous = true def contiguous = this } @@ -419,6 +449,7 @@ sealed abstract class ContByteString extends ByteString { * as much memory as required for its contents. */ sealed abstract class CompactByteString extends ContByteString with Serializable { + def isCompact = true def compact = this } From bc0693a2de80760949adc099adfd793bcaa5b793 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 19:23:29 +0200 Subject: [PATCH 18/60] Added variants of putBytes, putShorts, etc. to ByteStringBuilder Also removed byteOrder defaults for all put... methods. --- .../src/main/scala/akka/util/ByteString.scala | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 94fa7a4ea3..5d769d9fb6 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -574,7 +574,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Short to this builder. */ - def putShort(x: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putShort(x: Int)(implicit byteOrder: ByteOrder): this.type = { if (byteOrder == ByteOrder.BIG_ENDIAN) { this += (x >>> 8).toByte this += (x >>> 0).toByte @@ -587,7 +587,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Int to this builder. */ - def putInt(x: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putInt(x: Int)(implicit byteOrder: ByteOrder): this.type = { fillArray(4) { case (target, offset) ⇒ { if (byteOrder == ByteOrder.BIG_ENDIAN) { @@ -609,7 +609,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Long to this builder. */ - def putLong(x: Long)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = { + def putLong(x: Long)(implicit byteOrder: ByteOrder): this.type = { fillArray(8) { case (target, offset) ⇒ { if (byteOrder == ByteOrder.BIG_ENDIAN) { @@ -639,15 +639,21 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a single Float to this builder. */ - def putFloat(x: Float)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putFloat(x: Float)(implicit byteOrder: ByteOrder): this.type = putInt(java.lang.Float.floatToRawIntBits(x))(byteOrder) /** * Add a single Double to this builder. */ - def putDouble(x: Double)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putDouble(x: Double)(implicit byteOrder: ByteOrder): this.type = putLong(java.lang.Double.doubleToRawLongBits(x))(byteOrder) + /** + * Add a number of Bytes from an array to this builder. + */ + def putBytes(array: Array[Byte]): this.type = + putBytes(array, 0, array.length) + /** * Add a number of Bytes from an array to this builder. */ @@ -657,31 +663,61 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { /** * Add a number of Shorts from an array to this builder. */ - def putShorts(array: Array[Short], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putShorts(array: Array[Short])(implicit byteOrder: ByteOrder): this.type = + putShorts(array, 0, array.length)(byteOrder) + + /** + * Add a number of Shorts from an array to this builder. + */ + def putShorts(array: Array[Short], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 2, byteOrder) { _.asShortBuffer.put(array, start, len) } /** * Add a number of Ints from an array to this builder. */ - def putInts(array: Array[Int], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putInts(array: Array[Int])(implicit byteOrder: ByteOrder): this.type = + putInts(array, 0, array.length)(byteOrder) + + /** + * Add a number of Ints from an array to this builder. + */ + def putInts(array: Array[Int], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 4, byteOrder) { _.asIntBuffer.put(array, start, len) } /** * Add a number of Longs from an array to this builder. */ - def putLongs(array: Array[Long], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putLongs(array: Array[Long])(implicit byteOrder: ByteOrder): this.type = + putLongs(array, 0, array.length)(byteOrder) + + /** + * Add a number of Longs from an array to this builder. + */ + def putLongs(array: Array[Long], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 8, byteOrder) { _.asLongBuffer.put(array, start, len) } /** * Add a number of Floats from an array to this builder. */ - def putFloats(array: Array[Float], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putFloats(array: Array[Float])(implicit byteOrder: ByteOrder): this.type = + putFloats(array, 0, array.length)(byteOrder) + + /** + * Add a number of Floats from an array to this builder. + */ + def putFloats(array: Array[Float], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 4, byteOrder) { _.asFloatBuffer.put(array, start, len) } /** * Add a number of Doubles from an array to this builder. */ - def putDoubles(array: Array[Double], start: Int, len: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type = + def putDoubles(array: Array[Double])(implicit byteOrder: ByteOrder): this.type = + putDoubles(array, 0, array.length)(byteOrder) + + /** + * Add a number of Doubles from an array to this builder. + */ + def putDoubles(array: Array[Double], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 8, byteOrder) { _.asDoubleBuffer.put(array, start, len) } def clear() { From a032603858b49f5001ae9d8b721e03b7cde96d67 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 17 May 2012 19:23:59 +0200 Subject: [PATCH 19/60] Added variants of getBytes, getShorts, etc. to ByteByteIterator Also removed byteOrder defaults for all get... methods. --- .../main/scala/akka/util/ByteIterator.scala | 76 ++++++++++++++----- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 2b8e970d3e..c08a50be89 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -129,7 +129,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Short from this iterator. */ - def getShort(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Short = { + def getShort(implicit byteOrder: ByteOrder): Short = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next() & 0xff) << 8 | (next() & 0xff) << 0).toShort else if (byteOrder == ByteOrder.LITTLE_ENDIAN) @@ -140,7 +140,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Int from this iterator. */ - def getInt(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Int = { + def getInt(implicit byteOrder: ByteOrder): Int = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next() & 0xff) << 24 | (next() & 0xff) << 16 @@ -157,7 +157,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Long from this iterator. */ - def getLong(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Long = { + def getLong(implicit byteOrder: ByteOrder): Long = { if (byteOrder == ByteOrder.BIG_ENDIAN) ((next().toLong & 0xff) << 56 | (next().toLong & 0xff) << 48 @@ -179,12 +179,18 @@ abstract class ByteIterator extends BufferedIterator[Byte] { else throw new IllegalArgumentException("Unknown byte order " + byteOrder) } - def getFloat(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Float = + def getFloat(implicit byteOrder: ByteOrder): Float = java.lang.Float.intBitsToFloat(getInt(byteOrder)) - def getDouble(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): Double = + def getDouble(implicit byteOrder: ByteOrder): Double = java.lang.Double.longBitsToDouble(getLong(byteOrder)) + /** + * Get a specific number of Bytes from this iterator. In contrast to + * copyToArray, this method will fail if this.len < xs.length. + */ + def getBytes(xs: Array[Byte]): this.type = getBytes(xs, 0, xs.length) + /** * Get a specific number of Bytes from this iterator. In contrast to * copyToArray, this method will fail if length < n or if (xs.length - offset) < n. @@ -194,27 +200,57 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a number of Shorts from this iterator. */ - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getShorts(xs: Array[Short])(implicit byteOrder: ByteOrder): this.type = + getShorts(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Shorts from this iterator. + */ + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Get a number of Ints from this iterator. */ - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getInts(xs: Array[Int])(implicit byteOrder: ByteOrder): this.type = + getInts(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Ints from this iterator. + */ + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Get a number of Longs from this iterator. */ - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getLongs(xs: Array[Long])(implicit byteOrder: ByteOrder): this.type = + getLongs(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Longs from this iterator. + */ + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Get a number of Floats from this iterator. */ - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getFloats(xs: Array[Float])(implicit byteOrder: ByteOrder): this.type = + getFloats(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Floats from this iterator. + */ + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Get a number of Doubles from this iterator. */ - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN): this.type + def getDoubles(xs: Array[Double])(implicit byteOrder: ByteOrder): this.type = + getDoubles(xs, 0, xs.length)(byteOrder) + + /** + * Get a number of Doubles from this iterator. + */ + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type /** * Copy as many bytes as possible to a ByteBuffer, starting from it's @@ -342,19 +378,19 @@ class ByteArrayIterator private (private var array: Array[Byte], private var fro } else Iterator.empty.next } - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } def copyToBuffer(buffer: ByteBuffer): Int = { @@ -525,19 +561,19 @@ class MultiByteArrayIterator private (private var iterators: List[ByteArrayItera def getBytes(xs: Array[Byte], offset: Int, n: Int) = getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } def copyToBuffer(buffer: ByteBuffer): Int = { From 7d3edcddbc21d2cfc8ec7cd05e87b43903996162 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 18 May 2012 17:36:16 +0200 Subject: [PATCH 20/60] Improved implementation of ByteStrings.iterator --- akka-actor/src/main/scala/akka/util/ByteString.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 5d769d9fb6..56b1b98b8d 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -206,7 +206,7 @@ object ByteString { bytestrings(pos)(idx - seen) } else throw new IndexOutOfBoundsException(idx.toString) - override def iterator = MultiByteArrayIterator(bytestrings.toList.map { _.iterator }) + override def iterator = MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) def ++(that: ByteString): ByteString = { if (that.isEmpty) this From 0c76e0f0f6726ec1f14b8cd387b85667e42d063a Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 11:30:23 +0200 Subject: [PATCH 21/60] Moved ByteIterator implementations into object ByteIterator --- .../main/scala/akka/util/ByteIterator.scala | 630 +++++++++--------- .../src/main/scala/akka/util/ByteString.scala | 6 +- 2 files changed, 318 insertions(+), 318 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index c08a50be89..8b9b95d31b 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -36,6 +36,320 @@ object ByteIterator { nSkip } } + + object ByteArrayIterator { + private val emptyArray = Array.ofDim[Byte](0) + + protected[akka] def apply(array: Array[Byte]): ByteArrayIterator = + new ByteArrayIterator(array, 0, array.length) + + protected[akka] def apply(array: Array[Byte], from: Int, until: Int): ByteArrayIterator = + new ByteArrayIterator(array, from, until) + + val empty: ByteArrayIterator = apply(Array.empty[Byte]) + } + + class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { + protected[util] final def internalArray = array + protected[util] final def internalFrom = from + protected[util] final def internalUntil = until + + final def isIdenticalTo(that: Iterator[Byte]) = that match { + case that: ByteArrayIterator ⇒ + ((this.array) eq (that.internalArray)) && + ((this.from) == (that.from)) && ((this.until) == (that.until)) + case _ ⇒ false + } + + @inline final def len = until - from + + @inline final def hasNext = from < until + + @inline final def head = array(from) + + final def next() = { + if (!hasNext) Iterator.empty.next + else { val i = from; from = from + 1; array(i) } + } + + def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } + + final override def length = { val l = len; clear(); l } + + final override def ++(that: TraversableOnce[Byte]) = that match { + case that: ByteIterator ⇒ { + if (that.isEmpty) this + else if (this.isEmpty) that + else that match { + case that: ByteArrayIterator ⇒ { + if ((this.array eq that.array) && (this.until == that.from)) { + this.until = that.until + that.clear() + this + } else { + val result = MultiByteArrayIterator(List(this, that)) + this.clear() + result + } + } + case that: MultiByteArrayIterator ⇒ this +: that + } + } + case _ ⇒ super.++(that) + } + + final override def clone = new ByteArrayIterator(array, from, until) + + final override def take(n: Int) = { + until = until min (from + (0 max n)) + this + } + + final override def drop(n: Int) = { + from = until min (from + (0 max n)) + this + } + + final override def takeWhile(p: Byte ⇒ Boolean) = { + val prev = from + dropWhile(p) + until = from; from = prev + this + } + + final override def dropWhile(p: Byte ⇒ Boolean) = { + var stop = false + while (!stop && hasNext) { + if (p(array(from))) { from = from + 1 } else { stop = true } + } + this + } + + final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { + val n = 0 max ((xs.length - start) min this.len min len) + Array.copy(this.array, from, xs, start, n) + this.drop(n) + } + + final override def toByteString = { + val result = + if ((from == 0) && (until == array.length)) ByteString.ByteString1C(array) + else ByteString.ByteString1(array, from, len) + clear() + result + } + + def getBytes(xs: Array[Byte], offset: Int, n: Int) = { + if (n <= this.len) { + Array.copy(this.array, this.from, xs, offset, n) + this + } else Iterator.empty.next + } + + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } + + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } + + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } + + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } + + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } + + def copyToBuffer(buffer: ByteBuffer): Int = { + val copyLength = math.min(buffer.remaining, len) + if (copyLength > 0) { + buffer.put(array, from, copyLength) + drop(copyLength) + } + copyLength + } + } + + object MultiByteArrayIterator { + protected val clearedList = List(ByteArrayIterator.empty) + + val empty = new MultiByteArrayIterator(Nil) + + protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = + new MultiByteArrayIterator(iterators) + } + + class MultiByteArrayIterator private (private var iterators: List[ByteArrayIterator]) extends ByteIterator { + // After normalization: + // * iterators.isEmpty == false + // * (!iterator.head.isEmpty || iterators.tail.isEmpty) == true + private def normalize(): this.type = { + @tailrec def norm(xs: List[ByteArrayIterator]): List[ByteArrayIterator] = { + if (xs.isEmpty) MultiByteArrayIterator.clearedList + else if (xs.head.isEmpty) norm(xs.tail) + else xs + } + iterators = norm(iterators) + this + } + normalize() + + @inline private def current = iterators.head + @inline private def dropCurrent() { iterators = iterators.tail } + @inline def clear() { iterators = MultiByteArrayIterator.empty.iterators } + + final def isIdenticalTo(that: Iterator[Byte]) = false + + @inline final def hasNext = current.hasNext + + @inline final def head = current.head + + final def next() = { + val result = current.next() + normalize() + result + } + + final override def len = iterators.foldLeft(0) { _ + _.len } + + final override def length = { + var result = len + clear() + result + } + + def +:(that: ByteArrayIterator): this.type = { + iterators = that +: iterators + this + } + + final override def ++(that: TraversableOnce[Byte]) = that match { + case that: ByteIterator ⇒ { + if (that.isEmpty) this + else if (this.isEmpty) that + else { + that match { + case that: ByteArrayIterator ⇒ { + iterators = this.iterators :+ that + that.clear() + this + } + case that: MultiByteArrayIterator ⇒ { + iterators = this.iterators ++ that.iterators + that.clear() + this + } + } + } + } + case _ ⇒ super.++(that) + } + + final override def clone = new MultiByteArrayIterator(iterators map { _.clone }) + + final override def take(n: Int) = { + var rest = n + val builder = new ListBuffer[ByteArrayIterator] + while ((rest > 0) && !iterators.isEmpty) { + current.take(rest) + if (current.hasNext) { + rest -= current.len + builder += current + } + iterators = iterators.tail + } + iterators = builder.result + normalize() + } + + @tailrec final override def drop(n: Int) = if ((n > 0) && !isEmpty) { + val nCurrent = math.min(n, current.len) + current.drop(n) + val rest = n - nCurrent + assert(current.isEmpty || (rest == 0)) + normalize() + drop(rest) + } else this + + final override def takeWhile(p: Byte ⇒ Boolean) = { + var stop = false + var builder = new ListBuffer[ByteArrayIterator] + while (!stop && !iterators.isEmpty) { + val lastLen = current.len + current.takeWhile(p) + if (current.hasNext) builder += current + if (current.len < lastLen) stop = true + dropCurrent() + } + iterators = builder.result + normalize() + } + + @tailrec final override def dropWhile(p: Byte ⇒ Boolean) = if (!isEmpty) { + current.dropWhile(p) + val dropMore = current.isEmpty + normalize() + if (dropMore) dropWhile(p) else this + } else this + + final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { + var pos = start + var rest = len + while ((rest > 0) && !iterators.isEmpty) { + val n = 0 max ((xs.length - pos) min current.len min rest) + current.copyToArray(xs, pos, n) + pos += n + rest -= n + dropCurrent() + } + normalize() + } + + final override def toByteString = { + val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } + clear() + result + } + + @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { + if (isEmpty) Iterator.empty.next + val nDone = if (current.len >= elemSize) { + val nCurrent = math.min(elemSize, current.len / elemSize) + getMult(xs, offset, nCurrent) + nCurrent + } else { + xs(offset) = getSingle + 1 + } + normalize() + getToArray(xs, offset + nDone, n - nDone, elemSize)(getSingle)(getMult) + } + + def getBytes(xs: Array[Byte], offset: Int, n: Int) = + getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } + + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } + + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } + + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } + + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } + + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } + + def copyToBuffer(buffer: ByteBuffer): Int = { + val n = iterators.foldLeft(0) { _ + _.copyToBuffer(buffer) } + normalize() + n + } + } } abstract class ByteIterator extends BufferedIterator[Byte] { @@ -51,7 +365,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { def ++(that: TraversableOnce[Byte]): ByteIterator = { if (that.isEmpty) this - else ByteArrayIterator(that.toArray) + else ByteIterator.ByteArrayIterator(that.toArray) } // *must* be overridden by derived classes @@ -268,317 +582,3 @@ abstract class ByteIterator extends BufferedIterator[Byte] { */ def asInputStream: java.io.InputStream = new ByteIterator.InputStreamWrapper(this) } - -object ByteArrayIterator { - private val emptyArray = Array.ofDim[Byte](0) - - protected[akka] def apply(array: Array[Byte]): ByteArrayIterator = - new ByteArrayIterator(array, 0, array.length) - - protected[akka] def apply(array: Array[Byte], from: Int, until: Int): ByteArrayIterator = - new ByteArrayIterator(array, from, until) - - val empty: ByteArrayIterator = apply(Array.empty[Byte]) -} - -class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { - protected[util] final def internalArray = array - protected[util] final def internalFrom = from - protected[util] final def internalUntil = until - - final def isIdenticalTo(that: Iterator[Byte]) = that match { - case that: ByteArrayIterator ⇒ - ((this.array) eq (that.internalArray)) && - ((this.from) == (that.from)) && ((this.until) == (that.until)) - case _ ⇒ false - } - - @inline final def len = until - from - - @inline final def hasNext = from < until - - @inline final def head = array(from) - - final def next() = { - if (!hasNext) Iterator.empty.next - else { val i = from; from = from + 1; array(i) } - } - - def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } - - final override def length = { val l = len; clear(); l } - - final override def ++(that: TraversableOnce[Byte]) = that match { - case that: ByteIterator ⇒ { - if (that.isEmpty) this - else if (this.isEmpty) that - else that match { - case that: ByteArrayIterator ⇒ { - if ((this.array eq that.array) && (this.until == that.from)) { - this.until = that.until - that.clear() - this - } else { - val result = MultiByteArrayIterator(List(this, that)) - this.clear() - result - } - } - case that: MultiByteArrayIterator ⇒ this +: that - } - } - case _ ⇒ super.++(that) - } - - final override def clone = new ByteArrayIterator(array, from, until) - - final override def take(n: Int) = { - until = until min (from + (0 max n)) - this - } - - final override def drop(n: Int) = { - from = until min (from + (0 max n)) - this - } - - final override def takeWhile(p: Byte ⇒ Boolean) = { - val prev = from - dropWhile(p) - until = from; from = prev - this - } - - final override def dropWhile(p: Byte ⇒ Boolean) = { - var stop = false - while (!stop && hasNext) { - if (p(array(from))) { from = from + 1 } else { stop = true } - } - this - } - - final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { - val n = 0 max ((xs.length - start) min this.len min len) - Array.copy(this.array, from, xs, start, n) - this.drop(n) - } - - final override def toByteString = { - val result = - if ((from == 0) && (until == array.length)) ByteString.ByteString1C(array) - else ByteString.ByteString1(array, from, len) - clear() - result - } - - def getBytes(xs: Array[Byte], offset: Int, n: Int) = { - if (n <= this.len) { - Array.copy(this.array, this.from, xs, offset, n) - this - } else Iterator.empty.next - } - - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } - - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } - - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } - - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } - - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } - - def copyToBuffer(buffer: ByteBuffer): Int = { - val copyLength = math.min(buffer.remaining, len) - if (copyLength > 0) { - buffer.put(array, from, copyLength) - drop(copyLength) - } - copyLength - } -} - -object MultiByteArrayIterator { - protected val clearedList = List(ByteArrayIterator.empty) - - val empty = new MultiByteArrayIterator(Nil) - - protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = - new MultiByteArrayIterator(iterators) -} - -class MultiByteArrayIterator private (private var iterators: List[ByteArrayIterator]) extends ByteIterator { - // After normalization: - // * iterators.isEmpty == false - // * (!iterator.head.isEmpty || iterators.tail.isEmpty) == true - private def normalize(): this.type = { - @tailrec def norm(xs: List[ByteArrayIterator]): List[ByteArrayIterator] = { - if (xs.isEmpty) MultiByteArrayIterator.clearedList - else if (xs.head.isEmpty) norm(xs.tail) - else xs - } - iterators = norm(iterators) - this - } - normalize() - - @inline private def current = iterators.head - @inline private def dropCurrent() { iterators = iterators.tail } - @inline def clear() { iterators = MultiByteArrayIterator.empty.iterators } - - final def isIdenticalTo(that: Iterator[Byte]) = false - - @inline final def hasNext = current.hasNext - - @inline final def head = current.head - - final def next() = { - val result = current.next() - normalize() - result - } - - final override def len = iterators.foldLeft(0) { _ + _.len } - - final override def length = { - var result = len - clear() - result - } - - def +:(that: ByteArrayIterator): this.type = { - iterators = that +: iterators - this - } - - final override def ++(that: TraversableOnce[Byte]) = that match { - case that: ByteIterator ⇒ { - if (that.isEmpty) this - else if (this.isEmpty) that - else { - that match { - case that: ByteArrayIterator ⇒ { - iterators = this.iterators :+ that - that.clear() - this - } - case that: MultiByteArrayIterator ⇒ { - iterators = this.iterators ++ that.iterators - that.clear() - this - } - } - } - } - case _ ⇒ super.++(that) - } - - final override def clone = new MultiByteArrayIterator(iterators map { _.clone }) - - final override def take(n: Int) = { - var rest = n - val builder = new ListBuffer[ByteArrayIterator] - while ((rest > 0) && !iterators.isEmpty) { - current.take(rest) - if (current.hasNext) { - rest -= current.len - builder += current - } - iterators = iterators.tail - } - iterators = builder.result - normalize() - } - - @tailrec final override def drop(n: Int) = if ((n > 0) && !isEmpty) { - val nCurrent = math.min(n, current.len) - current.drop(n) - val rest = n - nCurrent - assert(current.isEmpty || (rest == 0)) - normalize() - drop(rest) - } else this - - final override def takeWhile(p: Byte ⇒ Boolean) = { - var stop = false - var builder = new ListBuffer[ByteArrayIterator] - while (!stop && !iterators.isEmpty) { - val lastLen = current.len - current.takeWhile(p) - if (current.hasNext) builder += current - if (current.len < lastLen) stop = true - dropCurrent() - } - iterators = builder.result - normalize() - } - - @tailrec final override def dropWhile(p: Byte ⇒ Boolean) = if (!isEmpty) { - current.dropWhile(p) - val dropMore = current.isEmpty - normalize() - if (dropMore) dropWhile(p) else this - } else this - - final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = { - var pos = start - var rest = len - while ((rest > 0) && !iterators.isEmpty) { - val n = 0 max ((xs.length - pos) min current.len min rest) - current.copyToArray(xs, pos, n) - pos += n - rest -= n - dropCurrent() - } - normalize() - } - - final override def toByteString = { - val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } - clear() - result - } - - @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { - if (isEmpty) Iterator.empty.next - val nDone = if (current.len >= elemSize) { - val nCurrent = math.min(elemSize, current.len / elemSize) - getMult(xs, offset, nCurrent) - nCurrent - } else { - xs(offset) = getSingle - 1 - } - normalize() - getToArray(xs, offset + nDone, n - nDone, elemSize)(getSingle)(getMult) - } - - def getBytes(xs: Array[Byte], offset: Int, n: Int) = - getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } - - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } - - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } - - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } - - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } - - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = - getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } - - def copyToBuffer(buffer: ByteBuffer): Int = { - val n = iterators.foldLeft(0) { _ + _.copyToBuffer(buffer) } - normalize() - n - } -} diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 56b1b98b8d..bfa75894ed 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -73,7 +73,7 @@ object ByteString { override def length = bytes.length - override def iterator = ByteArrayIterator(bytes, 0, bytes.length) + override def iterator = ByteIterator.ByteArrayIterator(bytes, 0, bytes.length) def toByteString1: ByteString1 = ByteString1(bytes) @@ -108,7 +108,7 @@ object ByteString { def apply(idx: Int): Byte = bytes(checkRangeConvert(idx)) - override def iterator = ByteArrayIterator(bytes, startIndex, startIndex + length) + override def iterator = ByteIterator.ByteArrayIterator(bytes, startIndex, startIndex + length) private def checkRangeConvert(index: Int) = { if (0 <= index && length > index) @@ -206,7 +206,7 @@ object ByteString { bytestrings(pos)(idx - seen) } else throw new IndexOutOfBoundsException(idx.toString) - override def iterator = MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) + override def iterator = ByteIterator.MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) def ++(that: ByteString): ByteString = { if (that.isEmpty) this From d3177bf08f2d08b29a6035f1a73130e238c66ed3 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 22 May 2012 11:14:02 +0200 Subject: [PATCH 22/60] Improved return type specialization for methods implemented in super-type --- .../main/scala/akka/util/ByteIterator.scala | 30 ++++++++++++------- .../src/main/scala/akka/util/ByteString.scala | 6 ++-- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 8b9b95d31b..6bf53eeaba 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -368,25 +368,35 @@ abstract class ByteIterator extends BufferedIterator[Byte] { else ByteIterator.ByteArrayIterator(that.toArray) } - // *must* be overridden by derived classes - override def clone: ByteIterator = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def clone: ByteIterator = throw new UnsupportedOperationException("Method clone is not implemented in ByteIterator") final override def duplicate = (this, clone) - // *must* be overridden by derived classes - override def take(n: Int): this.type = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def take(n: Int): this.type = throw new UnsupportedOperationException("Method take is not implemented in ByteIterator") - // *must* be overridden by derived classes - override def drop(n: Int): this.type = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def drop(n: Int): this.type = throw new UnsupportedOperationException("Method drop is not implemented in ByteIterator") final override def slice(from: Int, until: Int): this.type = drop(from).take(until - from) - // *must* be overridden by derived classes - override def takeWhile(p: Byte ⇒ Boolean): this.type = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def takeWhile(p: Byte ⇒ Boolean): this.type = throw new UnsupportedOperationException("Method takeWhile is not implemented in ByteIterator") - // *must* be overridden by derived classes - override def dropWhile(p: Byte ⇒ Boolean): this.type = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // the parent class. + override def dropWhile(p: Byte ⇒ Boolean): this.type = throw new UnsupportedOperationException("Method dropWhile is not implemented in ByteIterator") final override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { val that = clone diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index bfa75894ed..3f3f754857 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -261,8 +261,10 @@ object ByteString { sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] { override protected[this] def newBuilder = ByteString.newBuilder - // *must* be overridden by derived classes - override def iterator: ByteIterator = null + // *must* be overridden by derived classes. This construction is necessary + // to specialize the return type, as the method is already implemented in + // a parent trait. + override def iterator: ByteIterator = throw new UnsupportedOperationException("Method iterator is not implemented in ByteString") @inline final override def head: Byte = this(0) @inline final override def tail: ByteString = this.drop(1) From 7273ac2f02fbead065c4559b75b658d17685f7dd Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 24 May 2012 10:08:25 +0200 Subject: [PATCH 23/60] Fixed implementation of ByteIterator.InputStreamWrapper.next --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 6bf53eeaba..138c549d0b 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -22,7 +22,7 @@ object ByteIterator { final class InputStreamWrapper(val iterator: ByteIterator) extends java.io.InputStream { override def available = iterator.len - def read = iterator.next.toInt + def read = if (iterator.hasNext) (iterator.next.toInt & 0xff) else -1 override def read(b: Array[Byte], off: Int, len: Int) = { val nRead = math.min(iterator.len, len - off) From 5d08cd0b923e9963c0bd288f6105ea3200bfbc8e Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 11:40:23 +0200 Subject: [PATCH 24/60] Removed ContByteString A proper implementation of ContByteString with specialized return types for methods like take, drop, etc. would have made things more complex than it is worth. --- .../src/main/scala/akka/util/ByteString.scala | 38 ++----------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 3f3f754857..e4b7026056 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -102,7 +102,7 @@ object ByteString { /** * An unfragmented ByteString. */ - final class ByteString1 private (private val bytes: Array[Byte], private val startIndex: Int, val length: Int) extends ContByteString { + final class ByteString1 private (private val bytes: Array[Byte], private val startIndex: Int, val length: Int) extends ByteString { private def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length) @@ -218,13 +218,6 @@ object ByteString { } } - def isContiguous = (bytestrings.length == 1) - - def contiguous = { - if (isContiguous) bytestrings.head - else compact - } - def isCompact = { if (bytestrings.length == 1) bytestrings.head.isCompact else false @@ -309,18 +302,6 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz */ def copyToBuffer(buffer: ByteBuffer): Int = iterator.copyToBuffer(buffer) - /** - * Create a new ByteString with all contents contiguous in memory - * (in a single section of a byte array). - */ - def contiguous: ContByteString - - /** - * Check whether this ByteString is contiguous in memory - * (i.e. represented by a single section of a byte array). - */ - def isContiguous: Boolean - /** * Create a new ByteString with all contents compacted into a single, * full byte array. @@ -434,23 +415,12 @@ object CompactByteString { } /** - * A contiguous (i.e. non-fragmented) ByteString. + * A compact ByteString. * - * The ByteString is guarantieed to be contiguous in memory, making certain - * operations more efficient than on chunked ByteString instances. - */ -sealed abstract class ContByteString extends ByteString { - def isContiguous = true - def contiguous = this -} - -/** - * A compact and contiguous ByteString. - * - * The ByteString is guarantied to be contiguous in memory and to blocks only + * The ByteString is guarantied to be contiguous in memory and to use only * as much memory as required for its contents. */ -sealed abstract class CompactByteString extends ContByteString with Serializable { +sealed abstract class CompactByteString extends ByteString with Serializable { def isCompact = true def compact = this } From 18e5b625f493ee63af2592d268ecdd2cf40142fc Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 22 May 2012 19:21:49 +0200 Subject: [PATCH 25/60] Added missing class description of ByteIterator --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 138c549d0b..4aa242f38c 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -352,6 +352,10 @@ object ByteIterator { } } +/** + * An iterator over a ByteString. + */ + abstract class ByteIterator extends BufferedIterator[Byte] { def isIdenticalTo(that: Iterator[Byte]): Boolean From 75c04d89449061f42bbb4d894405c2c4456df0b9 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 12:08:44 +0200 Subject: [PATCH 26/60] Explicit types for everything related to ByteString and ByteStringIterator --- .../main/scala/akka/util/ByteIterator.scala | 104 +++++++++--------- .../src/main/scala/akka/util/ByteString.scala | 72 ++++++------ 2 files changed, 91 insertions(+), 85 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 4aa242f38c..e4bd8dd84e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -20,17 +20,17 @@ object ByteIterator { * An InputStream that directly wraps a ByteIterator without copying */ final class InputStreamWrapper(val iterator: ByteIterator) extends java.io.InputStream { - override def available = iterator.len + override def available: Int = iterator.len - def read = if (iterator.hasNext) (iterator.next.toInt & 0xff) else -1 + def read: Int = if (iterator.hasNext) (iterator.next.toInt & 0xff) else -1 - override def read(b: Array[Byte], off: Int, len: Int) = { + override def read(b: Array[Byte], off: Int, len: Int): Int = { val nRead = math.min(iterator.len, len - off) iterator.copyToArray(b, off, nRead) nRead } - override def skip(n: Long) = { + override def skip(n: Long): Long = { val nSkip = math.min(iterator.len, n.toInt) iterator.drop(nSkip) nSkip @@ -38,7 +38,7 @@ object ByteIterator { } object ByteArrayIterator { - private val emptyArray = Array.ofDim[Byte](0) + private val emptyArray: Array[Byte] = Array.ofDim[Byte](0) protected[akka] def apply(array: Array[Byte]): ByteArrayIterator = new ByteArrayIterator(array, 0, array.length) @@ -54,29 +54,29 @@ object ByteIterator { protected[util] final def internalFrom = from protected[util] final def internalUntil = until - final def isIdenticalTo(that: Iterator[Byte]) = that match { + final def isIdenticalTo(that: Iterator[Byte]): Boolean = that match { case that: ByteArrayIterator ⇒ ((this.array) eq (that.internalArray)) && ((this.from) == (that.from)) && ((this.until) == (that.until)) case _ ⇒ false } - @inline final def len = until - from + @inline final def len: Int = until - from - @inline final def hasNext = from < until + @inline final def hasNext: Boolean = from < until - @inline final def head = array(from) + @inline final def head: Byte = array(from) - final def next() = { + final def next(): Byte = { if (!hasNext) Iterator.empty.next else { val i = from; from = from + 1; array(i) } } - def clear() { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } + def clear(): Unit = { this.array = ByteArrayIterator.emptyArray; from = 0; until = from } - final override def length = { val l = len; clear(); l } + final override def length: Int = { val l = len; clear(); l } - final override def ++(that: TraversableOnce[Byte]) = that match { + final override def ++(that: TraversableOnce[Byte]): ByteIterator = that match { case that: ByteIterator ⇒ { if (that.isEmpty) this else if (this.isEmpty) that @@ -98,26 +98,26 @@ object ByteIterator { case _ ⇒ super.++(that) } - final override def clone = new ByteArrayIterator(array, from, until) + final override def clone: ByteArrayIterator = new ByteArrayIterator(array, from, until) - final override def take(n: Int) = { + final override def take(n: Int): this.type = { until = until min (from + (0 max n)) this } - final override def drop(n: Int) = { + final override def drop(n: Int): this.type = { from = until min (from + (0 max n)) this } - final override def takeWhile(p: Byte ⇒ Boolean) = { + final override def takeWhile(p: Byte ⇒ Boolean): this.type = { val prev = from dropWhile(p) until = from; from = prev this } - final override def dropWhile(p: Byte ⇒ Boolean) = { + final override def dropWhile(p: Byte ⇒ Boolean): this.type = { var stop = false while (!stop && hasNext) { if (p(array(from))) { from = from + 1 } else { stop = true } @@ -131,7 +131,7 @@ object ByteIterator { this.drop(n) } - final override def toByteString = { + final override def toByteString: ByteString = { val result = if ((from == 0) && (until == array.length)) ByteString.ByteString1C(array) else ByteString.ByteString1(array, from, len) @@ -139,26 +139,26 @@ object ByteIterator { result } - def getBytes(xs: Array[Byte], offset: Int, n: Int) = { + def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type = { if (n <= this.len) { Array.copy(this.array, this.from, xs, offset, n) this } else Iterator.empty.next } - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } def copyToBuffer(buffer: ByteBuffer): Int = { @@ -172,9 +172,9 @@ object ByteIterator { } object MultiByteArrayIterator { - protected val clearedList = List(ByteArrayIterator.empty) + protected val clearedList: List[ByteArrayIterator] = List(ByteArrayIterator.empty) - val empty = new MultiByteArrayIterator(Nil) + val empty: MultiByteArrayIterator = new MultiByteArrayIterator(Nil) protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = new MultiByteArrayIterator(iterators) @@ -195,25 +195,25 @@ object ByteIterator { } normalize() - @inline private def current = iterators.head - @inline private def dropCurrent() { iterators = iterators.tail } - @inline def clear() { iterators = MultiByteArrayIterator.empty.iterators } + @inline private def current: ByteArrayIterator = iterators.head + @inline private def dropCurrent(): Unit = { iterators = iterators.tail } + @inline def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } - final def isIdenticalTo(that: Iterator[Byte]) = false + final def isIdenticalTo(that: Iterator[Byte]): Boolean = false - @inline final def hasNext = current.hasNext + @inline final def hasNext: Boolean = current.hasNext - @inline final def head = current.head + @inline final def head: Byte = current.head - final def next() = { + final def next(): Byte = { val result = current.next() normalize() result } - final override def len = iterators.foldLeft(0) { _ + _.len } + final override def len: Int = iterators.foldLeft(0) { _ + _.len } - final override def length = { + final override def length: Int = { var result = len clear() result @@ -224,7 +224,7 @@ object ByteIterator { this } - final override def ++(that: TraversableOnce[Byte]) = that match { + final override def ++(that: TraversableOnce[Byte]): ByteIterator = that match { case that: ByteIterator ⇒ { if (that.isEmpty) this else if (this.isEmpty) that @@ -246,9 +246,9 @@ object ByteIterator { case _ ⇒ super.++(that) } - final override def clone = new MultiByteArrayIterator(iterators map { _.clone }) + final override def clone: MultiByteArrayIterator = new MultiByteArrayIterator(iterators map { _.clone }) - final override def take(n: Int) = { + final override def take(n: Int): this.type = { var rest = n val builder = new ListBuffer[ByteArrayIterator] while ((rest > 0) && !iterators.isEmpty) { @@ -263,7 +263,7 @@ object ByteIterator { normalize() } - @tailrec final override def drop(n: Int) = if ((n > 0) && !isEmpty) { + @tailrec final override def drop(n: Int): this.type = if ((n > 0) && !isEmpty) { val nCurrent = math.min(n, current.len) current.drop(n) val rest = n - nCurrent @@ -272,7 +272,7 @@ object ByteIterator { drop(rest) } else this - final override def takeWhile(p: Byte ⇒ Boolean) = { + final override def takeWhile(p: Byte ⇒ Boolean): this.type = { var stop = false var builder = new ListBuffer[ByteArrayIterator] while (!stop && !iterators.isEmpty) { @@ -286,7 +286,7 @@ object ByteIterator { normalize() } - @tailrec final override def dropWhile(p: Byte ⇒ Boolean) = if (!isEmpty) { + @tailrec final override def dropWhile(p: Byte ⇒ Boolean): this.type = if (!isEmpty) { current.dropWhile(p) val dropMore = current.isEmpty normalize() @@ -306,7 +306,7 @@ object ByteIterator { normalize() } - final override def toByteString = { + final override def toByteString: ByteString = { val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } clear() result @@ -326,22 +326,22 @@ object ByteIterator { getToArray(xs, offset + nDone, n - nDone, elemSize)(getSingle)(getMult) } - def getBytes(xs: Array[Byte], offset: Int, n: Int) = + def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type = getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } - def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } - def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 4) { getInt(byteOrder) } { current.getInts(_, _, _)(byteOrder) } - def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 8) { getLong(byteOrder) } { current.getLongs(_, _, _)(byteOrder) } - def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 8) { getFloat(byteOrder) } { current.getFloats(_, _, _)(byteOrder) } - def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder) = + def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 8) { getDouble(byteOrder) } { current.getDoubles(_, _, _)(byteOrder) } def copyToBuffer(buffer: ByteBuffer): Int = { @@ -377,7 +377,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { // the parent class. override def clone: ByteIterator = throw new UnsupportedOperationException("Method clone is not implemented in ByteIterator") - final override def duplicate = (this, clone) + override def duplicate: (ByteIterator, ByteIterator) = (this, clone) // *must* be overridden by derived classes. This construction is necessary // to specialize the return type, as the method is already implemented in @@ -402,7 +402,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { // the parent class. override def dropWhile(p: Byte ⇒ Boolean): this.type = throw new UnsupportedOperationException("Method dropWhile is not implemented in ByteIterator") - final override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { + override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { val that = clone that.takeWhile(p) drop(that.len) @@ -452,7 +452,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { /** * Get a single Byte from this iterator. Identical to next(). */ - def getByte = next() + def getByte: Byte = next() /** * Get a single Short from this iterator. diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index e4b7026056..f5c27ca19f 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -53,11 +53,13 @@ object ByteString { val empty: ByteString = CompactByteString(Array.empty[Byte]) - def newBuilder = new ByteStringBuilder + def newBuilder: ByteStringBuilder = new ByteStringBuilder - implicit def canBuildFrom = new CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] { - def apply(from: TraversableOnce[Byte]) = newBuilder - def apply() = newBuilder + implicit def canBuildFrom: CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] = { + new CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] { + def apply(from: TraversableOnce[Byte]) = newBuilder + def apply() = newBuilder + } } private[akka] object ByteString1C { @@ -71,9 +73,9 @@ object ByteString { final class ByteString1C private (private val bytes: Array[Byte]) extends CompactByteString { def apply(idx: Int): Byte = bytes(idx) - override def length = bytes.length + override def length: Int = bytes.length - override def iterator = ByteIterator.ByteArrayIterator(bytes, 0, bytes.length) + override def iterator: ByteIterator.ByteArrayIterator = ByteIterator.ByteArrayIterator(bytes, 0, bytes.length) def toByteString1: ByteString1 = ByteString1(bytes) @@ -95,8 +97,9 @@ object ByteString { } private[akka] object ByteString1 { - def apply(bytes: Array[Byte]) = new ByteString1(bytes) - def apply(bytes: Array[Byte], startIndex: Int, length: Int) = new ByteString1(bytes, startIndex, length) + def apply(bytes: Array[Byte]): ByteString1 = new ByteString1(bytes) + def apply(bytes: Array[Byte], startIndex: Int, length: Int): ByteString1 = + new ByteString1(bytes, startIndex, length) } /** @@ -108,9 +111,10 @@ object ByteString { def apply(idx: Int): Byte = bytes(checkRangeConvert(idx)) - override def iterator = ByteIterator.ByteArrayIterator(bytes, startIndex, startIndex + length) + override def iterator: ByteIterator.ByteArrayIterator = + ByteIterator.ByteArrayIterator(bytes, startIndex, startIndex + length) - private def checkRangeConvert(index: Int) = { + private def checkRangeConvert(index: Int): Int = { if (0 <= index && length > index) index + startIndex else @@ -119,7 +123,7 @@ object ByteString { override def clone: CompactByteString = ByteString1C(toArray) - def isCompact = (length == bytes.length) + def isCompact: Boolean = (length == bytes.length) def compact: CompactByteString = if (isCompact) ByteString1C(bytes) else clone @@ -206,7 +210,8 @@ object ByteString { bytestrings(pos)(idx - seen) } else throw new IndexOutOfBoundsException(idx.toString) - override def iterator = ByteIterator.MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) + override def iterator: ByteIterator.MultiByteArrayIterator = + ByteIterator.MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) def ++(that: ByteString): ByteString = { if (that.isEmpty) this @@ -218,12 +223,12 @@ object ByteString { } } - def isCompact = { + def isCompact: Boolean = { if (bytestrings.length == 1) bytestrings.head.isCompact else false } - def compact = { + def compact: CompactByteString = { if (isCompact) bytestrings.head.compact else { val ar = new Array[Byte](length) @@ -252,7 +257,7 @@ object ByteString { * TODO: Add performance characteristics */ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] { - override protected[this] def newBuilder = ByteString.newBuilder + override protected[this] def newBuilder: ByteStringBuilder = ByteString.newBuilder // *must* be overridden by derived classes. This construction is necessary // to specialize the return type, as the method is already implemented in @@ -273,8 +278,8 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz override def drop(n: Int): ByteString = slice(n, length) override def dropRight(n: Int): ByteString = slice(0, length - n) - override def takeWhile(p: Byte ⇒ Boolean) = iterator.takeWhile(p).toByteString - override def dropWhile(p: Byte ⇒ Boolean) = iterator.dropWhile(p).toByteString + override def takeWhile(p: Byte ⇒ Boolean): ByteString = iterator.takeWhile(p).toByteString + override def dropWhile(p: Byte ⇒ Boolean): ByteString = iterator.dropWhile(p).toByteString override def span(p: Byte ⇒ Boolean): (ByteString, ByteString) = { val (a, b) = iterator.span(p); (a.toByteString, b.toByteString) } @@ -283,8 +288,9 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz override def indexWhere(p: Byte ⇒ Boolean): Int = iterator.indexWhere(p) override def indexOf[B >: Byte](elem: B): Int = iterator.indexOf(elem) - override def toArray[B >: Byte](implicit arg0: ClassManifest[B]) = iterator.toArray - override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int) = iterator.copyToArray(xs, start, len) + override def toArray[B >: Byte](implicit arg0: ClassManifest[B]): Array[B] = iterator.toArray + override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = + iterator.copyToArray(xs, start, len) @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = iterator foreach f @@ -421,8 +427,8 @@ object CompactByteString { * as much memory as required for its contents. */ sealed abstract class CompactByteString extends ByteString with Serializable { - def isCompact = true - def compact = this + def isCompact: Boolean = true + def compact: this.type = this } object ByteStringBuilder { @@ -432,9 +438,9 @@ object ByteStringBuilder { * An OutputStream that directly wraps a ByteStringBuilder. */ class OutputStreamWrapper(val builder: ByteStringBuilder) extends java.io.OutputStream { - def write(b: Int) = builder += b.toByte + def write(b: Int): Unit = builder += b.toByte - override def write(b: Array[Byte], off: Int, len: Int) { builder.putBytes(b, off, len) } + override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } } } @@ -445,11 +451,11 @@ object ByteStringBuilder { */ final class ByteStringBuilder extends Builder[Byte, ByteString] { import ByteString.{ ByteString1C, ByteString1, ByteStrings } - private var _length = 0 - private val _builder = new VectorBuilder[ByteString1]() + private var _length: Int = 0 + private val _builder: VectorBuilder[ByteString1] = new VectorBuilder[ByteString1]() private var _temp: Array[Byte] = _ - private var _tempLength = 0 - private var _tempCapacity = 0 + private var _tempLength: Int = 0 + private var _tempCapacity: Int = 0 protected def fillArray(len: Int)(fill: (Array[Byte], Int) ⇒ Unit): this.type = { ensureTempSize(_tempLength + len) @@ -475,11 +481,11 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { def length: Int = _length - override def sizeHint(len: Int) { + override def sizeHint(len: Int): Unit = { resizeTemp(len - (_length - _tempLength)) } - private def clearTemp() { + private def clearTemp(): Unit = { if (_tempLength > 0) { val arr = new Array[Byte](_tempLength) Array.copy(_temp, 0, arr, 0, _tempLength) @@ -488,14 +494,14 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { } } - private def resizeTemp(size: Int) { + private def resizeTemp(size: Int): Unit = { val newtemp = new Array[Byte](size) if (_tempLength > 0) Array.copy(_temp, 0, newtemp, 0, _tempLength) _temp = newtemp _tempCapacity = _temp.length } - private def ensureTempSize(size: Int) { + private def ensureTempSize(size: Int): Unit = { if (_tempCapacity < size || _tempCapacity == 0) { var newSize = if (_tempCapacity == 0) 16 else _tempCapacity * 2 while (newSize < size) newSize *= 2 @@ -692,7 +698,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { def putDoubles(array: Array[Double], start: Int, len: Int)(implicit byteOrder: ByteOrder): this.type = fillByteBuffer(len * 8, byteOrder) { _.asDoubleBuffer.put(array, start, len) } - def clear() { + def clear(): Unit = { _builder.clear _length = 0 _tempLength = 0 @@ -713,5 +719,5 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { * Directly wraps this ByteStringBuilder in an OutputStream. Write * operations on the stream are forwarded to the builder. */ - def asOutputStream = new ByteStringBuilder.OutputStreamWrapper(this) + def asOutputStream: java.io.OutputStream = new ByteStringBuilder.OutputStreamWrapper(this) } From d3e878effb0ff7976586339ddeb09e7f79a18cd4 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 29 May 2012 21:35:57 +0200 Subject: [PATCH 27/60] Fixed ByteIterator.{take,drop,slice} for extreme argument values --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index e4bd8dd84e..60bc9dfdb8 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -101,12 +101,12 @@ object ByteIterator { final override def clone: ByteArrayIterator = new ByteArrayIterator(array, from, until) final override def take(n: Int): this.type = { - until = until min (from + (0 max n)) + if (n < len) until = { if (n > 0) (from + n) else from } this } final override def drop(n: Int): this.type = { - from = until min (from + (0 max n)) + if (n > 0) from = { if (n < len) (from + n) else until } this } @@ -389,8 +389,10 @@ abstract class ByteIterator extends BufferedIterator[Byte] { // the parent class. override def drop(n: Int): this.type = throw new UnsupportedOperationException("Method drop is not implemented in ByteIterator") - final override def slice(from: Int, until: Int): this.type = - drop(from).take(until - from) + final override def slice(from: Int, until: Int): this.type = { + if (from > 0) drop(from).take(until - from) + else take(until) + } // *must* be overridden by derived classes. This construction is necessary // to specialize the return type, as the method is already implemented in From 696cd5a192129e94bd932977849785ab70d07fac Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 16:28:45 +0200 Subject: [PATCH 28/60] Fixed implementations of ByteIterator.getBytes, getShorts, etc. Implementations were broken. --- .../main/scala/akka/util/ByteIterator.scala | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 60bc9dfdb8..37346d4cdd 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -142,24 +142,26 @@ object ByteIterator { def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type = { if (n <= this.len) { Array.copy(this.array, this.from, xs, offset, n) - this + this.drop(n) } else Iterator.empty.next } + private def wrappedByteBuffer: ByteBuffer = ByteBuffer.wrap(array, from, len).asReadOnlyBuffer + def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } + { wrappedByteBuffer.order(byteOrder).asShortBuffer.get(xs, offset, n); drop(2 * n) } def getInts(xs: Array[Int], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } + { wrappedByteBuffer.order(byteOrder).asIntBuffer.get(xs, offset, n); drop(4 * n) } def getLongs(xs: Array[Long], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } + { wrappedByteBuffer.order(byteOrder).asLongBuffer.get(xs, offset, n); drop(8 * n) } def getFloats(xs: Array[Float], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } + { wrappedByteBuffer.order(byteOrder).asFloatBuffer.get(xs, offset, n); drop(4 * n) } def getDoubles(xs: Array[Double], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = - { toByteString.asByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } + { wrappedByteBuffer.order(byteOrder).asDoubleBuffer.get(xs, offset, n); drop(8 * n) } def copyToBuffer(buffer: ByteBuffer): Int = { val copyLength = math.min(buffer.remaining, len) @@ -315,7 +317,7 @@ object ByteIterator { @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { if (isEmpty) Iterator.empty.next val nDone = if (current.len >= elemSize) { - val nCurrent = math.min(elemSize, current.len / elemSize) + val nCurrent = math.min(n, current.len / elemSize) getMult(xs, offset, nCurrent) nCurrent } else { @@ -327,7 +329,7 @@ object ByteIterator { } def getBytes(xs: Array[Byte], offset: Int, n: Int): this.type = - getToArray(xs, offset, n, 1) { getByte } { getBytes(_, _, _) } + getToArray(xs, offset, n, 1) { getByte } { current.getBytes(_, _, _) } def getShorts(xs: Array[Short], offset: Int, n: Int)(implicit byteOrder: ByteOrder): this.type = getToArray(xs, offset, n, 2) { getShort(byteOrder) } { current.getShorts(_, _, _)(byteOrder) } From c185f72c379598103d181e5d01e9280458d22e0c Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 29 May 2012 23:58:03 +0200 Subject: [PATCH 29/60] Fixed ByteIterator InputStream wrapping Result of ByteIterator.asInputStream did not work correctly in all cases --- .../main/scala/akka/util/ByteIterator.scala | 74 +++++++++++++------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 37346d4cdd..3e3b119369 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -16,27 +16,6 @@ import scala.annotation.tailrec import java.nio.ByteBuffer object ByteIterator { - /** - * An InputStream that directly wraps a ByteIterator without copying - */ - final class InputStreamWrapper(val iterator: ByteIterator) extends java.io.InputStream { - override def available: Int = iterator.len - - def read: Int = if (iterator.hasNext) (iterator.next.toInt & 0xff) else -1 - - override def read(b: Array[Byte], off: Int, len: Int): Int = { - val nRead = math.min(iterator.len, len - off) - iterator.copyToArray(b, off, nRead) - nRead - } - - override def skip(n: Long): Long = { - val nSkip = math.min(iterator.len, n.toInt) - iterator.drop(nSkip) - nSkip - } - } - object ByteArrayIterator { private val emptyArray: Array[Byte] = Array.ofDim[Byte](0) @@ -50,6 +29,8 @@ object ByteIterator { } class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { + iterator ⇒ + protected[util] final def internalArray = array protected[util] final def internalFrom = from protected[util] final def internalUntil = until @@ -171,6 +152,28 @@ object ByteIterator { } copyLength } + + def asInputStream: java.io.InputStream = new java.io.InputStream { + override def available: Int = iterator.len + + def read: Int = if (hasNext) (next().toInt & 0xff) else -1 + + override def read(b: Array[Byte], off: Int, len: Int): Int = { + if ((off < 0) || (len < 0) || (off + len > b.length)) throw new IndexOutOfBoundsException + if (len == 0) 0 + else if (!isEmpty) { + val nRead = math.min(available, len) + copyToArray(b, off, nRead) + nRead + } else -1 + } + + override def skip(n: Long): Long = { + val nSkip = math.min(iterator.len, n.toInt) + iterator.drop(nSkip) + nSkip + } + } } object MultiByteArrayIterator { @@ -351,6 +354,33 @@ object ByteIterator { normalize() n } + + def asInputStream: java.io.InputStream = new java.io.InputStream { + override def available: Int = current.len + + def read: Int = if (hasNext) (next().toInt & 0xff) else -1 + + override def read(b: Array[Byte], off: Int, len: Int): Int = { + val nRead = current.asInputStream.read(b, off, len) + normalize() + nRead + } + + override def skip(n: Long): Long = { + @tailrec def skipImpl(n: Long, skipped: Long): Long = if (n > 0) { + if (!isEmpty) { + val m = current.asInputStream.skip(n) + normalize() + val newN = n - m + val newSkipped = skipped + m + if (newN > 0) skipImpl(newN, newSkipped) + else newSkipped + } else 0 + } else 0 + + skipImpl(n, 0) + } + } } } @@ -598,5 +628,5 @@ abstract class ByteIterator extends BufferedIterator[Byte] { * Read and skip operations on the stream will advance the iterator * accordingly. */ - def asInputStream: java.io.InputStream = new ByteIterator.InputStreamWrapper(this) + def asInputStream: java.io.InputStream } From 0aff0ff1016b21ff285d4d3c816907df2badc9c2 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 16:26:22 +0200 Subject: [PATCH 30/60] Added lots of missing tests to ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 1a4dd40ffe..396fcd6b00 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -9,6 +9,9 @@ import org.scalacheck.Arbitrary._ import org.scalacheck.Prop._ import org.scalacheck.Gen._ +import java.nio.{ ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer } +import java.nio.{ ByteOrder }, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } + class ByteStringSpec extends WordSpec with MustMatchers with Checkers { def genSimpleByteString(min: Int, max: Int) = for { @@ -37,6 +40,29 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } yield (xs, from, until) } + type ArraySlice[A] = (Array[A], Int, Int) + + def arbSlice[A](arbArray: Arbitrary[Array[A]]): Arbitrary[ArraySlice[A]] = Arbitrary { + for { + xs ← arbArray.arbitrary + from ← choose(0, xs.length) + until ← choose(from, xs.length) + } yield (xs, from, until) + } + + val arbitraryByteArray: Arbitrary[Array[Byte]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Byte](n, arbitrary[Byte]) } } + implicit val arbitraryByteArraySlice: Arbitrary[ArraySlice[Byte]] = arbSlice(arbitraryByteArray) + val arbitraryShortArray: Arbitrary[Array[Short]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Short](n, arbitrary[Short]) } } + implicit val arbitraryShortArraySlice: Arbitrary[ArraySlice[Short]] = arbSlice(arbitraryShortArray) + val arbitraryIntArray: Arbitrary[Array[Int]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Int](n, arbitrary[Int]) } } + implicit val arbitraryIntArraySlice: Arbitrary[ArraySlice[Int]] = arbSlice(arbitraryIntArray) + val arbitraryLongArray: Arbitrary[Array[Long]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Long](n, arbitrary[Long]) } } + implicit val arbitraryLongArraySlice: Arbitrary[ArraySlice[Long]] = arbSlice(arbitraryLongArray) + val arbitraryFloatArray: Arbitrary[Array[Float]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Float](n, arbitrary[Float]) } } + implicit val arbitraryFloatArraySlice: Arbitrary[ArraySlice[Float]] = arbSlice(arbitraryFloatArray) + val arbitraryDoubleArray: Arbitrary[Array[Double]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Double](n, arbitrary[Double]) } } + implicit val arbitraryDoubleArraySlice: Arbitrary[ArraySlice[Double]] = arbSlice(arbitraryDoubleArray) + def likeVecIt(bs: ByteString)(body: BufferedIterator[Byte] ⇒ Any, strict: Boolean = true): Boolean = { val bsIterator = bs.iterator val vecIterator = Vector(bs: _*).iterator.buffered @@ -51,6 +77,84 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { (!strict || (bsAIt.toSeq, bsBIt.toSeq) == (vecAIt.toSeq, vecBIt.toSeq)) } + def testShortDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 2 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Short](n) + bytes.asByteBuffer.order(byteOrder).asShortBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Short](n) + for (i ← 0 to a - 1) decoded(i) = input.getShort(byteOrder) + input.getShorts(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getShort(byteOrder) + (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) + } + + def testIntDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 4 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Int](n) + bytes.asByteBuffer.order(byteOrder).asIntBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Int](n) + for (i ← 0 to a - 1) decoded(i) = input.getInt(byteOrder) + input.getInts(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getInt(byteOrder) + (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) + } + + def testLongDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 8 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Long](n) + bytes.asByteBuffer.order(byteOrder).asLongBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Long](n) + for (i ← 0 to a - 1) decoded(i) = input.getLong(byteOrder) + input.getLongs(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getLong(byteOrder) + (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) + } + + def testShortEncoding(slice: ArraySlice[Short], byteOrder: ByteOrder): Boolean = { + val elemSize = 2 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asShortBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putShort(data(i))(byteOrder) + builder.putShorts(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putShort(data(i))(byteOrder) + reference.toSeq == builder.result + } + + def testIntEncoding(slice: ArraySlice[Int], byteOrder: ByteOrder): Boolean = { + val elemSize = 4 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asIntBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putInt(data(i))(byteOrder) + builder.putInts(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putInt(data(i))(byteOrder) + reference.toSeq == builder.result + } + + def testLongEncoding(slice: ArraySlice[Long], byteOrder: ByteOrder): Boolean = { + val elemSize = 8 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asLongBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putLong(data(i))(byteOrder) + builder.putLongs(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putLong(data(i))(byteOrder) + reference.toSeq == builder.result + } + "A ByteString" must { "have correct size" when { "concatenating" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).size == a.size + b.size) } @@ -59,6 +163,14 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "be sequential" when { "taking" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).take(a.size) == a) } "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(a.size) == b) } + + "recombining" in { + check { (xs: ByteString, from: Int, until: Int) ⇒ + val (tmp, c) = xs.splitAt(until) + val (a, b) = tmp.splitAt(from) + (a ++ b ++ c) == xs + } + } } } @@ -103,5 +215,94 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } } } + + "function as expected" when { + "getting Bytes, using getByte and getBytes" in { + // mixing getByte and getBytes here for more rigorous testing + check { slice: ByteStringSlice ⇒ + val (bytes, from, until) = slice + val input = bytes.iterator + val output = Array.ofDim[Byte](bytes.length) + for (i ← 0 to from - 1) output(i) = input.getByte + input.getBytes(output, from, until - from) + for (i ← until to bytes.length - 1) output(i) = input.getByte + (output.toSeq == bytes) && (input.isEmpty) + } + } + + "getting Bytes, using the InputStream wrapper" in { + // combining skip and both read methods here for more rigorous testing + check { slice: ByteStringSlice ⇒ + val (bytes, from, until) = slice + val a = (0 max from) min bytes.length + val b = (a max until) min bytes.length + val input = bytes.iterator + val output = Array.ofDim[Byte](bytes.length) + + input.asInputStream.skip(a) + + val toRead = b - a + var (nRead, eof) = (0, false) + while ((nRead < toRead) && !eof) { + val n = input.asInputStream.read(output, a + nRead, toRead - nRead) + if (n == -1) eof = true + else nRead += n + } + if (eof) throw new RuntimeException("Unexpected EOF") + + for (i ← b to bytes.length - 1) output(i) = input.asInputStream.read().toByte + + (output.toSeq.drop(a) == bytes.drop(a)) && + (input.asInputStream.read() == -1) && + ((output.length < 1) || (input.asInputStream.read(output, 0, 1) == -1)) + } + } + } + + "decode data correctly" when { + "decoding Short in big-endian" in { check { slice: ByteStringSlice ⇒ testShortDecoding(slice, BIG_ENDIAN) } } + "decoding Short in little-endian" in { check { slice: ByteStringSlice ⇒ testShortDecoding(slice, LITTLE_ENDIAN) } } + "decoding Int in big-endian" in { check { slice: ByteStringSlice ⇒ testIntDecoding(slice, BIG_ENDIAN) } } + "decoding Int in little-endian" in { check { slice: ByteStringSlice ⇒ testIntDecoding(slice, LITTLE_ENDIAN) } } + "decoding Long in big-endian" in { check { slice: ByteStringSlice ⇒ testLongDecoding(slice, BIG_ENDIAN) } } + "decoding Long in little-endian" in { check { slice: ByteStringSlice ⇒ testLongDecoding(slice, LITTLE_ENDIAN) } } + } + } + + "A ByteStringBuilder" must { + "function as expected" when { + "putting Bytes, using putByte and putBytes" in { + // mixing putByte and putBytes here for more rigorous testing + check { slice: ArraySlice[Byte] ⇒ + val (data, from, until) = slice + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putByte(data(i)) + builder.putBytes(data, from, until - from) + for (i ← until to data.length - 1) builder.putByte(data(i)) + data.toSeq == builder.result + } + } + + "putting Bytes, using the OutputStream wrapper" in { + // mixing the write methods here for more rigorous testing + check { slice: ArraySlice[Byte] ⇒ + val (data, from, until) = slice + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.asOutputStream.write(data(i).toInt) + builder.asOutputStream.write(data, from, until - from) + for (i ← until to data.length - 1) builder.asOutputStream.write(data(i).toInt) + data.toSeq == builder.result + } + } + } + + "encode data correctly" when { + "encoding Short in big-endian" in { check { slice: ArraySlice[Short] ⇒ testShortEncoding(slice, BIG_ENDIAN) } } + "encoding Short in little-endian" in { check { slice: ArraySlice[Short] ⇒ testShortEncoding(slice, LITTLE_ENDIAN) } } + "encoding Int in big-endian" in { check { slice: ArraySlice[Int] ⇒ testIntEncoding(slice, BIG_ENDIAN) } } + "encoding Int in little-endian" in { check { slice: ArraySlice[Int] ⇒ testIntEncoding(slice, LITTLE_ENDIAN) } } + "encoding Long in big-endian" in { check { slice: ArraySlice[Long] ⇒ testLongEncoding(slice, BIG_ENDIAN) } } + "encoding Long in little-endian" in { check { slice: ArraySlice[Long] ⇒ testLongEncoding(slice, LITTLE_ENDIAN) } } + } } } From 151b74262ee67fed5b9d22d0939a60ec4129b3b5 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 12:28:33 +0200 Subject: [PATCH 31/60] Improved MultiByteArrayIterator.toByteString MultiByteArrayIterator.toByteString now returns the optimal ByteString representation for the data. --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 3e3b119369..494a053d8e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -312,9 +312,12 @@ object ByteIterator { } final override def toByteString: ByteString = { - val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } - clear() - result + if (iterators.tail isEmpty) iterators.head.toByteString + else { + val result = iterators.foldLeft(ByteString.empty) { _ ++ _.toByteString } + clear() + result + } } @tailrec protected final def getToArray[A](xs: Array[A], offset: Int, n: Int, elemSize: Int)(getSingle: ⇒ A)(getMult: (Array[A], Int, Int) ⇒ Unit): this.type = if (n <= 0) this else { From b54e9ba78e2a297fc1d422dc0960a828ab39657c Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 12:29:03 +0200 Subject: [PATCH 32/60] Improved documentation of ByteString methods compact and isCompact --- akka-actor/src/main/scala/akka/util/ByteString.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index f5c27ca19f..3eff32f10f 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -311,12 +311,16 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz /** * Create a new ByteString with all contents compacted into a single, * full byte array. + * If isCompact returns true, compact is an O(1) operation, but + * might return a different object with an optimized implementation. */ def compact: CompactByteString /** - * Check whether this ByteString is compact in memory - * (i.e. represented by a single, full byte array). + * Check whether this ByteString is compact in memory. + * If the ByteString is compact, it might, however, not be represented + * by an object that takes full adventage of that fact. Use compact to + * get such an object. */ def isCompact: Boolean From 93d5e440eaeee367cf25ea45c23c29d741252437 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 12:59:48 +0200 Subject: [PATCH 33/60] Removed object ByteStringBuilder * ByteStringBuilder.apply was unusual for Scala Builders * Moved OutputStreamWrapper implementation into ByteStringBuilder --- .../src/main/scala/akka/util/ByteString.scala | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 3eff32f10f..ef2e16ca5e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -435,25 +435,14 @@ sealed abstract class CompactByteString extends ByteString with Serializable { def compact: this.type = this } -object ByteStringBuilder { - def apply(initialSize: Int = 0): ByteStringBuilder = new ByteStringBuilder(initialSize) - - /** - * An OutputStream that directly wraps a ByteStringBuilder. - */ - class OutputStreamWrapper(val builder: ByteStringBuilder) extends java.io.OutputStream { - def write(b: Int): Unit = builder += b.toByte - - override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } - } -} - /** * A mutable builder for efficiently creating a [[akka.util.ByteString]]. * * The created ByteString is not automatically compacted. */ final class ByteStringBuilder extends Builder[Byte, ByteString] { + builder ⇒ + import ByteString.{ ByteString1C, ByteString1, ByteStrings } private var _length: Int = 0 private val _builder: VectorBuilder[ByteString1] = new VectorBuilder[ByteString1]() @@ -478,11 +467,6 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { } } - def this(initialSize: Int) = { - this() - if (initialSize > 0) sizeHint(initialSize) - } - def length: Int = _length override def sizeHint(len: Int): Unit = { @@ -719,9 +703,15 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { ByteStrings(bytestrings, _length) } + private val outputStreamWrapper: java.io.OutputStream = new java.io.OutputStream { + def write(b: Int): Unit = builder += b.toByte + + override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } + } + /** * Directly wraps this ByteStringBuilder in an OutputStream. Write * operations on the stream are forwarded to the builder. */ - def asOutputStream: java.io.OutputStream = new ByteStringBuilder.OutputStreamWrapper(this) + def asOutputStream: java.io.OutputStream = outputStreamWrapper } From 7a854a9a8340f17d076b0a8adbe07fc13c4a4957 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sat, 26 May 2012 14:11:53 +0200 Subject: [PATCH 34/60] Changed MultiByteArrayIterator to operate on a LinearSeq ByteString.ByteStrings new creates it's iterator on a Stream instead of a List, making ByteStings.iterator an O(1) operation. --- .../src/main/scala/akka/util/ByteIterator.scala | 13 ++++++++----- .../src/main/scala/akka/util/ByteString.scala | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 494a053d8e..0d19faf2ae 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -6,7 +6,7 @@ package akka.util import java.nio.{ ByteBuffer, ByteOrder } -import scala.collection.IndexedSeqOptimized +import scala.collection.{ LinearSeq, IndexedSeqOptimized } import scala.collection.mutable.{ Builder, WrappedArray } import scala.collection.immutable.{ IndexedSeq, VectorBuilder } import scala.collection.generic.CanBuildFrom @@ -181,16 +181,16 @@ object ByteIterator { val empty: MultiByteArrayIterator = new MultiByteArrayIterator(Nil) - protected[akka] def apply(iterators: List[ByteArrayIterator]): MultiByteArrayIterator = + protected[akka] def apply(iterators: LinearSeq[ByteArrayIterator]): MultiByteArrayIterator = new MultiByteArrayIterator(iterators) } - class MultiByteArrayIterator private (private var iterators: List[ByteArrayIterator]) extends ByteIterator { + class MultiByteArrayIterator private (private var iterators: LinearSeq[ByteArrayIterator]) extends ByteIterator { // After normalization: // * iterators.isEmpty == false // * (!iterator.head.isEmpty || iterators.tail.isEmpty) == true private def normalize(): this.type = { - @tailrec def norm(xs: List[ByteArrayIterator]): List[ByteArrayIterator] = { + @tailrec def norm(xs: LinearSeq[ByteArrayIterator]): LinearSeq[ByteArrayIterator] = { if (xs.isEmpty) MultiByteArrayIterator.clearedList else if (xs.head.isEmpty) norm(xs.tail) else xs @@ -251,7 +251,10 @@ object ByteIterator { case _ ⇒ super.++(that) } - final override def clone: MultiByteArrayIterator = new MultiByteArrayIterator(iterators map { _.clone }) + final override def clone: MultiByteArrayIterator = { + val clonedIterators: List[ByteArrayIterator] = iterators.map(_.clone)(collection.breakOut) + new MultiByteArrayIterator(clonedIterators) + } final override def take(n: Int): this.type = { var rest = n diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index ef2e16ca5e..8e88e6d9fd 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -211,7 +211,7 @@ object ByteString { } else throw new IndexOutOfBoundsException(idx.toString) override def iterator: ByteIterator.MultiByteArrayIterator = - ByteIterator.MultiByteArrayIterator(bytestrings.map(_.iterator)(collection.breakOut)) + ByteIterator.MultiByteArrayIterator(bytestrings.toStream map { _.iterator }) def ++(that: ByteString): ByteString = { if (that.isEmpty) this From ff57958688db781339aac17a6355cd5f81a2495c Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 01:12:45 +0200 Subject: [PATCH 35/60] Workaround in ByteStringSpec for Scala-2.9 iterator.span bug --- .../src/test/scala/akka/util/ByteStringSpec.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 396fcd6b00..8700c50336 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -183,7 +183,12 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "calling hasNext" in { check { a: ByteString ⇒ likeVecIt(a) { _.hasNext } } } "calling length" in { check { a: ByteString ⇒ likeVecIt(a) { _.length } } } "calling duplicate" in { check { a: ByteString ⇒ likeVecIt(a)({ _.duplicate match { case (a, b) ⇒ (a.toSeq, b.toSeq) } }, strict = false) } } - "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ == b) match { case (a, b) ⇒ (a.toSeq, b.toSeq) } }, strict = false) } } + + // Have to used toList instead of toSeq here, iterator.span (new in + // Scala-2.9) seems to be broken in combination with toSeq for the + // scala.collection default Iterator (see Scala issue SI-5838). + "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ == b) match { case (a, b) ⇒ (a.toList, b.toList) } }, strict = false) } } + "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.takeWhile(_ == b).toSeq }, strict = false) } } "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.dropWhile(_ == b).toSeq } } } "calling indexWhere" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexWhere(_ == b) } } } From 85b92b9547f0b86449f803d7599e0e1c592afc52 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 01:13:45 +0200 Subject: [PATCH 36/60] Improved ByteStringSpec tests for iterator.{span, takeWhile, dropWhile} --- .../src/test/scala/akka/util/ByteStringSpec.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 8700c50336..98f878ee90 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -187,10 +187,10 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { // Have to used toList instead of toSeq here, iterator.span (new in // Scala-2.9) seems to be broken in combination with toSeq for the // scala.collection default Iterator (see Scala issue SI-5838). - "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ == b) match { case (a, b) ⇒ (a.toList, b.toList) } }, strict = false) } } + "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.span(_ != b) match { case (a, b) ⇒ (a.toList, b.toList) } }, strict = false) } } - "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.takeWhile(_ == b).toSeq }, strict = false) } } - "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.dropWhile(_ == b).toSeq } } } + "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a)({ _.takeWhile(_ != b).toSeq }, strict = false) } } + "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.dropWhile(_ != b).toSeq } } } "calling indexWhere" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexWhere(_ == b) } } } "calling indexOf" in { check { (a: ByteString, b: Byte) ⇒ likeVecIt(a) { _.indexOf(b) } } } "calling toSeq" in { check { a: ByteString ⇒ likeVecIt(a) { _.toSeq } } } From 76010d5b5670767342381e1f9e3deaa84729994e Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 01:20:01 +0200 Subject: [PATCH 37/60] Changed ByteIterator.span to return operand iterator first in tuple Makes things more consistent, since ByteIterator.duplicate also returns the operand iterator first. --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 0d19faf2ae..52f0d96ced 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -444,9 +444,9 @@ abstract class ByteIterator extends BufferedIterator[Byte] { override def span(p: Byte ⇒ Boolean): (ByteIterator, ByteIterator) = { val that = clone - that.takeWhile(p) - drop(that.len) - (that, this) + this.takeWhile(p) + that.drop(this.len) + (this, that) } final override def indexWhere(p: Byte ⇒ Boolean): Int = { From 9865a4e3e1e4dfa2abcaa873f12c5c9251e8539d Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 01:58:17 +0200 Subject: [PATCH 38/60] Added specialized implementation of foreach to MultiByteArrayIterator --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 52f0d96ced..ad2d93e6d2 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -314,6 +314,11 @@ object ByteIterator { normalize() } + override def foreach[@specialized U](f: Byte ⇒ U): Unit = { + iterators foreach { _ foreach f } + clear() + } + final override def toByteString: ByteString = { if (iterators.tail isEmpty) iterators.head.toByteString else { @@ -474,12 +479,12 @@ abstract class ByteIterator extends BufferedIterator[Byte] { override def toSeq: ByteString = toByteString - @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = + override def foreach[@specialized U](f: Byte ⇒ U): Unit = while (hasNext) f(next()) final override def foldLeft[@specialized B](z: B)(op: (B, Byte) ⇒ B): B = { var acc = z - while (hasNext) acc = op(acc, next()) + foreach { byte ⇒ acc = op(acc, byte) } acc } From 34e469c609105382cb174b76d4776d256524b6b1 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 02:34:25 +0200 Subject: [PATCH 39/60] Fixed typos in ByteString comments --- akka-actor/src/main/scala/akka/util/ByteString.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 8e88e6d9fd..383371db0d 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -67,7 +67,7 @@ object ByteString { } /** - * A compact (unsliced) and unfragmented ByteString, implementaton of ByteString1C. + * A compact (unsliced) and unfragmented ByteString, implementation of ByteString1C. */ @SerialVersionUID(3956956327691936932L) final class ByteString1C private (private val bytes: Array[Byte]) extends CompactByteString { @@ -319,7 +319,7 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz /** * Check whether this ByteString is compact in memory. * If the ByteString is compact, it might, however, not be represented - * by an object that takes full adventage of that fact. Use compact to + * by an object that takes full advantage of that fact. Use compact to * get such an object. */ def isCompact: Boolean From 60c7a57ddf6ae65b1eba0cc0290ecc117a35b7ba Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 12:55:48 +0200 Subject: [PATCH 40/60] Code formatting changes as requested by Victor --- .../main/scala/akka/util/ByteIterator.scala | 20 ++++++------------- .../src/main/scala/akka/util/ByteString.scala | 14 ++++--------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index ad2d93e6d2..10a6ab64ac 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -58,11 +58,11 @@ object ByteIterator { final override def length: Int = { val l = len; clear(); l } final override def ++(that: TraversableOnce[Byte]): ByteIterator = that match { - case that: ByteIterator ⇒ { + case that: ByteIterator ⇒ if (that.isEmpty) this else if (this.isEmpty) that else that match { - case that: ByteArrayIterator ⇒ { + case that: ByteArrayIterator ⇒ if ((this.array eq that.array) && (this.until == that.from)) { this.until = that.until that.clear() @@ -72,10 +72,8 @@ object ByteIterator { this.clear() result } - } case that: MultiByteArrayIterator ⇒ this +: that } - } case _ ⇒ super.++(that) } @@ -230,24 +228,21 @@ object ByteIterator { } final override def ++(that: TraversableOnce[Byte]): ByteIterator = that match { - case that: ByteIterator ⇒ { + case that: ByteIterator ⇒ if (that.isEmpty) this else if (this.isEmpty) that else { that match { - case that: ByteArrayIterator ⇒ { + case that: ByteArrayIterator ⇒ iterators = this.iterators :+ that that.clear() this - } - case that: MultiByteArrayIterator ⇒ { + case that: MultiByteArrayIterator ⇒ iterators = this.iterators ++ that.iterators that.clear() this - } } } - } case _ ⇒ super.++(that) } @@ -410,10 +405,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { protected def clear(): Unit - def ++(that: TraversableOnce[Byte]): ByteIterator = { - if (that.isEmpty) this - else ByteIterator.ByteArrayIterator(that.toArray) - } + def ++(that: TraversableOnce[Byte]): ByteIterator = if (that.isEmpty) this else ByteIterator.ByteArrayIterator(that.toArray) // *must* be overridden by derived classes. This construction is necessary // to specialize the return type, as the method is already implemented in diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 383371db0d..128373076d 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -142,11 +142,10 @@ object ByteString { else if (this.isEmpty) that else that match { case b: ByteString1C ⇒ ByteStrings(this, b.toByteString1) - case b: ByteString1 ⇒ { + case b: ByteString1 ⇒ if ((bytes eq b.bytes) && (startIndex + length == b.startIndex)) new ByteString1(bytes, startIndex, length + b.length) else ByteStrings(this, b) - } case bs: ByteStrings ⇒ ByteStrings(this, bs) } } @@ -223,10 +222,7 @@ object ByteString { } } - def isCompact: Boolean = { - if (bytestrings.length == 1) bytestrings.head.isCompact - else false - } + def isCompact: Boolean = if (bytestrings.length == 1) bytestrings.head.isCompact else false def compact: CompactByteString = { if (isCompact) bytestrings.head.compact @@ -555,7 +551,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { */ def putInt(x: Int)(implicit byteOrder: ByteOrder): this.type = { fillArray(4) { - case (target, offset) ⇒ { + case (target, offset) ⇒ if (byteOrder == ByteOrder.BIG_ENDIAN) { target(offset + 0) = (x >>> 24).toByte target(offset + 1) = (x >>> 16).toByte @@ -567,7 +563,6 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { target(offset + 2) = (x >>> 16).toByte target(offset + 3) = (x >>> 24).toByte } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) - } } this } @@ -577,7 +572,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { */ def putLong(x: Long)(implicit byteOrder: ByteOrder): this.type = { fillArray(8) { - case (target, offset) ⇒ { + case (target, offset) ⇒ if (byteOrder == ByteOrder.BIG_ENDIAN) { target(offset + 0) = (x >>> 56).toByte target(offset + 1) = (x >>> 48).toByte @@ -597,7 +592,6 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { target(offset + 6) = (x >>> 48).toByte target(offset + 7) = (x >>> 56).toByte } else throw new IllegalArgumentException("Unknown byte order " + byteOrder) - } } this } From d59fbbf8e445e34d0ee402eec7e4303841fc3c55 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 12:56:28 +0200 Subject: [PATCH 41/60] Made bytestrings private in ByteString.ByteStrings --- akka-actor/src/main/scala/akka/util/ByteString.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 128373076d..dba4ed77f7 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -195,7 +195,7 @@ object ByteString { /** * A ByteString with 2 or more fragments. */ - final class ByteStrings private (val bytestrings: Vector[ByteString1], val length: Int) extends ByteString { + final class ByteStrings private (private[akka] val bytestrings: Vector[ByteString1], val length: Int) extends ByteString { if (bytestrings.isEmpty) throw new IllegalArgumentException("bytestrings must not be empty") def apply(idx: Int): Byte = From b5fa527d2cb42eea10e2d54ebbc3d5cc3c5892ca Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 27 May 2012 13:00:40 +0200 Subject: [PATCH 42/60] Simplified implementation of ByteIterator.indexOf --- .../src/main/scala/akka/util/ByteIterator.scala | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 10a6ab64ac..460246380a 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -453,19 +453,9 @@ abstract class ByteIterator extends BufferedIterator[Byte] { if (found) index else -1 } - final def indexOf(elem: Byte): Int = { - var index = 0 - var found = false - while (!found && hasNext) if (elem == next()) { found = true } else { index += 1 } - if (found) index else -1 - } + final def indexOf(elem: Byte): Int = indexWhere { _ == elem } - final override def indexOf[B >: Byte](elem: B): Int = { - var index = 0 - var found = false - while (!found && hasNext) if (elem == next()) { found = true } else { index += 1 } - if (found) index else -1 - } + final override def indexOf[B >: Byte](elem: B): Int = indexWhere { _ == elem } def toByteString: ByteString From 4658e4f16590ca43bc0351223b5b0d4ced02b1d3 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 30 May 2012 18:05:54 +0200 Subject: [PATCH 43/60] Removed some remnant exposed internals from ByteArrayIterator --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 460246380a..de7cb3ff76 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -31,13 +31,9 @@ object ByteIterator { class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { iterator ⇒ - protected[util] final def internalArray = array - protected[util] final def internalFrom = from - protected[util] final def internalUntil = until - final def isIdenticalTo(that: Iterator[Byte]): Boolean = that match { case that: ByteArrayIterator ⇒ - ((this.array) eq (that.internalArray)) && + ((this.array) eq (that.array)) && ((this.from) == (that.from)) && ((this.until) == (that.until)) case _ ⇒ false } From a0ea833d54e0492333fbde69aed58e3490a694dd Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Wed, 30 May 2012 18:06:01 +0200 Subject: [PATCH 44/60] Improved ByteArrayIterator.empty --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index de7cb3ff76..13915335fa 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -25,7 +25,7 @@ object ByteIterator { protected[akka] def apply(array: Array[Byte], from: Int, until: Int): ByteArrayIterator = new ByteArrayIterator(array, from, until) - val empty: ByteArrayIterator = apply(Array.empty[Byte]) + val empty: ByteArrayIterator = apply(emptyArray) } class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { From b285b0821f3287de5085e4b757d85b57821573f6 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 00:39:15 +0200 Subject: [PATCH 45/60] Made things that don't need to be public private in ByteString / -Iterator --- akka-actor/src/main/scala/akka/util/ByteIterator.scala | 4 ++-- akka-actor/src/main/scala/akka/util/ByteString.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 13915335fa..a2bc455c38 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -68,7 +68,7 @@ object ByteIterator { this.clear() result } - case that: MultiByteArrayIterator ⇒ this +: that + case that: MultiByteArrayIterator ⇒ this ++: that } case _ ⇒ super.++(that) } @@ -218,7 +218,7 @@ object ByteIterator { result } - def +:(that: ByteArrayIterator): this.type = { + private[akka] def ++:(that: ByteArrayIterator): this.type = { iterators = that +: iterators this } diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index dba4ed77f7..8aeed6daf2 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -77,7 +77,7 @@ object ByteString { override def iterator: ByteIterator.ByteArrayIterator = ByteIterator.ByteArrayIterator(bytes, 0, bytes.length) - def toByteString1: ByteString1 = ByteString1(bytes) + private[akka] def toByteString1: ByteString1 = ByteString1(bytes) override def clone: ByteString1C = new ByteString1C(toArray) From 5b83dc366ed1614e56ad4a65f61c9775fe698b2d Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 00:44:21 +0200 Subject: [PATCH 46/60] Removed unnecessary implementations of ByteString.clone * As all ByteString implementations are immutable, an explicit override of Object.clone doesn't seem necessary. --- akka-actor/src/main/scala/akka/util/ByteString.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 8aeed6daf2..98623429e3 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -79,8 +79,6 @@ object ByteString { private[akka] def toByteString1: ByteString1 = ByteString1(bytes) - override def clone: ByteString1C = new ByteString1C(toArray) - def asByteBuffer: ByteBuffer = toByteString1.asByteBuffer @@ -121,12 +119,10 @@ object ByteString { throw new IndexOutOfBoundsException(index.toString) } - override def clone: CompactByteString = ByteString1C(toArray) - def isCompact: Boolean = (length == bytes.length) def compact: CompactByteString = - if (isCompact) ByteString1C(bytes) else clone + if (isCompact) ByteString1C(bytes) else ByteString1C(toArray) def asByteBuffer: ByteBuffer = { val buffer = ByteBuffer.wrap(bytes, startIndex, length).asReadOnlyBuffer From 3c9409a4b0086e39da09ad7365ec7ddd274a7127 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 00:48:56 +0200 Subject: [PATCH 47/60] Added test for ByteString.compact to ByteStringSpec --- .../src/test/scala/akka/util/ByteStringSpec.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 98f878ee90..da4b3a87a0 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -163,7 +163,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "be sequential" when { "taking" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).take(a.size) == a) } "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(a.size) == b) } - + } + "be equal to the original" when { + "compacting" in { check { xs: ByteString ⇒ val ys = xs.compact; (xs == ys) && ys.isCompact } } "recombining" in { check { (xs: ByteString, from: Int, until: Int) ⇒ val (tmp, c) = xs.splitAt(until) From 904f1ba0bcbc57acbaec56dfd460539af48cfb04 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 00:56:33 +0200 Subject: [PATCH 48/60] Removed obsolete ByteIterator.isIdenticalTo --- .../src/main/scala/akka/util/ByteIterator.scala | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index a2bc455c38..5de9f5f333 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -31,13 +31,6 @@ object ByteIterator { class ByteArrayIterator private (private var array: Array[Byte], private var from: Int, private var until: Int) extends ByteIterator { iterator ⇒ - final def isIdenticalTo(that: Iterator[Byte]): Boolean = that match { - case that: ByteArrayIterator ⇒ - ((this.array) eq (that.array)) && - ((this.from) == (that.from)) && ((this.until) == (that.until)) - case _ ⇒ false - } - @inline final def len: Int = until - from @inline final def hasNext: Boolean = from < until @@ -198,8 +191,6 @@ object ByteIterator { @inline private def dropCurrent(): Unit = { iterators = iterators.tail } @inline def clear(): Unit = { iterators = MultiByteArrayIterator.empty.iterators } - final def isIdenticalTo(that: Iterator[Byte]): Boolean = false - @inline final def hasNext: Boolean = current.hasNext @inline final def head: Byte = current.head @@ -391,8 +382,6 @@ object ByteIterator { */ abstract class ByteIterator extends BufferedIterator[Byte] { - def isIdenticalTo(that: Iterator[Byte]): Boolean - def len: Int def head: Byte From 7231b48c055724693f313b0101433a9eacb23b37 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 8 Jun 2012 17:57:41 +0900 Subject: [PATCH 49/60] Added tests for Float/Double encoding/decoding to ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index da4b3a87a0..db7162c3be 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -11,6 +11,8 @@ import org.scalacheck.Gen._ import java.nio.{ ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer } import java.nio.{ ByteOrder }, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } +import java.lang.Float.floatToRawIntBits +import java.lang.Double.doubleToRawLongBits class ByteStringSpec extends WordSpec with MustMatchers with Checkers { @@ -119,6 +121,36 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { (decoded.toSeq == reference.toSeq) && (input.toSeq == bytes.drop(n * elemSize)) } + def testFloatDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 4 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Float](n) + bytes.asByteBuffer.order(byteOrder).asFloatBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Float](n) + for (i ← 0 to a - 1) decoded(i) = input.getFloat(byteOrder) + input.getFloats(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getFloat(byteOrder) + ((decoded.toSeq map floatToRawIntBits) == (reference.toSeq map floatToRawIntBits)) && + (input.toSeq == bytes.drop(n * elemSize)) + } + + def testDoubleDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { + val elemSize = 8 + val (bytes, from, until) = slice + val (n, a, b) = (bytes.length / elemSize, from / elemSize, until / elemSize) + val reference = Array.ofDim[Double](n) + bytes.asByteBuffer.order(byteOrder).asDoubleBuffer.get(reference, 0, n) + val input = bytes.iterator + val decoded = Array.ofDim[Double](n) + for (i ← 0 to a - 1) decoded(i) = input.getDouble(byteOrder) + input.getDoubles(decoded, a, b - a)(byteOrder) + for (i ← b to n - 1) decoded(i) = input.getDouble(byteOrder) + ((decoded.toSeq map doubleToRawLongBits) == (reference.toSeq map doubleToRawLongBits)) && + (input.toSeq == bytes.drop(n * elemSize)) + } + def testShortEncoding(slice: ArraySlice[Short], byteOrder: ByteOrder): Boolean = { val elemSize = 2 val (data, from, until) = slice @@ -155,6 +187,30 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { reference.toSeq == builder.result } + def testFloatEncoding(slice: ArraySlice[Float], byteOrder: ByteOrder): Boolean = { + val elemSize = 4 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asFloatBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putFloat(data(i))(byteOrder) + builder.putFloats(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putFloat(data(i))(byteOrder) + reference.toSeq == builder.result + } + + def testDoubleEncoding(slice: ArraySlice[Double], byteOrder: ByteOrder): Boolean = { + val elemSize = 8 + val (data, from, until) = slice + val reference = Array.ofDim[Byte](data.length * elemSize) + ByteBuffer.wrap(reference).order(byteOrder).asDoubleBuffer.put(data) + val builder = ByteString.newBuilder + for (i ← 0 to from - 1) builder.putDouble(data(i))(byteOrder) + builder.putDoubles(data, from, until - from)(byteOrder) + for (i ← until to data.length - 1) builder.putDouble(data(i))(byteOrder) + reference.toSeq == builder.result + } + "A ByteString" must { "have correct size" when { "concatenating" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).size == a.size + b.size) } @@ -273,6 +329,10 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "decoding Int in little-endian" in { check { slice: ByteStringSlice ⇒ testIntDecoding(slice, LITTLE_ENDIAN) } } "decoding Long in big-endian" in { check { slice: ByteStringSlice ⇒ testLongDecoding(slice, BIG_ENDIAN) } } "decoding Long in little-endian" in { check { slice: ByteStringSlice ⇒ testLongDecoding(slice, LITTLE_ENDIAN) } } + "decoding Float in big-endian" in { check { slice: ByteStringSlice ⇒ testFloatDecoding(slice, BIG_ENDIAN) } } + "decoding Float in little-endian" in { check { slice: ByteStringSlice ⇒ testFloatDecoding(slice, LITTLE_ENDIAN) } } + "decoding Double in big-endian" in { check { slice: ByteStringSlice ⇒ testDoubleDecoding(slice, BIG_ENDIAN) } } + "decoding Double in little-endian" in { check { slice: ByteStringSlice ⇒ testDoubleDecoding(slice, LITTLE_ENDIAN) } } } } @@ -310,6 +370,10 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "encoding Int in little-endian" in { check { slice: ArraySlice[Int] ⇒ testIntEncoding(slice, LITTLE_ENDIAN) } } "encoding Long in big-endian" in { check { slice: ArraySlice[Long] ⇒ testLongEncoding(slice, BIG_ENDIAN) } } "encoding Long in little-endian" in { check { slice: ArraySlice[Long] ⇒ testLongEncoding(slice, LITTLE_ENDIAN) } } + "encoding Float in big-endian" in { check { slice: ArraySlice[Float] ⇒ testFloatEncoding(slice, BIG_ENDIAN) } } + "encoding Float in little-endian" in { check { slice: ArraySlice[Float] ⇒ testFloatEncoding(slice, LITTLE_ENDIAN) } } + "encoding Double in big-endian" in { check { slice: ArraySlice[Double] ⇒ testDoubleEncoding(slice, BIG_ENDIAN) } } + "encoding Double in little-endian" in { check { slice: ArraySlice[Double] ⇒ testDoubleEncoding(slice, LITTLE_ENDIAN) } } } } } From f01d5c619ec3c9465a3de1e6ec82129dd1b81b41 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Thu, 31 May 2012 10:15:44 +0200 Subject: [PATCH 50/60] Completed ByteIterator tests in ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index db7162c3be..d3d4371c26 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -266,6 +266,16 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } } + "calling take and drop" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVecIt(xs)({ + _.drop(from).take(until - from).toSeq + }, strict = false) + } + } + } + "calling copyToArray" in { check { slice: ByteStringSlice ⇒ slice match { @@ -320,6 +330,18 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { ((output.length < 1) || (input.asInputStream.read(output, 0, 1) == -1)) } } + + "calling copyToBuffer" in { + check { bytes: ByteString ⇒ + import java.nio.ByteBuffer + val buffer = ByteBuffer.allocate(bytes.size) + bytes.copyToBuffer(buffer) + buffer.flip() + val array = Array.ofDim[Byte](bytes.size) + buffer.get(array) + bytes == array.toSeq + } + } } "decode data correctly" when { From a87faade0608cc598c598a2a56f79b9939fd76cb Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 12 Jun 2012 14:12:46 +0900 Subject: [PATCH 51/60] Fixed serious bug in ByteStringBuilder.++= --- akka-actor/src/main/scala/akka/util/ByteString.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 98623429e3..271e17289b 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -515,9 +515,11 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { clearTemp() _builder += ByteString1(xs.array.clone) _length += xs.length - case _: collection.IndexedSeq[_] ⇒ + case seq: collection.IndexedSeq[_] ⇒ ensureTempSize(_tempLength + xs.size) xs.copyToArray(_temp, _tempLength) + _tempLength += seq.length + _length += seq.length case _ ⇒ super.++=(xs) } From e01e324c80cef26092f3366be5cbe3cc1a003b44 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 12 Jun 2012 14:13:17 +0900 Subject: [PATCH 52/60] Extended ByteStringBuilder tests in ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index d3d4371c26..cc9c5f93a7 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -9,6 +9,8 @@ import org.scalacheck.Arbitrary._ import org.scalacheck.Prop._ import org.scalacheck.Gen._ +import scala.collection.mutable.Builder + import java.nio.{ ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer } import java.nio.{ ByteOrder }, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } import java.lang.Float.floatToRawIntBits @@ -79,6 +81,16 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { (!strict || (bsAIt.toSeq, bsBIt.toSeq) == (vecAIt.toSeq, vecBIt.toSeq)) } + def likeVecBld(body: Builder[Byte, _] ⇒ Unit): Boolean = { + val bsBuilder = ByteString.newBuilder + val vecBuilder = Vector.newBuilder[Byte] + + body(bsBuilder) + body(vecBuilder) + + bsBuilder.result == vecBuilder.result + } + def testShortDecoding(slice: ByteStringSlice, byteOrder: ByteOrder): Boolean = { val elemSize = 2 val (bytes, from, until) = slice @@ -359,6 +371,19 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } "A ByteStringBuilder" must { + "function like a VectorBuilder" when { + "adding various contents using ++= and +=" in { + check { (array1: Array[Byte], array2: Array[Byte], bs1: ByteString, bs2: ByteString, bs3: ByteString) ⇒ + likeVecBld { builder ⇒ + builder ++= array1 + bs1 foreach { b ⇒ builder += b } + builder ++= bs2 + bs3 foreach { b ⇒ builder += b } + builder ++= Vector(array2: _*) + } + } + } + } "function as expected" when { "putting Bytes, using putByte and putBytes" in { // mixing putByte and putBytes here for more rigorous testing From 91ff7a29b911fabb85aa5c7640bc04170e60ef64 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 12 Jun 2012 15:03:07 +0900 Subject: [PATCH 53/60] Completed ByteString tests in ByteStringSpec --- .../test/scala/akka/util/ByteStringSpec.scala | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index cc9c5f93a7..81b972f520 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -67,6 +67,17 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { val arbitraryDoubleArray: Arbitrary[Array[Double]] = Arbitrary { Gen.sized { n ⇒ Gen.containerOfN[Array, Double](n, arbitrary[Double]) } } implicit val arbitraryDoubleArraySlice: Arbitrary[ArraySlice[Double]] = arbSlice(arbitraryDoubleArray) + def likeVector(bs: ByteString)(body: IndexedSeq[Byte] ⇒ Any): Boolean = { + val vec = Vector(bs: _*) + body(bs) == body(vec) + } + + def likeVectors(bsA: ByteString, bsB: ByteString)(body: (IndexedSeq[Byte], IndexedSeq[Byte]) ⇒ Any): Boolean = { + val vecA = Vector(bsA: _*) + val vecB = Vector(bsB: _*) + body(bsA, bsB) == body(vecA, vecB) + } + def likeVecIt(bs: ByteString)(body: BufferedIterator[Byte] ⇒ Any, strict: Boolean = true): Boolean = { val bsIterator = bs.iterator val vecIterator = Vector(bs: _*).iterator.buffered @@ -228,10 +239,12 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { "concatenating" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).size == a.size + b.size) } "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(b.size).size == a.size) } } + "be sequential" when { "taking" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).take(a.size) == a) } "dropping" in { check((a: ByteString, b: ByteString) ⇒ (a ++ b).drop(a.size) == b) } } + "be equal to the original" when { "compacting" in { check { xs: ByteString ⇒ val ys = xs.compact; (xs == ys) && ys.isCompact } } "recombining" in { @@ -242,6 +255,81 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } } } + + "behave as expected" when { + "created from and decoding to String" in { check { s: String ⇒ ByteString(s, "UTF-8").decodeString("UTF-8") == s } } + + "compacting" in { + check { a: ByteString ⇒ + val wasCompact = a.isCompact + val b = a.compact + ((!wasCompact) || (b eq a)) && + (b == a) && + b.isCompact && + (b.compact eq b) + } + } + } + "behave like a Vector" when { + "concatenating" in { check { (a: ByteString, b: ByteString) ⇒ likeVectors(a, b) { (a, b) ⇒ (a ++ b) } } } + + "calling apply" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, i1, i2) ⇒ likeVector(xs) { seq ⇒ + (if ((i1 >= 0) && (i1 < seq.length)) seq(i1) else 0, + if ((i2 >= 0) && (i2 < seq.length)) seq(i2) else 0) + } + } + } + } + + "calling head" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.head } } } + "calling last" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.last } } } + "calling length" in { check { a: ByteString ⇒ likeVector(a) { _.length } } } + + "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a)({ _.span(_ != b) match { case (a, b) ⇒ (a, b) } }) } } + + "calling takeWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a)({ _.takeWhile(_ != b) }) } } + "calling dropWhile" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a) { _.dropWhile(_ != b) } } } + "calling indexWhere" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a) { _.indexWhere(_ == b) } } } + "calling indexOf" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a) { _.indexOf(b) } } } + "calling foreach" in { check { a: ByteString ⇒ likeVector(a) { it ⇒ var acc = 0; it foreach { acc += _ }; acc } } } + "calling foldLeft" in { check { a: ByteString ⇒ likeVector(a) { _.foldLeft(0) { _ + _ } } } } + "calling toArray" in { check { a: ByteString ⇒ likeVector(a) { _.toArray.toSeq } } } + + "calling slice" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVector(xs)({ + _.slice(from, until) + }) + } + } + } + + "calling take and drop" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVector(xs)({ + _.drop(from).take(until - from) + }) + } + } + } + + "calling copyToArray" in { + check { slice: ByteStringSlice ⇒ + slice match { + case (xs, from, until) ⇒ likeVector(xs)({ it ⇒ + val array = Array.ofDim[Byte](xs.length) + it.slice(from, until).copyToArray(array, from, until) + array.toSeq + }) + } + } + } + } } "A ByteStringIterator" must { From 2ed00f0936234595ac3b5dcdef31dd2ad9ec1f1f Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Fri, 15 Jun 2012 17:27:27 +0900 Subject: [PATCH 54/60] Removed some superfluous braces. --- akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala | 2 +- akka-actor/src/main/scala/akka/util/ByteString.scala | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 81b972f520..3029ca5df0 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -12,7 +12,7 @@ import org.scalacheck.Gen._ import scala.collection.mutable.Builder import java.nio.{ ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer } -import java.nio.{ ByteOrder }, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } +import java.nio.ByteOrder, ByteOrder.{ BIG_ENDIAN, LITTLE_ENDIAN } import java.lang.Float.floatToRawIntBits import java.lang.Double.doubleToRawLongBits diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 271e17289b..8b26a9325e 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -55,12 +55,11 @@ object ByteString { def newBuilder: ByteStringBuilder = new ByteStringBuilder - implicit def canBuildFrom: CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] = { + implicit def canBuildFrom: CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] = new CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] { def apply(from: TraversableOnce[Byte]) = newBuilder def apply() = newBuilder } - } private[akka] object ByteString1C { def apply(bytes: Array[Byte]) = new ByteString1C(bytes) From 394cce0af40f8d4c83783b4c2f1236e093edd8c2 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 17:58:20 +0200 Subject: [PATCH 55/60] Simplified implementation of ByteStringBuilder.asOutputStream --- akka-actor/src/main/scala/akka/util/ByteString.scala | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 8b26a9325e..a3f1634bfc 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -694,15 +694,13 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] { ByteStrings(bytestrings, _length) } - private val outputStreamWrapper: java.io.OutputStream = new java.io.OutputStream { - def write(b: Int): Unit = builder += b.toByte - - override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } - } - /** * Directly wraps this ByteStringBuilder in an OutputStream. Write * operations on the stream are forwarded to the builder. */ - def asOutputStream: java.io.OutputStream = outputStreamWrapper + def asOutputStream: java.io.OutputStream = new java.io.OutputStream { + def write(b: Int): Unit = builder += b.toByte + + override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) } + } } From 8d4986dde3d1fde28bcce24fd74f53df17b98696 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 19:28:08 +0200 Subject: [PATCH 56/60] Added new ByteString features to IO (Scala) documentation --- akka-docs/scala/io.rst | 101 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/akka-docs/scala/io.rst b/akka-docs/scala/io.rst index 2916dcd03d..d2ac3783a9 100644 --- a/akka-docs/scala/io.rst +++ b/akka-docs/scala/io.rst @@ -17,10 +17,109 @@ ByteString A primary goal of Akka's IO module is to only communicate between actors with immutable objects. When dealing with network IO on the jvm ``Array[Byte]`` and ``ByteBuffer`` are commonly used to represent collections of ``Byte``\s, but they are mutable. Scala's collection library also lacks a suitably efficient immutable collection for ``Byte``\s. Being able to safely and efficiently move ``Byte``\s around is very important for this IO module, so ``ByteString`` was developed. -``ByteString`` is a `Rope-like `_ data structure that is immutable and efficient. When 2 ``ByteString``\s are concatenated together they are both stored within the resulting ``ByteString`` instead of copying both to a new ``Array``. Operations such as ``drop`` and ``take`` return ``ByteString``\s that still reference the original ``Array``, but just change the offset and length that is visible. Great care has also been taken to make sure that the internal ``Array`` cannot be modified. Whenever a potentially unsafe ``Array`` is used to create a new ``ByteString`` a defensive copy is created. +``ByteString`` is a `Rope-like `_ data structure that is immutable and efficient. When 2 ``ByteString``\s are concatenated together they are both stored within the resulting ``ByteString`` instead of copying both to a new ``Array``. Operations such as ``drop`` and ``take`` return ``ByteString``\s that still reference the original ``Array``, but just change the offset and length that is visible. Great care has also been taken to make sure that the internal ``Array`` cannot be modified. Whenever a potentially unsafe ``Array`` is used to create a new ``ByteString`` a defensive copy is created. If you require a ``ByteString`` that only blocks a much memory as necessary for it's content, use the ``compact`` method to get a ``CompactByteString`` instance. If the ``ByteString`` represented only a slice of the original array, this will result in copying all bytes in that slice. ``ByteString`` inherits all methods from ``IndexedSeq``, and it also has some new ones. For more information, look up the ``akka.util.ByteString`` class and it's companion object in the ScalaDoc. +``ByteString`` also comes with it's own optimized builder and iterator classes ``ByteStringBuilder`` and ``ByteIterator`` which provides special features in addition to the standard builder / iterator methods: + +Compatibility with java.io +.......................... + +A ``ByteStringBuilder`` can be wrapped in a `java.io.OutputStream` via the ``asOutputStream`` method. Likewise, ``ByteIterator`` can we wrapped in a ``java.io.InputStream`` via ``asInputStream``. Using these, ``akka.io`` applications can integrate legacy code based on ``java.io`` streams. + +Encoding and decoding of binary data +.................................... + +``ByteStringBuilder`` and ``ByteIterator`` support encoding and decoding of binary data. As an example, consider a stream of binary data frames with the following format: + +.. code-block:: text + + frameLen: Int + n: Int + m: Int + n times { + a: Short + b: Long + } + data: m times Double + +In this example, the data is to be stored in arrays of ``a``, ``b`` and ``data``. + +Decoding of such frames can be efficiently implemented in the following fashion: + +.. code-block:: scala + + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + + val FrameDecoder = for { + frameLenBytes <- IO.take(4) + frameLen = frameLenBytes.iterator.getInt + frame <- IO.take(frameLen) + } yield { + val in = frame.iterator + + val n = in.getInt + val m = in.getInt + + val a = ArrayBuilder.make[Short]() + val b = ArrayBuilder.make[Long]() + + for (i <- 1 to n) { + a += in.getShort + b += in.getInt + } + + val data = Array.ofDim[Double](m) + in.getDoubles(data) + + (a.result, b.result, data) + } + +This implementation naturally follows the example data format. In a true Scala application, one might, of course, want use specialized immutable Short/Long/Double containers instead of mutable Arrays. + +After extracting data from a ``ByteIterator``, the remaining content can be turned back into a ``ByteString`` using the ``toSeq`` method + +.. code-block:: scala + + val bytes: ByteString = ... + val in: bytes.iterator + ... = in.getInt() + ... = in.get... + val rest: ByteString = in.toSeq + +with no copying from bytes to rest involved. In general, conversions from ByteString to ByteIterator and vice versa are O(1) for non-chunked ``ByteString``s and (at worst) O(nChunks) for chunked ByteStrings. + + +Encoding of data also is very natural, using ``ByteStringBuilder`` + +.. code-block:: scala + + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + + val frameBuilder = ByteString.newBuilder + + val n = a.length + val m = data.length + + frameBuilder.putInt(n) + frameBuilder.putInt(m) + + for (i <- 0 to n-1) { + frameBuilder.putShort(a(i)) + frameBuilder.putLong(b(i)) + } + frameBuilder.putDoubles(data) + val frame = frameBuilder.result() + +The encoded data then can be sent over socket (see ``IOManager``): + +.. code-block:: scala + + socket.send(ByteString.newBuilder.putInt(frame.length).result) + socket.send(frame) + + IO.Handle ^^^^^^^^^ From 3b67a4fdef450f6297f16337111973acd255b621 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 22:11:55 +0200 Subject: [PATCH 57/60] Added explicit apply method with return type Byte to class ByteString This is necessary to prevent boxing/unboxing of Bytes on apply. --- akka-actor/src/main/scala/akka/util/ByteString.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index a3f1634bfc..7b727cd3a6 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -248,6 +248,8 @@ object ByteString { * TODO: Add performance characteristics */ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] { + def apply(idx: Int): Byte + override protected[this] def newBuilder: ByteStringBuilder = ByteString.newBuilder // *must* be overridden by derived classes. This construction is necessary From b6b117cf54a60c42e55d8d7be4b69cba135606c6 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 22:18:51 +0200 Subject: [PATCH 58/60] Improved ByteString.{head,tail,last, init} and added missing tests --- .../src/test/scala/akka/util/ByteStringSpec.scala | 2 ++ akka-actor/src/main/scala/akka/util/ByteString.scala | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 3029ca5df0..981d407472 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -285,7 +285,9 @@ class ByteStringSpec extends WordSpec with MustMatchers with Checkers { } "calling head" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.head } } } + "calling tail" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.tail } } } "calling last" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.last } } } + "calling init" in { check { a: ByteString ⇒ a.isEmpty || likeVector(a) { _.init } } } "calling length" in { check { a: ByteString ⇒ likeVector(a) { _.length } } } "calling span" in { check { (a: ByteString, b: Byte) ⇒ likeVector(a)({ _.span(_ != b) match { case (a, b) ⇒ (a, b) } }) } } diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 7b727cd3a6..714075c6e8 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -257,10 +257,10 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz // a parent trait. override def iterator: ByteIterator = throw new UnsupportedOperationException("Method iterator is not implemented in ByteString") - @inline final override def head: Byte = this(0) - @inline final override def tail: ByteString = this.drop(1) - @inline final override def last: Byte = this(this.length - 1) - override def init: ByteString = this.take(this.length - 1) + override def head: Byte = apply(0) + override def tail: ByteString = drop(1) + override def last: Byte = apply(length - 1) + override def init: ByteString = dropRight(1) override def slice(from: Int, until: Int): ByteString = if ((from == 0) && (until == length)) this From 20e313e6a5d628e5de9bf43d9f3c367d72d257a8 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Sun, 17 Jun 2012 22:22:40 +0200 Subject: [PATCH 59/60] Removed final specifier on some ByteString and ByteIterator methods --- .../src/main/scala/akka/util/ByteIterator.scala | 12 ++++++------ akka-actor/src/main/scala/akka/util/ByteString.scala | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/akka-actor/src/main/scala/akka/util/ByteIterator.scala b/akka-actor/src/main/scala/akka/util/ByteIterator.scala index 5de9f5f333..dc5e4a3d5c 100644 --- a/akka-actor/src/main/scala/akka/util/ByteIterator.scala +++ b/akka-actor/src/main/scala/akka/util/ByteIterator.scala @@ -409,7 +409,7 @@ abstract class ByteIterator extends BufferedIterator[Byte] { // the parent class. override def drop(n: Int): this.type = throw new UnsupportedOperationException("Method drop is not implemented in ByteIterator") - final override def slice(from: Int, until: Int): this.type = { + override def slice(from: Int, until: Int): this.type = { if (from > 0) drop(from).take(until - from) else take(until) } @@ -431,16 +431,16 @@ abstract class ByteIterator extends BufferedIterator[Byte] { (this, that) } - final override def indexWhere(p: Byte ⇒ Boolean): Int = { + override def indexWhere(p: Byte ⇒ Boolean): Int = { var index = 0 var found = false while (!found && hasNext) if (p(next())) { found = true } else { index += 1 } if (found) index else -1 } - final def indexOf(elem: Byte): Int = indexWhere { _ == elem } + def indexOf(elem: Byte): Int = indexWhere { _ == elem } - final override def indexOf[B >: Byte](elem: B): Int = indexWhere { _ == elem } + override def indexOf[B >: Byte](elem: B): Int = indexWhere { _ == elem } def toByteString: ByteString @@ -449,13 +449,13 @@ abstract class ByteIterator extends BufferedIterator[Byte] { override def foreach[@specialized U](f: Byte ⇒ U): Unit = while (hasNext) f(next()) - final override def foldLeft[@specialized B](z: B)(op: (B, Byte) ⇒ B): B = { + override def foldLeft[@specialized B](z: B)(op: (B, Byte) ⇒ B): B = { var acc = z foreach { byte ⇒ acc = op(acc, byte) } acc } - final override def toArray[B >: Byte](implicit arg0: ClassManifest[B]): Array[B] = { + override def toArray[B >: Byte](implicit arg0: ClassManifest[B]): Array[B] = { val target = Array.ofDim[B](len) copyToArray(target) target diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 714075c6e8..b0dc19d49f 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -285,7 +285,7 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = iterator.copyToArray(xs, start, len) - @inline final override def foreach[@specialized U](f: Byte ⇒ U): Unit = iterator foreach f + override def foreach[@specialized U](f: Byte ⇒ U): Unit = iterator foreach f /** * Efficiently concatenate another ByteString. @@ -327,7 +327,7 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz * Creates a new ByteBuffer with a copy of all bytes contained in this * ByteString. */ - final def toByteBuffer: ByteBuffer = ByteBuffer.wrap(toArray) + def toByteBuffer: ByteBuffer = ByteBuffer.wrap(toArray) /** * Decodes this ByteString as a UTF-8 encoded String. From 9c6deaa47576c020a17df83d8e0f0d90b9304173 Mon Sep 17 00:00:00 2001 From: Oliver Schulz Date: Tue, 19 Jun 2012 17:16:30 +0200 Subject: [PATCH 60/60] Changed ByteString docs to hoist example code from separate file --- .../code/akka/docs/io/BinaryCoding.scala | 84 +++++++++++++++++++ akka-docs/scala/io.rst | 65 ++------------ 2 files changed, 93 insertions(+), 56 deletions(-) create mode 100644 akka-docs/scala/code/akka/docs/io/BinaryCoding.scala diff --git a/akka-docs/scala/code/akka/docs/io/BinaryCoding.scala b/akka-docs/scala/code/akka/docs/io/BinaryCoding.scala new file mode 100644 index 0000000000..d9aeb334fc --- /dev/null +++ b/akka-docs/scala/code/akka/docs/io/BinaryCoding.scala @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2012 Typesafe Inc. + */ +package akka.docs.io + +//#imports +import akka.actor._ +import akka.util.{ ByteString, ByteStringBuilder, ByteIterator } +//#imports + +abstract class BinaryDecoding { + //#decoding + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + + val FrameDecoder = for { + frameLenBytes ← IO.take(4) + frameLen = frameLenBytes.iterator.getInt + frame ← IO.take(frameLen) + } yield { + val in = frame.iterator + + val n = in.getInt + val m = in.getInt + + val a = Array.newBuilder[Short] + val b = Array.newBuilder[Long] + + for (i ← 1 to n) { + a += in.getShort + b += in.getInt + } + + val data = Array.ofDim[Double](m) + in.getDoubles(data) + + (a.result, b.result, data) + } + + //#decoding +} + +abstract class RestToSeq { + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + val bytes: ByteString + val in = bytes.iterator + + //#rest-to-seq + val n = in.getInt + val m = in.getInt + // ... in.get... + val rest: ByteString = in.toSeq + //#rest-to-seq +} + +abstract class BinaryEncoding { + //#encoding + implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN + + val a: Array[Short] + val b: Array[Long] + val data: Array[Double] + + val frameBuilder = ByteString.newBuilder + + val n = a.length + val m = data.length + + frameBuilder.putInt(n) + frameBuilder.putInt(m) + + for (i ← 0 to n - 1) { + frameBuilder.putShort(a(i)) + frameBuilder.putLong(b(i)) + } + frameBuilder.putDoubles(data) + val frame = frameBuilder.result() + //#encoding + + //#sending + val socket: IO.SocketHandle + socket.write(ByteString.newBuilder.putInt(frame.length).result) + socket.write(frame) + //#sending +} diff --git a/akka-docs/scala/io.rst b/akka-docs/scala/io.rst index d2ac3783a9..85d2b6bef5 100644 --- a/akka-docs/scala/io.rst +++ b/akka-docs/scala/io.rst @@ -48,76 +48,29 @@ In this example, the data is to be stored in arrays of ``a``, ``b`` and ``data`` Decoding of such frames can be efficiently implemented in the following fashion: -.. code-block:: scala - - implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN - - val FrameDecoder = for { - frameLenBytes <- IO.take(4) - frameLen = frameLenBytes.iterator.getInt - frame <- IO.take(frameLen) - } yield { - val in = frame.iterator - - val n = in.getInt - val m = in.getInt - - val a = ArrayBuilder.make[Short]() - val b = ArrayBuilder.make[Long]() - - for (i <- 1 to n) { - a += in.getShort - b += in.getInt - } - - val data = Array.ofDim[Double](m) - in.getDoubles(data) - - (a.result, b.result, data) - } +.. includecode:: code/akka/docs/io/BinaryCoding.scala + :include: decoding This implementation naturally follows the example data format. In a true Scala application, one might, of course, want use specialized immutable Short/Long/Double containers instead of mutable Arrays. -After extracting data from a ``ByteIterator``, the remaining content can be turned back into a ``ByteString`` using the ``toSeq`` method +After extracting data from a ``ByteIterator``, the remaining content can also be turned back into a ``ByteString`` using the ``toSeq`` method -.. code-block:: scala - - val bytes: ByteString = ... - val in: bytes.iterator - ... = in.getInt() - ... = in.get... - val rest: ByteString = in.toSeq +.. includecode:: code/akka/docs/io/BinaryCoding.scala + :include: rest-to-seq with no copying from bytes to rest involved. In general, conversions from ByteString to ByteIterator and vice versa are O(1) for non-chunked ``ByteString``s and (at worst) O(nChunks) for chunked ByteStrings. Encoding of data also is very natural, using ``ByteStringBuilder`` -.. code-block:: scala +.. includecode:: code/akka/docs/io/BinaryCoding.scala + :include: encoding - implicit val byteOrder = java.nio.ByteOrder.BIG_ENDIAN - - val frameBuilder = ByteString.newBuilder - - val n = a.length - val m = data.length - - frameBuilder.putInt(n) - frameBuilder.putInt(m) - - for (i <- 0 to n-1) { - frameBuilder.putShort(a(i)) - frameBuilder.putLong(b(i)) - } - frameBuilder.putDoubles(data) - val frame = frameBuilder.result() The encoded data then can be sent over socket (see ``IOManager``): -.. code-block:: scala - - socket.send(ByteString.newBuilder.putInt(frame.length).result) - socket.send(frame) +.. includecode:: code/akka/docs/io/BinaryCoding.scala + :include: sending IO.Handle