Avoid boxing in Framing (#1247)
* Fix FramingBenchmark * Add specialized indexOfByte * Rename indexOfByte to indexOf * Add missing @since(1.1.0) and reformat scaladoc * Fix Scala 2.12 ambiguity problem * Inline nextString * Change default ByteString#indexOf to call indexWhere This is always overriden, but I guess it won't hurt to have a default * Remove default indexOf override Calling indexWhere is already the default * Fix MiMa issues
This commit is contained in:
parent
b6f2c5774e
commit
cce5f9b0d9
5 changed files with 318 additions and 11 deletions
|
|
@ -717,6 +717,82 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
|
|||
compact.indexOf('g', 5) should ===(5)
|
||||
compact.indexOf('g', 6) should ===(-1)
|
||||
}
|
||||
"indexOf (specialized)" in {
|
||||
ByteString.empty.indexOf(5.toByte) should ===(-1)
|
||||
val byteString1 = ByteString1.fromString("abc")
|
||||
byteString1.indexOf('a'.toByte) should ===(0)
|
||||
byteString1.indexOf('b'.toByte) should ===(1)
|
||||
byteString1.indexOf('c'.toByte) should ===(2)
|
||||
byteString1.indexOf('d'.toByte) should ===(-1)
|
||||
|
||||
val byteStrings = ByteStrings(ByteString1.fromString("abc"), ByteString1.fromString("efg"))
|
||||
byteStrings.indexOf('a'.toByte) should ===(0)
|
||||
byteStrings.indexOf('c'.toByte) should ===(2)
|
||||
byteStrings.indexOf('d'.toByte) should ===(-1)
|
||||
byteStrings.indexOf('e'.toByte) should ===(3)
|
||||
byteStrings.indexOf('f'.toByte) should ===(4)
|
||||
byteStrings.indexOf('g'.toByte) should ===(5)
|
||||
|
||||
val compact = byteStrings.compact
|
||||
compact.indexOf('a'.toByte) should ===(0)
|
||||
compact.indexOf('c'.toByte) should ===(2)
|
||||
compact.indexOf('d'.toByte) should ===(-1)
|
||||
compact.indexOf('e'.toByte) should ===(3)
|
||||
compact.indexOf('f'.toByte) should ===(4)
|
||||
compact.indexOf('g'.toByte) should ===(5)
|
||||
|
||||
}
|
||||
"indexOf (specialized) from offset" in {
|
||||
ByteString.empty.indexOf(5.toByte, -1) should ===(-1)
|
||||
ByteString.empty.indexOf(5.toByte, 0) should ===(-1)
|
||||
ByteString.empty.indexOf(5.toByte, 1) should ===(-1)
|
||||
val byteString1 = ByteString1.fromString("abc")
|
||||
byteString1.indexOf('d'.toByte, -1) should ===(-1)
|
||||
byteString1.indexOf('d'.toByte, 0) should ===(-1)
|
||||
byteString1.indexOf('d'.toByte, 1) should ===(-1)
|
||||
byteString1.indexOf('d'.toByte, 4) should ===(-1)
|
||||
byteString1.indexOf('a'.toByte, -1) should ===(0)
|
||||
byteString1.indexOf('a'.toByte, 0) should ===(0)
|
||||
byteString1.indexOf('a'.toByte, 1) should ===(-1)
|
||||
|
||||
val byteStrings = ByteStrings(ByteString1.fromString("abc"), ByteString1.fromString("efg"))
|
||||
byteStrings.indexOf('c'.toByte, -1) should ===(2)
|
||||
byteStrings.indexOf('c'.toByte, 0) should ===(2)
|
||||
byteStrings.indexOf('c'.toByte, 2) should ===(2)
|
||||
byteStrings.indexOf('c'.toByte, 3) should ===(-1)
|
||||
|
||||
byteStrings.indexOf('e'.toByte, -1) should ===(3)
|
||||
byteStrings.indexOf('e'.toByte, 0) should ===(3)
|
||||
byteStrings.indexOf('e'.toByte, 1) should ===(3)
|
||||
byteStrings.indexOf('e'.toByte, 4) should ===(-1)
|
||||
byteStrings.indexOf('e'.toByte, 6) should ===(-1)
|
||||
|
||||
byteStrings.indexOf('g'.toByte, -1) should ===(5)
|
||||
byteStrings.indexOf('g'.toByte, 0) should ===(5)
|
||||
byteStrings.indexOf('g'.toByte, 1) should ===(5)
|
||||
byteStrings.indexOf('g'.toByte, 4) should ===(5)
|
||||
byteStrings.indexOf('g'.toByte, 5) should ===(5)
|
||||
byteStrings.indexOf('g'.toByte, 6) should ===(-1)
|
||||
|
||||
val compact = byteStrings.compact
|
||||
compact.indexOf('c'.toByte, -1) should ===(2)
|
||||
compact.indexOf('c'.toByte, 0) should ===(2)
|
||||
compact.indexOf('c'.toByte, 2) should ===(2)
|
||||
compact.indexOf('c'.toByte, 3) should ===(-1)
|
||||
|
||||
compact.indexOf('e'.toByte, -1) should ===(3)
|
||||
compact.indexOf('e'.toByte, 0) should ===(3)
|
||||
compact.indexOf('e'.toByte, 1) should ===(3)
|
||||
compact.indexOf('e'.toByte, 4) should ===(-1)
|
||||
compact.indexOf('e'.toByte, 6) should ===(-1)
|
||||
|
||||
compact.indexOf('g'.toByte, -1) should ===(5)
|
||||
compact.indexOf('g'.toByte, 0) should ===(5)
|
||||
compact.indexOf('g'.toByte, 1) should ===(5)
|
||||
compact.indexOf('g'.toByte, 4) should ===(5)
|
||||
compact.indexOf('g'.toByte, 5) should ===(5)
|
||||
compact.indexOf('g'.toByte, 6) should ===(-1)
|
||||
}
|
||||
"copyToArray" in {
|
||||
val byteString = ByteString(1, 2) ++ ByteString(3) ++ ByteString(4)
|
||||
|
||||
|
|
|
|||
|
|
@ -246,6 +246,20 @@ object ByteString {
|
|||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte): Int = indexOf(elem, 0)
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
var found = -1
|
||||
var i = math.max(from, 0)
|
||||
while (i < length && found == -1) {
|
||||
if (bytes(i) == elem) found = i
|
||||
i += 1
|
||||
}
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
override def slice(from: Int, until: Int): ByteString =
|
||||
if (from <= 0 && until >= length) this
|
||||
else if (from >= length || until <= 0 || from >= until) ByteString.empty
|
||||
|
|
@ -433,6 +447,20 @@ object ByteString {
|
|||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte): Int = indexOf(elem, 0)
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
var found = -1
|
||||
var i = math.max(from, 0)
|
||||
while (i < length && found == -1) {
|
||||
if (bytes(startIndex + i) == elem) found = i
|
||||
i += 1
|
||||
}
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
protected def writeReplace(): AnyRef = new SerializationProxy(this)
|
||||
|
||||
override def toArrayUnsafe(): Array[Byte] = {
|
||||
|
|
@ -682,8 +710,34 @@ object ByteString {
|
|||
} else {
|
||||
val subIndexOf = bs.indexOf(elem, relativeIndex)
|
||||
if (subIndexOf < 0) {
|
||||
val nextString = bsIdx + 1
|
||||
find(nextString, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else subIndexOf + bytesPassed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
find(0, math.max(from, 0), 0)
|
||||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte): Int = indexOf(elem, 0)
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
val byteStringsSize = bytestrings.size
|
||||
|
||||
@tailrec
|
||||
def find(bsIdx: Int, relativeIndex: Int, bytesPassed: Int): Int = {
|
||||
if (bsIdx >= byteStringsSize) -1
|
||||
else {
|
||||
val bs = bytestrings(bsIdx)
|
||||
|
||||
if (bs.length <= relativeIndex) {
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else {
|
||||
val subIndexOf = bs.indexOf(elem, relativeIndex)
|
||||
if (subIndexOf < 0) {
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else subIndexOf + bytesPassed
|
||||
}
|
||||
}
|
||||
|
|
@ -788,7 +842,33 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz
|
|||
override def indexWhere(p: Byte => Boolean): Int = iterator.indexWhere(p)
|
||||
|
||||
// optimized in subclasses
|
||||
override def indexOf[B >: Byte](elem: B): Int = indexOf(elem, 0)
|
||||
override def indexOf[B >: Byte](elem: B): Int = indexOf[B](elem, 0)
|
||||
|
||||
// optimized version of indexOf for bytes, implemented in subclasses
|
||||
/**
|
||||
* Finds index of first occurrence of some byte in this ByteString after or at some start index.
|
||||
*
|
||||
* Similar to indexOf, but it avoids boxing if the value is already a byte.
|
||||
*
|
||||
* @param elem the element value to search for.
|
||||
* @param from the start index
|
||||
* @return the index `>= from` of the first element of this ByteString that is equal (as determined by `==`)
|
||||
* to `elem`, or `-1`, if none exists.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
def indexOf(elem: Byte, from: Int): Int = indexOf[Byte](elem, from)
|
||||
|
||||
/**
|
||||
* Finds index of first occurrence of some byte in this ByteString.
|
||||
*
|
||||
* Similar to indexOf, but it avoids boxing if the value is already a byte.
|
||||
*
|
||||
* @param elem the element value to search for.
|
||||
* @return the index `>= from` of the first element of this ByteString that is equal (as determined by `==`)
|
||||
* to `elem`, or `-1`, if none exists.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
def indexOf(elem: Byte): Int = indexOf(elem, 0)
|
||||
|
||||
override def grouped(size: Int): Iterator[ByteString] = {
|
||||
if (size <= 0) {
|
||||
|
|
|
|||
|
|
@ -243,6 +243,19 @@ object ByteString {
|
|||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
var found = -1
|
||||
var i = math.max(from, 0)
|
||||
while (i < length && found == -1) {
|
||||
if (bytes(i) == elem) found = i
|
||||
i += 1
|
||||
}
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
override def slice(from: Int, until: Int): ByteString =
|
||||
if (from <= 0 && until >= length) this
|
||||
else if (from >= length || until <= 0 || from >= until) ByteString.empty
|
||||
|
|
@ -434,6 +447,19 @@ object ByteString {
|
|||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
var found = -1
|
||||
var i = math.max(from, 0)
|
||||
while (i < length && found == -1) {
|
||||
if (bytes(startIndex + i) == elem) found = i
|
||||
i += 1
|
||||
}
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
|
||||
// min of the bytes available to copy, bytes there is room for in dest and the requested number of bytes
|
||||
val toCopy = math.min(math.min(len, length), dest.length - start)
|
||||
|
|
@ -691,8 +717,33 @@ object ByteString {
|
|||
} else {
|
||||
val subIndexOf = bs.indexOf(elem, relativeIndex)
|
||||
if (subIndexOf < 0) {
|
||||
val nextString = bsIdx + 1
|
||||
find(nextString, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else subIndexOf + bytesPassed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
find(0, math.max(from, 0), 0)
|
||||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
val byteStringsSize = bytestrings.size
|
||||
|
||||
@tailrec
|
||||
def find(bsIdx: Int, relativeIndex: Int, bytesPassed: Int): Int = {
|
||||
if (bsIdx >= byteStringsSize) -1
|
||||
else {
|
||||
val bs = bytestrings(bsIdx)
|
||||
|
||||
if (bs.length <= relativeIndex) {
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else {
|
||||
val subIndexOf = bs.indexOf(elem, relativeIndex)
|
||||
if (subIndexOf < 0) {
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else subIndexOf + bytesPassed
|
||||
}
|
||||
}
|
||||
|
|
@ -821,8 +872,31 @@ sealed abstract class ByteString
|
|||
|
||||
override def indexWhere(p: Byte => Boolean, from: Int): Int = iterator.indexWhere(p, from)
|
||||
|
||||
// optimized in subclasses
|
||||
override def indexOf[B >: Byte](elem: B, from: Int): Int = indexOf(elem, from)
|
||||
// optimized version of indexOf for bytes, optimized in subclasses
|
||||
/**
|
||||
* Finds index of first occurrence of some byte in this ByteString after or at some start index.
|
||||
*
|
||||
* Similar to indexOf, but it avoids boxing if the value is already a byte.
|
||||
*
|
||||
* @param elem the element value to search for.
|
||||
* @param from the start index
|
||||
* @return the index `>= from` of the first element of this ByteString that is equal (as determined by `==`)
|
||||
* to `elem`, or `-1`, if none exists.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
def indexOf(elem: Byte, from: Int): Int = indexOf[Byte](elem, from)
|
||||
|
||||
/**
|
||||
* Finds index of first occurrence of some byte in this ByteString.
|
||||
*
|
||||
* Similar to indexOf, but it avoids boxing if the value is already a byte.
|
||||
*
|
||||
* @param elem the element value to search for.
|
||||
* @return the index `>= from` of the first element of this ByteString that is equal (as determined by `==`)
|
||||
* to `elem`, or `-1`, if none exists.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
def indexOf(elem: Byte): Int = indexOf(elem, 0)
|
||||
|
||||
override def grouped(size: Int): Iterator[ByteString] = {
|
||||
if (size <= 0) {
|
||||
|
|
|
|||
|
|
@ -244,6 +244,19 @@ object ByteString {
|
|||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
var found = -1
|
||||
var i = math.max(from, 0)
|
||||
while (i < length && found == -1) {
|
||||
if (bytes(i) == elem) found = i
|
||||
i += 1
|
||||
}
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
override def slice(from: Int, until: Int): ByteString =
|
||||
if (from <= 0 && until >= length) this
|
||||
else if (from >= length || until <= 0 || from >= until) ByteString.empty
|
||||
|
|
@ -435,6 +448,19 @@ object ByteString {
|
|||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
var found = -1
|
||||
var i = math.max(from, 0)
|
||||
while (i < length && found == -1) {
|
||||
if (bytes(startIndex + i) == elem) found = i
|
||||
i += 1
|
||||
}
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
|
||||
// min of the bytes available to copy, bytes there is room for in dest and the requested number of bytes
|
||||
val toCopy = math.min(math.min(len, length), dest.length - start)
|
||||
|
|
@ -692,8 +718,33 @@ object ByteString {
|
|||
} else {
|
||||
val subIndexOf = bs.indexOf(elem, relativeIndex)
|
||||
if (subIndexOf < 0) {
|
||||
val nextString = bsIdx + 1
|
||||
find(nextString, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else subIndexOf + bytesPassed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
find(0, math.max(from, 0), 0)
|
||||
}
|
||||
}
|
||||
|
||||
override def indexOf(elem: Byte, from: Int): Int = {
|
||||
if (from >= length) -1
|
||||
else {
|
||||
val byteStringsSize = bytestrings.size
|
||||
|
||||
@tailrec
|
||||
def find(bsIdx: Int, relativeIndex: Int, bytesPassed: Int): Int = {
|
||||
if (bsIdx >= byteStringsSize) -1
|
||||
else {
|
||||
val bs = bytestrings(bsIdx)
|
||||
|
||||
if (bs.length <= relativeIndex) {
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else {
|
||||
val subIndexOf = bs.indexOf(elem, relativeIndex)
|
||||
if (subIndexOf < 0) {
|
||||
find(bsIdx + 1, relativeIndex - bs.length, bytesPassed + bs.length)
|
||||
} else subIndexOf + bytesPassed
|
||||
}
|
||||
}
|
||||
|
|
@ -823,7 +874,33 @@ sealed abstract class ByteString
|
|||
override def indexWhere(p: Byte => Boolean, from: Int): Int = iterator.indexWhere(p, from)
|
||||
|
||||
// optimized in subclasses
|
||||
override def indexOf[B >: Byte](elem: B, from: Int): Int = indexOf(elem, from)
|
||||
override def indexOf[B >: Byte](elem: B, from: Int): Int = super.indexOf(elem, from)
|
||||
|
||||
// optimized version of indexOf for bytes, optimized in subclasses
|
||||
/**
|
||||
* Finds index of first occurrence of some byte in this ByteString after or at some start index.
|
||||
*
|
||||
* Similar to indexOf, but it avoids boxing if the value is already a byte.
|
||||
*
|
||||
* @param elem the element value to search for.
|
||||
* @param from the start index
|
||||
* @return the index `>= from` of the first element of this ByteString that is equal (as determined by `==`)
|
||||
* to `elem`, or `-1`, if none exists.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
def indexOf(elem: Byte, from: Int): Int = indexOf[Byte](elem, from)
|
||||
|
||||
/**
|
||||
* Finds index of first occurrence of some byte in this ByteString.
|
||||
*
|
||||
* Similar to indexOf, but it avoids boxing if the value is already a byte.
|
||||
*
|
||||
* @param elem the element value to search for.
|
||||
* @return the index `>= from` of the first element of this ByteString that is equal (as determined by `==`)
|
||||
* to `elem`, or `-1`, if none exists.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
def indexOf(elem: Byte): Int = indexOf(elem, 0)
|
||||
|
||||
override def grouped(size: Int): Iterator[ByteString] = {
|
||||
if (size <= 0) {
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class FramingBenchmark {
|
|||
val frame = List.range(0, messageSize, 1).map(_ => Random.nextPrintableChar()).mkString + "\n"
|
||||
val messageChunk = ByteString(List.range(0, framePerSeq, 1).map(_ => frame).mkString)
|
||||
|
||||
Source
|
||||
flow = Source
|
||||
.fromGraph(new BenchTestSourceSameElement(100000, messageChunk))
|
||||
.via(Framing.delimiter(ByteString("\n"), Int.MaxValue))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue