InfalibleCoinage
InfalibleCoinage

Reputation: 608

Negative short in Java to uint16_t in NDK not handled correctly?

I'm puzzled by behavior I'm seeing with the Android NDK regarding negative shorts as parameters. I've reduced it to a simple example.

I have the following c function:

void shortTest(uint16_t input)
{
    uint16_t tmp = -1;
    LogInfo("shortTest input: 0x%X\n", input); // wrapper for logging
    LogInfo("shortTest tmp: 0x%X\n", tmp);
}

The corresponding Java signature:

void shortTest(short input);

The function is called like this:

short s = -1;
MyLibrary.INSTANCE.shortTest(s);

The output:

INFO   : 21:10:01:158 - shortTest:6158: shortTest input: 0xFFFFFFFF
INFO   : 21:10:01:158 - shortTest:6159: shortTest tmp: 0xFFFF

This behavior can cause failures, for example with comparisons (input is not equal to tmp). As a work-around I just changed the c function to take a uint32_t and pass in an int. However, I'm concerned about this biting me in other areas as I use uint16's pretty regularly. It seems to somehow force the storage type from 2 bytes to 4. Am I missing something? Is this a bug?

Upvotes: 0

Views: 341

Answers (1)

Michael
Michael

Reputation: 58467

A Java short is signed, so you're sort of lying to the compiler about what shortTest will receive.

Let's consider the consequences of this by looking at the following C code and its corresponding ARM assembly:

// Here I'm using int16_t, because that's basically what you've declared that
// you will pass to shortTest from your Java code.
void shortTest(int16_t input) {
    printf("shortTest input: 0x%X\n", input);
}

int main() {
    shortTest(-1);
}

shortTest:
    stmfd   sp!, {r4, lr}
    mov     r1, r0              @ Just pass on input as the second argument to printf
    movw    r0, #:lower16:.LC0  @ Load string address
    movt    r0, #:upper16:.LC0  @ ...
    bl      printf              @ Call printf
    ldmfd   sp!, {r4, pc}       @ Return


main:
    stmfd   sp!, {r4, lr}
    mvn     r0, #0         @ First argument = 0xFFFFFFFF
    bl      shortTest      @ Call shortTest
    mov     r0, #0         @ Value to return from main
    ldmfd   sp!, {r4, pc}  @ Return

My main here would be your Java code, so it's passing 0xFFFFFFFF to shortTest, which is fine because for all it knows shortTest wants a signed short. Then you arrive at shortTest which (in your case) thinks it received an unsigned short, but it assumes that the caller passed the right kind of value so it won't do any zero-extention.

Then you try to print this value, and if your logging function works like printf/sprintf it will implicitly promote your short to an int, i.e. it will print a 32-bit value (excluding leading zeroes). And the 32-bit value in this case is 0xFFFFFFFF.

Upvotes: 1

Related Questions