uqa_storage/sqlite/compressed_vfs/
options.rs1use super::{DEFAULT_CHUNK_PAGES, DEFAULT_LEVEL, DEFAULT_PAGE_SIZE};
10
11#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
12pub enum SQLiteCompressionCodec {
13 #[default]
14 Zstd,
15 LZ4,
16}
17
18impl SQLiteCompressionCodec {
19 pub(super) const fn id(self) -> u32 {
20 match self {
21 Self::Zstd => 1,
22 Self::LZ4 => 2,
23 }
24 }
25
26 pub(super) fn from_id(id: u32) -> Result<Self, String> {
27 match id {
28 0 | 1 => Ok(Self::Zstd),
29 2 => Ok(Self::LZ4),
30 _ => Err(format!("unsupported compression codec id {id}")),
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct SQLiteCompressionOptions {
37 pub codec: SQLiteCompressionCodec,
38 pub page_size: u32,
39 pub chunk_pages: u32,
40 pub level: i32,
41}
42
43impl Default for SQLiteCompressionOptions {
44 fn default() -> Self {
45 Self {
46 codec: SQLiteCompressionCodec::default(),
47 page_size: DEFAULT_PAGE_SIZE,
48 chunk_pages: DEFAULT_CHUNK_PAGES,
49 level: DEFAULT_LEVEL,
50 }
51 }
52}
53
54impl SQLiteCompressionOptions {
55 pub fn zstd() -> Self {
56 Self {
57 codec: SQLiteCompressionCodec::Zstd,
58 ..Self::default()
59 }
60 }
61
62 pub fn lz4() -> Self {
63 Self {
64 codec: SQLiteCompressionCodec::LZ4,
65 level: 0,
66 ..Self::default()
67 }
68 }
69
70 pub fn validate(self) -> Result<Self, String> {
71 if self.page_size == 0 || !self.page_size.is_power_of_two() {
72 return Err("page_size must be a non-zero power of two".to_string());
73 }
74 if !(512..=65_536).contains(&self.page_size) {
75 return Err("page_size must be between 512 and 65536 bytes".to_string());
76 }
77 if self.chunk_pages == 0 {
78 return Err("chunk_pages must be non-zero".to_string());
79 }
80 let chunk_size = u64::from(self.page_size) * u64::from(self.chunk_pages);
81 if !(u64::from(self.page_size)..=1_048_576).contains(&chunk_size) {
82 return Err("chunk size must be at most 1 MiB".to_string());
83 }
84 if self.codec == SQLiteCompressionCodec::Zstd && !(-7..=22).contains(&self.level) {
85 return Err("zstd level must be between -7 and 22".to_string());
86 }
87 Ok(self)
88 }
89
90 pub fn chunk_size(self) -> Result<usize, String> {
91 let validated = self.validate()?;
92 let page_size = usize::try_from(validated.page_size)
93 .map_err(|_| "page_size exceeds the addressable range".to_string())?;
94 let chunk_pages = usize::try_from(validated.chunk_pages)
95 .map_err(|_| "chunk_pages exceeds the addressable range".to_string())?;
96 page_size
97 .checked_mul(chunk_pages)
98 .ok_or_else(|| "chunk size exceeds the addressable range".to_string())
99 }
100}