jnr / jnr-ffi

Java Abstracted Foreign Function Layer
Other
1.25k stars 156 forks source link

Add support for native `uint64` (`unsigned long long`) using Java `BigInteger` #289

Open basshelal opened 2 years ago

basshelal commented 2 years ago

Currently the largest native unsigned type we can properly support is uint32 (unsigned int).

If a value with an unsigned native type ends up exceeding Java's corresponding same size type's limits then there is overflow, for example:

public byte ret_uint8(byte b);

If this function returns a value larger than Byte.MAX_VALUE (127) (which it can because uint8's limit is higher) then it will be returned in Java as a negative number (it overflowed).

This is expected behavior and is solved by using larger size Java types with JNR-FFI annotations that tell which unsigned type is expected so we fix the above problem by this mapping:

public @u_int8_t short ret_uint8(@u_int8_t short b);

This behaves correctly, now when we have a value that would fit in native uint8 but not in Java byte it won't matter, because we use short and it would now fit on short's positive spectrum, a value such as 220 for example fits as shown below:

Byte.MIN_VALUE ... uint8_min ... Byte.MAX_VALUE ... 220 ... uint8_max ... Short.MAX_VALUE

This works well until the largest native unsigned type uint64 (unsigned long long) because there is no larger primitive number type in Java than long which is not larger in size than uint64 (both are 64 bits) and thus will not be able to fit those unsigned numbers that are larger than Long.MAX_VALUE.

I propose we add a way to remedy this by allowing users to map uint64 types using Java's BigInteger type with the correct annotation as so:

public @u_int64_t BigInteger ret_uint64(@u_int64_t BigInteger b);

I chose BigInteger because it is the Java standard way of using numbers that don't fit into long.

I will try to implement this myself and see what can be done about it.