use super::disk_manager::{FileHeader, BLOCK_SIZE};
use super::error::Result;
#[repr(C, align(4096))]
pub struct AlignedBlock {
pub data: [u8; BLOCK_SIZE],
}
impl AlignedBlock {
pub fn new() -> Self {
Self {
data: [0u8; BLOCK_SIZE],
}
}
pub fn new_boxed() -> Box<Self> {
unsafe {
let layout = std::alloc::Layout::new::<Self>();
let ptr = std::alloc::alloc_zeroed(layout) as *mut Self;
assert!(!ptr.is_null(), "failed to allocate AlignedBlock on heap");
Box::from_raw(ptr)
}
}
pub fn from_data(data: [u8; BLOCK_SIZE]) -> Self {
Self { data }
}
#[inline]
pub fn as_slice(&self) -> &[u8; BLOCK_SIZE] {
&self.data
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8; BLOCK_SIZE] {
&mut self.data
}
}
impl Default for AlignedBlock {
fn default() -> Self {
Self::new()
}
}
impl std::ops::Deref for AlignedBlock {
type Target = [u8; BLOCK_SIZE];
#[inline]
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl std::ops::DerefMut for AlignedBlock {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl std::fmt::Debug for AlignedBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AlignedBlock")
.field("len", &BLOCK_SIZE)
.field("align", &std::mem::align_of::<Self>())
.finish()
}
}
pub trait BlockStorage: Send + Sync + 'static {
fn read_block(&self, block_id: u32, buffer: &mut [u8; BLOCK_SIZE]) -> Result<()>;
fn write_block(&self, block_id: u32, buffer: &[u8; BLOCK_SIZE]) -> Result<()>;
fn read_bytes(&self, block_id: u32, offset: usize, buffer: &mut [u8]) -> Result<()>;
fn write_bytes(&self, block_id: u32, offset: usize, data: &[u8]) -> Result<()>;
fn allocate_block(&self) -> Result<u32>;
fn free_block(&self, block_id: u32) -> Result<()>;
fn read_header(&self) -> Result<FileHeader>;
fn write_header(&self, header: &FileHeader) -> Result<()>;
fn read_header_bytes(&self, buffer: &mut [u8]) -> Result<()>;
fn write_header_bytes(&self, bytes: &[u8]) -> Result<()>;
fn root_ptr(&self) -> Result<u64>;
fn set_root_ptr(&self, ptr: u64) -> Result<()>;
fn entry_count(&self) -> Result<u64>;
fn set_entry_count(&self, count: u64) -> Result<()>;
fn image_checkpoint_lsn(&self) -> Result<u64> {
Ok(0)
}
fn set_image_checkpoint_lsn(&self, _lsn: u64) -> Result<()> {
Ok(())
}
fn file_size(&self) -> u64;
fn block_count(&self) -> Result<u32>;
fn path(&self) -> &str;
fn sync(&self) -> Result<()>;
fn read_blocks_batch(&self, requests: &mut [(u32, &mut [u8; BLOCK_SIZE])]) -> Result<()> {
for (block_id, buffer) in requests.iter_mut() {
self.read_block(*block_id, buffer)?;
}
Ok(())
}
fn write_blocks_batch(&self, requests: &[(u32, &[u8; BLOCK_SIZE])]) -> Result<()> {
for (block_id, buffer) in requests {
self.write_block(*block_id, buffer)?;
}
Ok(())
}
unsafe fn register_buffer_pool(&self, _buffers: &[(*mut u8, usize)]) -> Result<()> {
Ok(())
}
fn unregister_buffer_pool(&self) -> Result<()> {
Ok(())
}
fn read_block_fixed(
&self,
block_id: u32,
buffer: &mut [u8; BLOCK_SIZE],
_buf_index: u16,
) -> Result<()> {
self.read_block(block_id, buffer)
}
fn write_block_fixed(
&self,
block_id: u32,
buffer: &[u8; BLOCK_SIZE],
_buf_index: u16,
) -> Result<()> {
self.write_block(block_id, buffer)
}
fn supports_fixed_buffers(&self) -> bool {
false
}
fn read_blocks_batch_fixed(
&self,
requests: &mut [(u32, &mut [u8; BLOCK_SIZE], u16)],
) -> Result<()> {
for (block_id, buffer, buf_index) in requests.iter_mut() {
self.read_block_fixed(*block_id, buffer, *buf_index)?;
}
Ok(())
}
fn write_blocks_batch_fixed(&self, requests: &[(u32, &[u8; BLOCK_SIZE], u16)]) -> Result<()> {
for &(block_id, buffer, buf_index) in requests {
self.write_block_fixed(block_id, buffer, buf_index)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aligned_block_alignment() {
let block = AlignedBlock::new();
let addr = &block as *const AlignedBlock as usize;
assert_eq!(
addr % 4096,
0,
"AlignedBlock must be 4096-byte aligned, got addr 0x{:x}",
addr
);
}
#[test]
fn test_aligned_block_size() {
assert_eq!(
std::mem::size_of::<AlignedBlock>(),
BLOCK_SIZE,
"AlignedBlock size must equal BLOCK_SIZE"
);
}
#[test]
fn test_aligned_block_deref() {
let mut block = AlignedBlock::new();
block[0] = 0xDE;
block[1] = 0xAD;
assert_eq!(block.data[0], 0xDE);
assert_eq!(block.data[1], 0xAD);
let slice: &[u8; BLOCK_SIZE] = &*block;
assert_eq!(slice[0], 0xDE);
}
#[test]
fn test_aligned_block_from_data() {
let mut data = [0u8; BLOCK_SIZE];
data[0] = 42;
let block = AlignedBlock::from_data(data);
assert_eq!(block.data[0], 42);
}
#[test]
fn test_aligned_block_debug() {
let block = AlignedBlock::new();
let debug = format!("{:?}", block);
assert!(debug.contains("AlignedBlock"));
assert!(debug.contains("4096"));
}
}