1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use std::u32;
use std::sync::Arc;
use parking_lot::RwLock;
use elements::{MemoryType, ResizableLimits};
use interpreter::{Error, UserError};
use interpreter::module::check_limits;

/// Linear memory page size.
pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536;
/// Maximal number of pages.
const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;

/// Linear memory instance.
pub struct MemoryInstance<E: UserError> {
	/// Memofy limits.
	limits: ResizableLimits,
	/// Linear memory buffer.
	buffer: RwLock<Vec<u8>>,
	/// Maximum buffer size.
	maximum_size: u32,
	/// Dummy to avoid compilation error.
	_dummy: ::std::marker::PhantomData<E>,
}

struct CheckedRegion<'a, B: 'a> where B: ::std::ops::Deref<Target=Vec<u8>> {
	buffer: &'a B,
	offset: usize,
	size: usize,
}

impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref<Target=Vec<u8>> {
	fn range(&self) -> ::std::ops::Range<usize> {
		self.offset..self.offset+self.size
	}

	fn slice(&self) -> &[u8] {
		&self.buffer[self.range()]
	}
}

impl<E> MemoryInstance<E> where E: UserError {
	/// Create new linear memory instance.
	pub fn new(memory_type: &MemoryType) -> Result<Arc<Self>, Error<E>> {
		check_limits(memory_type.limits())?;

		let maximum_size = match memory_type.limits().maximum() {
			Some(maximum_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))),
			Some(maximum_pages) => maximum_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE),
			None => u32::MAX,
		};
		let initial_size = calculate_memory_size(0, memory_type.limits().initial(), maximum_size)
			.ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?;

		let memory = MemoryInstance {
			limits: memory_type.limits().clone(),
			buffer: RwLock::new(vec![0; initial_size as usize]),
			maximum_size: maximum_size,
			_dummy: Default::default(),
		};

		Ok(Arc::new(memory))
	}

	/// Return linear memory limits.
	pub fn limits(&self) -> &ResizableLimits {
		&self.limits
	}

	/// Return linear memory size (in pages).
	pub fn size(&self) -> u32 {
		self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
	}

	/// Get data at given offset.
	pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error<E>> {
		let buffer = self.buffer.read();
		let region = self.checked_region(&buffer, offset as usize, size)?;

		Ok(region.slice().to_vec())
	}

	/// Set data at given offset.
	pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error<E>> {
		let mut buffer = self.buffer.write();
		let range = self.checked_region(&buffer, offset as usize, value.len())?.range();

		buffer[range].copy_from_slice(value);

		Ok(())
	}

	/// Increases the size of the linear memory by given number of pages.
	/// Returns -1 if allocation fails or previous memory size, if succeeds.
	pub fn grow(&self, pages: u32) -> Result<u32, Error<E>> {
		let mut buffer = self.buffer.write();
		let old_size = buffer.len() as u32;
		match calculate_memory_size(old_size, pages, self.maximum_size) {
			None => Ok(u32::MAX),
			Some(new_size) => {
				buffer.resize(new_size as usize, 0);
				Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
			},
		}
	}

	fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error<E>> 
		where B: ::std::ops::Deref<Target=Vec<u8>>
	{
		let end = offset.checked_add(size)
			.ok_or(Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;

		if end > buffer.len() {
			return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len())));
		}

		Ok(CheckedRegion {
			buffer: buffer,
			offset: offset,
			size: size,
		})
	}

	/// Copy memory region
	pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error<E>> {
		let buffer = self.buffer.write();

		let read_region = self.checked_region(&buffer, src_offset, len)?;
		let write_region = self.checked_region(&buffer, dst_offset, len)?;

		unsafe { ::std::ptr::copy(
			buffer[read_region.range()].as_ptr(), 
			buffer[write_region.range()].as_ptr() as *mut _,
			len,
		)} 

		Ok(())
	}

	/// Zero memory region
	pub fn zero(&self, offset: usize, len: usize) -> Result<(), Error<E>> {
		let mut buffer = self.buffer.write();

		let range = self.checked_region(&buffer, offset, len)?.range();
		for val in &mut buffer[range] { *val = 0 }
		Ok(())
	}
}

fn calculate_memory_size(old_size: u32, additional_pages: u32, maximum_size: u32) -> Option<u32> {
	additional_pages
		.checked_mul(LINEAR_MEMORY_PAGE_SIZE)
		.and_then(|size| size.checked_add(old_size))
		.and_then(|size| if size > maximum_size {
			None
		} else {
			Some(size)
		})
}