0

I have a binary protocol which extracts the MSB of each payload byte into a MSB collection byte (septett) for transmission, and re-injects the MSBs on receiver side. The payload consists of n four byte frames, depending on sender (six in my case).

Those are two example frames, with their septett (last byte), as seen on the wire:

0x2E 0x00 0x5F 0x00 0x04
0x79 0x01 0x38 0x22 0x04

Those are the same frames, client side, with the MSBs re-injected:

0x2E 0x00 0xDF 0x00
0x79 0x01 0xB8 0x22

The C functions that do the transformation are defined on pages 9 and 10 in this document. My version of these, in Java, is below. The problem I have is that none of this works, and I'm confused as to why. I pass my four bytes from the wire, and get same bytes out, untouched. I could use some help figuring out what's wrong here (probably something trivial that I fail to see).

private static byte[] vbusExtractSeptett(byte[] data, int offset, int length) {
    byte septett = 0;
    for (int i = 0; i < length; i++) {
        if ((data[offset + i] & 0x80) != 0) {
            data[offset + i] &= 0x7F;
            septett |= 1 << i;
        }
    }

    data[offset + length] = septett;
    return data;
}

private static byte[] vbusInjectSeptett(final byte[] data, int offset, int length) {
    byte septett = data[offset + length];

    for (int i = 0; i < length; i++) {
        if ((septett & (1 << i)) != 0)
            data[offset + i] |= 0x80;
    }

    return data;
}
ptashek
  • 186
  • 2
  • 6

2 Answers2

1

In Java, a byte is signed. Without reading through all your code, I bet that is the problem.

The C code in your document uses unsigned char math.

Since Java doesn't have "unsigned", you probably need to do all your math in shorts (or ints) and then convert back to bytes. Be sure to mask off the sign bit, e.g. something like

byte theResult = theIntIDidtheMathOn & 0xFF;
data[index] = theResult;
user949300
  • 15,364
  • 7
  • 35
  • 66
0

The other answer is correct, you are not taking into account Java's use of signed bytes.

There are a few possibilities for solving this:

  1. You can do the stuff above with "& 0xFF"
  2. You can just treat everything as ints (as I have done below)
  3. You could use a library to help. Some examples are JOOU (which was started in response to this lack of unsigned types) or netty channelbuffers (NB: although netty is focussed on network IO, like sockets etc., its channel buffer class is great for dealing with bytestreams and signed/unsigned types of different lengths and I have used it quite a lot for transforming binary protocols like the one you're handling.)

I have written a "solution" to your question below.

public class SOAnswer
{
    private static void vbusExtractSeptett(int[] data, int offset, int length) {
        int septett = 0;

        for (int i = 0; i < length; i++) {
            if ((data[offset + i] & 0x80) != 0) {
                data[offset + i] &= 0x7F;
                septett |= 1 << i;
            }
        }

        data[offset + length] = septett;
    }

    private static void vbusInjectSeptett(final int[] data, int offset, int length) {
        int septett = data[offset + length];

        for (int i = 0; i < length; i++) {
            if ((septett & (1 << i)) != 0)
                data[offset + i] |= 0x80;
        }

        // clear the septett byte
        data[offset + length] = 0x00;
    }

    private static void printIntArrayAsHEX(int[] array)
    {
        StringBuilder builder = new StringBuilder();

        for ( int a : array )
        {
            String s = Integer.toHexString( a );
            if (s.length() == 1)
                builder.append( "0" );
            builder.append(s + ":");
        }

        builder.substring( 0, builder.lastIndexOf( ":" ) - 1 );

        System.out.println(builder.toString());
    }

    public static void main( String[] args )
    {
        // Create an array long enough for the extracting/injecting
        int[] arr = new int[]{0x2E, 0x00, 0xDF, 0x00, 0x00};
        // see what it looks like
        printIntArrayAsHEX(arr);

        // perform extraction
        vbusExtractSeptett( arr, 0, 4 );
        // see what it looks like
        printIntArrayAsHEX(arr);

        // Perform injection
        vbusInjectSeptett( arr, 0, 4 );
        // see what it looks like
        printIntArrayAsHEX(arr);
    }
}

One recommendation I would have for you is to think about if you really want to to re-implement the C code verbatim (especially the very functional programming style of passing an array of primitive types, an offset into the array and the length of the array). Maybe something more like this would be more OO:

private static int[] vbusExtractSeptettJAVASTYLE(int[] data) {
    int[] extractedData = Arrays.copyOf( data, data.length +1 );

    int septett = 0;

    for (int i = 0; i < data.length; i++) {
        if ((data[i] & 0x80) != 0) {
            extractedData[i] = data[i] &= 0x7F;
            septett |= 1 << i;
        }
    }

    extractedData[extractedData.length-1] = septett;

    return extractedData;
}
Doddie
  • 1,393
  • 13
  • 21