vojta
vojta

Reputation: 5651

ByteBuffer putLong() for long of 7-byte value

I have a long. I know this long is not very big. It could be encoded as an unsigned integer into 7 bytes, actually.

I would like to write my long into byte[7] using ByteBuffer and the little endian.

This

long myLong = ... //some value
byte[] myLongBytes = ByteBuffer.allocate(7).order(ByteOrder.LITTLE_ENDIAN).putLong(myLong).array();

throws IndexOutOfBoundsException, of course. Can I even do this with ByteBuffer? Should I do it in a different way?

Upvotes: 1

Views: 3686

Answers (4)

vlp
vlp

Reputation: 8116

(Assuming little endian byte order as indicated in the OP)

(Assuming you want 56 least significant bits from the long being stored)

long myLong = someLongValue & 0x00FFFFFFFFFFFFFFL;
ByteBuffer bb = ByteBuffer.allocate(xxx).order(ByteOrder.LITTLE_ENDIAN);

Option A: Write 64 bit long and than remove one byte

bb.putLong(myLong);
bb.position(bb.position()-1);

for tail write (i.e. this long is the last write to the ByteBuffer):

bb.putLong(myLong);
bb.limit(bb.position()-1);

Beware that this approach works only if you can ensure the ByteBuffer capacity is large enough to fit this temporary extra byte.

Some example cases where this approach should be OK:

  • where this 56bit value is always followed by another field (then you are sure there is some capacity reserve)

  • when you are strictly using the ByteBuffer.limit() feature and not (ab)using the ByteBuffer for comfortable byte array writes (which is implied by your post)

Option B: Write the long "per partes"

bb.putInt((int)myLong);
bb.putShort((short)(myLong>>>32));
bb.put((byte)(myLong>>>48));

This works every time.

Option C: Use byte[] instead of long

You might consider using byte array instead of long (depends on your use case).

Good luck!

Upvotes: 6

Peter Lawrey
Peter Lawrey

Reputation: 533472

You need to write a byte at a time such as (based on java.nio.Bits)

static private long makeLong(byte b6, byte b5, byte b4,
                             byte b3, byte b2, byte b1, byte b0)
{
    return ((((long)b6 & 0xff) << 48) |
            (((long)b5 & 0xff) << 40) |
            (((long)b4 & 0xff) << 32) |
            (((long)b3 & 0xff) << 24) |
            (((long)b2 & 0xff) << 16) |
            (((long)b1 & 0xff) <<  8) |
            (((long)b0 & 0xff)      ));
}

static long getLongL(ByteBuffer bb, int bi) {
    return makeLong(bb.get(bi + 6),
                    bb.get(bi + 5),
                    bb.get(bi + 4),
                    bb.get(bi + 3),
                    bb.get(bi + 2),
                    bb.get(bi + 1),
                    bb.get(bi    ));
}

private static byte long6(long x) { return (byte)(x >> 48); }
private static byte long5(long x) { return (byte)(x >> 40); }
private static byte long4(long x) { return (byte)(x >> 32); }
private static byte long3(long x) { return (byte)(x >> 24); }
private static byte long2(long x) { return (byte)(x >> 16); }
private static byte long1(long x) { return (byte)(x >>  8); }
private static byte long0(long x) { return (byte)(x      ); }

static void putLongL(ByteBuffer bb, int bi, long x) {
    bb.put(bi + 6, long6(x));
    bb.put(bi + 5, long5(x));
    bb.put(bi + 4, long4(x));
    bb.put(bi + 3, long3(x));
    bb.put(bi + 2, long2(x));
    bb.put(bi + 1, long1(x));
    bb.put(bi    , long0(x));
}

In general, I avoid making micro-optimisation like this as it adds complexity for little gain IMHO. If you want to save space I suggest using something like stop bit encoding which uses one byte for every 7 bits. i.e. a long might only use 1 byte for small values but could be much larger as required.

Upvotes: 5

Audrius Meškauskas
Audrius Meškauskas

Reputation: 21728

Java long is the 64 bit so 64/8 = 8 byte value so you are not allocating enough space to it. Allocate 8 bytes, not seven.

Upvotes: -2

sanky jain
sanky jain

Reputation: 873

Do as below

byte[] myLongBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(myLong).array();

Upvotes: -2

Related Questions