2

Here's what I want to do, except there are two problems with it: position() does an absolute positioning, not relative (and an argument of -1 is thus illegal), and you apparently can't chain another method call following a position() call - the compiler complains that it doesn't recognize putShort().

// Method to create a packet header for sending a packet. The placement of the two numbers is
//  done according to little-endian encoding.
private byte[] createPacketHeader(EPacketType packetType, int fourBits,
                                  int totalMessageLength, int segmentSize) {

    return ByteBuffer.allocate(CPacketHeaderSize).order(ByteOrder.LITTLE_ENDIAN).
            put((byte) ((byte) (packetType.getValue() << 4) | (byte) fourBits)).
            putInt(totalMessageLength).  // Bottom 3 bytes of total length (+ 1 byte discarded)
            position(-1).  // Reposition to discard last byte from above call !!DOESN'T WORK!!
            putShort((short) segmentSize).  // Segment length
            put(_connectIdUtf8).  // Connection ID in UTF-8, should be <= 10 bytes
            array();  // This assumes zero initialization so final bytes are zero
}

So here's what I'm currently doing. It does work, but seems rather inelegant compared to what I was hoping I could do.

    ByteBuffer byteBuffer =
                     ByteBuffer.allocate(CPacketHeaderSize).order(ByteOrder.LITTLE_ENDIAN);

    byteBuffer.put((byte) ((byte) (packetType.getValue() << 4) | (byte) fourBits)).
            putInt(totalMessageLength).  // Bottom 3 bytes of total length (+ 1 byte discarded)
            position(byteBuffer.position() -1);  // Discard last byte from above call

    byteBuffer.putShort((short) segmentSize).  // Segment length
            put(_connectIdUtf8);  // Connection ID in UTF-8, should be <= 10 bytes

    return byteBuffer.array();  // This assumes zero initialization so final bytes are zero

Any suggestions as to how I can get back to something closer to my first attempt?

EDIT: Thanks for the answers, they were all helpful. If anyone is curious, here's what I ended up doing:

// Method to create a packet header for sending a packet. The placement of the two numbers is
//  done according to little-endian encoding.
private byte[] createPacketHeader(EPacketType packetType, int fourBits,
                                  int totalMessageLength, int segmentSize) {

    return ByteBuffer.allocate(CPacketHeaderSize).order(ByteOrder.LITTLE_ENDIAN).
            put((byte) ((byte) (packetType.getValue() << 4) | (byte) fourBits)).
            put(intToThreeBytes(totalMessageLength)).  // Bottom 3 bytes of total length
            putShort((short) segmentSize).  // Segment length
            put(_connectIdUtf8).  // Connection ID in UTF-8, should be <= 10 bytes
            array();  // This assumes zero initialization so final bytes are zero
}


// Method to convert an int into a three-byte byte array, using little-endian encoding
private byte[] intToThreeBytes(int aNumber) {
    byte[] byteArray = new byte[3];
    for (int i = 0; i < 3; i++)
        byteArray[i] = (byte)(aNumber >> i * 8);
    return byteArray;
}
RenniePet
  • 11,420
  • 7
  • 80
  • 106

3 Answers3

1

I don't think you can. ByteBuffer just doesn't have the functionality to decrement the write cursor in a relative way. The write cursor only increases relatively.

I was thinking you could use mark, but as you are adding 4 bytes in one operation, you can't mark the third for an easy reset.

William Morrison
  • 10,953
  • 2
  • 31
  • 48
  • Thanks for answering. I think I'll end up doing something along the lines of what Joop is suggesting. – RenniePet Aug 02 '13 at 13:30
  • I'd stay with what you have now. Its more efficient, less code, and as easy to follow as his suggestion. No offense Joop, your soltion is still perfectly valid.But i think rennie's is better for those reasons. – William Morrison Aug 02 '13 at 13:42
  • It's just that I hate writing code that to me is not "elegant", whatever that means. Anyway, I've posted my current code as an edit to my question. Thanks again for indicating that what I wanted doesn't exist, I'm very new to Java. – RenniePet Aug 02 '13 at 13:50
  • Ok, good luck @RenniePet Might I suggest you look into [Google ProtoBuf's](http://code.google.com/p/protobuf/) if you are parsing a binary protocol? It handles generating code for parsing/unparsing bytes so you'd not need maintain it. Don't need to worry about clean code as much when its auto-generated. – William Morrison Aug 02 '13 at 13:55
  • I'm all over it! As the young people say. http://stackoverflow.com/questions/17809913/java-net-object-interchange-not-web-based Could I maybe open a chat with you about it? And how does one open a chat here on StackOverflow? – RenniePet Aug 02 '13 at 14:05
1

position method is not defined in ByteBuffer. But in its super class Buffer. So you will have to explicitly typecast to ByteBuffer after calling position method and before calling putShort method. Change the code as below:

return ((ByteBuffer)(ByteBuffer.allocate(CPacketHeaderSize).order(ByteOrder.LITTLE_ENDIAN).
        put((byte) ((byte) (packetType.getValue() << 4) | (byte) fourBits)).
        putInt(totalMessageLength).  // Bottom 3 bytes of total length (+ 1 byte discarded)
        position(-1))).  // Reposition to discard last byte from above call !!DOESN'T WORK!!
        putShort((short) segmentSize).  // Segment length
        put(_connectIdUtf8).  // Connection ID in UTF-8, should be <= 10 bytes
        array();
Prem
  • 329
  • 1
  • 11
1

Missing in elegance too:

byte[] bytes = ByteBuffer.allocate(4).putInt(totalMessageLength).array();
byteBuffer.put(bytes, 0, 3);
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138