aboutsummaryrefslogtreecommitdiff
path: root/kamon-core/src/main/scala/kamon/util/HexCodec.scala
blob: e8807e74d01d1a7d8408ab9854b74039b2c7fcb0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/* =========================================================================================
 * Copyright © 2013-2017 the kamon project <http://kamon.io/>
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions
 * and limitations under the License.
 * =========================================================================================
 */

package kamon.util

// Extracted from https://github.com/openzipkin/brave/blob/master/brave/src/main/java/brave/internal/HexCodec.java
object HexCodec {
  /**
    * Parses a 1 to 32 character lower-hex string with no prefix into an unsigned long, tossing any
    * bits higher than 64.
    */
  def lowerHexToUnsignedLong(lowerHex: String): Long = {
    val length = lowerHex.length
    if (length < 1 || length > 32) throw isntLowerHexLong(lowerHex)
    // trim off any high bits
    val beginIndex = if (length > 16) length - 16
    else 0
    lowerHexToUnsignedLong(lowerHex, beginIndex)
  }

  private def isntLowerHexLong(lowerHex: String) =
    throw new NumberFormatException(lowerHex + " should be a 1 to 32 character lower-hex string with no prefix")

  /**
    * Parses a 16 character lower-hex string with no prefix into an unsigned long, starting at the
    * spe index.
    */
  private def lowerHexToUnsignedLong(lowerHex: String, index: Int): Long = {
    var i = index
    var result = 0L
    val endIndex = Math.min(index + 16, lowerHex.length)
    while (i < endIndex) {
      val c = lowerHex.charAt(i)
      result <<= 4
      if (c >= '0' && c <= '9')
        result |= c - '0'
      else if (c >= 'a' && c <= 'f')
        result |= c - 'a' + 10
      else throw isntLowerHexLong(lowerHex)

      i += 1
    }
    result
  }

  /**
    * Returns 16 or 32 character hex string depending on if {@code high} is zero.
    */
  private def toLowerHex(high: Long, low: Long): String = {
    val result = new Array[Char](if (high != 0) 32 else 16)
    var pos = 0
    if (high != 0) {
      writeHexLong(result, pos, high)
      pos += 16
    }
    writeHexLong(result, pos, low)
    new String(result)
  }

  /**
    * Inspired by {@code okio.Buffer.writeLong}
    */
  def toLowerHex(v: Long): String = {
    val data = new Array[Char](16)
    writeHexLong(data, 0, v)
    new String(data)
  }

  private def writeHexLong(data: Array[Char], pos: Int, v: Long): Unit = {
    writeHexByte(data, pos + 0, ((v >>> 56L) & 0xff).toByte)
    writeHexByte(data, pos + 2, ((v >>> 48L) & 0xff).toByte)
    writeHexByte(data, pos + 4, ((v >>> 40L) & 0xff).toByte)
    writeHexByte(data, pos + 6, ((v >>> 32L) & 0xff).toByte)
    writeHexByte(data, pos + 8, ((v >>> 24L) & 0xff).toByte)
    writeHexByte(data, pos + 10, ((v >>> 16L) & 0xff).toByte)
    writeHexByte(data, pos + 12, ((v >>> 8L) & 0xff).toByte)
    writeHexByte(data, pos + 14, (v & 0xff).toByte)
  }

  private val HEX_DIGITS = Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')

  private def writeHexByte(data: Array[Char], pos: Int, b: Byte): Unit = {
    data(pos + 0) = HEX_DIGITS((b >> 4) & 0xf)
    data(pos + 1) = HEX_DIGITS(b & 0xf)
  }
}