How we interact with libusb from Java is quite inconsistent, and it mainly stems from the fact Java has no
unsigned integers, which is what libusb uses everywhere.
As it was before, there are parts of the library that return bytes & shorts, like the Descriptors (mostly to
be compatible with javax.usb), and others that take and return int, even where an 8 or 16-bit quantity would
be expected by libusb. I suspect this was done to avoid casting and try to side-step the sign-extension
problem with Java as much as possible, but in the end it leads to an API that's inconsistent, that doesn't
express the ranges it can use well (it's an int but in reality only 16 bits for libusb and so on). It also
fails at basic operations in ways you'd not expect because of the above inconsistencies:
if (deviceDescriptor.bDeviceClass() == LibUsb.CLASS_VENDOR_SPEC) { do something; }
will never work, since the constant is an int and the returned short from bDeviceClass() will be cast up to an
int too, but CLASS_VENDOR_SPEC is 0xFF (MSB=1), it will be sign-extended and the comparison will fail always.
There are two solutions I can see here:
A) consistently use int/long everywhere, both input and output. Everything becomes simpler to write, with less
casts, BUT javax.usb compatibility is gone for the Descriptors and it becomes even unclearer what those values
effectively mean and what range they support.
B) restrict the API to use the proper integer sizes as the C API, ie. 8-bit quantities (char, uint8_t, int8_t)
are represented by jbyte, 16-bit by jshort, and all the constants get properly sized too. With this approach
the sizes and ranges are much clearer, comparisons like the above work out-of-the-box, javax.usb compatibility
is maintained for the Descriptors. The downside is more casting and the occasional need to mask off returned
values to kill the sign-extension when you want to use those values as integers (like when printing stuff in
decimal notation).
I chose to try out the approach from B) for now, this commit implements it as explained above.
It requires minimal changes to the javax.usb implementation, just two casts to byte in AbstractDevice.java.