use core::num::NonZeroU32;
use scale_info::{
scale::{Decode, Encode},
TypeInfo,
};
pub const WASM_PAGE_SIZE: usize = 0x10000;
pub const GEAR_PAGE_SIZE: usize = 0x4000;
static_assertions::const_assert!(WASM_PAGE_SIZE < u32::MAX as usize);
static_assertions::const_assert_eq!(WASM_PAGE_SIZE % GEAR_PAGE_SIZE, 0);
#[derive(Debug, Clone, derive_more::Display)]
pub enum PageError {
#[display(fmt = "{_0} + {_1} overflows u32")]
AddOverflowU32(u32, u32),
#[display(fmt = "{_0} - {_1} overflows u32")]
SubOverflowU32(u32, u32),
#[display(fmt = "{_0} is too big to be number of page with size {_1}")]
OverflowU32MemorySize(u32, u32),
#[display(fmt = "Cannot make pages iter from {_0} to {_1}")]
WrongRange(u32, u32),
}
#[derive(
Clone, Copy, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash, TypeInfo, Default,
)]
pub struct GearPage(pub(crate) u32);
impl From<u16> for GearPage {
fn from(value: u16) -> Self {
static_assertions::const_assert!(GEAR_PAGE_SIZE <= 0x10000);
GearPage(value as u32)
}
}
impl From<GearPage> for u32 {
fn from(value: GearPage) -> Self {
value.0
}
}
impl PageU32Size for GearPage {
fn size_non_zero() -> NonZeroU32 {
static_assertions::const_assert_ne!(GEAR_PAGE_SIZE, 0);
unsafe { NonZeroU32::new_unchecked(GEAR_PAGE_SIZE as u32) }
}
unsafe fn new_unchecked(num: u32) -> Self {
Self(num)
}
}
impl PageNumber for GearPage {
unsafe fn from_raw(raw: u32) -> Self {
Self(raw)
}
}
impl PageDynSize for GearPage {
const SIZE_NO: usize = PageSizeNo::GearSizeNo as usize;
}
#[derive(Clone, Copy, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, TypeInfo, Default)]
pub struct WasmPage(pub(crate) u32);
impl From<u16> for WasmPage {
fn from(value: u16) -> Self {
static_assertions::const_assert!(WASM_PAGE_SIZE == 0x10000);
WasmPage(value as u32)
}
}
impl From<WasmPage> for u32 {
fn from(value: WasmPage) -> Self {
value.0
}
}
impl PageNumber for WasmPage {
unsafe fn from_raw(raw: u32) -> Self {
Self(raw)
}
}
impl PageU32Size for WasmPage {
fn size_non_zero() -> NonZeroU32 {
static_assertions::const_assert_ne!(WASM_PAGE_SIZE, 0);
unsafe { NonZeroU32::new_unchecked(WASM_PAGE_SIZE as u32) }
}
unsafe fn new_unchecked(num: u32) -> Self {
Self(num)
}
}
impl PageDynSize for WasmPage {
const SIZE_NO: usize = PageSizeNo::WasmSizeNo as usize;
}
pub trait PageNumber: Into<u32> + Sized + Clone + Copy + PartialEq + Eq + PartialOrd + Ord {
unsafe fn from_raw(raw: u32) -> Self;
fn raw(&self) -> u32 {
Into::<u32>::into(*self)
}
fn checked_sub(&self, other: Self) -> Option<Self> {
self.raw()
.checked_sub(PageNumber::raw(&other))
.map(|p| unsafe { Self::from_raw(p) })
}
fn iter_end_inclusive(&self, end: Self) -> Result<PagesIterInclusive<Self>, PageError> {
if end.raw() >= self.raw() {
Ok(PagesIterInclusive {
page: Some(*self),
end,
})
} else {
Err(PageError::WrongRange(self.raw(), end.raw()))
}
}
}
pub trait PageU32Size: PageNumber {
fn size_non_zero() -> NonZeroU32;
unsafe fn new_unchecked(num: u32) -> Self;
fn size() -> u32 {
Self::size_non_zero().into()
}
fn from_offset(offset: u32) -> Self {
unsafe { Self::new_unchecked(offset / Self::size()) }
}
fn new(num: u32) -> Result<Self, PageError> {
let page_begin = num
.checked_mul(Self::size())
.ok_or(PageError::OverflowU32MemorySize(num, Self::size()))?;
let last_byte_offset = Self::size() - 1;
page_begin
.checked_add(last_byte_offset)
.ok_or(PageError::OverflowU32MemorySize(num, Self::size()))?;
unsafe { Ok(Self::new_unchecked(num)) }
}
fn offset(&self) -> u32 {
self.raw() * Self::size()
}
fn end_offset(&self) -> u32 {
self.raw() * Self::size() + (Self::size() - 1)
}
fn to_page<P1: PageU32Size>(&self) -> P1 {
P1::from_offset(self.offset())
}
fn to_last_page<P1: PageU32Size>(&self) -> P1 {
P1::from_offset(self.end_offset())
}
fn add_raw(&self, raw: u32) -> Result<Self, PageError> {
self.raw()
.checked_add(raw)
.map(Self::new)
.ok_or(PageError::AddOverflowU32(self.raw(), raw))?
}
fn sub_raw(&self, raw: u32) -> Result<Self, PageError> {
self.raw()
.checked_sub(raw)
.map(Self::new)
.ok_or(PageError::SubOverflowU32(self.raw(), raw))?
}
fn add(&self, other: Self) -> Result<Self, PageError> {
self.add_raw(other.raw())
}
fn sub(&self, other: Self) -> Result<Self, PageError> {
self.sub_raw(other.raw())
}
fn inc(&self) -> Result<Self, PageError> {
self.add_raw(1)
}
fn dec(&self) -> Result<Self, PageError> {
self.sub_raw(1)
}
fn align_down(&self, size: NonZeroU32) -> Self {
let size: u32 = size.into();
Self::from_offset((self.offset() / size) * size)
}
fn zero() -> Self {
unsafe { Self::new_unchecked(0) }
}
fn iter_count(&self, count: Self) -> Result<PagesIter<Self>, PageError> {
self.add(count).map(|end| PagesIter { page: *self, end })
}
fn iter_end(&self, end: Self) -> Result<PagesIter<Self>, PageError> {
if end.raw() >= self.raw() {
Ok(PagesIter { page: *self, end })
} else {
Err(PageError::WrongRange(self.raw(), end.raw()))
}
}
fn iter_from_zero_inclusive(&self) -> PagesIterInclusive<Self> {
PagesIterInclusive {
page: Some(Self::zero()),
end: *self,
}
}
fn iter_from_zero(&self) -> PagesIter<Self> {
PagesIter {
page: Self::zero(),
end: *self,
}
}
fn iter_once(&self) -> PagesIterInclusive<Self> {
PagesIterInclusive {
page: Some(*self),
end: *self,
}
}
fn to_pages_iter<P: PageU32Size>(&self) -> PagesIterInclusive<P> {
let start: P = self.to_page();
let end: P = P::from_offset(self.end_offset());
PagesIterInclusive {
page: Some(start),
end,
}
}
}
pub trait SizeManager {
fn size_non_zero<P: PageDynSize>(&self) -> NonZeroU32;
fn size<P: PageDynSize>(&self) -> u32 {
self.size_non_zero::<P>().into()
}
}
impl SizeManager for u32 {
fn size_non_zero<P: PageDynSize>(&self) -> NonZeroU32 {
NonZeroU32::new(*self).expect("Size cannot be zero")
}
}
pub trait PageDynSize: PageNumber {
const SIZE_NO: usize;
fn size<S: SizeManager>(ctx: &S) -> u32 {
ctx.size::<Self>()
}
fn new<S: SizeManager>(raw: u32, ctx: &S) -> Option<Self> {
let page_size = <Self as PageDynSize>::size(ctx);
let page_begin = raw.checked_mul(page_size)?;
let last_byte_offset = page_size - 1;
page_begin.checked_add(last_byte_offset)?;
Some(unsafe { Self::from_raw(raw) })
}
fn offset<S: SizeManager>(&self, ctx: &S) -> u32 {
PageNumber::raw(self) * <Self as PageDynSize>::size(ctx)
}
fn end_offset<S: SizeManager>(&self, ctx: &S) -> u32 {
let size = <Self as PageDynSize>::size(ctx);
PageNumber::raw(self) * size + (size - 1)
}
fn from_offset<S: SizeManager>(ctx: &S, offset: u32) -> Self {
unsafe { Self::from_raw(offset / <Self as PageDynSize>::size(ctx)) }
}
}
pub enum PageSizeNo {
WasmSizeNo = 0,
GearSizeNo = 1,
Amount = 2,
}
#[derive(Debug, Clone)]
pub struct PagesIter<P: PageU32Size> {
page: P,
end: P,
}
impl<P: PageU32Size> Iterator for PagesIter<P> {
type Item = P;
fn next(&mut self) -> Option<Self::Item> {
if self.page.raw() >= self.end.raw() {
return None;
};
let res = self.page;
unsafe {
self.page = P::new_unchecked(self.page.raw() + 1);
}
Some(res)
}
}
#[derive(Debug, Clone)]
pub struct PagesIterInclusive<P: PageNumber> {
page: Option<P>,
end: P,
}
impl<P: PageNumber> Iterator for PagesIterInclusive<P> {
type Item = P;
fn next(&mut self) -> Option<Self::Item> {
let page = self.page?;
match self.end.raw() {
end if end == page.raw() => self.page = None,
end if end > page.raw() => unsafe {
self.page = Some(P::from_raw(page.raw() + 1));
},
_ => unreachable!(
"`page` {} cannot be bigger than `end` {}",
page.raw(),
self.end.raw(),
),
}
Some(page)
}
}
impl<P: PageNumber> PagesIterInclusive<P> {
pub fn current(&self) -> Option<P> {
self.page
}
pub fn end(&self) -> P {
self.end
}
}
impl<P: PageU32Size> PagesIterInclusive<P> {
pub fn convert<P1: PageU32Size>(&self) -> PagesIterInclusive<P1> {
PagesIterInclusive::<P1> {
page: self.page.map(|p| p.to_page()),
end: self.end.to_last_page(),
}
}
}