pub mod dealer;
pub mod heap;
pub mod shared;
pub mod wire;
use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
use crate::error::{Result, StatusCode};
pub use dealer::{Allocation, MemoryDealer, ALLOCATION_ALIGNMENT};
pub use heap::{
is_supported, MappedHeap, MemoryHeapBase, FLAG_DONT_MAP_LOCALLY, FLAG_FORCE_MEMFD,
FLAG_MEMFD_ALLOW_SEALING, FLAG_NO_CACHING, SEAL_FUTURE_WRITE, SEAL_GROW, SEAL_SEAL,
SEAL_SHRINK, SEAL_WRITE,
};
pub use shared::{region_size, SharedMemory};
pub use wire::{
export_heap, BnMemory, BnMemoryHeap, BpMemory, BpMemoryHeap, HeapCache, MemoryBase, GET_MEMORY,
HEAP_ID, IMEMORY_DESCRIPTOR, IMEMORY_HEAP_DESCRIPTOR,
};
pub const FLAG_READ_ONLY: u32 = 0x0000_0001;
pub(crate) const WORD: usize = std::mem::size_of::<usize>();
#[derive(Clone, Copy)]
pub(crate) struct Region<'a> {
words: &'a [AtomicUsize],
tail: &'a [AtomicU8],
}
impl<'a> Region<'a> {
pub(crate) fn new(words: &'a [AtomicUsize], tail: &'a [AtomicU8]) -> Self {
Self { words, tail }
}
pub(crate) fn len(&self) -> usize {
self.words.len() * WORD + self.tail.len()
}
fn check(&self, off: usize, len: usize) -> Result<()> {
let end = off.checked_add(len).ok_or(StatusCode::BadValue)?;
if end > self.len() {
return Err(StatusCode::BadValue);
}
Ok(())
}
pub(crate) fn load(&self, off: usize, dst: &mut [u8]) -> Result<()> {
self.check(off, dst.len())?;
let word_bytes = self.words.len() * WORD;
let mut done = 0;
while done < dst.len() && off + done < word_bytes {
let at = off + done;
let shift = at % WORD;
let n = (WORD - shift).min(dst.len() - done);
let w = self.words[at / WORD].load(Ordering::Relaxed).to_ne_bytes();
dst[done..done + n].copy_from_slice(&w[shift..shift + n]);
done += n;
}
let tail_at = (off + done).saturating_sub(word_bytes);
for (d, s) in dst[done..].iter_mut().zip(&self.tail[tail_at..]) {
*d = s.load(Ordering::Relaxed);
}
Ok(())
}
pub(crate) fn store(&self, off: usize, src: &[u8]) -> Result<()> {
self.check(off, src.len())?;
let word_bytes = self.words.len() * WORD;
let mut done = 0;
while done < src.len() && off + done < word_bytes {
let at = off + done;
let shift = at % WORD;
let n = (WORD - shift).min(src.len() - done);
let w = &self.words[at / WORD];
let chunk = &src[done..done + n];
if n == WORD {
let full = usize::from_ne_bytes(chunk.try_into().expect("one word"));
w.store(full, Ordering::Relaxed);
} else {
let mut cur = w.load(Ordering::Relaxed);
loop {
let mut bytes = cur.to_ne_bytes();
bytes[shift..shift + n].copy_from_slice(chunk);
let next = usize::from_ne_bytes(bytes);
match w.compare_exchange_weak(cur, next, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => break,
Err(seen) => cur = seen,
}
}
}
done += n;
}
let tail_at = (off + done).saturating_sub(word_bytes);
for (d, s) in self.tail[tail_at..].iter().zip(&src[done..]) {
d.store(*s, Ordering::Relaxed);
}
Ok(())
}
}
#[derive(Clone, Copy)]
pub struct SharedBytes<'a> {
region: Region<'a>,
off: usize,
len: usize,
}
impl<'a> SharedBytes<'a> {
pub(crate) fn whole(region: Region<'a>) -> Self {
Self {
region,
off: 0,
len: region.len(),
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn load(&self, i: usize) -> Option<u8> {
let mut b = [0u8; 1];
self.copy_to(i, &mut b).ok().map(|()| b[0])
}
pub fn copy_to(&self, off: usize, dst: &mut [u8]) -> Result<()> {
let end = off.checked_add(dst.len()).ok_or(StatusCode::BadValue)?;
if end > self.len {
return Err(StatusCode::BadValue);
}
self.region.load(self.off + off, dst)
}
pub fn slice(&self, off: usize, len: usize) -> Option<SharedBytes<'a>> {
let end = off.checked_add(len)?;
if end > self.len {
return None;
}
Some(Self {
region: self.region,
off: self.off + off,
len,
})
}
pub fn to_vec(&self) -> Vec<u8> {
let mut v = vec![0u8; self.len];
self.region
.load(self.off, &mut v)
.expect("window is within the region");
v
}
}
impl std::fmt::Debug for SharedBytes<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SharedBytes")
.field("len", &self.len)
.finish()
}
}
pub trait IMemoryHeap: Send + Sync {
fn heap_id(&self) -> i32;
fn size(&self) -> usize;
fn flags(&self) -> u32;
fn offset(&self) -> usize;
fn base(&self) -> Option<SharedBytes<'_>>;
}
pub trait IMemory: Send + Sync {
fn memory(&self) -> &dyn IMemoryHeap;
fn offset(&self) -> usize;
fn size(&self) -> usize;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flags_match_aosp_constants() {
assert_eq!(FLAG_READ_ONLY, 0x0000_0001);
assert_eq!(FLAG_DONT_MAP_LOCALLY, 0x0000_0100);
assert_eq!(FLAG_NO_CACHING, 0x0000_0200);
assert_eq!(FLAG_FORCE_MEMFD, 0x0000_0400);
assert_eq!(FLAG_MEMFD_ALLOW_SEALING, 0x0000_0800);
}
#[test]
fn constructor_matches_is_supported() {
let r = MemoryHeapBase::new(4096, FLAG_READ_ONLY);
if is_supported() {
assert!(r.is_ok());
} else {
assert_eq!(r.unwrap_err(), crate::StatusCode::InvalidOperation);
}
}
#[test]
fn imemoryheap_is_object_safe() {
struct Stub;
impl IMemoryHeap for Stub {
fn heap_id(&self) -> i32 {
42
}
fn size(&self) -> usize {
0
}
fn flags(&self) -> u32 {
0
}
fn offset(&self) -> usize {
0
}
fn base(&self) -> Option<SharedBytes<'_>> {
None
}
}
let h: &dyn IMemoryHeap = &Stub;
assert_eq!(h.heap_id(), 42);
}
}