4

I'm writing to a storage format that has uint32, with a max allowed value of "4294967295".

Integer in Java is, of course, just under half that at "2147483647". So internally, I have to use either Long or Guava's UnsignedInteger.

To write to this format, the byte array length needs to be 4, which fits Integer just fine, but converting Long to a byte array requires an array of length 8.

How can I convert a Long or UnsignedInteger representing a max value of "4294967295" as a 4 byte array?

user2887053
  • 103
  • 1
  • 2
  • 8

3 Answers3

5

Simply convert it to an 8 byte array and then take only the last 4 bytes:

 public static byte[] fromUnsignedInt(long value)
 {
     byte[] bytes = new byte[8];
     ByteBuffer.wrap(bytes).putLong(value);

     return Arrays.copyOfRange(bytes, 4, 8);
 }

To reverse this you can use the following method:

 public static long toUnsignedInt(byte[] bytes)
 {
     ByteBuffer buffer = ByteBuffer.allocate(8).put(new byte[]{0, 0, 0, 0}).put(bytes);
     buffer.position(0);

     return buffer.getLong();
 }

Note that this method CAN take a negative long or a long that exceed the range of an unsigned int and won't throw a exception in such a case!

Felix
  • 2,256
  • 2
  • 15
  • 35
1

You can just cast it to an int and do whatever you do that turns ints into arrays, such as this: (not tested)

public static byte[] getUnsignedInt(long value)
{
    byte[] bytes = new byte[4];
    ByteBuffer.wrap(bytes).putInt((int)value);
    return bytes;
}

Of course if you're putting these things in a ByteBuffer anyway, you might as well do that directly.

The "meaning" or "interpretation" of the top bit is irrelevant if all you're doing it storing it. For example, 4294967295 would be interpreted as -1, but it's really the same number: 0xFFFFFFFF in hexadecimal, so you will get the byte array { 0xFF, 0xFF, 0xFF, 0xFF }.

To reverse it, you could do something like this (not tested)

public static long toUnsignedInt(byte[] bytes)
{
    ByteBuffer buffer = ByteBuffer.allocate(4).put(bytes);
    buffer.position(0);
    return buffer.getInt() & 0xFFFFFFFFL;
}
harold
  • 61,398
  • 6
  • 86
  • 164
  • This doesn't work because int has a max value of 2147483647, while unsigned ints have a max value of more than twice that. Casting the long down to int loses anything over the max int value. – user2887053 Jun 30 '17 at 20:38
  • @user2887053 no, it doesn't. The `int` will be negative, but that is of no consequence. – harold Jun 30 '17 at 20:45
1

An answer without the object creation and array copying of the accepted answer... It's easy to do yourself w/ shift operations. See:

import java.io.*;

public class TestUINT32 {

  public static void writeLongAsUINT32(long value, OutputStream os) throws IOException {
    for (int i=0; i<4; ++i) {
      os.write((byte) value);
      value = value >> 8;
    }
  }

  public static void main(String[] args) throws IOException {
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    long value = 0xFFEEDDBB;
    writeLongAsUINT32(value, os);
    byte[] ba = os.toByteArray();
    for (int i=ba.length; i>0; --i) {
      System.out.print(String.format("%02X", ba[i-1]));
    }
    System.out.println();
  }

}

Example run:

$ java TestUINT32 
FFEEDDBB
keredson
  • 3,019
  • 1
  • 17
  • 20