Scala hex literal for bytes
Asked Answered
G

2

7

Hex literal containing A-F digit are converting to int by default. When I am trying to declear an Int with 0x it is creating correctly.

val a: Int = 0x34
val b: Int = 0xFF

But when I am trying to declear a Byte with 0x second line is not compiling

val a: Byte = 0x34
val b: Byte = 0xFF // compilation error

I have found a workaround that is

val a: Byte = 0x34
val b: Byte = 0xFF.toByte

But is there any decent way to declear a Byte from its hex literal?

For example I am trying to declear a Byte array in a Test method in this way

anObject.someMethod(1, 1.1f, 0xAB, "1") shouldBe
  Array[Byte](0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF)
anObject.someMethod(2, 2.2f, 0xCD, "2") shouldBe
  Array[Byte](0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE)
anObject.someMethod(3, 3.2f, 0xEF, "3") shouldBe
  Array[Byte](0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD)

But not in this way

anObject.someMethod(1, 1.1f, 0xAB.toByte, "1") shouldBe
  Array[Byte](0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte, 0xAF.toByte)
anObject.someMethod(2, 2.2f, 0xCD.toByte, "2") shouldBe
  Array[Byte](0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte, 0xBE.toByte)
anObject.someMethod(3, 3.2f, 0xEF.toByte, "3") shouldBe
  Array[Byte](0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte, 0xCD.toByte)

Tested in scala 2.12.4

Golding answered 22/2, 2018 at 11:29 Comment(1)
You might want to take a look at different ways to define byte arrays instead of single bytes.Cheek
S
6

You can do this with implicit conversions.

Before:

def f(b: Byte) = println(s"Byte = $b")
f(0x34)
f(0xFF) // compilation error

After:

implicit def int2Byte(i: Int) = i.toByte

def f(b: Byte) = println(s"Byte = $b")
f(0x34)
f(0xFF)

Output:

Byte = 52
Byte = -1
Slighting answered 22/2, 2018 at 12:2 Comment(0)
C
5

Recall that in Scala we can easily define new ways to interpret arbitrary String literals by adding methods to a special class StringContext using the "pimp-my-library"-pattern. For example, we can add the method b for creating single bytes to StringContext so that we can write down bytes as follows:

val myByte = b"5C"

Here is how it can be implemented:

implicit class SingleByteContext(private val sc: StringContext) {
   def b(args: Any*): Byte = {
     val parts = sc.parts.toList
     assert(
       parts.size == 1 && args.size == 0, 
       "Expected a string literal with exactly one part"
     )
     Integer.parseInt(parts(0), 16).toByte
   }
}

In order to use this new syntax, we have to import the above object into implicit scope. Then we can do this:

/* Examples */ {

  def printByte(b: Byte) = println("%02X".format(b))

  printByte(b"01")
  printByte(b"7F")
  printByte(b"FF")
  printByte(b"80")
}

This will print:

01
7F
FF
80

You can obviously tweak the implementation (e.g. you can rename "b" to "hex" or "x" or "Ox" or something like this).


Note that this technique can be easily extended to deal with entire byte arrays, as described in this answer to a similar question. This would allow you to write down byte arrays without repeating the annoying 0x-prefix, e.g.:

val myBytes = hexBytes"AB,CD,5E,7F,5A,8C,80,BC"
Cheek answered 22/2, 2018 at 13:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.