Skip to content

Commit 3477052

Browse files
authored
Merge pull request #511 from scodec/topic/equalsConstantTime
Add ByteVector#equalsConstantTime
2 parents a3383d1 + f82638c commit 3477052

File tree

2 files changed

+22
-0
lines changed

2 files changed

+22
-0
lines changed

core/shared/src/main/scala/scodec/bits/ByteVector.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,21 @@ sealed abstract class ByteVector
12931293
case _ => false
12941294
}
12951295

1296+
/** Like [[equals]] but compares all bytes instead of returning after first non-matching byte.
1297+
* @group collection
1298+
*/
1299+
def equalsConstantTime(other: ByteVector): Boolean =
1300+
if (this.size != other.size) false
1301+
else {
1302+
var result, idx = 0
1303+
val s = this.size
1304+
while (idx < s) {
1305+
result = result | (this(idx) ^ other(idx))
1306+
idx += 1
1307+
}
1308+
result == 0
1309+
}
1310+
12961311
/** Display the size and bytes of this `ByteVector`. For bit vectors beyond a certain size, only a
12971312
* hash of the contents are shown.
12981313
* @group collection

core/shared/src/test/scala/scodec/bits/ByteVectorTest.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ class ByteVectorTest extends BitsSuite {
5959
forAll((b: ByteVector, b2: ByteVector) => assert((b == b2) == (b === b2)))
6060
}
6161

62+
property("equalsConstantTime") {
63+
forAll(bytesWithIndex) { case (b, m) =>
64+
assert((b.take(m) ++ b.drop(m)).equalsConstantTime(b))
65+
} &&
66+
forAll((b: ByteVector, b2: ByteVector) => assert((b === b2) == (b.equalsConstantTime(b2))))
67+
}
68+
6269
test("compact is a no-op for already compact byte vectors") {
6370
val b = ByteVector(0x80)
6471
assert((b.compact eq b.compact) == true)

0 commit comments

Comments
 (0)