Skip to main content

cartel_sqlite/settings/
memory.rs

1use crate::{
2    api::stmt,
3    catalogs, config, conn,
4    runtime::{Error, heap::layout::Layout, local, raw},
5};
6
7#[derive(Clone, Copy, PartialEq, Eq)]
8pub struct Memory {
9    pub sqlite_bytes: usize,
10    pub results: Results,
11    pub statement_bytes: usize,
12    pub cache: Cache,
13    pub lookaside: Lookaside,
14}
15
16impl Memory {
17    /// Computes the exact worker backing reservation for a catalog.
18    pub fn reservation<I: crate::Catalog>(self) -> Option<Reservation> {
19        self.admit::<I>().ok().map(|(plan, _)| plan)
20    }
21
22    pub(crate) fn admit<I: crate::Catalog>(self) -> Result<(Reservation, Layout), Error> {
23        let sqlite = Layout::new(self.sqlite_bytes).ok_or(Error::SqliteBytes)?;
24        let shape = catalogs::Shape::of::<I>();
25        let stmts = Statements::new(shape).ok_or(Error::CapacityOverflow)?;
26        let results = round(self.results.bytes).ok_or(Error::CapacityOverflow)?;
27        let aside = round(self.lookaside.bytes()).ok_or(Error::CapacityOverflow)?;
28        let phase = conn::raw::Vacant::<'_, '_, I>::LAYOUT;
29        if phase.align() > raw::Span::ALIGNMENT {
30            return Err(Error::Layout);
31        }
32        let phase = round(phase.size()).ok_or(Error::CapacityOverflow)?;
33        let arena = sqlite.backing().ok_or(Error::CapacityOverflow)?;
34        let retain = local::State::<I>::RETENTION_BYTES
35            .checked_add(phase)
36            .and_then(|bytes| bytes.checked_add(arena))
37            .ok_or(Error::CapacityOverflow)?;
38        let backing = retain
39            .checked_add(results)
40            .and_then(|bytes| bytes.checked_add(stmts.slots_bytes))
41            .and_then(|bytes| bytes.checked_add(aside))
42            .ok_or(Error::CapacityOverflow)?;
43        let targets =
44            Targets::new(self.cache, self.statement_bytes).ok_or(Error::CapacityOverflow)?;
45        Ok((
46            Reservation {
47                backing_bytes: backing,
48                control_bytes: retain - self.sqlite_bytes,
49                sqlite_bytes: self.sqlite_bytes,
50                sqlite_capacity: sqlite.capacity(),
51                result_bytes: results,
52                statements: stmts,
53                lookaside_bytes: aside,
54                targets,
55            },
56            sqlite,
57        ))
58    }
59}
60
61fn round(bytes: usize) -> Option<usize> {
62    Some(bytes.checked_add(63)? / 64 * 64)
63}
64
65#[derive(Clone, Copy, PartialEq, Eq)]
66pub struct Results {
67    pub rows: usize,
68    pub bytes: usize,
69}
70
71#[derive(Clone, Copy, PartialEq, Eq)]
72pub struct Cache {
73    pub main_bytes: usize,
74    pub temporary_bytes: usize,
75}
76
77#[derive(Clone, Copy, PartialEq, Eq)]
78pub struct Lookaside {
79    slot_bytes: u16,
80    slots: u16,
81}
82
83impl Lookaside {
84    pub const DISABLED: Self = Self {
85        slot_bytes: 0,
86        slots: 0,
87    };
88
89    pub const fn try_new(bytes: u16, slots: u16) -> Option<Self> {
90        if bytes as usize <= size_of::<usize>()
91            || !bytes.is_multiple_of(8)
92            || slots == 0
93            || bytes as u32 * slots as u32 > 0x7fff0000
94        {
95            return None;
96        }
97        Some(Self {
98            slot_bytes: bytes,
99            slots,
100        })
101    }
102
103    pub const fn slot_bytes(self) -> usize {
104        self.slot_bytes as usize
105    }
106
107    pub const fn slots(self) -> usize {
108        self.slots as usize
109    }
110
111    pub const fn bytes(self) -> usize {
112        const { assert!(usize::BITS >= 2 * u16::BITS) };
113        self.slot_bytes() * self.slots()
114    }
115
116    pub(crate) const fn sqlite_parts(self) -> (i32, i32) {
117        (self.slot_bytes as i32, self.slots as i32)
118    }
119}
120
121#[derive(Clone, Copy, PartialEq, Eq)]
122pub struct Reservation {
123    /// Total bytes in the worker's single aligned backing allocation.
124    pub backing_bytes: usize,
125    /// Connection state, intrusive ownership and allocator control in the same allocation.
126    pub control_bytes: usize,
127    /// Physical partition reserved for SQLite's allocator.
128    pub sqlite_bytes: usize,
129    /// Usable SQLite allocation capacity after allocator metadata.
130    pub sqlite_capacity: usize,
131    /// Aligned retained-result arena bytes.
132    pub result_bytes: usize,
133    /// Validated prepared-statement layout.
134    pub statements: Statements,
135    /// Aligned per-connection lookaside bytes.
136    pub lookaside_bytes: usize,
137    /// Logical targets charged to SQLite's allocator partition.
138    pub targets: Targets,
139}
140
141#[derive(Clone, Copy, PartialEq, Eq)]
142pub struct Statements {
143    /// Validated compile-time catalog shape.
144    pub catalog: crate::Shape,
145    /// Prepared catalog and internal control statement slots.
146    pub items: usize,
147    /// Maximum SQL bytes across catalog and internal control statements.
148    pub sql_bytes: usize,
149    /// Aligned prepared-statement registry bytes.
150    pub slots_bytes: usize,
151}
152
153impl Statements {
154    fn new(catalog: crate::Shape) -> Option<Self> {
155        let items = catalog
156            .statements
157            .checked_add(config::CONTROL_STATEMENT_ITEMS)?;
158        let sql = catalog.max_sql_bytes.max(stmt::Control::MAX_SQL_BYTES);
159        let bytes = stmt::bytes(items)?;
160        Some(Self {
161            catalog,
162            items,
163            sql_bytes: sql,
164            slots_bytes: bytes,
165        })
166    }
167}
168
169#[derive(Clone, Copy, PartialEq, Eq)]
170pub struct Targets {
171    /// SQLite-reported prepared-program bytes.
172    pub statements: usize,
173    /// Main plus temporary page-cache bytes.
174    pub pager: usize,
175    /// Statements plus page-cache bytes.
176    pub retained: usize,
177}
178
179impl Targets {
180    fn new(cache: Cache, stmts: usize) -> Option<Self> {
181        let pager = cache.main_bytes.checked_add(cache.temporary_bytes)?;
182        let retained = pager.checked_add(stmts)?;
183        Some(Self {
184            statements: stmts,
185            pager,
186            retained,
187        })
188    }
189}