Memory units (#42)

* Initial implementation

* Not use grow as it is makes debug builds very slow

* Use Pages::BYTE_SIZE for LINEAR_MEMORY_PAGE_SIZE

* Tidy docs.

* Use memory_units from git.
This commit is contained in:
Sergey Pepyakin 2018-02-09 16:45:21 +03:00 committed by GitHub
parent a3ad4b0e49
commit 483736b1bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 155 additions and 90 deletions

View File

@ -11,6 +11,7 @@ exclude = [ "res/*", "spec/*" ]
[dependencies] [dependencies]
parity-wasm = "0.23" parity-wasm = "0.23"
byteorder = "1.0" byteorder = "1.0"
memory_units = { git = "https://github.com/pepyakin/memory_units.git", rev = "e09093e" }
[dev-dependencies] [dev-dependencies]
wabt = "0.1.2" wabt = "0.1.2"

View File

@ -16,6 +16,7 @@ use wasmi::{
Module, Signature, MemoryDescriptor, Trap, Module, Signature, MemoryDescriptor, Trap,
TableDescriptor, GlobalDescriptor, FuncInstance, RuntimeArgs, TableDescriptor, GlobalDescriptor, FuncInstance, RuntimeArgs,
}; };
use wasmi::memory_units::Pages;
#[derive(Debug)] #[derive(Debug)]
enum Error { enum Error {
@ -43,7 +44,7 @@ impl SpecModule {
fn new() -> Self { fn new() -> Self {
SpecModule { SpecModule {
table: TableInstance::alloc(10, Some(20)).unwrap(), table: TableInstance::alloc(10, Some(20)).unwrap(),
memory: MemoryInstance::alloc(1, Some(2)).unwrap(), memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false), global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
global_i64: GlobalInstance::alloc(RuntimeValue::I64(666), false), global_i64: GlobalInstance::alloc(RuntimeValue::I64(666), false),
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0), false), global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0), false),

View File

@ -100,6 +100,7 @@
extern crate wabt; extern crate wabt;
extern crate parity_wasm; extern crate parity_wasm;
extern crate byteorder; extern crate byteorder;
extern crate memory_units as memory_units_crate;
#[cfg(all(not(feature = "32bit_opt_in"), target_pointer_width = "32"))] #[cfg(all(not(feature = "32bit_opt_in"), target_pointer_width = "32"))]
compile_error! {"32-bit targets are not supported at the moment. compile_error! {"32-bit targets are not supported at the moment.
@ -360,6 +361,12 @@ pub use self::global::{GlobalInstance, GlobalRef};
pub use self::func::{FuncInstance, FuncRef}; pub use self::func::{FuncInstance, FuncRef};
pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor}; pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor};
/// WebAssembly-specific sizes and units.
pub mod memory_units {
pub use memory_units_crate::wasm32::*;
pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of};
}
/// Deserialized module prepared for instantiation. /// Deserialized module prepared for instantiation.
pub struct Module { pub struct Module {
labels: HashMap<usize, HashMap<usize, usize>>, labels: HashMap<usize, HashMap<usize, usize>>,

View File

@ -6,17 +6,17 @@ use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use parity_wasm::elements::ResizableLimits; use parity_wasm::elements::ResizableLimits;
use Error; use Error;
use module::check_limits; use memory_units::{RoundUpTo, Pages, Bytes};
/// Size of a page of [linear memory][`MemoryInstance`] - 64KiB. /// Size of a page of [linear memory][`MemoryInstance`] - 64KiB.
/// ///
/// The size of a memory is always a integer multiple of a page size. /// The size of a memory is always a integer multiple of a page size.
/// ///
/// [`MemoryInstance`]: struct.MemoryInstance.html /// [`MemoryInstance`]: struct.MemoryInstance.html
pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536; pub const LINEAR_MEMORY_PAGE_SIZE: Bytes = Bytes(65536);
/// Maximal number of pages. /// Maximal number of pages.
const LINEAR_MEMORY_MAX_PAGES: u32 = 65536; const LINEAR_MEMORY_MAX_PAGES: Pages = Pages(65536);
/// Reference to a memory (See [`MemoryInstance`] for details). /// Reference to a memory (See [`MemoryInstance`] for details).
/// ///
@ -52,8 +52,8 @@ pub struct MemoryInstance {
limits: ResizableLimits, limits: ResizableLimits,
/// Linear memory buffer. /// Linear memory buffer.
buffer: RefCell<Vec<u8>>, buffer: RefCell<Vec<u8>>,
/// Maximum buffer size. initial: Pages,
maximum_size: u32, maximum: Option<Pages>,
} }
impl fmt::Debug for MemoryInstance { impl fmt::Debug for MemoryInstance {
@ -61,7 +61,8 @@ impl fmt::Debug for MemoryInstance {
f.debug_struct("MemoryInstance") f.debug_struct("MemoryInstance")
.field("limits", &self.limits) .field("limits", &self.limits)
.field("buffer.len", &self.buffer.borrow().len()) .field("buffer.len", &self.buffer.borrow().len())
.field("maximum_size", &self.maximum_size) .field("maximum", &self.maximum)
.field("initial", &self.initial)
.finish() .finish()
} }
} }
@ -92,12 +93,12 @@ impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref<Target=Vec<u8>>
impl MemoryInstance { impl MemoryInstance {
/// Allocate a memory instance. /// Allocate a memory instance.
/// ///
/// The memory allocated with initial number of pages specified by `initial_pages`. /// The memory allocated with initial number of pages specified by `initial`.
/// Minimal possible value for `initial_pages` is 0 and maximum possible is `65536`. /// Minimal possible value for `initial` is 0 and maximum possible is `65536`.
/// (Since maximum addressible memory is 2<sup>32</sup> = 4GiB = 65536 * [64KiB][`LINEAR_MEMORY_PAGE_SIZE`]). /// (Since maximum addressible memory is 2<sup>32</sup> = 4GiB = 65536 * [64KiB][`LINEAR_MEMORY_PAGE_SIZE`]).
/// ///
/// It is possible to limit maximum number of pages this memory instance can have by specifying /// It is possible to limit maximum number of pages this memory instance can have by specifying
/// `maximum_page`. If not specified, this memory instance would be able to allocate up to 4GiB. /// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB.
/// ///
/// Allocated memory is always zeroed. /// Allocated memory is always zeroed.
/// ///
@ -105,39 +106,28 @@ impl MemoryInstance {
/// ///
/// Returns `Err` if: /// Returns `Err` if:
/// ///
/// - `initial_pages` is greater than `maximum_pages` /// - `initial` is greater than `maximum`
/// - either `initial_pages` or `maximum_pages` is greater than `65536`. /// - either `initial` or `maximum` is greater than `65536`.
/// ///
/// [`LINEAR_MEMORY_PAGE_SIZE`]: constant.LINEAR_MEMORY_PAGE_SIZE.html /// [`LINEAR_MEMORY_PAGE_SIZE`]: constant.LINEAR_MEMORY_PAGE_SIZE.html
pub fn alloc(initial_pages: u32, maximum_pages: Option<u32>) -> Result<MemoryRef, Error> { pub fn alloc(initial: Pages, maximum: Option<Pages>) -> Result<MemoryRef, Error> {
let memory = MemoryInstance::new(ResizableLimits::new(initial_pages, maximum_pages))?; validate_memory(initial, maximum).map_err(Error::Memory)?;
let memory = MemoryInstance::new(initial, maximum);
Ok(MemoryRef(Rc::new(memory))) Ok(MemoryRef(Rc::new(memory)))
} }
/// Create new linear memory instance. /// Create new linear memory instance.
fn new(limits: ResizableLimits) -> Result<Self, Error> { fn new(initial: Pages, maximum: Option<Pages>) -> Self {
check_limits(&limits)?; let limits = ResizableLimits::new(initial.0 as u32, maximum.map(|p| p.0 as u32));
let initial_pages = limits.initial(); let initial_size: Bytes = initial.into();
let maximum_pages = limits.maximum().unwrap_or(LINEAR_MEMORY_MAX_PAGES); MemoryInstance {
if initial_pages > LINEAR_MEMORY_MAX_PAGES {
return Err(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)));
}
if maximum_pages > LINEAR_MEMORY_MAX_PAGES {
return Err(Error::Memory(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)));
}
let maximum_size = maximum_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE);
let initial_size = initial_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE);
let memory = MemoryInstance {
limits: limits, limits: limits,
buffer: RefCell::new(vec![0; initial_size as usize]), buffer: RefCell::new(vec![0; initial_size.0]),
maximum_size: maximum_size, initial: initial,
}; maximum: maximum,
}
Ok(memory)
} }
/// Return linear memory limits. /// Return linear memory limits.
@ -146,24 +136,47 @@ impl MemoryInstance {
} }
/// Returns number of pages this `MemoryInstance` was created with. /// Returns number of pages this `MemoryInstance` was created with.
pub fn initial_pages(&self) -> u32 { pub fn initial(&self) -> Pages {
self.limits.initial() self.initial
} }
/// Returns maximum amount of pages this `MemoryInstance` can grow to. /// Returns maximum amount of pages this `MemoryInstance` can grow to.
/// ///
/// Returns `None` if there is no limit set. /// Returns `None` if there is no limit set.
/// This means that memory can grow up to 4GiB. /// Maximum memory size cannot exceed `65536` pages or 4GiB.
pub fn maximum_pages(&self) -> Option<u32> { pub fn maximum(&self) -> Option<Pages> {
self.limits.maximum() self.maximum
} }
/// Return linear memory size (in pages). /// Returns current linear memory size.
pub fn size(&self) -> u32 { ///
self.buffer.borrow().len() as u32 / LINEAR_MEMORY_PAGE_SIZE /// Maximum memory size cannot exceed `65536` pages or 4GiB.
///
/// # Example
///
/// To convert number of pages to number of bytes you can use the following code:
///
/// ```rust
/// use wasmi::MemoryInstance;
/// use wasmi::memory_units::*;
///
/// let memory = MemoryInstance::alloc(Pages(1), None).unwrap();
/// let byte_size: Bytes = memory.current_size().into();
/// assert_eq!(
/// byte_size,
/// Bytes(65536),
/// );
/// ```
pub fn current_size(&self) -> Pages {
Bytes(self.buffer.borrow().len()).round_up_to()
} }
/// Copy data from memory at given offset. /// Copy data from memory at given offset.
///
/// This will allocate vector for you.
/// If you can provide a mutable slice you can use [`get_into`].
///
/// [`get_into`]: #method.get_into
pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> { pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> {
let buffer = self.buffer.borrow(); let buffer = self.buffer.borrow();
let region = self.checked_region(&buffer, offset as usize, size)?; let region = self.checked_region(&buffer, offset as usize, size)?;
@ -196,27 +209,42 @@ impl MemoryInstance {
} }
/// Increases the size of the linear memory by given number of pages. /// Increases the size of the linear memory by given number of pages.
/// Returns previous memory size (in pages) if succeeds. /// Returns previous memory size if succeeds.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns `Err` if attempted to allocate more memory than permited by the limit. /// Returns `Err` if attempted to allocate more memory than permited by the limit.
pub fn grow(&self, pages: u32) -> Result<u32, Error> { pub fn grow(&self, additional: Pages) -> Result<Pages, Error> {
let mut buffer = self.buffer.borrow_mut(); let size_before_grow: Pages = self.current_size();
let old_size = buffer.len() as u32; println!("grow({:?}) = {:?}", additional, size_before_grow);
match calculate_memory_size(old_size, pages, self.maximum_size) {
None => Err(Error::Memory( if additional == Pages(0) {
format!( return Ok(size_before_grow);
"Trying to grow memory by {} pages when already have {}",
pages,
old_size / LINEAR_MEMORY_PAGE_SIZE,
)
)),
Some(new_size) => {
buffer.resize(new_size as usize, 0);
Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
},
} }
if additional > Pages(65536) {
return Err(Error::Memory(format!(
"Trying to grow memory by more than 65536 pages"
)));
}
let new_size: Pages = size_before_grow + additional;
let maximum = self.maximum.unwrap_or(LINEAR_MEMORY_MAX_PAGES);
if new_size > maximum {
return Err(Error::Memory(format!(
"Trying to grow memory by {} pages when already have {}",
additional.0, size_before_grow.0,
)));
}
// Resize underlying buffer up to a new size filling newly allocated space with zeroes.
// This size is guaranteed to be larger than current size.
let new_buffer_length: Bytes = new_size.into();
{
let mut buffer = self.buffer.borrow_mut();
debug_assert!(new_buffer_length.0 > buffer.len());
buffer.resize(new_buffer_length.0, 0);
}
Ok(size_before_grow)
} }
fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error> fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error>
@ -313,21 +341,32 @@ impl MemoryInstance {
} }
} }
fn calculate_memory_size(old_size: u32, additional_pages: u32, maximum_size: u32) -> Option<u32> { pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), String> {
let size = additional_pages if initial > LINEAR_MEMORY_MAX_PAGES {
.saturating_mul(LINEAR_MEMORY_PAGE_SIZE); return Err(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
match size.checked_add(old_size) {
Some(size) if size <= maximum_size => Some(size),
_ => None,
} }
if let Some(maximum) = maximum {
if initial > maximum {
return Err(format!(
"maximum limit {} is less than minimum {}",
maximum.0,
initial.0,
));
}
if maximum > LINEAR_MEMORY_MAX_PAGES {
return Err(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
}
}
Ok(())
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{MemoryInstance, LINEAR_MEMORY_MAX_PAGES}; use super::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
use Error; use Error;
use parity_wasm::elements::ResizableLimits; use memory_units::Pages;
#[test] #[test]
fn alloc() { fn alloc() {
@ -338,16 +377,19 @@ mod tests {
(1, Some(1), true), (1, Some(1), true),
(0, Some(1), true), (0, Some(1), true),
(1, Some(0), false), (1, Some(0), false),
(0, Some(LINEAR_MEMORY_MAX_PAGES), true), (0, Some(65536), true),
(LINEAR_MEMORY_MAX_PAGES, Some(LINEAR_MEMORY_MAX_PAGES), true), (65536, Some(65536), true),
(LINEAR_MEMORY_MAX_PAGES, Some(0), false), (65536, Some(0), false),
(LINEAR_MEMORY_MAX_PAGES, None, true), (65536, None, true),
]; ];
for &(initial, maybe_max, expected_ok) in fixtures { for (index, &(initial, maybe_max, expected_ok)) in fixtures.iter().enumerate() {
let result = MemoryInstance::alloc(initial, maybe_max); let initial: Pages = Pages(initial);
let maximum: Option<Pages> = maybe_max.map(|m| Pages(m));
let result = MemoryInstance::alloc(initial, maximum);
if result.is_ok() != expected_ok { if result.is_ok() != expected_ok {
panic!( panic!(
"unexpected error, initial={}, max={:?}, expected={}, result={:?}", "unexpected error at {}, initial={:?}, max={:?}, expected={}, result={:?}",
index,
initial, initial,
maybe_max, maybe_max,
expected_ok, expected_ok,
@ -357,9 +399,14 @@ mod tests {
} }
} }
#[test]
fn ensure_page_size() {
use memory_units::ByteSize;
assert_eq!(LINEAR_MEMORY_PAGE_SIZE, Pages::byte_size());
}
fn create_memory(initial_content: &[u8]) -> MemoryInstance { fn create_memory(initial_content: &[u8]) -> MemoryInstance {
let mem = MemoryInstance::new(ResizableLimits::new(1, Some(1))) let mem = MemoryInstance::new(Pages(1), Some(Pages(1)));
.expect("MemoryInstance created successfuly");
mem.set(0, initial_content).expect("Successful initialize the memory"); mem.set(0, initial_content).expect("Successful initialize the memory");
mem mem
} }
@ -418,7 +465,7 @@ mod tests {
#[test] #[test]
fn get_into() { fn get_into() {
let mem = MemoryInstance::new(ResizableLimits::new(1, None)).expect("memory instance creation should not fail"); let mem = MemoryInstance::new(Pages(1), None);
mem.set(6, &[13, 17, 129]).expect("memory set should not fail"); mem.set(6, &[13, 17, 129]).expect("memory set should not fail");
let mut data = [0u8; 2]; let mut data = [0u8; 2];

View File

@ -14,6 +14,7 @@ use memory::MemoryRef;
use host::Externals; use host::Externals;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor}; use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
use memory_units::Pages;
/// Reference to a [`ModuleInstance`]. /// Reference to a [`ModuleInstance`].
/// ///
@ -325,10 +326,11 @@ impl ModuleInstance {
&[], &[],
) )
{ {
let memory = MemoryInstance::alloc( let initial: Pages = Pages(memory_type.limits().initial() as usize);
memory_type.limits().initial(), let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
memory_type.limits().maximum()
)?; let memory = MemoryInstance::alloc(initial, maximum)
.expect("Due to validation `initial` and `maximum` should be valid");
instance.push_memory(memory); instance.push_memory(memory);
} }

View File

@ -16,6 +16,7 @@ use host::Externals;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType};
use common::stack::StackWithLimit; use common::stack::StackWithLimit;
use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT}; use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
use memory_units::Pages;
/// Function interpreter. /// Function interpreter.
pub struct Interpreter<'a, E: Externals + 'a> { pub struct Interpreter<'a, E: Externals + 'a> {
@ -643,7 +644,7 @@ impl<'a, E: Externals> Interpreter<'a, E> {
let m = context.module() let m = context.module()
.memory_by_index(DEFAULT_MEMORY_INDEX) .memory_by_index(DEFAULT_MEMORY_INDEX)
.expect("Due to validation memory should exists"); .expect("Due to validation memory should exists");
let s = m.size(); let s = m.current_size().0;
context context
.value_stack_mut() .value_stack_mut()
.push(RuntimeValue::I32(s as i32))?; .push(RuntimeValue::I32(s as i32))?;
@ -657,9 +658,10 @@ impl<'a, E: Externals> Interpreter<'a, E> {
let m = context.module() let m = context.module()
.memory_by_index(DEFAULT_MEMORY_INDEX) .memory_by_index(DEFAULT_MEMORY_INDEX)
.expect("Due to validation memory should exists"); .expect("Due to validation memory should exists");
// Pushes -1 if allocation fails or previous memory size, if succeeds. let m = match m.grow(Pages(pages as usize)) {
let m = m.grow(pages) Ok(Pages(new_size)) => new_size as u32,
.unwrap_or(u32::MAX); Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error.
};
context context
.value_stack_mut() .value_stack_mut()
.push(RuntimeValue::I32(m as i32))?; .push(RuntimeValue::I32(m as i32))?;

View File

@ -4,6 +4,7 @@ use {
RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind,
}; };
use types::ValueType; use types::ValueType;
use memory_units::Pages;
use super::parse_wat; use super::parse_wat;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -36,7 +37,7 @@ struct TestHost {
impl TestHost { impl TestHost {
fn new() -> TestHost { fn new() -> TestHost {
TestHost { TestHost {
memory: Some(MemoryInstance::alloc(1, Some(1)).unwrap()), memory: Some(MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap()),
instance: None, instance: None,
} }
} }
@ -483,7 +484,7 @@ fn defer_providing_externals() {
// Create HostImportResolver with some initialized memory instance. // Create HostImportResolver with some initialized memory instance.
// This memory instance will be provided as 'mem' export. // This memory instance will be provided as 'mem' export.
let host_import_resolver = let host_import_resolver =
HostImportResolver { mem: MemoryInstance::alloc(1, Some(1)).unwrap() }; HostImportResolver { mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap() };
// Instantiate module with `host_import_resolver` as import resolver for "host" module. // Instantiate module with `host_import_resolver` as import resolver for "host" module.
let instance = ModuleInstance::new( let instance = ModuleInstance::new(

View File

@ -3,6 +3,7 @@ use {
MemoryRef, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, MemoryRef, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue,
TableInstance, TableRef, Module, GlobalDescriptor, TableDescriptor, MemoryDescriptor, TableInstance, TableRef, Module, GlobalDescriptor, TableDescriptor, MemoryDescriptor,
}; };
use memory_units::Pages;
use std::fs::File; use std::fs::File;
struct Env { struct Env {
@ -17,7 +18,7 @@ impl Env {
Env { Env {
table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false), table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false), memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
memory: MemoryInstance::alloc(256, None).unwrap(), memory: MemoryInstance::alloc(Pages(256), None).unwrap(),
table: TableInstance::alloc(64, None).unwrap(), table: TableInstance::alloc(64, None).unwrap(),
} }
} }

View File

@ -8,6 +8,7 @@ use parity_wasm::elements::{
use common::stack; use common::stack;
use self::context::ModuleContextBuilder; use self::context::ModuleContextBuilder;
use self::func::Validator; use self::func::Validator;
use memory_units::Pages;
mod context; mod context;
mod func; mod func;
@ -267,7 +268,7 @@ fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
if let Some(maximum) = limits.maximum() { if let Some(maximum) = limits.maximum() {
if limits.initial() > maximum { if limits.initial() > maximum {
return Err(Error(format!( return Err(Error(format!(
"maximum limit {} is lesser than minimum {}", "maximum limit {} is less than minimum {}",
maximum, maximum,
limits.initial() limits.initial()
))); )));
@ -277,7 +278,9 @@ fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
} }
fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> { fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> {
validate_limits(memory_type.limits()) let initial: Pages = Pages(memory_type.limits().initial() as usize);
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
::memory::validate_memory(initial, maximum).map_err(Error)
} }
fn validate_table_type(table_type: &TableType) -> Result<(), Error> { fn validate_table_type(table_type: &TableType) -> Result<(), Error> {