v8: transparent lz4 compression
This commit is contained in:
Generated
+7
@@ -318,6 +318,12 @@ version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "lz4_flex"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
@@ -454,6 +460,7 @@ dependencies = [
|
||||
"libc",
|
||||
"libredox",
|
||||
"log",
|
||||
"lz4_flex",
|
||||
"range-tree",
|
||||
"redox-path",
|
||||
"redox-scheme",
|
||||
|
||||
@@ -40,6 +40,7 @@ getrandom = { version = "0.2.5", optional = true }
|
||||
libc = "0.2"
|
||||
libredox = { version = "0.1.6", features = ["call"], optional = true }
|
||||
log = { version = "0.4.14", default-features = false, optional = true }
|
||||
lz4_flex = { version = "0.11", default-features = false, features = ["checked-decode"] }
|
||||
range-tree = { version = "0.1", optional = true }
|
||||
redox-path = "0.3.0"
|
||||
redox-scheme = { version = "0.7.0", optional = true }
|
||||
|
||||
+6
-6
@@ -2,7 +2,7 @@ use alloc::vec::Vec;
|
||||
use core::{fmt, mem, ops, slice};
|
||||
use endian_num::Le;
|
||||
|
||||
use crate::{BlockAddr, BlockLevel, BlockPtr, BlockTrait, BLOCK_SIZE};
|
||||
use crate::{BlockAddr, BlockLevel, BlockMeta, BlockPtr, BlockTrait, BLOCK_SIZE};
|
||||
|
||||
pub const ALLOC_LIST_ENTRIES: usize =
|
||||
(BLOCK_SIZE as usize - mem::size_of::<BlockPtr<AllocList>>()) / mem::size_of::<AllocEntry>();
|
||||
@@ -52,10 +52,10 @@ impl Allocator {
|
||||
|
||||
/// Find a free block of the given level, mark it as "used", and return its address.
|
||||
/// Returns [`None`] if there are no free blocks with this level.
|
||||
pub fn allocate(&mut self, block_level: BlockLevel) -> Option<BlockAddr> {
|
||||
pub fn allocate(&mut self, meta: BlockMeta) -> Option<BlockAddr> {
|
||||
// First, find the lowest level with a free block
|
||||
let mut index_opt = None;
|
||||
let mut level = block_level.0;
|
||||
let mut level = meta.level.0;
|
||||
// Start searching at the level we want. Smaller levels are too small!
|
||||
while level < self.levels.len() {
|
||||
if !self.levels[level].is_empty() {
|
||||
@@ -68,13 +68,13 @@ impl Allocator {
|
||||
// If a free block was found, split it until we find a usable block of the right level.
|
||||
// The left side of the split block is kept free, and the right side is allocated.
|
||||
let index = index_opt?;
|
||||
while level > block_level.0 {
|
||||
while level > meta.level.0 {
|
||||
level -= 1;
|
||||
let level_size = 1 << level;
|
||||
self.levels[level].push(index + level_size);
|
||||
}
|
||||
|
||||
Some(unsafe { BlockAddr::new(index, block_level) })
|
||||
Some(unsafe { BlockAddr::new(index, meta) })
|
||||
}
|
||||
|
||||
/// Try to allocate the exact block specified, making all necessary splits.
|
||||
@@ -112,7 +112,7 @@ impl Allocator {
|
||||
}
|
||||
}
|
||||
|
||||
Some(unsafe { BlockAddr::new(index_opt?, exact_addr.level()) })
|
||||
Some(unsafe { BlockAddr::new(index_opt?, exact_addr.meta()) })
|
||||
}
|
||||
|
||||
/// Deallocate the given block, marking it "free" so that it can be re-used later.
|
||||
|
||||
+65
-14
@@ -9,38 +9,67 @@ const BLOCK_LIST_ENTRIES: usize = BLOCK_SIZE as usize / mem::size_of::<BlockPtr<
|
||||
///
|
||||
/// This encodes a block's position _and_ [`BlockLevel`]:
|
||||
/// the first four bits of this `u64` encode the block's level,
|
||||
/// the next four bits indicates decompression level,
|
||||
/// the rest encode its index.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct BlockAddr(u64);
|
||||
|
||||
impl BlockAddr {
|
||||
const INDEX_SHIFT: u64 = 8;
|
||||
const DECOMP_LEVEL_MASK: u64 = 0xF0;
|
||||
const DECOMP_LEVEL_SHIFT: u64 = 4;
|
||||
const LEVEL_MASK: u64 = 0xF;
|
||||
|
||||
// Unsafe because this can create invalid blocks
|
||||
pub(crate) unsafe fn new(index: u64, level: BlockLevel) -> Self {
|
||||
// Level must only use the lowest four bits
|
||||
if level.0 > 0xF {
|
||||
panic!("block level used more than four bits");
|
||||
pub(crate) unsafe fn new(index: u64, meta: BlockMeta) -> Self {
|
||||
// Level must fit within LEVEL_MASK
|
||||
if meta.level.0 > Self::LEVEL_MASK as usize {
|
||||
panic!("block level too large");
|
||||
}
|
||||
|
||||
// Index must not use the highest four bits
|
||||
// Decomp level must fit within DECOMP_LEVEL_MASK
|
||||
let decomp_level = meta.decomp_level.unwrap_or_default();
|
||||
if (decomp_level.0 << Self::DECOMP_LEVEL_SHIFT) > Self::DECOMP_LEVEL_MASK as usize {
|
||||
panic!("decompressed block level too large");
|
||||
}
|
||||
|
||||
// Index must not use the metadata bits
|
||||
let inner = index
|
||||
.checked_shl(4)
|
||||
.expect("block index used highest four bits")
|
||||
| (level.0 as u64);
|
||||
.checked_shl(Self::INDEX_SHIFT as u32)
|
||||
.expect("block index too large")
|
||||
| ((decomp_level.0 as u64) << Self::DECOMP_LEVEL_SHIFT)
|
||||
| (meta.level.0 as u64);
|
||||
Self(inner)
|
||||
}
|
||||
|
||||
pub fn null(level: BlockLevel) -> Self {
|
||||
unsafe { Self::new(0, level) }
|
||||
pub fn null(meta: BlockMeta) -> Self {
|
||||
unsafe { Self::new(0, meta) }
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u64 {
|
||||
// The first four bits store the level
|
||||
self.0 >> 4
|
||||
self.0 >> Self::INDEX_SHIFT
|
||||
}
|
||||
|
||||
pub fn level(&self) -> BlockLevel {
|
||||
// The first four bits store the level
|
||||
BlockLevel((self.0 & 0xF) as usize)
|
||||
BlockLevel((self.0 & Self::LEVEL_MASK) as usize)
|
||||
}
|
||||
|
||||
pub fn decomp_level(&self) -> Option<BlockLevel> {
|
||||
let value = (self.0 & Self::DECOMP_LEVEL_MASK) >> Self::DECOMP_LEVEL_SHIFT;
|
||||
if value != 0 {
|
||||
Some(BlockLevel(value as usize))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta(&self) -> BlockMeta {
|
||||
BlockMeta {
|
||||
level: self.level(),
|
||||
decomp_level: self.decomp_level(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
@@ -48,6 +77,28 @@ impl BlockAddr {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub struct BlockMeta {
|
||||
pub(crate) level: BlockLevel,
|
||||
pub(crate) decomp_level: Option<BlockLevel>,
|
||||
}
|
||||
|
||||
impl BlockMeta {
|
||||
pub fn new(level: BlockLevel) -> Self {
|
||||
Self {
|
||||
level,
|
||||
decomp_level: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_compressed(level: BlockLevel, decomp_level: BlockLevel) -> Self {
|
||||
Self {
|
||||
level,
|
||||
decomp_level: Some(decomp_level),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The size of a block.
|
||||
///
|
||||
/// Level 0 blocks are blocks of [`BLOCK_SIZE`] bytes.
|
||||
@@ -211,9 +262,9 @@ pub struct BlockPtr<T> {
|
||||
}
|
||||
|
||||
impl<T> BlockPtr<T> {
|
||||
pub fn null(level: BlockLevel) -> Self {
|
||||
pub fn null(meta: BlockMeta) -> Self {
|
||||
Self {
|
||||
addr: BlockAddr::null(level).0.into(),
|
||||
addr: BlockAddr::null(meta).0.into(),
|
||||
hash: 0.into(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
|
||||
+8
-6
@@ -5,7 +5,9 @@ use xts_mode::{get_tweak_default, Xts128};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::{AllocEntry, AllocList, BlockData, BlockTrait, Key, KeySlot, Node, Salt, TreeList};
|
||||
use crate::{Allocator, BlockAddr, BlockLevel, Disk, Header, Transaction, BLOCK_SIZE, HEADER_RING};
|
||||
use crate::{
|
||||
Allocator, BlockAddr, BlockLevel, BlockMeta, Disk, Header, Transaction, BLOCK_SIZE, HEADER_RING,
|
||||
};
|
||||
|
||||
/// A file system
|
||||
pub struct FileSystem<D: Disk> {
|
||||
@@ -174,12 +176,12 @@ impl<D: Disk> FileSystem<D> {
|
||||
// Set tree and alloc pointers and write header generation one
|
||||
fs.tx(|tx| unsafe {
|
||||
let tree = BlockData::new(
|
||||
BlockAddr::new(HEADER_RING + 1, BlockLevel::default()),
|
||||
BlockAddr::new(HEADER_RING + 1, BlockMeta::default()),
|
||||
TreeList::empty(BlockLevel::default()).unwrap(),
|
||||
);
|
||||
|
||||
let mut alloc = BlockData::new(
|
||||
BlockAddr::new(HEADER_RING + 2, BlockLevel::default()),
|
||||
BlockAddr::new(HEADER_RING + 2, BlockMeta::default()),
|
||||
AllocList::empty(BlockLevel::default()).unwrap(),
|
||||
);
|
||||
|
||||
@@ -199,7 +201,7 @@ impl<D: Disk> FileSystem<D> {
|
||||
|
||||
fs.tx(|tx| unsafe {
|
||||
let mut root = BlockData::new(
|
||||
BlockAddr::new(HEADER_RING + 3, BlockLevel::default()),
|
||||
BlockAddr::new(HEADER_RING + 3, BlockMeta::default()),
|
||||
Node::new(Node::MODE_DIR | 0o755, 0, 0, ctime, ctime_nsec),
|
||||
);
|
||||
root.data_mut().set_links(1);
|
||||
@@ -254,12 +256,12 @@ impl<D: Disk> FileSystem<D> {
|
||||
if count < 0 {
|
||||
for i in 0..-count {
|
||||
//TODO: replace assert with error?
|
||||
let addr = BlockAddr::new(index + i as u64, BlockLevel::default());
|
||||
let addr = BlockAddr::new(index + i as u64, BlockMeta::default());
|
||||
assert_eq!(self.allocator.allocate_exact(addr), Some(addr));
|
||||
}
|
||||
} else {
|
||||
for i in 0..count {
|
||||
let addr = BlockAddr::new(index + i as u64, BlockLevel::default());
|
||||
let addr = BlockAddr::new(index + i as u64, BlockMeta::default());
|
||||
self.allocator.deallocate(addr);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ pub const BLOCK_SIZE: u64 = 4096;
|
||||
pub const RECORD_LEVEL: usize = 5;
|
||||
pub const RECORD_SIZE: u64 = BLOCK_SIZE << RECORD_LEVEL;
|
||||
pub const SIGNATURE: &[u8; 8] = b"RedoxFS\0";
|
||||
pub const VERSION: u64 = 7;
|
||||
pub const VERSION: u64 = 8;
|
||||
pub const DIR_ENTRY_MAX_LENGTH: usize = 252;
|
||||
|
||||
pub static IS_UMT: AtomicUsize = AtomicUsize::new(0);
|
||||
@@ -25,7 +25,7 @@ pub use self::allocator::{AllocEntry, AllocList, Allocator, ALLOC_LIST_ENTRIES};
|
||||
#[cfg(feature = "std")]
|
||||
pub use self::archive::{archive, archive_at};
|
||||
pub use self::block::{
|
||||
BlockAddr, BlockData, BlockLevel, BlockList, BlockPtr, BlockRaw, BlockTrait,
|
||||
BlockAddr, BlockData, BlockLevel, BlockList, BlockMeta, BlockPtr, BlockRaw, BlockTrait,
|
||||
};
|
||||
pub use self::dir::{DirEntry, DirList};
|
||||
pub use self::disk::*;
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ use crate::{BlockLevel, BlockTrait, RECORD_LEVEL};
|
||||
|
||||
//TODO: this is a box to prevent stack overflows
|
||||
#[derive(Clone)]
|
||||
pub struct RecordRaw(Box<[u8]>);
|
||||
pub struct RecordRaw(pub(crate) Box<[u8]>);
|
||||
|
||||
unsafe impl BlockTrait for RecordRaw {
|
||||
fn empty(level: BlockLevel) -> Option<Self> {
|
||||
|
||||
+96
-30
@@ -14,9 +14,9 @@ use syscall::error::{
|
||||
|
||||
use crate::{
|
||||
htree::{self, HTreeHash, HTreeNode, HTreePtr},
|
||||
AllocEntry, AllocList, Allocator, BlockAddr, BlockData, BlockLevel, BlockPtr, BlockTrait,
|
||||
DirEntry, DirList, Disk, FileSystem, Header, Node, NodeLevel, RecordRaw, TreeData, TreePtr,
|
||||
ALLOC_GC_THRESHOLD, ALLOC_LIST_ENTRIES, DIR_ENTRY_MAX_LENGTH, HEADER_RING,
|
||||
AllocEntry, AllocList, Allocator, BlockAddr, BlockData, BlockLevel, BlockMeta, BlockPtr,
|
||||
BlockTrait, DirEntry, DirList, Disk, FileSystem, Header, Node, NodeLevel, RecordRaw, TreeData,
|
||||
TreePtr, ALLOC_GC_THRESHOLD, ALLOC_LIST_ENTRIES, DIR_ENTRY_MAX_LENGTH, HEADER_RING,
|
||||
};
|
||||
|
||||
pub struct Transaction<'a, D: Disk> {
|
||||
@@ -57,11 +57,11 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
// MARK: block operations
|
||||
//
|
||||
|
||||
/// Allocate a new block of size `level`, returning its address.
|
||||
/// Allocate a new block of size defined by `meta`, returning its address.
|
||||
/// - returns `Err(ENOSPC)` if a block of this size could not be alloated.
|
||||
/// - unsafe because order must be done carefully and changes must be flushed to disk
|
||||
pub(crate) unsafe fn allocate(&mut self, level: BlockLevel) -> Result<BlockAddr> {
|
||||
match self.allocator.allocate(level) {
|
||||
pub(crate) unsafe fn allocate(&mut self, meta: BlockMeta) -> Result<BlockAddr> {
|
||||
match self.allocator.allocate(meta) {
|
||||
Some(addr) => {
|
||||
self.allocator_log.push_back(AllocEntry::allocate(addr));
|
||||
Ok(addr)
|
||||
@@ -180,7 +180,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
while new_blocks.len() * ALLOC_LIST_ENTRIES
|
||||
<= self.allocator_log.len() + self.deallocate.len()
|
||||
{
|
||||
new_blocks.push(unsafe { self.allocate(BlockLevel::default())? });
|
||||
new_blocks.push(unsafe { self.allocate(BlockMeta::default())? });
|
||||
}
|
||||
|
||||
// De-allocate old blocks (after allocation to prevent re-use)
|
||||
@@ -321,8 +321,9 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
ptr: BlockPtr<T>,
|
||||
) -> Result<BlockData<T>> {
|
||||
if ptr.is_null() {
|
||||
match T::empty(ptr.addr().level()) {
|
||||
Some(empty) => Ok(BlockData::new(BlockAddr::default(), empty)),
|
||||
let addr = ptr.addr();
|
||||
match T::empty(addr.level()) {
|
||||
Some(empty) => Ok(BlockData::new(addr, empty)),
|
||||
None => {
|
||||
#[cfg(feature = "log")]
|
||||
log::error!("READ_BLOCK_OR_EMPTY: INVALID BLOCK LEVEL FOR TYPE");
|
||||
@@ -336,15 +337,44 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
|
||||
unsafe fn read_record<T: BlockTrait + DerefMut<Target = [u8]>>(
|
||||
&mut self,
|
||||
ptr: BlockPtr<T>,
|
||||
mut ptr: BlockPtr<T>,
|
||||
level: BlockLevel,
|
||||
) -> Result<BlockData<T>> {
|
||||
let record = unsafe { self.read_block_or_empty(ptr)? };
|
||||
// Set null pointers to correct size (reduces number of copies below)
|
||||
if ptr.is_null() {
|
||||
ptr = BlockPtr::<T>::null(BlockMeta::new(level));
|
||||
}
|
||||
|
||||
// Read record from disk, or construct empty one for null pointers
|
||||
let mut record = unsafe { self.read_block_or_empty(ptr)? };
|
||||
if record.addr().level() >= level {
|
||||
// Return record if it is larger than or equal to requested level
|
||||
return Ok(record);
|
||||
}
|
||||
|
||||
// Attempt to decompress if address metadata indicates compression
|
||||
if let Some(decomp_level) = record.addr().decomp_level() {
|
||||
// First 2 bytes store compressed data length
|
||||
// This means only compressed record sizes up to 64 KiB are supported
|
||||
let mut decomp = match T::empty(decomp_level) {
|
||||
Some(empty) => empty,
|
||||
None => {
|
||||
#[cfg(feature = "log")]
|
||||
log::error!("READ_RECORD: INVALID DECOMPRESSED BLOCK LEVEL FOR TYPE");
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
};
|
||||
let comp_len = record.data()[0] as usize | ((record.data()[1] as usize) << 8);
|
||||
if let Err(err) =
|
||||
lz4_flex::decompress_into(&record.data()[2..(comp_len + 2)], &mut decomp)
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
log::error!("READ_RECORD: FAILED TO DECOMPRESS: {:?}", err);
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
record = BlockData::new(BlockAddr::null(BlockMeta::new(decomp_level)), decomp);
|
||||
}
|
||||
|
||||
// If a larger level was requested,
|
||||
// create a fake record with the requested level
|
||||
// and fill it with the data in the original record.
|
||||
@@ -359,7 +389,8 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
};
|
||||
let len = min(raw.len(), old_raw.len());
|
||||
raw[..len].copy_from_slice(&old_raw[..len]);
|
||||
Ok(BlockData::new(BlockAddr::null(level), raw))
|
||||
|
||||
Ok(BlockData::new(BlockAddr::null(BlockMeta::new(level)), raw))
|
||||
}
|
||||
|
||||
/// Write block data to a new address, returning new address
|
||||
@@ -368,8 +399,8 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
mut block: BlockData<T>,
|
||||
) -> Result<BlockPtr<T>> {
|
||||
// Swap block to new address
|
||||
let level = block.addr().level();
|
||||
let old_addr = block.swap_addr(unsafe { self.allocate(level)? });
|
||||
let meta = block.addr().meta();
|
||||
let old_addr = block.swap_addr(unsafe { self.allocate(meta)? });
|
||||
// Deallocate old address (will only take effect after sync_allocator, which helps to
|
||||
// prevent re-use before a new header is written
|
||||
if !old_addr.is_null() {
|
||||
@@ -773,7 +804,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
unsafe {
|
||||
let parent = self.read_tree(parent_ptr)?;
|
||||
let node_block_data = BlockData::new(
|
||||
self.allocate(BlockLevel::default())?,
|
||||
self.allocate(BlockMeta::default())?,
|
||||
Node::new(
|
||||
mode,
|
||||
parent.data().uid(),
|
||||
@@ -1594,7 +1625,8 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
let mut node_changed = false;
|
||||
|
||||
let record_level = node.data().record_level();
|
||||
let node_records = node.data().size().div_ceil(record_level.bytes());
|
||||
let node_size = node.data().size();
|
||||
let node_records = node_size.div_ceil(record_level.bytes());
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() {
|
||||
@@ -1605,30 +1637,64 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
let mut record_ptr = if node_records > (*offset / record_level.bytes()) {
|
||||
self.node_record_ptr(node, *offset / record_level.bytes())?
|
||||
} else {
|
||||
BlockPtr::null(level)
|
||||
BlockPtr::null(BlockMeta::new(level))
|
||||
};
|
||||
let mut record = unsafe { self.read_record(record_ptr, level)? };
|
||||
|
||||
// If record has changed
|
||||
if buf[i..i + len] != record.data()[j..j + len] {
|
||||
unsafe {
|
||||
// CoW record using its current level
|
||||
let mut old_addr = record.swap_addr(self.allocate(record.addr().level())?);
|
||||
// Update record in memory
|
||||
record.data_mut()[j..j + len].copy_from_slice(&buf[i..i + len]);
|
||||
|
||||
// If the record was resized we need to dealloc the original ptr
|
||||
if old_addr.is_null() {
|
||||
old_addr = record_ptr.addr();
|
||||
}
|
||||
|
||||
record.data_mut()[j..j + len].copy_from_slice(&buf[i..i + len]);
|
||||
record_ptr = self.write_block(record)?;
|
||||
|
||||
if !old_addr.is_null() {
|
||||
self.deallocate(old_addr);
|
||||
// Handle record compression, if record is larger than one block
|
||||
let decomp_level = record.addr().level();
|
||||
if decomp_level.0 > 0 {
|
||||
// Maximum compressed record size is 64 KiB
|
||||
let mut comp = vec![0; 64 * 1024];
|
||||
// First two bytes store compressed data length
|
||||
match lz4_flex::compress_into(record.data(), &mut comp[2..]) {
|
||||
Ok(comp_len) => {
|
||||
comp[0] = comp_len as u8;
|
||||
comp[1] = (comp_len >> 8) as u8;
|
||||
let comp_level = BlockLevel::for_bytes((comp_len as u64) + 2);
|
||||
comp.truncate(comp_level.bytes() as usize);
|
||||
// Replace record with compressed record, if it saves space
|
||||
if comp_level < decomp_level {
|
||||
record = BlockData::new(
|
||||
BlockAddr::null(BlockMeta::new_compressed(
|
||||
comp_level,
|
||||
decomp_level,
|
||||
)),
|
||||
RecordRaw(comp.into_boxed_slice()),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
// Failures to compress can be ignored, with the original record data used
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CoW record using its current level
|
||||
let mut old_addr =
|
||||
unsafe { record.swap_addr(self.allocate(record.addr().meta())?) };
|
||||
|
||||
// If the record was resized we need to dealloc the original ptr
|
||||
if old_addr.is_null() {
|
||||
old_addr = record_ptr.addr();
|
||||
}
|
||||
|
||||
// Write record to disk
|
||||
record_ptr = unsafe { self.write_block(record)? };
|
||||
|
||||
// Update record pointer
|
||||
self.sync_node_record_ptr(node, *offset / record_level.bytes(), record_ptr)?;
|
||||
node_changed = true;
|
||||
|
||||
// Deallocate old record
|
||||
if !old_addr.is_null() {
|
||||
unsafe { self.deallocate(old_addr) };
|
||||
}
|
||||
}
|
||||
|
||||
i += len;
|
||||
|
||||
Reference in New Issue
Block a user