1use crate::{Error, Result};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct ResourceLimits {
7 pub data_bytes: u64,
8 pub wal_bytes: u64,
9 pub tracked_pages: u32,
11 pub readers: u32,
12 pub record_bytes: u32,
13 pub recovery_bytes: u64,
16}
17
18impl ResourceLimits {
19 pub fn validate(self) -> Result<Self> {
20 if self.data_bytes < 3 * 4096
21 || self.data_bytes % 4096 != 0
22 || self.data_bytes / 4096 > u32::MAX as u64
23 || self.wal_bytes < 4096
24 || self.tracked_pages == 0
25 || self.readers == 0
26 || self.readers > 4096
27 || self.record_bytes == 0
28 || self.record_bytes as u64 + 4096 > self.wal_bytes
29 {
30 return Err(Error::ResourceLimit("invalid resource limits"));
31 }
32 self.total_bytes()?;
33 Ok(self)
34 }
35 pub fn freelist_bytes(self) -> u64 {
36 28 + 24 * self.tracked_pages as u64
37 }
38 pub fn total_bytes(self) -> Result<u64> {
42 self.data_bytes
43 .checked_add(self.wal_bytes)
44 .and_then(|n| n.checked_add(2 * self.freelist_bytes()))
45 .and_then(|n| n.checked_add(48 * self.readers as u64))
46 .and_then(|n| n.checked_add(self.recovery_bytes))
47 .ok_or(Error::ResourceLimit("total byte limit overflow"))
48 }
49 pub fn encode(self) -> Vec<u8> {
53 let mut b = b"E4LIMIT1".to_vec();
54 for n in [
55 self.data_bytes,
56 self.wal_bytes,
57 self.tracked_pages as u64,
58 self.readers as u64,
59 self.record_bytes as u64,
60 self.recovery_bytes,
61 ] {
62 b.extend_from_slice(&n.to_le_bytes());
63 }
64 b
65 }
66 pub fn decode(b: &[u8]) -> Result<Self> {
67 if b.len() != 56 || &b[..8] != b"E4LIMIT1" {
68 return Err(Error::ResourceLimit(
69 "damaged resource policy; use source-preserving recovery",
70 ));
71 }
72 let n = |i| u64::from_le_bytes(b[i..i + 8].try_into().unwrap());
73 let small =
74 |i| u32::try_from(n(i)).map_err(|_| Error::ResourceLimit("resource policy overflow"));
75 Self {
76 data_bytes: n(8),
77 wal_bytes: n(16),
78 tracked_pages: small(24)?,
79 readers: small(32)?,
80 record_bytes: small(40)?,
81 recovery_bytes: n(48),
82 }
83 .validate()
84 }
85}
86
87pub(crate) fn read(dir: &std::path::Path) -> Result<Option<ResourceLimits>> {
89 if !dir.join("data").exists() {
90 return Ok(None);
91 }
92 let file = crate::io::open_file_readonly(&dir.join("data"))?;
93 let budget = std::sync::Arc::new(crate::budget::MemoryBudget::new(65536));
94 let pool = crate::pool::BufferPool::new(file.into(), budget, 16)?;
95 crate::meta::Meta::read_limits(&pool)
96}