flapigen-rs icon indicating copy to clipboard operation
flapigen-rs copied to clipboard

[u8] slices to Java?

Open rubdos opened this issue 5 years ago • 10 comments

I see that [i8] maps to a JavaByteArray, but [u8] slices don't seem implemented. Is there a way to implement this?

rubdos avatar Oct 12 '18 18:10 rubdos

I'm sorry to be late with the reply. For some reason, github did not show activity in my personal projects on my personal page. And I just got to my post today.

I suppose this is possible, but will require some additional CPU cycles. Because of Java has no primitive unsigned types. So all u8 types I have to convert to i16(Rust) = short (Java). That is why rust_swig have no such feature by default.

Dushistov avatar Oct 18 '18 10:10 Dushistov

I actually mean where you go from [u8] (byte slice) to JavaByteArray, but I guess there are two interpretations here. No worries with the delay, no urgency here.

rubdos avatar Oct 18 '18 12:10 rubdos

I actually mean where you go from [u8] (byte slice) to JavaByteArray

rust_swig was made extensible, because of always there is specific for project types mapping. So you can create you own SwigInto/SwigFrom implementation to convert to/from [u8] slice, and use rust_swig::Generator::merge_type_map to extend knowledge of rust_swig about your custom type conversation rules.

And after merging your custom type rules with standard one rust_swig converts all your method with -> &[u8] to any Java type that you want.

As example you can look for macroslib/src/java_jni/jni-include.rs

impl<'a> SwigInto<jbyteArray> for &'a [i8] {
    fn swig_into(self, env: *mut JNIEnv) -> jbyteArray {
        JavaByteArray::from_slice_to_raw(self, env)
    }
}

and it is not only code that rust_swig insert into your project if you return [i8] slice, but this is also instruction to rust_swig how to handle such type as type of return value.

Dushistov avatar Oct 18 '18 13:10 Dushistov

I have the same problem. So I expand the type rules using following file:

mod swig_foreign_types_map {}

impl SwigDeref for JavaByteArray {
    type Target = [u8];
    fn swig_deref(&self) -> &Self::Target {
        self.to_slice()
    }
}

impl SwigFrom<jbyteArray> for JavaByteArray {
    fn swig_from(x: jbyteArray, env: *mut JNIEnv) -> Self {
        JavaByteArray::new(env, x)
    }
}

impl<'a> SwigInto<jbyteArray> for &'a [u8] {
    fn swig_into(self, env: *mut JNIEnv) -> jbyteArray {
        JavaByteArray::from_slice_to_raw(self, env)
    }
}

Is it okay to reuse JavaByteArray both for i8 and u8?

VMatrix1900 avatar Feb 24 '19 15:02 VMatrix1900

@VMatrix1900

Is it okay to reuse JavaByteArray both for i8 and u8?

It depend on your java code, if it is ok for you to pass 255 to u8 in Rust, and get -1 in Java then ok, in other case better to use short [] Java type for u8 array, or use some opaque java class to convert internal byte from byte [] to short

Dushistov avatar Feb 24 '19 17:02 Dushistov

I don't think the conversion via short is necessary - I've always done reinterpret_cast<unsigned char>(signed_char) and vice-versa with no problems. AFAIK so long as we don't edit the bits (e.g. by incrementing it), the "byte" value we care about stays the same.

N.B. I've never coded for a platform where sizeof(char) is not 1. I have no experience to know if the same thing is safe in that case.

What's the easiest way to simply do a cast like that so we can get [u8]/Vec<u8> support?

arrtchiu avatar Aug 29 '19 09:08 arrtchiu

I've always done reinterpret_cast(signed_char) and vice-versa with no problems

Not sure what do you mean, this is issue related to Java only, for C++ u8 support is just fine. There are no unsigned integer types in Java. So on Java side this data will be useless, what point to return integer that impossible to use?

Dushistov avatar Aug 29 '19 09:08 Dushistov

In my case I care about it as a vector of bytes as opposed to a vector of integers. I don’t want the compiler or CPU to care about signedness. It’s just 8 bits of info. That’s sorta the essence of byte arrays in Java and why C++17 got std::byte (Though the implementation of the latter leaves a lot to be desired, but that’s a completely other topic!)

I’m new to Rust, but everywhere seems to use u8 for bytes like this. So in my case where I want to move an arbitrary vector of bytes (eg PNG representation of an image), it feels most natural for me to be able to expose it as Vec<u8> because that’s what Rust code is using under the hood. The fact that jByteArray’a C type is a signed char is ideally invisible.

Or maybe I’m missing something totally here too!

arrtchiu avatar Aug 29 '19 10:08 arrtchiu

So in my case where I want to move an arbitrary vector of bytes

This issue is about common case, type conversations rule that possible to use for every one. The specific rule should be part of your project, not part of rust_swig, via your custom "type map" https://docs.rs/rust_swig/0.4.0/rust_swig/struct.Generator.html#method.merge_type_map , or you can defined type conversation rules just in .rs.in file.

You can a bunch of options for this, for example define rule that std::mem::transmutate VectoVec` for output type, if it is completely fine for you use case.

Dushistov avatar Aug 29 '19 10:08 Dushistov

Hey,

I haven't managed to figure out how to do it. I tried using @VMatrix1900's solution but I'm still getting errors:

error: Do not know conversation from Java type to such rust type '& [ u8 ]'
    fn Collection::set_content(&mut self, content: &[u8]) -> Result<()>;

Update: that's what I ended up using (which works):


foreign_typemap!(
    ($p:r_type) Vec<u8> => jbyteArray {
        let slice = &($p)[..];
        let slice = unsafe { std::mem::transmute::<&[u8], &[i8]>(slice) };
        let raw = JavaByteArray::from_slice_to_raw(slice, env);
        $out = raw;
    };
    ($p:f_type) => "jbyteArray";
);

foreign_typemap!(
    ($p:r_type) &'a [u8] => jbyteArray {
        let slice = unsafe { std::mem::transmute::<&[u8], &[i8]>($p) };
        let raw = JavaByteArray::from_slice_to_raw(slice, env);
        $out = raw;
    };
    ($p:f_type) => "jbyteArray";
    ($p:r_type) &'a [u8] <= jbyteArray {
        let arr = JavaByteArray::new(env, $p);
        let slice = arr.to_slice();
        let slice = unsafe { std::mem::transmute::<&[i8], &[u8]>(slice) };
        $out = slice;
    };
    ($p:f_type) <= "jbyteArray";
);

tasn avatar Jul 27 '20 05:07 tasn