Improving the performance of ByteString.decodeString, and adding base64
* Improving the performance of ByteString.decodeString, and adding ByteString.encodeBase64 and ByteString.decodeBase64 * ByteString.take should return itself whenever possible * Implementing fallback for the rare case where the JDKs Base64 returns a non-array-backed ByteBuffer * Adding mima excludes for encodeBase64/decodeBase64
This commit is contained in:
parent
cee4792c31
commit
66f4d30098
4 changed files with 115 additions and 6 deletions
|
|
@ -788,6 +788,20 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
|
|||
}
|
||||
}
|
||||
|
||||
"taking its own length" in {
|
||||
check { b: ByteString =>
|
||||
b.take(b.length) eq b
|
||||
}
|
||||
}
|
||||
|
||||
"created from and decoding to Base64" in {
|
||||
check { a: ByteString =>
|
||||
val encoded = a.encodeBase64
|
||||
encoded == ByteString(java.util.Base64.getEncoder.encode(a.toArray)) &&
|
||||
encoded.decodeBase64 == a
|
||||
}
|
||||
}
|
||||
|
||||
"compacting" in {
|
||||
check { a: ByteString =>
|
||||
val wasCompact = a.isCompact
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.util.ByteString.decodeBase64")
|
||||
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.util.ByteString.encodeBase64")
|
||||
|
|
@ -8,6 +8,7 @@ import java.io.{ ObjectInputStream, ObjectOutputStream }
|
|||
import java.nio.{ ByteBuffer, ByteOrder }
|
||||
import java.lang.{ Iterable => JIterable }
|
||||
import java.nio.charset.{ Charset, StandardCharsets }
|
||||
import java.util.Base64
|
||||
|
||||
import scala.annotation.{ tailrec, varargs }
|
||||
import scala.collection.mutable.{ Builder, WrappedArray }
|
||||
|
|
@ -196,6 +197,12 @@ object ByteString {
|
|||
override def decodeString(charset: Charset): String =
|
||||
if (isEmpty) "" else new String(bytes, charset)
|
||||
|
||||
override def decodeBase64: ByteString =
|
||||
if (isEmpty) this else ByteString1C(Base64.getDecoder.decode(bytes))
|
||||
|
||||
override def encodeBase64: ByteString =
|
||||
if (isEmpty) this else ByteString1C(Base64.getEncoder.encode(bytes))
|
||||
|
||||
override def ++(that: ByteString): ByteString = {
|
||||
if (that.isEmpty) this
|
||||
else if (this.isEmpty) that
|
||||
|
|
@ -204,6 +211,7 @@ object ByteString {
|
|||
|
||||
override def take(n: Int): ByteString =
|
||||
if (n <= 0) ByteString.empty
|
||||
else if (n >= length) this
|
||||
else toByteString1.take(n)
|
||||
|
||||
override def dropRight(n: Int): ByteString =
|
||||
|
|
@ -361,10 +369,34 @@ object ByteString {
|
|||
def asByteBuffers: scala.collection.immutable.Iterable[ByteBuffer] = List(asByteBuffer)
|
||||
|
||||
override def decodeString(charset: String): String =
|
||||
new String(if (length == bytes.length) bytes else toArray, charset)
|
||||
if (isEmpty) ""
|
||||
else new String(bytes, startIndex, length, charset)
|
||||
|
||||
override def decodeString(charset: Charset): String = // avoids Charset.forName lookup in String internals
|
||||
new String(if (length == bytes.length) bytes else toArray, charset)
|
||||
if (isEmpty) ""
|
||||
else new String(bytes, startIndex, length, charset)
|
||||
|
||||
override def decodeBase64: ByteString =
|
||||
if (isEmpty) this
|
||||
else if (isCompact) ByteString1C(Base64.getDecoder.decode(bytes))
|
||||
else {
|
||||
val dst = Base64.getDecoder.decode(ByteBuffer.wrap(bytes, startIndex, length))
|
||||
if (dst.hasArray) {
|
||||
if (dst.array.length == dst.remaining) ByteString1C(dst.array)
|
||||
else ByteString1(dst.array, dst.arrayOffset + dst.position, dst.remaining)
|
||||
} else CompactByteString(dst)
|
||||
}
|
||||
|
||||
override def encodeBase64: ByteString =
|
||||
if (isEmpty) this
|
||||
else if (isCompact) ByteString1C(Base64.getEncoder.encode(bytes))
|
||||
else {
|
||||
val dst = Base64.getEncoder.encode(ByteBuffer.wrap(bytes, startIndex, length))
|
||||
if (dst.hasArray) {
|
||||
if (dst.array.length == dst.remaining) ByteString1C(dst.array)
|
||||
else ByteString1(dst.array, dst.arrayOffset + dst.position, dst.remaining)
|
||||
} else CompactByteString(dst)
|
||||
}
|
||||
|
||||
def ++(that: ByteString): ByteString = {
|
||||
if (that.isEmpty) this
|
||||
|
|
@ -535,6 +567,10 @@ object ByteString {
|
|||
|
||||
def decodeString(charset: Charset): String = compact.decodeString(charset)
|
||||
|
||||
override def decodeBase64: ByteString = compact.decodeBase64
|
||||
|
||||
override def encodeBase64: ByteString = compact.encodeBase64
|
||||
|
||||
private[akka] def writeToOutputStream(os: ObjectOutputStream): Unit = {
|
||||
os.writeInt(bytestrings.length)
|
||||
bytestrings.foreach(_.writeToOutputStream(os))
|
||||
|
|
@ -887,6 +923,17 @@ sealed abstract class ByteString
|
|||
*/
|
||||
def decodeString(charset: Charset): String
|
||||
|
||||
/*
|
||||
* Returns a ByteString which is the binary representation of this ByteString
|
||||
* if this ByteString is Base64-encoded.
|
||||
*/
|
||||
def decodeBase64: ByteString
|
||||
|
||||
/**
|
||||
* Returns a ByteString which is the Base64 representation of this ByteString
|
||||
*/
|
||||
def encodeBase64: ByteString
|
||||
|
||||
/**
|
||||
* map method that will automatically cast Int back into Byte.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package akka.util
|
|||
import java.io.{ ObjectInputStream, ObjectOutputStream }
|
||||
import java.nio.{ ByteBuffer, ByteOrder }
|
||||
import java.lang.{ Iterable => JIterable }
|
||||
import java.util.Base64
|
||||
|
||||
import scala.annotation.{ tailrec, varargs }
|
||||
import scala.collection.IndexedSeqOptimized
|
||||
|
|
@ -193,6 +194,12 @@ object ByteString {
|
|||
override def decodeString(charset: Charset): String =
|
||||
if (isEmpty) "" else new String(bytes, charset)
|
||||
|
||||
override def decodeBase64: ByteString =
|
||||
if (isEmpty) this else ByteString1C(Base64.getDecoder.decode(bytes))
|
||||
|
||||
override def encodeBase64: ByteString =
|
||||
if (isEmpty) this else ByteString1C(Base64.getEncoder.encode(bytes))
|
||||
|
||||
override def ++(that: ByteString): ByteString = {
|
||||
if (that.isEmpty) this
|
||||
else if (this.isEmpty) that
|
||||
|
|
@ -201,6 +208,7 @@ object ByteString {
|
|||
|
||||
override def take(n: Int): ByteString =
|
||||
if (n <= 0) ByteString.empty
|
||||
else if (n >= length) this
|
||||
else toByteString1.take(n)
|
||||
|
||||
override def dropRight(n: Int): ByteString =
|
||||
|
|
@ -351,10 +359,34 @@ object ByteString {
|
|||
def asByteBuffers: scala.collection.immutable.Iterable[ByteBuffer] = List(asByteBuffer)
|
||||
|
||||
override def decodeString(charset: String): String =
|
||||
new String(if (length == bytes.length) bytes else toArray, charset)
|
||||
if (isEmpty) ""
|
||||
else new String(bytes, startIndex, length, charset)
|
||||
|
||||
override def decodeString(charset: Charset): String = // avoids Charset.forName lookup in String internals
|
||||
new String(if (length == bytes.length) bytes else toArray, charset)
|
||||
if (isEmpty) ""
|
||||
else new String(bytes, startIndex, length, charset)
|
||||
|
||||
override def decodeBase64: ByteString =
|
||||
if (isEmpty) this
|
||||
else if (isCompact) ByteString1C(Base64.getDecoder.decode(bytes))
|
||||
else {
|
||||
val dst = Base64.getDecoder.decode(ByteBuffer.wrap(bytes, startIndex, length))
|
||||
if (dst.hasArray) {
|
||||
if (dst.array.length == dst.remaining) ByteString1C(dst.array)
|
||||
else ByteString1(dst.array, dst.arrayOffset + dst.position, dst.remaining)
|
||||
} else CompactByteString(dst)
|
||||
}
|
||||
|
||||
override def encodeBase64: ByteString =
|
||||
if (isEmpty) this
|
||||
else if (isCompact) ByteString1C(Base64.getEncoder.encode(bytes))
|
||||
else {
|
||||
val dst = Base64.getEncoder.encode(ByteBuffer.wrap(bytes, startIndex, length))
|
||||
if (dst.hasArray) {
|
||||
if (dst.array.length == dst.remaining) ByteString1C(dst.array)
|
||||
else ByteString1(dst.array, dst.arrayOffset + dst.position, dst.remaining)
|
||||
} else CompactByteString(dst)
|
||||
}
|
||||
|
||||
def ++(that: ByteString): ByteString = {
|
||||
if (that.isEmpty) this
|
||||
|
|
@ -517,6 +549,10 @@ object ByteString {
|
|||
|
||||
def decodeString(charset: Charset): String = compact.decodeString(charset)
|
||||
|
||||
override def decodeBase64: ByteString = compact.decodeBase64
|
||||
|
||||
override def encodeBase64: ByteString = compact.encodeBase64
|
||||
|
||||
private[akka] def writeToOutputStream(os: ObjectOutputStream): Unit = {
|
||||
os.writeInt(bytestrings.length)
|
||||
bytestrings.foreach(_.writeToOutputStream(os))
|
||||
|
|
@ -835,6 +871,17 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz
|
|||
*/
|
||||
def decodeString(charset: Charset): String
|
||||
|
||||
/*
|
||||
* Returns a ByteString which is the binary representation of this ByteString
|
||||
* if this ByteString is Base64-encoded.
|
||||
*/
|
||||
def decodeBase64: ByteString
|
||||
|
||||
/**
|
||||
* Returns a ByteString which is the Base64 representation of this ByteString
|
||||
*/
|
||||
def encodeBase64: ByteString
|
||||
|
||||
/**
|
||||
* map method that will automatically cast Int back into Byte.
|
||||
*/
|
||||
|
|
@ -1237,8 +1284,7 @@ final class ByteStringBuilder extends Builder[Byte, ByteString] {
|
|||
* operations on the stream are forwarded to the builder.
|
||||
*/
|
||||
def asOutputStream: java.io.OutputStream = new java.io.OutputStream {
|
||||
def write(b: Int): Unit = builder += b.toByte
|
||||
|
||||
override def write(b: Int): Unit = builder += b.toByte
|
||||
override def write(b: Array[Byte], off: Int, len: Int): Unit = { builder.putBytes(b, off, len) }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue