use crate::{
api::stmt,
catalogs, config, conn,
runtime::{Error, heap::layout::Layout, raw},
};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Memory {
pub sqlite_bytes: usize,
pub results: Results,
pub statement_bytes: usize,
pub cache: Cache,
pub lookaside: Lookaside,
}
impl Memory {
pub fn reservation<I: crate::Catalog>(self) -> Option<Reservation> {
self.admit::<I>().ok().map(|(plan, _)| plan)
}
pub(crate) fn admit<I: crate::Catalog>(self) -> Result<(Reservation, Layout), Error> {
let sqlite = Layout::new(self.sqlite_bytes).ok_or(Error::SqliteBytes)?;
let shape = catalogs::Shape::of::<I>();
let stmts = Statements::new(shape).ok_or(Error::CapacityOverflow)?;
let results = round(self.results.bytes).ok_or(Error::CapacityOverflow)?;
let aside = round(self.lookaside.bytes()).ok_or(Error::CapacityOverflow)?;
let phase = conn::raw::Vacant::<'_, '_, I>::LAYOUT;
if phase.align() > raw::Span::ALIGNMENT {
return Err(Error::Layout);
}
let phase = round(phase.size()).ok_or(Error::CapacityOverflow)?;
let arena = sqlite.backing().ok_or(Error::CapacityOverflow)?;
let retain = phase.checked_add(arena).ok_or(Error::CapacityOverflow)?;
let backing = retain
.checked_add(results)
.and_then(|bytes| bytes.checked_add(stmts.slots_bytes))
.and_then(|bytes| bytes.checked_add(aside))
.ok_or(Error::CapacityOverflow)?;
let targets =
Targets::new(self.cache, self.statement_bytes).ok_or(Error::CapacityOverflow)?;
Ok((
Reservation {
backing_bytes: backing,
control_bytes: retain - self.sqlite_bytes,
sqlite_bytes: self.sqlite_bytes,
sqlite_capacity: sqlite.capacity(),
result_bytes: results,
statements: stmts,
lookaside_bytes: aside,
targets,
},
sqlite,
))
}
}
fn round(bytes: usize) -> Option<usize> {
Some(bytes.checked_add(63)? / 64 * 64)
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Results {
pub rows: usize,
pub bytes: usize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Cache {
pub main_bytes: usize,
pub temporary_bytes: usize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Lookaside {
slot_bytes: u16,
slots: u16,
}
impl Lookaside {
pub const DISABLED: Self = Self {
slot_bytes: 0,
slots: 0,
};
pub const fn try_new(bytes: u16, slots: u16) -> Option<Self> {
if bytes as usize <= size_of::<usize>()
|| !bytes.is_multiple_of(8)
|| slots == 0
|| bytes as u32 * slots as u32 > 0x7fff0000
{
return None;
}
Some(Self {
slot_bytes: bytes,
slots,
})
}
pub const fn slot_bytes(self) -> usize {
self.slot_bytes as usize
}
pub const fn slots(self) -> usize {
self.slots as usize
}
pub const fn bytes(self) -> usize {
const { assert!(usize::BITS >= 2 * u16::BITS) };
self.slot_bytes() * self.slots()
}
pub(crate) const fn sqlite_parts(self) -> (i32, i32) {
(self.slot_bytes as i32, self.slots as i32)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Reservation {
pub backing_bytes: usize,
pub control_bytes: usize,
pub sqlite_bytes: usize,
pub sqlite_capacity: usize,
pub result_bytes: usize,
pub statements: Statements,
pub lookaside_bytes: usize,
pub targets: Targets,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Statements {
pub catalog: crate::Shape,
pub items: usize,
pub sql_bytes: usize,
pub slots_bytes: usize,
}
impl Statements {
fn new(catalog: crate::Shape) -> Option<Self> {
let items = catalog
.statements
.checked_add(config::CONTROL_STATEMENT_ITEMS)?;
let sql = catalog.max_sql_bytes.max(stmt::Control::MAX_SQL_BYTES);
let bytes = stmt::bytes(items)?;
Some(Self {
catalog,
items,
sql_bytes: sql,
slots_bytes: bytes,
})
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Targets {
pub statements: usize,
pub pager: usize,
pub retained: usize,
}
impl Targets {
fn new(cache: Cache, stmts: usize) -> Option<Self> {
let pager = cache.main_bytes.checked_add(cache.temporary_bytes)?;
let retained = pager.checked_add(stmts)?;
Some(Self {
statements: stmts,
pager,
retained,
})
}
}