0

What's the best way to put an int at a certain point in a byte[] array?

Say you have a byte array:

byte[] bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int someInt = 12355; //0x43, 0x30

How can I do like bytes[4] = someInt; so that now bytes[4] will equal 0x43 and bytes[5] will be equal to 0x30?

I'm used to just using memcpy with C++ and don't know the alternatives in Java.

Thanks

marcaddeo
  • 177
  • 2
  • 3
  • 12
  • 2
    possible duplicate of [Java - Convert int to Byte Array of 4 Bytes?](http://stackoverflow.com/questions/6374915/java-convert-int-to-byte-array-of-4-bytes) and a number of others ... – Brian Roach Dec 27 '11 at 00:47
  • Actually, memcpy in C++ is not a portable solution because assumes that you want to copy the bytes into the array in the "natural" order of the platform ... – Stephen C Dec 27 '11 at 02:25

5 Answers5

4

If you want also high 0-bytes of the int put into the byte[]:

void place(int num, byte[] store, int where){
    for(int i = 0; i < 4; ++i){
        store[where+i] = (byte)(num & 0xFF);
        num >>= 8;
    }
}

If you only want the bytes to the highest nonzero byte:

void place(int num, byte[] store, int where){
    while(num != 0){
        store[where++] = (byte)(num & 0xFF);
        num >>>= 8;
    }
}

If you want the bytes big-endian (highest byte at lowest index), the version storing all four bytes is very easy, the other one slightly more difficult:

void placeBigEndian(int num , byte[] store, int where){
    for(int i = 3; i >= 0; --i){
        store[where+i] = (byte)(num & 0xFF);
        num >>= 8;
    }
}

void placeBigEndian(int num, byte[] store, int where){
    in mask = 0xFF000000, shift = 24;
    while((mask & num) == 0){
        mask >>>= 8;
        shift -= 8;
    }
    while(shift > 0){
        store[where++] = (byte)((num & mask) >>> shift);
        mask >>>= 8;
        shift -= 8;
    }
}
Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • @Daniel Fischer: + 1, but same comment as the one I made to *Martijn Courteaux*'s answer... The and *(& 0xFF)* is not necessary if you're converting to a *(byte)* anyway. – TacticalCoder Dec 27 '11 at 00:48
  • Yeah, it's not necessary, it just feels cleaner to explicitly mask the byte off. May be idiosyncratic. – Daniel Fischer Dec 27 '11 at 00:58
  • Oops. I spotted a bug. You are putting the bytes in the wrong order in the array. Lowest bits should be at the right. – Martijn Courteaux Dec 27 '11 at 01:12
  • 1
    @Martijn I followed the example, where `bytes[4]` got the low byte and `bytes[5]` the higher. Nevertheless, added versions for the other order. – Daniel Fischer Dec 27 '11 at 01:17
  • I see, but I think it is a mistake from the OP, because `memcopy` won't work like the OP showed in his example. – Martijn Courteaux Dec 27 '11 at 01:27
  • @Martijn That would depend on the machine's endianness. On little-endian machines, `memcpy(bytes+4,&someInt,2)` would do what the OP wrote. – Daniel Fischer Dec 27 '11 at 01:37
2

Note, you assume a big endian ordering! x86 is little endian... What's more, your int is 32bits long, hence 0x00004330 in big endian.

If this is what you want, use a ByteBuffer (which uses big endian ordering by default):

ByteBuffer buf = ByteBuffer.allocate(8);
// then use buf.putInt(yourint, index)
// buf.get(index) will read byte at index index, starting from 0
fge
  • 119,121
  • 33
  • 254
  • 329
  • For primitive types?? That is a good way of slowing things down if that's the case! – fge Dec 27 '11 at 00:53
  • And I can assert this is _not_ the case when looking at JNI headers, where `jint` is a typedef for `int` – fge Dec 27 '11 at 00:58
  • +1 for using an existing class (ByteBuffer) instead of writing your own code. Perhaps a better answer than my DataOutputStream proposal. – user949300 Dec 27 '11 at 01:02
  • @EtiennedeMartel this is for _class_ data, here I talk about primitive integer types at run time – fge Dec 27 '11 at 01:09
  • @fge There's a second part in the accepted answer which talks about operands. – Etienne de Martel Dec 27 '11 at 01:16
  • @EtiennedeMartel read http://arstechnica.com/civis/viewtopic.php?f=20&t=437821, specifically: "the vm internally does all of its calculations in the native endianness, and only converts to big endian when saving to a file or writing over the network" – fge Dec 27 '11 at 01:22
1

I don't see the problem, it looks like you solved it your own way:

public static void putShort(bytes[] array, int position, short value)
{
    byte leftByte = (byte) (value >>> 8);
    byte rightByte = (byte) (value & 0xFF);

    array[position] = leftByte;
    array[position + 1] = rightByte;
}

Note that an int is 4 bytes and a short is 2 bytes.

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
  • I'm not sure... How about negative numbers? – Martijn Courteaux Dec 27 '11 at 00:47
  • Even if the &0xFF isn't necessary, I've seen so many problems with sign extension I'd add it anyway. :-) – user949300 Dec 27 '11 at 00:53
  • @MartijnCourteaux: *(byte) (value & 0xFF)* cannot give a different result than *(byte) value*. Paste an *int* for which *(byte) (value & 0xFF)* does give something else than *(byte) value* if you find one ; ) You're really saying *"zero all bits besides the 8 lowest bits and then keep only the 8 lowest bits"* versus directly *"give me the lowest 8 bits"*. There's no point in zero'ing 24 bits that you're going to "discard" anyway : ) – TacticalCoder Dec 27 '11 at 01:01
  • I wasn't sure because I know that the casting in Java for primitives isn't always that basic. Example: `byte b = (byte) -1;` Now `b = 0xFF`. And when you do afterwards: `int i = b;`. Now `i = 0xFFFFFFFF`, and not longer the original value of b which was `0xFF`. – Martijn Courteaux Dec 27 '11 at 01:05
  • @Martijn Courteaux: it isn't called "casting", it's called converting : ) You're referring to a different type of conversion: *"widening primitive conversion"* (*e.g.* from *byte* to *int*). But when you're doing a *"narrowing primitive conversion"* (like from *int* to *byte*) then there's no issue because you're actually discarding all but the lowest bits : ) – TacticalCoder Dec 27 '11 at 01:12
  • Thank you! I'll remember that. Now, I finally know exactly how it is working. – Martijn Courteaux Dec 27 '11 at 01:16
0

First of all, in Java you don't need to initialize byte arrays to zeroes. All arrays are initialized on construction time to 0/false/null.

Second, ints are signed 32-bit big-endian integers, so 12355 is actually 0x00003043. If you want to use 16-bit integers, use the short type.

Then, to get the individual bytes in your integer, you could do:

bytes[ i ]   = (byte) (someInt >> 24);
bytes[ i+1 ] = (byte) (someInt >> 16);
bytes[ i+2 ] = (byte) (someInt >> 8);
bytes[ i+3 ] = (byte) (someInt);

The conversion to byte truncates the remaining bits, so no & 0xFF mask is needed. I'm assuming i is the index of the array. To change the endianness, swap the offsets at the indices.

Darkhogg
  • 13,707
  • 4
  • 22
  • 24
0

One approach would be to use a DataOutputStream and it's writeInt() method, wrapped around a ByteArrayOutputStream. e.g. (with no error-handling)

public byte[] writeIntAtPositionX(int position, int iVal) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      DataOutputStream dos = new DataOutputStream(baos);

      // now, advancing to a specific spot is awkward.
      // presumably you are actually writing other stuff out before the integer
      // but if you really want to advance to a specific position

      for (int i = 0; i < position; i++)
         dos.writeByte(0);

      dos.writeInt(iVal);

      dos.flush();
      dos.close();
      return baos.toByteArray();  
   }

The big advantage of this method is that the guys who wrote Java figured out the byte ordering and the masking with 0xFF and all that stuff. Plus, if you ever envision writing doubles, shorts, longs or Strings etc. to your buffer you won't need to add all those methods, the work is already done.

user949300
  • 15,364
  • 7
  • 35
  • 66