Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating Uint8Array from Vec<u8> using sub_array() gives diffferent values compared to view() #1619

Closed
nrayamajhee opened this issue Jun 24, 2019 · 6 comments
Labels

Comments

@nrayamajhee
Copy link

nrayamajhee commented Jun 24, 2019

I am working on this WebGL example from: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL
in Rust.

I tried creating a Uint8Array similarly to how its done for f32 at: https://rustwasm.github.io/docs/wasm-bindgen/examples/webgl.html

But the result produced by Uint8Array(..).sub_array(..) produces different output than produced by the unsafe view(..) method:

let data = vec![255,0,255,255];
let memory_buffer = wasm_bindgen::memory()
	.dyn_into::<WebAssembly::Memory>()?
	.buffer();
let buffer_loc = data.as_ptr() as u32;
let array = Uint8Array::new(&memory_buffer)
	.subarray(buffer_loc, buffer_loc + data.len() as u32)
log!("{:?}", array.to_string()); //displays "255,0,0,0"
let array = unsafe {
       js_sys::Uint8Array::view(&data[..])
};
log!("{:?}", array.to_string()); //displays "255,0,255,255"
@nrayamajhee
Copy link
Author

Never mind, It seems to work if I do it on a separate empty project. Must be something wrong with the rest of my code!

@nrayamajhee
Copy link
Author

Hmm.. This is weird. I just tried commenting the bottom block let array = unsafe{... from the above code and the problem still exists. Here's my entire code. I just spun up a new wasm-pack project and edited the lib.rs file:

use wasm_bindgen::prelude::*;

macro_rules! log {
    ( $( $t:tt )* ) => {
        web_sys::console::log_1(&format!( $( $t )* ).into());
    }
}

#[wasm_bindgen(start)]
pub fn main() {
		use wasm_bindgen::JsCast;
		let data = vec![255,0,255,255];
		let memory_buffer = wasm_bindgen::memory()
			.dyn_into::<js_sys::WebAssembly::Memory>().unwrap()
			.buffer();
		let buffer_loc = data.as_ptr() as u32;
		let array = js_sys::Uint8Array::new(&memory_buffer)
			.subarray(buffer_loc, buffer_loc + data.len() as u32);
		log!("AN ARRAY: {:?}", array.to_string()); //displays "255,0,0,0"
//		let another_array = unsafe {
//			   js_sys::Uint8Array::view(&data[..])
//		};
//		log!("ANOTHER ARRAY: {:?}", another_array.to_string()); //displays "255,0,255,255"
}

Screenshot from 2019-06-24 01-31-11
If I uncomment, magically both output become the same.
Screenshot from 2019-06-24 01-30-40

@nrayamajhee nrayamajhee reopened this Jun 24, 2019
@nrayamajhee
Copy link
Author

Turns out I should've been using a slice instead of vec and should have explicitly mentioned the type of vec.
Calling .as_ptr() on a vec without specified type was the culprit.

        let vec: Vec<u8> = vec![255,0,255,255];
        let data = &vec[..];

This fixed the issue. You might be wondering why I didn't use a fixed sized array instead. But the reason I got into this confusion initially was because I was aiming to write a generic function for converting a vec into some generic JsArray type.

Something like fn js_array_from_vec<T>(data: &Vec<T>)->JsArray

where JsArray would be a enum:

pub enum JsArray {
	F32(Float32Array),
	U16(Uint16Array),
	U8(Uint8Array),
}

And I would then implement the Into trait to convert each individual into respective js array type. But doing so resulted in incorrect values. So I am now resorting to writing three individual functions instead such as: js_u8_from_slice(..), js_f32_from_slice, etc.

This issue can be closed. But any suggestion on how to successfully write such a generic function without messing up the u8 array conversion or using the unsafe view(..) would be highly appreciated.

@nrayamajhee
Copy link
Author

Also guessing from #1615,
using subarray instead of view results in copying the data so I might just stick to view since it simplifies my code as I no longer have to write custom code to convert from specific slice to specific typed js array.

@Pauan
Copy link
Contributor

Pauan commented Jun 24, 2019

Be very careful when using view, it is an unsafe function for a reason: you have to immediately make a copy (without doing any Rust allocation) in order to avoid undefined behavior.

Is there any particular reason the following doesn't work for you?

impl<'a> From<&'a [u8]> for JsArray {
    fn from(slice: &[u8]) -> Self {
        JsArray::U8(unsafe { Uint8Array::new(&Uint8Array::view(slice)) })
    }
}

impl<'a> From<&'a [f32]> for JsArray {
    fn from(slice: &[f32]) -> Self {
        JsArray::F32(unsafe { Float32Array::new(&Float32Array::view(slice)) })
    }
}

impl<'a> From<&'a [u16]> for JsArray {
    fn from(slice: &[u16]) -> Self {
        JsArray::U16(unsafe { Uint16Array::new(&Uint16Array::view(slice)) })
    }
}

(There's no need to do dangerous things like using as_ptr, the view function does all that for you)

Now, rather than using js_array_from_vec(&foo) you can instead use JsArray::from(foo.as_slice())

Or if you really want a separate function, that's not hard to do:

fn js_array_from_vec<T>(data: T) -> JsArray where T: Into<JsArray> {
    data.into()
}

And now you can use js_array_from_vec(data.as_slice())

P.S. It's always better to use &[T] rather than &Vec<T> for the arguments of a function.

@nrayamajhee
Copy link
Author

Thanks! This is exactly what I wanted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants