Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala hex literal for bytes

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

like image 991
Nafeez Abrar Avatar asked Feb 22 '18 11:02

Nafeez Abrar


2 Answers

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
like image 125
Tom Avatar answered Sep 20 '22 08:09

Tom


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"
like image 39
Andrey Tyukin Avatar answered Sep 20 '22 08:09

Andrey Tyukin