UJJAL DUTTA
UJJAL DUTTA

Reputation: 113

"RuntimeError : Memory access out of bounds" when trying to save a string in Wasm heap and then saving the pointer to the string in a struct

I am trying to save a String in Wasm heap as Uint8Array after encoding and saving the pointer to the string and the length in a Struct so that i can access the string later. Below is My code in rust

#[wasm_bindgen]
pub struct CustomString{
    ptr : *const u8,
    len: usize
}

#[wasm_bindgen]
impl CustomString{
    pub fn create(len:usize) -> Self{
        let mut d = Vec::with_capacity(len);
        log!("{}",len);

        CustomString{
            ptr: d.as_ptr(),
            len
        }
    }

    pub fn as_ptr(&self) -> *const u8{
        self.ptr
    }

    pub fn print_string(&self){
        let js = unsafe { std::slice::from_raw_parts(self.ptr, self.len) };
        let js = unsafe { std::str::from_utf8_unchecked(js) };
        log!("{}",js) 
    }
}

My JS side code is as follows :

function myTest (){
    debugger;
    const { memory } = wasm;
    let encoder = new TextEncoder();
    let mystring = "Ujjal";
    let encodedString = encoder.encode(mystring);

    let length = encodedString.length;
    console.log(length)

    let cs = CustomString.create(length);

    let ptr = cs.as_ptr();

    const asBytes = new Uint8Array(memory.buffer, ptr, length);

    asBytes.set(encodedString);

    return cs;

}

let cs = myTest();


function decode(cs){

    cs.print_string();
}

decode(cs);

Ideally it should print the given string but it shows some unrelated junk value. Couldn't figure what is going wrong as I am pretty new to rust and wasm.

This is the console message I am getting when i run this and sometime Memory error. Console log

Upvotes: 0

Views: 9315

Answers (2)

UJJAL DUTTA
UJJAL DUTTA

Reputation: 113

So finally I could figure it out. Below is my solution.

Rust Code:

pub struct CustomString{
    ptr : *mut u8,
    len: usize
}

#[wasm_bindgen]
impl CustomString{
    pub fn create(len:usize) -> Self{
        let mut d = String::with_capacity(len);
        // log!("{}",len);
        let ptr = d.as_mut_ptr();
        std::mem::forget(d);
        CustomString{
            ptr,
            len
        }

    }

    pub fn as_ptr(&self) -> *mut u8{
        self.ptr
    }

    pub fn as_string(&self)-> String{
        let m = unsafe { String::from_raw_parts(self.ptr, self.len, self.len) };
        m
    }

    pub fn print_string(&self){
        let m = unsafe { String::from_raw_parts(self.ptr, self.len, self.len) };
        log!("{}",m) 
    }
}

JS Code :

function myCS(string){
    const { memory } = wasm;
    let encoder = new TextEncoder();
    let encodedString = encoder.encode(string);

    let length = encodedString.length;

    let cs = CustomString.create(length);

    let ptr = cs.as_ptr();

    const asBytes = new Uint8Array(memory.buffer, ptr, length);

    asBytes.set(encodedString);

    return cs;

}

One problem with this solution is that it is not very performant. I tested it with 500000 random length strings. It takes about 4000+ ms .

Is there a better way to do this keeping performance in mind.

Upvotes: 0

draganrakita
draganrakita

Reputation: 198

In create you are creating vec d and taking its pointer, but after function ends vec d is freed and your pointer points to invalid memory.

Upvotes: 3

Related Questions