use crate::allocator::Allocator;
use crate::page::Pages;
use crate::page::PAGE_HEADER_CAP;
#[cfg(test)]
use crate::test_util::device::TestSeekableAsyncFile as SeekableAsyncFile;
#[cfg(test)]
use crate::test_util::journal::TestTransaction as Transaction;
use crate::util::ceil_pow2;
use crate::util::div_mod_pow2;
use off64::int::Off64ReadInt;
use off64::u8;
#[cfg(not(test))]
use seekable_async_file::SeekableAsyncFile;
#[cfg(not(test))]
use write_journal::Transaction;
#[derive(Clone, Copy)]
pub(crate) struct ObjectOffsets {
key_len: u16,
lpage_count: u64,
tail_page_count: u8,
assoc_data_len: u16,
}
impl ObjectOffsets {
pub fn with_key_len(self, key_len: u16) -> Self {
Self { key_len, ..self }
}
pub fn with_lpages(self, lpage_count: u64) -> Self {
Self {
lpage_count,
..self
}
}
pub fn with_tail_pages(self, tail_page_count: u8) -> Self {
Self {
tail_page_count,
..self
}
}
pub fn with_assoc_data_len(self, assoc_data_len: u16) -> Self {
Self {
assoc_data_len,
..self
}
}
pub fn _reserved_by_header(self) -> u64 {
0
}
pub fn created_ms(self) -> u64 {
self._reserved_by_header() + PAGE_HEADER_CAP
}
pub fn size(self) -> u64 {
self.created_ms() + 6
}
pub fn id(self) -> u64 {
self.size() + 5
}
pub fn key_len(self) -> u64 {
self.id() + 8
}
pub fn key(self) -> u64 {
self.key_len() + 2
}
pub fn lpages(self) -> u64 {
self.key() + u64::from(self.key_len)
}
pub fn lpage(self, idx: u64) -> u64 {
self.lpages() + 6 * idx
}
pub fn tail_pages(self) -> u64 {
self.lpage(self.lpage_count)
}
pub fn tail_page(self, idx: u8) -> u64 {
self.tail_pages() + 6 * u64::from(idx)
}
pub fn assoc_data_len(self) -> u64 {
self.tail_page(self.tail_page_count)
}
pub fn assoc_data(self) -> u64 {
self.assoc_data_len() + 2
}
pub fn _total_size(self) -> u64 {
self.assoc_data() + u64::from(self.assoc_data_len)
}
}
pub(crate) const OBJECT_OFF: ObjectOffsets = ObjectOffsets {
assoc_data_len: 0,
key_len: 0,
lpage_count: 0,
tail_page_count: 0,
};
pub const OBJECT_KEY_LEN_MAX: u16 = 497;
#[derive(Clone, Copy)]
pub struct TailPageSizes(u64, u64);
impl TailPageSizes {
pub fn new() -> Self {
Self(0, 0)
}
fn shard_get_len(shard: u64) -> u8 {
u8!((shard & (0b1111 << 60)) >> 60)
}
fn shard_set_len(shard: &mut u64, len: u8) {
assert!(len <= 12);
*shard &= !(0b1111 << 60);
*shard |= u64::from(len) << 60;
}
fn shard_get(shard: u64, idx: u8) -> u8 {
assert!(idx < 12);
let shift = idx * 5;
u8!((shard & (0b11111 << shift)) >> shift)
}
fn shard_set(shard: &mut u64, idx: u8, val: u8) {
assert!(val < (1 << 5));
let shift = idx * 5;
*shard &= !(0b11111 << shift);
*shard |= u64::from(val) << shift;
}
pub fn len(&self) -> u8 {
Self::shard_get_len(self.0) + Self::shard_get_len(self.1)
}
pub fn get(&self, idx: u8) -> Option<u8> {
if idx >= self.len() {
return None;
};
Some(if idx < 12 {
Self::shard_get(self.0, idx)
} else {
Self::shard_get(self.1, idx - 12)
})
}
pub fn push(&mut self, pow2: u8) {
let idx = self.len();
if idx < 12 {
Self::shard_set(&mut self.0, idx, pow2);
Self::shard_set_len(&mut self.0, idx + 1);
} else {
Self::shard_set(&mut self.1, idx - 12, pow2);
Self::shard_set_len(&mut self.1, idx - 12 + 1);
};
}
}
impl IntoIterator for TailPageSizes {
type IntoIter = TailPageSizesIter;
type Item = (u8, u8);
fn into_iter(self) -> Self::IntoIter {
TailPageSizesIter(self, 0)
}
}
pub struct TailPageSizesIter(TailPageSizes, u8);
impl Iterator for TailPageSizesIter {
type Item = (u8, u8);
fn next(&mut self) -> Option<Self::Item> {
let idx = self.1;
let entry = self.0.get(idx)?;
self.1 += 1;
Some((idx, entry))
}
}
pub(crate) struct ObjectLayout {
pub lpage_count: u64,
pub tail_page_sizes_pow2: TailPageSizes,
}
pub(crate) fn calc_object_layout(pages: &Pages, object_size: u64) -> ObjectLayout {
let (lpage_count, tail_size) = div_mod_pow2(object_size, pages.lpage_size_pow2);
let mut rem = ceil_pow2(tail_size, pages.spage_size_pow2);
let mut tail_page_sizes_pow2 = TailPageSizes::new();
loop {
let pos = rem.leading_zeros();
if pos == 64 {
break;
};
let pow2 = u8!(63 - pos);
tail_page_sizes_pow2.push(pow2);
rem &= !(1 << pow2);
}
ObjectLayout {
lpage_count,
tail_page_sizes_pow2,
}
}
#[must_use]
pub(crate) struct ReleasedObject {
pub object_metadata_size: u64,
pub object_data_size: u64,
}
pub(crate) async fn release_object(
txn: &mut Transaction,
dev: &SeekableAsyncFile,
pages: &Pages,
alloc: &mut Allocator,
page_dev_offset: u64,
page_size_pow2: u8,
) -> ReleasedObject {
let raw = dev.read_at(page_dev_offset, 1 << page_size_pow2).await;
let object_size = raw.read_u40_be_at(OBJECT_OFF.size());
let key_len = raw.read_u16_be_at(OBJECT_OFF.key_len());
let ObjectLayout {
lpage_count,
tail_page_sizes_pow2,
} = calc_object_layout(pages, object_size);
let off = OBJECT_OFF
.with_key_len(key_len)
.with_lpages(lpage_count)
.with_tail_pages(tail_page_sizes_pow2.len());
for i in 0..lpage_count {
let page_dev_offset = raw.read_u48_be_at(off.lpage(i));
alloc
.release(txn, page_dev_offset, pages.lpage_size_pow2)
.await;
}
for (i, tail_page_size_pow2) in tail_page_sizes_pow2 {
let page_dev_offset = raw.read_u48_be_at(off.tail_page(i));
alloc
.release(txn, page_dev_offset, tail_page_size_pow2)
.await;
}
alloc.release(txn, page_dev_offset, page_size_pow2).await;
ReleasedObject {
object_data_size: object_size,
object_metadata_size: off._total_size(),
}
}