Update IO to new iteratee based api
This commit is contained in:
parent
e8f16952a6
commit
a1f9b81ce9
3 changed files with 870 additions and 479 deletions
|
|
@ -1,10 +1,11 @@
|
|||
package akka.util
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.Charset
|
||||
|
||||
import scala.collection.IndexedSeqOptimized
|
||||
import scala.collection.mutable.{ Builder, ArrayBuilder }
|
||||
import scala.collection.immutable.IndexedSeq
|
||||
import scala.collection.{ IndexedSeqOptimized, LinearSeq }
|
||||
import scala.collection.mutable.{ Builder, ArrayBuilder, WrappedArray }
|
||||
import scala.collection.immutable.{ IndexedSeq, VectorBuilder }
|
||||
import scala.collection.generic.{ CanBuildFrom, GenericCompanion }
|
||||
|
||||
object ByteString {
|
||||
|
|
@ -30,35 +31,43 @@ object ByteString {
|
|||
|
||||
def apply(string: String, charset: String): ByteString = ByteString1(string.getBytes(charset))
|
||||
|
||||
def fromArray(array: Array[Byte], offset: Int, length: Int): ByteString = {
|
||||
val copyOffset = math.max(offset, 0)
|
||||
val copyLength = math.max(math.min(array.length - copyOffset, length), 0)
|
||||
if (copyLength == 0) empty
|
||||
else {
|
||||
val copyArray = new Array[Byte](copyLength)
|
||||
Array.copy(array, copyOffset, copyArray, 0, copyLength)
|
||||
ByteString1(copyArray)
|
||||
}
|
||||
}
|
||||
|
||||
val empty: ByteString = ByteString1(Array.empty[Byte])
|
||||
|
||||
def newBuilder: Builder[Byte, ByteString] = new ArrayBuilder.ofByte mapResult apply
|
||||
def newBuilder = new ByteStringBuilder
|
||||
|
||||
implicit def canBuildFrom = new CanBuildFrom[TraversableOnce[Byte], Byte, ByteString] {
|
||||
def apply(from: TraversableOnce[Byte]) = newBuilder
|
||||
def apply() = newBuilder
|
||||
}
|
||||
|
||||
private object ByteString1 {
|
||||
private[akka] object ByteString1 {
|
||||
def apply(bytes: Array[Byte]) = new ByteString1(bytes)
|
||||
}
|
||||
|
||||
final class ByteString1 private (bytes: Array[Byte], startIndex: Int, endIndex: Int) extends ByteString {
|
||||
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)
|
||||
|
||||
def apply(idx: Int): Byte = bytes(checkRangeConvert(idx))
|
||||
|
||||
private def checkRangeConvert(index: Int) = {
|
||||
val idx = index + startIndex
|
||||
if (0 <= index && idx < endIndex)
|
||||
idx
|
||||
if (0 <= index && length > index)
|
||||
index + startIndex
|
||||
else
|
||||
throw new IndexOutOfBoundsException(index.toString)
|
||||
}
|
||||
|
||||
def length: Int = endIndex - startIndex
|
||||
|
||||
def toArray: Array[Byte] = {
|
||||
val ar = new Array[Byte](length)
|
||||
Array.copy(bytes, startIndex, ar, 0, length)
|
||||
|
|
@ -68,8 +77,7 @@ object ByteString {
|
|||
override def clone: ByteString = new ByteString1(toArray)
|
||||
|
||||
def compact: ByteString =
|
||||
if (startIndex == 0 && endIndex == bytes.length) this
|
||||
else clone
|
||||
if (length == bytes.length) this else clone
|
||||
|
||||
def asByteBuffer: ByteBuffer = {
|
||||
val buffer = ByteBuffer.wrap(bytes, startIndex, length).asReadOnlyBuffer
|
||||
|
|
@ -77,8 +85,8 @@ object ByteString {
|
|||
else buffer
|
||||
}
|
||||
|
||||
def utf8String: String =
|
||||
new String(if (startIndex == 0 && endIndex == bytes.length) bytes else toArray, "UTF-8")
|
||||
def decodeString(charset: String): String =
|
||||
new String(if (length == bytes.length) bytes else toArray, charset)
|
||||
|
||||
def ++(that: ByteString): ByteString = that match {
|
||||
case b: ByteString1 ⇒ ByteStrings(this, b)
|
||||
|
|
@ -87,42 +95,50 @@ object ByteString {
|
|||
|
||||
override def slice(from: Int, until: Int): ByteString = {
|
||||
val newStartIndex = math.max(from, 0) + startIndex
|
||||
val newEndIndex = math.min(until, length) + startIndex
|
||||
if (newEndIndex <= newStartIndex) ByteString.empty
|
||||
else new ByteString1(bytes, newStartIndex, newEndIndex)
|
||||
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 object ByteStrings {
|
||||
def apply(bytestrings: Vector[ByteString1]): ByteString = new ByteStrings(bytestrings)
|
||||
private[akka] object ByteStrings {
|
||||
def apply(bytestrings: Vector[ByteString1]): ByteString = new ByteStrings(bytestrings, (0 /: bytestrings)(_ + _.length))
|
||||
|
||||
def apply(bytestrings: Vector[ByteString1], length: Int): ByteString = new ByteStrings(bytestrings, length)
|
||||
|
||||
def apply(b1: ByteString1, b2: ByteString1): ByteString = compare(b1, b2) match {
|
||||
case 3 ⇒ new ByteStrings(Vector(b1, b2))
|
||||
case 3 ⇒ new ByteStrings(Vector(b1, b2), b1.length + b2.length)
|
||||
case 2 ⇒ b2
|
||||
case 1 ⇒ b1
|
||||
case 0 ⇒ ByteString.empty
|
||||
}
|
||||
|
||||
def apply(b: ByteString1, bs: ByteStrings): ByteString = compare(b, bs) match {
|
||||
case 3 ⇒ new ByteStrings(b +: bs.bytestrings)
|
||||
case 3 ⇒ new ByteStrings(b +: bs.bytestrings, bs.length + b.length)
|
||||
case 2 ⇒ bs
|
||||
case 1 ⇒ b
|
||||
case 0 ⇒ ByteString.empty
|
||||
}
|
||||
|
||||
def apply(bs: ByteStrings, b: ByteString1): ByteString = compare(bs, b) match {
|
||||
case 3 ⇒ new ByteStrings(bs.bytestrings :+ b)
|
||||
case 3 ⇒ new ByteStrings(bs.bytestrings :+ b, bs.length + b.length)
|
||||
case 2 ⇒ b
|
||||
case 1 ⇒ bs
|
||||
case 0 ⇒ ByteString.empty
|
||||
}
|
||||
|
||||
def apply(bs1: ByteStrings, bs2: ByteStrings): ByteString = compare(bs1, bs2) match {
|
||||
case 3 ⇒ new ByteStrings(bs1.bytestrings ++ bs2.bytestrings)
|
||||
case 3 ⇒ new ByteStrings(bs1.bytestrings ++ bs2.bytestrings, bs1.length + bs2.length)
|
||||
case 2 ⇒ bs2
|
||||
case 1 ⇒ bs1
|
||||
case 0 ⇒ ByteString.empty
|
||||
|
|
@ -133,9 +149,10 @@ object ByteString {
|
|||
if (b1.length == 0)
|
||||
if (b2.length == 0) 0 else 2
|
||||
else if (b2.length == 0) 1 else 3
|
||||
|
||||
}
|
||||
|
||||
final class ByteStrings private (private val bytestrings: Vector[ByteString1]) extends ByteString {
|
||||
final class ByteStrings private (val bytestrings: Vector[ByteString1], val length: Int) extends ByteString {
|
||||
|
||||
def apply(idx: Int): Byte =
|
||||
if (0 <= idx && idx < length) {
|
||||
|
|
@ -148,37 +165,39 @@ object ByteString {
|
|||
bytestrings(pos)(idx - seen)
|
||||
} else throw new IndexOutOfBoundsException(idx.toString)
|
||||
|
||||
val length: Int = (0 /: bytestrings)(_ + _.length)
|
||||
|
||||
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 + bytestrings(pos).length) {
|
||||
seen += bytestrings(pos).length
|
||||
while (from >= seen + cur.length) {
|
||||
seen += cur.length
|
||||
pos += 1
|
||||
cur = iter.next
|
||||
}
|
||||
val startpos = pos
|
||||
val startidx = start - seen
|
||||
while (until > seen + bytestrings(pos).length) {
|
||||
seen += bytestrings(pos).length
|
||||
while (until > seen + cur.length) {
|
||||
seen += cur.length
|
||||
pos += 1
|
||||
cur = iter.next
|
||||
}
|
||||
val endpos = pos
|
||||
val endidx = end - seen
|
||||
if (startpos == endpos)
|
||||
bytestrings(startpos).slice(startidx, endidx)
|
||||
cur.slice(startidx, endidx)
|
||||
else {
|
||||
val first = bytestrings(startpos).drop(startidx).asInstanceOf[ByteString1]
|
||||
val last = bytestrings(endpos).take(endidx).asInstanceOf[ByteString1]
|
||||
val last = cur.take(endidx).asInstanceOf[ByteString1]
|
||||
if ((endpos - startpos) == 1)
|
||||
new ByteStrings(Vector(first, last))
|
||||
new ByteStrings(Vector(first, last), until - from)
|
||||
else
|
||||
new ByteStrings(first +: bytestrings.slice(startpos + 1, endpos) :+ last)
|
||||
new ByteStrings(first +: bytestrings.slice(startpos + 1, endpos) :+ last, until - from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -200,7 +219,16 @@ object ByteString {
|
|||
|
||||
def asByteBuffer: ByteBuffer = compact.asByteBuffer
|
||||
|
||||
def utf8String: String = compact.utf8String
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -208,9 +236,92 @@ object ByteString {
|
|||
sealed trait ByteString extends IndexedSeq[Byte] with IndexedSeqOptimized[Byte, ByteString] {
|
||||
override protected[this] def newBuilder = ByteString.newBuilder
|
||||
def ++(that: ByteString): ByteString
|
||||
def copyToBuffer(buffer: ByteBuffer): Int
|
||||
def compact: ByteString
|
||||
def asByteBuffer: ByteBuffer
|
||||
def toByteBuffer: ByteBuffer = ByteBuffer.wrap(toArray)
|
||||
def utf8String: String
|
||||
def mapI(f: Byte ⇒ Int): ByteString = map(f andThen (_.toByte))
|
||||
final def toByteBuffer: ByteBuffer = ByteBuffer.wrap(toArray)
|
||||
final def utf8String: String = decodeString("UTF-8")
|
||||
def decodeString(charset: String): String
|
||||
final def mapI(f: Byte ⇒ Int): ByteString = map(f andThen (_.toByte))
|
||||
}
|
||||
|
||||
final class ByteStringBuilder extends Builder[Byte, ByteString] {
|
||||
import ByteString.{ ByteString1, ByteStrings }
|
||||
private var _length = 0
|
||||
private val _builder = new VectorBuilder[ByteString1]()
|
||||
private var _temp: Array[Byte] = _
|
||||
private var _tempLength = 0
|
||||
private var _tempCapacity = 0
|
||||
|
||||
private def clearTemp() {
|
||||
if (_tempLength > 0) {
|
||||
val arr = new Array[Byte](_tempLength)
|
||||
Array.copy(_temp, 0, arr, 0, _tempLength)
|
||||
_builder += ByteString1(arr)
|
||||
_tempLength = 0
|
||||
}
|
||||
}
|
||||
|
||||
private def resizeTemp(size: Int) {
|
||||
val newtemp = new Array[Byte](size)
|
||||
if (_tempLength > 0) Array.copy(_temp, 0, newtemp, 0, _tempLength)
|
||||
_temp = newtemp
|
||||
}
|
||||
|
||||
private def ensureTempSize(size: Int) {
|
||||
if (_tempCapacity < size || _tempCapacity == 0) {
|
||||
var newSize = if (_tempCapacity == 0) 16 else _tempCapacity * 2
|
||||
while (newSize < size) newSize *= 2
|
||||
resizeTemp(newSize)
|
||||
}
|
||||
}
|
||||
|
||||
def +=(elem: Byte): this.type = {
|
||||
ensureTempSize(_tempLength + 1)
|
||||
_temp(_tempLength) = elem
|
||||
_tempLength += 1
|
||||
_length += 1
|
||||
this
|
||||
}
|
||||
|
||||
override def ++=(xs: TraversableOnce[Byte]): this.type = {
|
||||
xs match {
|
||||
case b: ByteString1 ⇒
|
||||
clearTemp()
|
||||
_builder += b
|
||||
_length += b.length
|
||||
case bs: ByteStrings ⇒
|
||||
clearTemp()
|
||||
_builder ++= bs.bytestrings
|
||||
_length += bs.length
|
||||
case xs: WrappedArray.ofByte ⇒
|
||||
clearTemp()
|
||||
_builder += ByteString1(xs.array.clone)
|
||||
_length += xs.length
|
||||
case _: collection.IndexedSeq[_] ⇒
|
||||
ensureTempSize(_tempLength + xs.size)
|
||||
xs.copyToArray(_temp, _tempLength)
|
||||
case _ ⇒
|
||||
super.++=(xs)
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
def clear() {
|
||||
_builder.clear
|
||||
_length = 0
|
||||
_tempLength = 0
|
||||
}
|
||||
|
||||
def result: ByteString =
|
||||
if (_length == 0) ByteString.empty
|
||||
else {
|
||||
clearTemp()
|
||||
val bytestrings = _builder.result
|
||||
if (bytestrings.size == 1)
|
||||
bytestrings.head
|
||||
else
|
||||
ByteStrings(bytestrings, _length)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue