Skip to content

Commit b26ccce

Browse files
authored
Merge pull request #420 from scodec/topic/improve-toHex
Optimize toHex
2 parents 67ee8c2 + 46bd0eb commit b26ccce

File tree

3 files changed

+89
-10
lines changed

3 files changed

+89
-10
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2013, Scodec
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without modification,
6+
* are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* 3. Neither the name of the copyright holder nor the names of its contributors
16+
* may be used to endorse or promote products derived from this software without
17+
* specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
package scodec.bits
32+
33+
import org.openjdk.jmh.annotations._
34+
35+
import java.util.concurrent.TimeUnit
36+
37+
@BenchmarkMode(Array(Mode.Throughput))
38+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
39+
@State(Scope.Benchmark)
40+
class ByteVectorBenchmark {
41+
@Param(Array("1000", "10000", "100000"))
42+
var size: Int = _
43+
44+
private var bv: ByteVector = _
45+
46+
def bytes: Array[Byte] = {
47+
val r = new scala.util.Random(size)
48+
val bs = new Array[Byte](size * 3)
49+
r.nextBytes(bs)
50+
bs
51+
}
52+
53+
@Setup(Level.Trial)
54+
def setup(): Unit =
55+
bv = ByteVector.view(bytes)
56+
57+
@Benchmark
58+
def encodeHex: String =
59+
bv.toHex
60+
}

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -804,18 +804,33 @@ sealed abstract class ByteVector
804804
* @group conversions
805805
*/
806806
final def toHex(alphabet: Bases.HexAlphabet): String = {
807-
val bldr = new StringBuilder
808-
foreachS {
809-
new F1BU {
810-
def apply(b: Byte) = {
811-
bldr
812-
.append(alphabet.toChar((b >> 4 & 0x0f).toByte.toInt))
813-
.append(alphabet.toChar((b & 0x0f).toByte.toInt))
814-
()
807+
val out = new Array[Char](size.toInt * 2)
808+
var i = 0
809+
def update(b: Byte): Unit = {
810+
out(i) = alphabet.toChar(b >> 4 & 0x0f)
811+
out(i + 1) = alphabet.toChar(b & 0x0f)
812+
i += 2
813+
}
814+
foreachV {
815+
case View(atArr: AtArray, offset, length) =>
816+
var j = offset
817+
val end = offset + length
818+
while (j < end) {
819+
update(atArr(j))
820+
j += 1
815821
}
816-
}
822+
case View(atByteBuffer: AtByteBuffer, offset, length) =>
823+
val buf = atByteBuffer.buf.duplicate()
824+
buf.position(offset.toInt)
825+
buf.limit((offset + length).toInt)
826+
while (buf.hasRemaining())
827+
update(buf.get())
828+
case other =>
829+
other.foreach(new F1BU {
830+
def apply(b: Byte) = update(b)
831+
})
817832
}
818-
bldr.toString
833+
new String(out)
819834
}
820835

821836
/** Generates a hex dump of this vector using the default format (with no color / ANSI escapes).

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ class ByteVectorTest extends BitsSuite {
186186
)
187187
}
188188

189+
property("toHex fromHex roundtrip") {
190+
forAll((b: ByteVector) => ByteVector.fromHex(b.toHex).get == b)
191+
}
192+
189193
test("toBin") {
190194
assert(deadbeef.toBin == "11011110101011011011111011101111")
191195
}

0 commit comments

Comments
 (0)