use std::str;
use bitcode::{Decode, Encode};
use wbase::{
base32::{BASE32_LEN_U128, Base32Buf128, decode_u128, encode_u128},
crc::Crc32Hasher,
};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
#[repr(u8)]
pub(crate) enum CprPhase {
#[default]
Rest = 0,
Prepare = 1,
InProgress = 2,
WaitFlush = 3,
Complete = 4,
}
impl CprPhase {
#[inline]
pub(crate) const fn next_phase(self) -> Self {
match self {
Self::Rest => Self::Prepare,
Self::Prepare => Self::InProgress,
Self::InProgress => Self::WaitFlush,
Self::WaitFlush => Self::Complete,
Self::Complete => Self::Rest,
}
}
}
pub const FORMAT_VERSION: u32 = 3;
pub(crate) const META_PREFIX: &str = "checkpoint_";
pub(crate) const META_EXT: &str = ".meta";
pub(crate) const INDEX_PREFIX: &str = "index_";
pub(crate) const INDEX_EXT: &str = ".ckpt";
pub(crate) const TMP_EXT: &str = ".tmp";
#[inline]
fn build_base32_filename(prefix: &str, token: u128, ext: &str, suffix: &str) -> String {
let b32 = encode_u128(token);
let mut out = String::with_capacity(prefix.len() + b32.len() + ext.len() + suffix.len());
out.push_str(prefix);
out.push_str(b32.as_str());
out.push_str(ext);
out.push_str(suffix);
out
}
#[inline]
pub fn meta_filename(token: u128) -> String {
build_base32_filename(META_PREFIX, token, META_EXT, "")
}
#[inline]
pub fn meta_tmp_filename(token: u128) -> String {
build_base32_filename(META_PREFIX, token, META_EXT, TMP_EXT)
}
#[inline]
pub fn index_filename(token: u128) -> String {
build_base32_filename(INDEX_PREFIX, token, INDEX_EXT, "")
}
#[inline]
pub fn index_tmp_filename(token: u128) -> String {
build_base32_filename(INDEX_PREFIX, token, INDEX_EXT, TMP_EXT)
}
#[inline]
pub fn token_to_base32(token: u128) -> Base32Buf128 {
encode_u128(token)
}
#[inline]
pub(crate) fn parse_token(s: &str) -> Option<u128> {
let s = s.trim();
if s.len() == BASE32_LEN_U128 {
decode_u128(s)
} else {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub enum CheckpointType {
FoldOver,
Snapshot,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub struct IndexMeta {
pub size: usize,
pub overflow_count: u64,
pub entry_count: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub struct HlogMeta {
pub begin_address: u64,
pub head_address: u64,
pub flushed_until_address: u64,
pub tail_address: u64,
}
#[derive(Debug, Clone, PartialEq, Encode, Decode)]
pub struct StoreMeta {
pub index_size: usize,
pub page_size: usize,
pub num_pages: usize,
pub mutable_fraction: f64,
pub max_sessions: usize,
pub enable_revivification: bool,
pub enable_read_cache: bool,
pub read_cache_num_pages: usize,
pub range_index_dir: Option<String>,
pub next_key_id: u64,
}
#[derive(Debug, Clone, PartialEq, Encode, Decode)]
pub struct CheckpointMeta {
pub token: u128,
pub cp_type: CheckpointType,
pub index_meta: IndexMeta,
pub hlog_meta: HlogMeta,
pub store_meta: StoreMeta,
pub created_at: u64,
pub checkpoint_aof_address: Option<u64>,
pub format_version: u32,
pub integrity_crc32: u32,
}
impl CheckpointMeta {
pub fn integrity_digest(&self) -> u32 {
let mut h = Crc32Hasher::new();
h.update_u32(self.format_version);
h.update(&self.token.to_le_bytes());
h.update(&[match self.cp_type {
CheckpointType::FoldOver => 0u8,
CheckpointType::Snapshot => 1u8,
}]);
let g = &self.hlog_meta;
h.update_u64(g.begin_address);
h.update_u64(g.head_address);
h.update_u64(g.flushed_until_address);
h.update_u64(g.tail_address);
h.update_u64(self.index_meta.size as u64);
h.update_u64(self.index_meta.overflow_count);
h.update_u64(self.index_meta.entry_count as u64);
let s = &self.store_meta;
h.update_u64(s.index_size as u64);
h.update_u64(s.page_size as u64);
h.update_u64(s.num_pages as u64);
h.update_u64(s.mutable_fraction.to_bits());
h.update_u64(s.max_sessions as u64);
h.update(&[
u8::from(s.enable_revivification),
u8::from(s.enable_read_cache),
]);
h.update_u64(s.read_cache_num_pages as u64);
match &s.range_index_dir {
Some(p) => {
h.update_u64(p.len() as u64);
h.update(p.as_bytes());
}
None => h.update_u64(u64::MAX),
}
h.update_u64(s.next_key_id);
h.update_u64(self.created_at);
match self.checkpoint_aof_address {
Some(a) => h.update_u64(a),
None => h.update_u64(u64::MAX),
}
h.finalize()
}
#[inline]
pub fn seal(&mut self) {
self.integrity_crc32 = self.integrity_digest();
}
#[inline]
pub fn encode(&self) -> Vec<u8> {
bitcode::encode(self)
}
#[inline]
pub fn decode(bytes: &[u8]) -> Result<Self> {
bitcode::decode(bytes).map_err(Error::from)
}
}