Class: Wasmtime::Memory

Inherits:
Object
  • Object
show all
Defined in:
ext/src/ruby_api/memory.rs

Overview

Represents a WebAssembly memory.

Defined Under Namespace

Classes: UnsafeSlice

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.new(store, min_size:, max_size: nil) ⇒ Object

Parameters:

  • store (Store)
  • min_size (Integer)

    The minimum memory pages.

  • max_size (Integer, nil) (defaults to: nil)

    The maximum memory pages.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'ext/src/ruby_api/memory.rs', line 87

pub fn new(args: &[Value]) -> Result<Self, Error> {
    let args = scan_args::scan_args::<(Obj<Store>,), (), (), (), _, ()>(args)?;
    let kw = scan_args::get_kwargs::<_, (u32,), (Option<u32>,), ()>(
        args.keywords,
        &[*MIN_SIZE],
        &[*MAX_SIZE],
    )?;
    let (store,) = args.required;
    let (min,) = kw.required;
    let (max,) = kw.optional;

    let memtype = wasmtime::MemoryType::new(min, max);

    let inner = MemoryImpl::new(store.context_mut(), memtype).map_err(|e| error!("{}", e))?;
    let memsize = inner.data_size(store.context_mut());

    Ok(Self {
        store: store.into(),
        inner: ManuallyTracked::wrap(inner, memsize),
    })
}

Instance Method Details

#data_sizeInteger

Returns The number of bytes of the memory.

Returns:

  • (Integer)

    The number of bytes of the memory.



433
434
435
# File 'ext/src/ruby_api/memory.rs', line 433

pub fn data_size(&self) -> Result<usize, Error> {
    Ok(self.get_wasmtime_memory().data_size(self.store.context()?))
}

#grow(delta) ⇒ Integer

Grows a memory by delta pages. Raises if the memory grows beyond its limit.

Parameters:

  • delta (Integer)

    The number of pages to grow by.

Returns:

  • (Integer)

    The number of pages the memory had before being resized.



413
414
415
416
417
418
419
420
421
422
423
# File 'ext/src/ruby_api/memory.rs', line 413

pub fn grow(&self, delta: usize) -> Result<u64, Error> {
    let ret = self
        .get_wasmtime_memory()
        .grow(self.store.context_mut()?, delta as _)
        .map_err(|e| error!("{}", e));

    self.inner
        .increase_memory_usage(delta * (WASM_PAGE_SIZE as usize));

    ret
}

#max_sizeInteger?

Returns The maximum number of memory pages.

Returns:

  • (Integer, nil)

    The maximum number of memory pages.



129
130
131
132
133
134
# File 'ext/src/ruby_api/memory.rs', line 129

pub fn max_size(&self) -> Result<Option<u64>, Error> {
    Ok(self
        .get_wasmtime_memory()
        .ty(self.store.context()?)
        .maximum())
}

#min_sizeInteger

Returns The minimum number of memory pages.

Returns:

  • (Integer)

    The minimum number of memory pages.



120
121
122
123
124
125
# File 'ext/src/ruby_api/memory.rs', line 120

pub fn min_size(&self) -> Result<u64, Error> {
    Ok(self
        .get_wasmtime_memory()
        .ty(self.store.context()?)
        .minimum())
}

#read(offset, size) ⇒ String

Read size bytes starting at offset. Result is a ASCII-8BIT encoded string.

Parameters:

  • offset (Integer)
  • size (Integer)

Returns:

  • (String)

    Binary String of the memory.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'ext/src/ruby_api/memory.rs', line 143

pub fn read(
    ruby: &Ruby,
    rb_self: Obj<Self>,
    offset: usize,
    size: usize,
) -> Result<RString, Error> {
    rb_self
        .get_wasmtime_memory()
        .data(rb_self.store.context()?)
        .get(offset..)
        .and_then(|s| s.get(..size))
        .map(|s| ruby.str_from_slice(s))
        .ok_or_else(|| error!("out of bounds memory access"))
}

#read_cstring(offset) ⇒ String

Read a NUL-terminated C string starting at offset as an ASCII-8BIT (binary) String.

Parameters:

  • offset (Integer)

Returns:

  • (String)


362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'ext/src/ruby_api/memory.rs', line 362

pub fn read_cstring(ruby: &Ruby, rb_self: Obj<Self>, offset: usize) -> Result<RString, Error> {
    let context = rb_self.store.context()?;
    let data = rb_self.get_wasmtime_memory().data(context);

    let bytes: &[u8] = match data.get(offset..) {
        Some(slice) => {
            let end = slice.iter().position(|&b| b == 0).unwrap_or(slice.len());
            &slice[..end]
        }
        None => &[],
    };

    Ok(ruby.str_from_slice(bytes))
}

#read_f32(offset) ⇒ Float

Read a little-endian f32 starting at offset.

Parameters:

  • offset (Integer)

Returns:

  • (Float)


275
276
277
# File 'ext/src/ruby_api/memory.rs', line 275

pub fn read_f32(&self, offset: usize) -> Result<f32, Error> {
    Ok(f32::from_le_bytes(self.read_fixed::<4>(offset)?))
}

#read_f64(offset) ⇒ Float

Read a little-endian f64 starting at offset.

Parameters:

  • offset (Integer)

Returns:

  • (Float)


285
286
287
# File 'ext/src/ruby_api/memory.rs', line 285

pub fn read_f64(&self, offset: usize) -> Result<f64, Error> {
    Ok(f64::from_le_bytes(self.read_fixed::<8>(offset)?))
}

#read_i32(offset) ⇒ Integer

Read a little-endian i32 starting at offset.

Parameters:

  • offset (Integer)

Returns:

  • (Integer)


235
236
237
# File 'ext/src/ruby_api/memory.rs', line 235

pub fn read_i32(&self, offset: usize) -> Result<i32, Error> {
    Ok(i32::from_le_bytes(self.read_fixed::<4>(offset)?))
}

#read_i64(offset) ⇒ Integer

Read a little-endian i64 starting at offset.

Parameters:

  • offset (Integer)

Returns:

  • (Integer)


255
256
257
# File 'ext/src/ruby_api/memory.rs', line 255

pub fn read_i64(&self, offset: usize) -> Result<i64, Error> {
    Ok(i64::from_le_bytes(self.read_fixed::<8>(offset)?))
}

#read_u32(offset) ⇒ Integer

Read a little-endian u32 starting at offset.

Parameters:

  • offset (Integer)

Returns:

  • (Integer)


245
246
247
# File 'ext/src/ruby_api/memory.rs', line 245

pub fn read_u32(&self, offset: usize) -> Result<u32, Error> {
    Ok(u32::from_le_bytes(self.read_fixed::<4>(offset)?))
}

#read_u64(offset) ⇒ Integer

Read a little-endian u64 starting at offset.

Parameters:

  • offset (Integer)

Returns:

  • (Integer)


265
266
267
# File 'ext/src/ruby_api/memory.rs', line 265

pub fn read_u64(&self, offset: usize) -> Result<u64, Error> {
    Ok(u64::from_le_bytes(self.read_fixed::<8>(offset)?))
}

#read_unsafe_slice(offset, size) ⇒ Wasmtime::Memory::UnsafeSlice

Read size bytes starting at offset into an UnsafeSlice. This provides a way to read a slice of memory without copying the underlying data.

The returned UnsafeSlice lazily reads the underlying memory, meaning that the actual pointer to the string buffer is not materialzed until Wasmtime::Memory::UnsafeSlice#to_str is called.

SAFETY: Resizing the memory (as with #grow) will invalidate the UnsafeSlice, and future attempts to read the slice will raise an error. However, it is not possible to invalidate the Ruby String object after calling Wasmtime::Memory::UnsafeSlice#to_str. As such, the caller must ensure that the Wasmtime Wasmtime::Memory is not resized while holding the Ruby string. Failing to do so could result in the String buffer pointing to invalid memory.

In general, you should prefer using #read or #read_utf8 over this method unless you know what you're doing.

Parameters:

  • offset (Integer)
  • size (Integer)

Returns:



205
206
207
208
209
210
211
212
# File 'ext/src/ruby_api/memory.rs', line 205

pub fn read_unsafe_slice(
    ruby: &Ruby,
    rb_self: Obj<Self>,
    offset: usize,
    size: usize,
) -> Result<Obj<UnsafeSlice<'a>>, Error> {
    Ok(ruby.obj_wrap(UnsafeSlice::new(rb_self, offset..(offset + size))?))
}

#read_utf8(offset, size) ⇒ String

Read size bytes starting at offset. Result is a UTF-8 encoded string.

Parameters:

  • offset (Integer)
  • size (Integer)

Returns:

  • (String)

    UTF-8 String of the memory.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'ext/src/ruby_api/memory.rs', line 165

pub fn read_utf8(
    ruby: &Ruby,
    rb_self: Obj<Self>,
    offset: usize,
    size: usize,
) -> Result<RString, Error> {
    rb_self
        .get_wasmtime_memory()
        .data(rb_self.store.context()?)
        .get(offset..)
        .and_then(|s| s.get(..size))
        .ok_or_else(|| error!("out of bounds memory access"))
        .and_then(|s| std::str::from_utf8(s).map_err(|e| error!("{}", e)))
        .map(|s| ruby.str_new(s))
}

#sizeInteger

Returns The number of pages of the memory.

Returns:

  • (Integer)

    The number of pages of the memory.



427
428
429
# File 'ext/src/ruby_api/memory.rs', line 427

pub fn size(&self) -> Result<u64, Error> {
    Ok(self.get_wasmtime_memory().size(self.store.context()?))
}

#write(offset, value) ⇒ void

This method returns an undefined value.

Write value starting at offset.

Parameters:

  • offset (Integer)
  • value (String)


221
222
223
224
225
226
227
# File 'ext/src/ruby_api/memory.rs', line 221

pub fn write(&self, offset: usize, value: RString) -> Result<(), Error> {
    let slice = unsafe { value.as_slice() };

    self.get_wasmtime_memory()
        .write(self.store.context_mut()?, offset, slice)
        .map_err(|e| error!("{}", e))
}

#write_cstring(offset, value) ⇒ void

This method returns an undefined value.

Write +value+'s bytes followed by a NUL terminator at offset.

Parameters:

  • offset (Integer)
  • value (String)


384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'ext/src/ruby_api/memory.rs', line 384

pub fn write_cstring(&self, offset: usize, value: RString) -> Result<(), Error> {
    let slice = unsafe { value.as_slice() };
    if slice.contains(&0) {
        return Err(Error::new(
            Ruby::get_with(value).exception_arg_error(),
            "string contains null byte",
        ));
    }
    let len = slice.len();
    let mut context = self.store.context_mut()?;
    let dst = self
        .get_wasmtime_memory()
        .data_mut(&mut context)
        .get_mut(offset..)
        .and_then(|s| s.get_mut(..len + 1))
        .ok_or_else(|| error!("out of bounds memory access"))?;

    dst[..len].copy_from_slice(slice);
    dst[len] = 0;
    Ok(())
}

#write_f32(offset, value) ⇒ void

This method returns an undefined value.

Write a little-endian f32 starting at offset.

Parameters:

  • offset (Integer)
  • value (Float)


340
341
342
# File 'ext/src/ruby_api/memory.rs', line 340

pub fn write_f32(&self, offset: usize, value: f32) -> Result<(), Error> {
    self.write_fixed(offset, value.to_le_bytes())
}

#write_f64(offset, value) ⇒ void

This method returns an undefined value.

Write a little-endian f64 starting at offset.

Parameters:

  • offset (Integer)
  • value (Float)


351
352
353
# File 'ext/src/ruby_api/memory.rs', line 351

pub fn write_f64(&self, offset: usize, value: f64) -> Result<(), Error> {
    self.write_fixed(offset, value.to_le_bytes())
}

#write_i32(offset, value) ⇒ void

This method returns an undefined value.

Write a little-endian i32 starting at offset.

Parameters:

  • offset (Integer)
  • value (Integer)


296
297
298
# File 'ext/src/ruby_api/memory.rs', line 296

pub fn write_i32(&self, offset: usize, value: i32) -> Result<(), Error> {
    self.write_fixed(offset, value.to_le_bytes())
}

#write_i64(offset, value) ⇒ void

This method returns an undefined value.

Write a little-endian i64 starting at offset.

Parameters:

  • offset (Integer)
  • value (Integer)


318
319
320
# File 'ext/src/ruby_api/memory.rs', line 318

pub fn write_i64(&self, offset: usize, value: i64) -> Result<(), Error> {
    self.write_fixed(offset, value.to_le_bytes())
}

#write_u32(offset, value) ⇒ void

This method returns an undefined value.

Write a little-endian u32 starting at offset.

Parameters:

  • offset (Integer)
  • value (Integer)


307
308
309
# File 'ext/src/ruby_api/memory.rs', line 307

pub fn write_u32(&self, offset: usize, value: u32) -> Result<(), Error> {
    self.write_fixed(offset, value.to_le_bytes())
}

#write_u64(offset, value) ⇒ void

This method returns an undefined value.

Write a little-endian u64 starting at offset.

Parameters:

  • offset (Integer)
  • value (Integer)


329
330
331
# File 'ext/src/ruby_api/memory.rs', line 329

pub fn write_u64(&self, offset: usize, value: u64) -> Result<(), Error> {
    self.write_fixed(offset, value.to_le_bytes())
}