Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract Longs from ByteBuffer (Java/Scala)

I'm constructing BigInt numbers that consist of two Longs each in the following way:

val msb = -1L // some arbitrary long value, can be anything between Long.Min/MaxValue
val lsb = 25L // a second arbitrary long value        

val bb = ByteBuffer
  .allocate(17)
  .put(0.toByte) // 1 byte
  .putLong(msb) // 8 bytes
  .putLong(lsb) // 8 bytes

val number = BigInt(bb.array) // in this case: 340282366920938463444927863358058659865

The reason I'm adding another 0-Byte at the front is to guarantee that the result is a positive number. Otherwise, the resulting BigInt could be negative due to two's complement. An algorithm that is called afterwards expects numbers greater or equal than zero.

So far, so good.

I'm having trouble with reversing this whole process - transforming the BigInt back to two Longs (exactly the two values that were used as the input). I can't just do the following:

val arr = number.toByteArray
val bb = ByteBuffer.wrap(arr)
val ignore = bb.getByte
val msb = bb.getLong
val lsb = bb.getLong

Imagine the BigInt number is e.g. 3. Then .toByteArray would result in an Array of size 1, not 16 (or 17), and therefore the calls to getLong would cause a BufferUnderflowException.

What's the easiest way to solve this problem? I tried several ways to manually fill up the buffer until there are 16 bytes available, but since this "padding" must correctly take the two's complement of the two numbers into account, I wasn't succesful.

like image 791
ceran Avatar asked Sep 28 '16 23:09

ceran


People also ask

How do I get data from ByteBuffer?

In order to get the byte array from ByteBuffer just call the ByteBuffer. array() method. This method will return the backed array. Now you can call the String constructor which accepts a byte array and character encoding to create String.

How do I get ByteBuffer length?

After you've written to the ByteBuffer, the number of bytes you've written can be found with the position() method. If you then flip() the buffer, the number of bytes in the buffer can be found with the limit() or remaining() methods.

How do I get strings from ByteBuffer?

The toString() method of ByteBuffer class is the inbuilt method used to returns a string representing the data contained by ByteBuffer Object. A new String object is created and initialized to get the character sequence from this ByteBuffer object and then String is returned by toString().

How do I print ByteBuffer?

Method. Prints the given buffer in hex format from the 0 position to the limit. Print the contents of the buffer out to the PrintStream in hex and ASCII. StringBuffer sbuf = new StringBuffer(); int length = buffer.


2 Answers

Modulo operation can be helpful here:

....
val number = BigInt(bb.array) // in this case: 340282366920938463444927863358058659865

val modulo = BigInt(2).pow(64)
val lsb2 = (number / modulo).toLong     //25
val msb2 = (number.mod(modulo)).toLong  //-1
like image 156
Piotr Reszke Avatar answered Sep 28 '22 06:09

Piotr Reszke


Using the plumbing/padding approach, and with number as defined in the question,

val msb, lsb = split(number) // (-1,25)

/** split the passed Bigint into a (msb: Long, lsb: Long) tuple */
def split(bi: BigInt) = splitArray(bi.toByteArray.takeRight(16)) // Considers only the last bytes if there are more than 16

/** assumes arrays of size 16 or less */
def splitArray(ba: Array[Byte]): (Long, Long) = (
    toLong(ba.take(ba.length - 8)), // Take the msb part: anything before the last 8 bytes (take() seems happy with negative numbers ;))
    toLong(ba.takeRight(8))         // Take at most 8 bytes from the lsb part
   ) 

/** Convert the passed byte-array to a long. Expect arrays of size 8 and less. */
def toLong(ba: Array[Byte]) = ByteBuffer.wrap(zeroPad(ba)).getLong

/** prefix the passed array with 0 bytes. Expect arrays of size 8 and less,
    returns an array of length 8. */
def zeroPad(ba: Array[Byte]) = Array.fill[Byte](8 - ba.length)(0) ++ ba 

Not as succinct as Piotr's modulo proposal, bus worth the little mental gymnastic :)

like image 38
Shastick Avatar answered Sep 28 '22 08:09

Shastick