From be12ca01aaa33a7c31f253d2455822ec3d7a22c1 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Wed, 12 Nov 2025 12:04:42 +0400 Subject: [PATCH] add is_hole / punch_hole optional methods to IO trait and remove is_hole method from Database trait --- core/io/memory.rs | 30 ++++++++++++++++-------------- core/io/mod.rs | 12 ++++++++++++ core/lib.rs | 2 +- core/storage/database.rs | 7 ------- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/core/io/memory.rs b/core/io/memory.rs index 4e3649e4e..3f52e507f 100644 --- a/core/io/memory.rs +++ b/core/io/memory.rs @@ -1,6 +1,6 @@ use super::{Buffer, Clock, Completion, File, OpenFlags, IO}; -use crate::LimboError; use crate::{io::clock::DefaultClock, Result}; +use crate::{turso_assert, LimboError}; use crate::io::clock::Instant; use std::{ @@ -238,23 +238,25 @@ impl File for MemoryFile { fn has_hole(&self, pos: usize, len: usize) -> Result { let start_page = pos / PAGE_SIZE; let end_page = ((pos + len.max(1)) - 1) / PAGE_SIZE; - let (mut holes, mut pages) = (0, 0); for page_no in start_page..=end_page { - match self.get_page(page_no) { - Some(_) => pages += 1, - None => holes += 1, - }; + if self.get_page(page_no).is_some() { + return Ok(false); + } } + Ok(true) + } - if holes == 0 { - Ok(false) - } else if pages == 0 { - Ok(true) - } else { - Err(LimboError::InternalError(format!( - "ambigous has_hole result: pos={pos}, len={len}, pages={pages}, holes={holes}" - ))) + fn punch_hole(&self, pos: usize, len: usize) -> Result<()> { + turso_assert!( + pos % PAGE_SIZE == 0 && len % PAGE_SIZE == 0, + "hole must be page aligned" + ); + let start_page = pos / PAGE_SIZE; + let end_page = ((pos + len.max(1)) - 1) / PAGE_SIZE; + for page_no in start_page..=end_page { + unsafe { (*self.pages.get()).remove(&page_no) }; } + Ok(()) } } diff --git a/core/io/mod.rs b/core/io/mod.rs index 3ff9bdd42..3f917b264 100644 --- a/core/io/mod.rs +++ b/core/io/mod.rs @@ -92,10 +92,22 @@ pub trait File: Send + Sync { } fn size(&self) -> Result; fn truncate(&self, len: u64, c: Completion) -> Result; + + /// Optional method implemented by the IO which supports "partial" files (e.g. file with "holes") + /// This method is used in sync engine only for now (in partial sync mode) and never used in the core database code + /// + /// The hole is the contiguous file region which is not allocated by the file-system + /// If there is a single byte which is allocated within a given range - method must return false in this case // todo: need to add custom completion type? fn has_hole(&self, _pos: usize, _len: usize) -> Result { panic!("has_hole is not supported for the given IO implementation") } + /// Optional method implemented by the IO which supports "partial" files (e.g. file with "holes") + /// This method is used in sync engine only for now (in partial sync mode) and never used in the core database code + // todo: need to add custom completion type? + fn punch_hole(&self, _pos: usize, _len: usize) -> Result<()> { + panic!("punch_hole is not supported for the given IO implementation") + } } #[derive(Debug, Copy, Clone, PartialEq)] diff --git a/core/lib.rs b/core/lib.rs index af316135a..a32e5497b 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -10,7 +10,7 @@ mod functions; mod incremental; pub mod index_method; mod info; -mod io; +pub mod io; #[cfg(feature = "json")] mod json; pub mod mvcc; diff --git a/core/storage/database.rs b/core/storage/database.rs index 76b476017..3e1db8107 100644 --- a/core/storage/database.rs +++ b/core/storage/database.rs @@ -85,8 +85,6 @@ pub trait DatabaseStorage: Send + Sync { fn sync(&self, c: Completion) -> Result; fn size(&self) -> Result; fn truncate(&self, len: usize, c: Completion) -> Result; - // todo: need to add custom completion type? - fn has_hole(&self, pos: usize, len: usize) -> Result; } #[derive(Clone)] @@ -261,11 +259,6 @@ impl DatabaseStorage for DatabaseFile { let c = self.file.truncate(len as u64, c)?; Ok(c) } - - #[instrument(skip_all, level = Level::INFO)] - fn has_hole(&self, pos: usize, len: usize) -> Result { - self.file.has_hole(pos, len) - } } #[cfg(feature = "fs")]