use crate::{IndexType, MemoryError};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct MemoryTypeInner {
minimum: u64,
maximum: Option<u64>,
page_size_log2: u8,
index_type: IndexType,
}
#[derive(Debug, Copy, Clone)]
pub struct SizeOverflow;
impl MemoryTypeInner {
fn minimum_byte_size(&self) -> Result<u128, SizeOverflow> {
let min = u128::from(self.minimum);
if min > self.absolute_max() {
return Err(SizeOverflow);
}
Ok(min << self.page_size_log2)
}
fn maximum_byte_size(&self) -> Result<u128, SizeOverflow> {
match self.maximum {
Some(max) => {
let max = u128::from(max);
if max > self.absolute_max() {
return Err(SizeOverflow);
}
Ok(max << self.page_size_log2)
}
None => Ok(self.max_size_based_on_index_type()),
}
}
fn page_size(&self) -> u32 {
debug_assert!(
self.page_size_log2 == 16 || self.page_size_log2 == 0,
"invalid `page_size_log2`: {}; must be 16 or 0",
self.page_size_log2
);
1 << self.page_size_log2
}
fn max_size_based_on_index_type(&self) -> u128 {
self.index_type.max_size()
}
fn absolute_max(&self) -> u128 {
self.max_size_based_on_index_type() >> self.page_size_log2
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct MemoryType {
inner: MemoryTypeInner,
}
pub struct MemoryTypeBuilder {
inner: MemoryTypeInner,
}
impl Default for MemoryTypeBuilder {
fn default() -> Self {
Self {
inner: MemoryTypeInner {
minimum: 0,
maximum: None,
page_size_log2: MemoryType::DEFAULT_PAGE_SIZE_LOG2,
index_type: IndexType::I32,
},
}
}
}
impl MemoryTypeBuilder {
pub fn memory64(&mut self, memory64: bool) -> &mut Self {
self.inner.index_type = match memory64 {
true => IndexType::I64,
false => IndexType::I32,
};
self
}
pub fn min(&mut self, minimum: u64) -> &mut Self {
self.inner.minimum = minimum;
self
}
pub fn max(&mut self, maximum: Option<u64>) -> &mut Self {
self.inner.maximum = maximum;
self
}
pub fn page_size_log2(&mut self, page_size_log2: u8) -> &mut Self {
self.inner.page_size_log2 = page_size_log2;
self
}
pub fn build(self) -> Result<MemoryType, MemoryError> {
self.validate()?;
Ok(MemoryType { inner: self.inner })
}
fn validate(&self) -> Result<(), MemoryError> {
match self.inner.page_size_log2 {
0 | MemoryType::DEFAULT_PAGE_SIZE_LOG2 => {}
_ => {
return Err(MemoryError::InvalidMemoryType);
}
}
if self.inner.minimum_byte_size().is_err() {
return Err(MemoryError::InvalidMemoryType);
}
if let Some(max) = self.inner.maximum {
if self.inner.maximum_byte_size().is_err() {
return Err(MemoryError::InvalidMemoryType);
}
if self.inner.minimum > max {
return Err(MemoryError::InvalidMemoryType);
}
}
Ok(())
}
}
impl MemoryType {
const DEFAULT_PAGE_SIZE_LOG2: u8 = 16;
pub fn builder() -> MemoryTypeBuilder {
MemoryTypeBuilder::default()
}
pub fn is_64(&self) -> bool {
self.index_ty().is_64()
}
pub fn index_ty(&self) -> IndexType {
self.inner.index_type
}
pub fn minimum(self) -> u64 {
self.inner.minimum
}
pub fn maximum(self) -> Option<u64> {
self.inner.maximum
}
pub fn page_size(self) -> u32 {
self.inner.page_size()
}
pub fn page_size_log2(self) -> u8 {
self.inner.page_size_log2
}
pub(crate) fn minimum_byte_size(self) -> Result<u128, SizeOverflow> {
self.inner.minimum_byte_size()
}
pub(crate) fn absolute_max(&self) -> u128 {
self.inner.absolute_max()
}
pub fn is_subtype_of(&self, other: &Self) -> bool {
if self.is_64() != other.is_64() {
return false;
}
if self.page_size() != other.page_size() {
return false;
}
if self.minimum() < other.minimum() {
return false;
}
match (self.maximum(), other.maximum()) {
(_, None) => true,
(Some(max), Some(other_max)) => max <= other_max,
_ => false,
}
}
}