1111 * Copyright (C) 2009-2022 Lightbend Inc. <https://www.lightbend.com>
1212 */
1313
14+ /*
15+ * Copyright 2012 The Netty Project
16+ *
17+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
18+ * "License"); you may not use this file except in compliance with the License. You may obtain a
19+ * copy of the License at:
20+ *
21+ * https://www.apache.org/licenses/LICENSE-2.0
22+ *
23+ * Unless required by applicable law or agreed to in writing, software distributed under the License
24+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
25+ * or implied. See the License for the specific language governing permissions and limitations under
26+ * the License.
27+ */
28+
1429package org .apache .pekko .util
1530
1631import java .io .{ InputStream , ObjectInputStream , ObjectOutputStream , SequenceInputStream }
@@ -314,6 +329,31 @@ object ByteString {
314329 else - 1
315330 }
316331
332+ // Derived from code in Netty
333+ // https://github.com/netty/netty/blob/d28a0fc6598b50fbe8f296831777cf4b653a475f/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java#L366-L408
334+ override private [util] def bytesMatch (fromIndex : Int , checkBytes : Array [Byte ], bytesFromIndex : Int ,
335+ checkLength : Int ): Boolean = {
336+ var aIndex = fromIndex
337+ var bIndex = bytesFromIndex
338+ val longCount = checkLength >>> 3
339+ val byteCount = checkLength & 7
340+ var i = 0
341+ while (i < longCount) {
342+ if (SWARUtil .getLong(bytes, aIndex) != SWARUtil .getLong(checkBytes, bIndex)) return false
343+ aIndex += 8
344+ bIndex += 8
345+ i += 1
346+ }
347+ i = 0
348+ while (i < byteCount) {
349+ if (apply(aIndex) != checkBytes(bIndex)) return false
350+ aIndex += 1
351+ bIndex += 1
352+ i += 1
353+ }
354+ true
355+ }
356+
317357 override def slice (from : Int , until : Int ): ByteString =
318358 if (from <= 0 && until >= length) this
319359 else if (from >= length || until <= 0 || from >= until) ByteString .empty
@@ -575,6 +615,31 @@ object ByteString {
575615 else - 1
576616 }
577617
618+ // Derived from code in Netty
619+ // https://github.com/netty/netty/blob/d28a0fc6598b50fbe8f296831777cf4b653a475f/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java#L366-L408
620+ override private [util] def bytesMatch (fromIndex : Int , checkBytes : Array [Byte ], bytesFromIndex : Int ,
621+ checkLength : Int ): Boolean = {
622+ var aIndex = fromIndex + startIndex
623+ var bIndex = bytesFromIndex
624+ val longCount = checkLength >>> 3
625+ val byteCount = checkLength & 7
626+ var i = 0
627+ while (i < longCount) {
628+ if (SWARUtil .getLong(bytes, aIndex) != SWARUtil .getLong(checkBytes, bIndex)) return false
629+ aIndex += 8
630+ bIndex += 8
631+ i += 1
632+ }
633+ i = 0
634+ while (i < byteCount) {
635+ if (apply(aIndex) != checkBytes(bIndex)) return false
636+ aIndex += 1
637+ bIndex += 1
638+ i += 1
639+ }
640+ true
641+ }
642+
578643 override def copyToArray [B >: Byte ](dest : Array [B ], start : Int , len : Int ): Int = {
579644 // min of the bytes available to copy, bytes there is room for in dest and the requested number of bytes
580645 val toCopy = math.min(math.min(len, length), dest.length - start)
@@ -912,6 +977,20 @@ object ByteString {
912977 }
913978 }
914979
980+ private [util] def bytesMatch (fromIndex : Int , checkBytes : Array [Byte ], checkBytesFromIndex : Int , checkLength : Int )
981+ : Boolean = {
982+ if (checkLength > 1 && bytestrings.nonEmpty && bytestrings.head.length >= fromIndex + checkLength - 1 ) {
983+ bytestrings.head.bytesMatch(fromIndex, checkBytes, checkBytesFromIndex, checkLength)
984+ } else {
985+ var i = 0
986+ while (i < checkLength) {
987+ if (apply(fromIndex + i) != checkBytes(checkBytesFromIndex + i)) return false
988+ i += 1
989+ }
990+ true
991+ }
992+ }
993+
915994 protected def writeReplace (): AnyRef = new SerializationProxy (this )
916995 }
917996
@@ -1093,22 +1172,10 @@ sealed abstract class ByteString
10931172 * @since 2.0.0
10941173 */
10951174 def indexOfSlice (slice : Array [Byte ], from : Int ): Int = {
1096- // this is only called if the first byte matches, so we can skip that check
1097- def check (startPos : Int ): Boolean = {
1098- var i = startPos + 1
1099- var j = 1
1100- // let's trust the calling code has ensured that we have enough bytes in this ByteString
1101- while (j < slice.length) {
1102- if (apply(i) != slice(j)) return false
1103- i += 1
1104- j += 1
1105- }
1106- true
1107- }
11081175 @ tailrec def rec (from : Int ): Int = {
11091176 val startPos = indexOf(slice.head, from, length - slice.length + 1 )
11101177 if (startPos == - 1 ) - 1
1111- else if (check (startPos)) startPos
1178+ else if (bytesMatch (startPos, slice, 0 , slice.length )) startPos
11121179 else rec(startPos + 1 )
11131180 }
11141181 val sliceLength = slice.length
@@ -1147,18 +1214,7 @@ sealed abstract class ByteString
11471214 */
11481215 def startsWith (bytes : Array [Byte ], offset : Int ): Boolean = {
11491216 if (length - offset < bytes.length) false
1150- else {
1151- var i = offset
1152- var j = 0
1153- while (j < bytes.length) {
1154- // we know that byteString is at least as long as bytes,
1155- // so no need to check i < length
1156- if (apply(i) != bytes(j)) return false
1157- i += 1
1158- j += 1
1159- }
1160- true
1161- }
1217+ else bytesMatch(offset, bytes, 0 , bytes.length)
11621218 }
11631219
11641220 /**
@@ -1170,6 +1226,13 @@ sealed abstract class ByteString
11701226 */
11711227 def startsWith (bytes : Array [Byte ]): Boolean = startsWith(bytes, 0 )
11721228
1229+ /**
1230+ * Tests whether the bytes in a segment of this ByteString match the provided bytes.
1231+ * Internal use only. ByteString1 and ByteString1C have optimized versions.
1232+ */
1233+ private [util] def bytesMatch (fromIndex : Int , checkBytes : Array [Byte ], checkBytesFromIndex : Int , checkLength : Int )
1234+ : Boolean
1235+
11731236 override def grouped (size : Int ): Iterator [ByteString ] = {
11741237 if (size <= 0 ) {
11751238 throw new IllegalArgumentException (s " size= $size must be positive " )
0 commit comments