From a6b7c50dfc94be3f82621d5417009ee505225965 Mon Sep 17 00:00:00 2001 From: qwp0905 Date: Sat, 9 May 2026 20:25:32 +0900 Subject: [PATCH 1/2] add inline vec --- src/cursor/btree.rs | 7 +- src/cursor/cursor.rs | 8 +- src/cursor/entry.rs | 13 +- src/cursor/entry_view.rs | 18 +- src/cursor/header.rs | 4 +- src/cursor/internal.rs | 4 +- src/cursor/leaf.rs | 5 +- src/cursor/node.rs | 8 +- src/cursor/tests/entry.rs | 4 +- src/cursor/tests/node.rs | 12 +- src/cursor/types.rs | 4 +- src/disk/page.rs | 3 +- src/serialize/mod.rs | 8 +- src/utils/inline_vec.rs | 398 ++++++++++++++++++++++++++++++++++ src/utils/mod.rs | 3 + src/utils/tests/inline_vec.rs | 354 ++++++++++++++++++++++++++++++ tests/e2e.rs | 4 +- 17 files changed, 801 insertions(+), 56 deletions(-) create mode 100644 src/utils/inline_vec.rs create mode 100644 src/utils/tests/inline_vec.rs diff --git a/src/cursor/btree.rs b/src/cursor/btree.rs index 095b82e..30e4543 100644 --- a/src/cursor/btree.rs +++ b/src/cursor/btree.rs @@ -1,7 +1,8 @@ use std::{collections::VecDeque, mem::replace, ops::Bound, sync::Arc}; use crate::{ - cache::WritableSlot, disk::Pointer, table::TableHandle, wal::TxId, Error, Result, + cache::WritableSlot, disk::Pointer, table::TableHandle, utils::InlineVec, wal::TxId, + Error, Result, }; use crossbeam::epoch::{pin, Guard}; @@ -242,7 +243,7 @@ impl BTreeIndex { let entry = DataEntry::init(create_record()); let entry_ptr = self.0.alloc_and_log(&entry, table)?; - let split = match node.insert_and_split(pos, key.to_vec(), entry_ptr) { + let split = match node.insert_and_split(pos, key.into(), entry_ptr) { Some(split) => split, None => { return self @@ -317,7 +318,7 @@ impl BTreeIndex { return Ok(RecordData::Data(data)); } - let mut pointers = Vec::with_capacity(data.len().div_ceil(CHUNK_SIZE)); + let mut pointers = InlineVec::with_capacity(data.len().div_ceil(CHUNK_SIZE)); while data.len() > CHUNK_SIZE { let remain = data.split_off(CHUNK_SIZE); let chunk = DataChunk::new(data); diff --git a/src/cursor/cursor.rs b/src/cursor/cursor.rs index 81e992b..ef8aa0d 100644 --- a/src/cursor/cursor.rs +++ b/src/cursor/cursor.rs @@ -83,7 +83,7 @@ impl<'a> Cursor<'a> { self .metrics .operation_insert - .measure(|| self.index.insert(key, value, table)) + .measure(|| self.index.insert(key.into(), value, table)) .map(|_| ()) } @@ -102,7 +102,7 @@ impl<'a> Cursor<'a> { .measure(|| { self .index - .insert_record(key.as_ref().to_vec(), RecordData::Tombstone, table) + .insert_record(key.as_ref().into(), RecordData::Tombstone, table) }) .map(|_| ()); } @@ -131,8 +131,8 @@ impl<'a> Cursor<'a> { &self.table, self.compaction.as_ref(), &self.index, - range.start_bound().map(|k| k.as_ref().to_vec()), - range.end_bound().map(|k| k.as_ref().to_vec()), + range.start_bound().map(|k| k.as_ref().into()), + range.end_bound().map(|k| k.as_ref().into()), ) } } diff --git a/src/cursor/entry.rs b/src/cursor/entry.rs index b45f792..76b3bbd 100644 --- a/src/cursor/entry.rs +++ b/src/cursor/entry.rs @@ -5,6 +5,7 @@ use crate::{ serialize::{ Deserializable, Serializable, SerializeType, TypedObject, SERIALIZABLE_BYTES, }, + utils::InlineVec, wal::{TxId, TX_ID_BYTES}, Error, }; @@ -27,7 +28,7 @@ pub const CHUNK_SIZE: usize = SERIALIZABLE_BYTES - 2; #[derive(Debug)] pub enum RecordData { Data(Vec), - Chunked(Vec), + Chunked(InlineVec), Tombstone, } impl RecordData { @@ -128,9 +129,7 @@ impl DataEntry { } } impl TypedObject for DataEntry { - fn get_type() -> SerializeType { - SerializeType::DataEntry - } + const TYPE: SerializeType = SerializeType::DataEntry; } impl Serializable for DataEntry { fn write_at(&self, writer: &mut crate::disk::PageWriter) -> crate::Result { @@ -175,7 +174,7 @@ impl Deserializable for DataEntry { 1 => RecordData::Tombstone, 2 => { let l = reader.read()? as usize; - let mut pointers = Vec::with_capacity(l); + let mut pointers = InlineVec::with_capacity(l); for _ in 0..l { pointers.push(reader.read_u64()?); } @@ -201,9 +200,7 @@ impl DataChunk { } } impl TypedObject for DataChunk { - fn get_type() -> SerializeType { - SerializeType::DataChunk - } + const TYPE: SerializeType = SerializeType::DataChunk; } impl Serializable for DataChunk { fn write_at(&self, writer: &mut crate::disk::PageWriter) -> crate::Result { diff --git a/src/cursor/entry_view.rs b/src/cursor/entry_view.rs index d40f6e4..f07d945 100644 --- a/src/cursor/entry_view.rs +++ b/src/cursor/entry_view.rs @@ -1,13 +1,15 @@ +use super::{DataChunk, DataEntry}; use crate::{ disk::{Page, Pointer}, serialize::{Deserializable, SerializeType, TypedObject, Viewable}, + utils::InlineVec, wal::TxId, Error, }; pub enum RecordDataView { Data(usize, usize), - Chunked(Vec), + Chunked(InlineVec), Tombstone, } @@ -27,8 +29,8 @@ impl VersionRecordView { } pub struct DataEntryView { - next: Option, versions: Vec, + next: Option, } impl DataEntryView { pub fn find

(&self, predicate: P) -> Option<&VersionRecordView> @@ -56,9 +58,7 @@ impl DataEntryView { } } impl TypedObject for DataEntryView { - fn get_type() -> SerializeType { - SerializeType::DataEntry - } + const TYPE: SerializeType = DataEntry::TYPE; } impl Deserializable for DataEntryView { fn read_from(reader: &mut crate::disk::PageScanner) -> crate::Result { @@ -77,7 +77,7 @@ impl Deserializable for DataEntryView { 1 => RecordDataView::Tombstone, 2 => { let l = reader.read()? as usize; - let mut pointers = Vec::with_capacity(l); + let mut pointers = InlineVec::with_capacity(l); for _ in 0..l { pointers.push(reader.read_u64()?); } @@ -85,7 +85,7 @@ impl Deserializable for DataEntryView { } _ => return Err(Error::InvalidFormat("invalid type for data version record")), }; - versions.push(VersionRecordView::new(owner, version, data)) + versions.push(VersionRecordView::new(owner, version, data)); } Ok(Self { versions, @@ -105,9 +105,7 @@ impl<'a> DataChunkView<'a> { } } impl<'a> TypedObject for DataChunkView<'a> { - fn get_type() -> SerializeType { - SerializeType::DataChunk - } + const TYPE: SerializeType = DataChunk::TYPE; } impl<'a> Viewable<'a> for DataChunkView<'a> { fn read_from( diff --git a/src/cursor/header.rs b/src/cursor/header.rs index cb8fbfc..bc53978 100644 --- a/src/cursor/header.rs +++ b/src/cursor/header.rs @@ -36,9 +36,7 @@ impl TreeHeader { } impl TypedObject for TreeHeader { - fn get_type() -> SerializeType { - SerializeType::Header - } + const TYPE: SerializeType = SerializeType::Header; } impl Serializable for TreeHeader { diff --git a/src/cursor/internal.rs b/src/cursor/internal.rs index d85b6f8..d534b6b 100644 --- a/src/cursor/internal.rs +++ b/src/cursor/internal.rs @@ -23,7 +23,7 @@ impl InternalNode { if scanner.read()? == 1 { let ptr = scanner.read_u64()?; let len = scanner.read_u16()? as usize; - let key = scanner.read_n(len)?.to_vec(); + let key = scanner.read_n(len)?.into(); right = Some((ptr, key)); }; @@ -31,7 +31,7 @@ impl InternalNode { let mut keys = Vec::with_capacity(len); for _ in 0..len { let l = scanner.read_u16()? as usize; - keys.push(scanner.read_n(l)?.to_vec()); + keys.push(scanner.read_n(l)?.into()); } let mut children = Vec::with_capacity(len + 1); diff --git a/src/cursor/leaf.rs b/src/cursor/leaf.rs index c491273..2283f4f 100644 --- a/src/cursor/leaf.rs +++ b/src/cursor/leaf.rs @@ -4,6 +4,7 @@ use super::{StaticKey, StaticKeyRef}; use crate::{ disk::{Page, PageScanner, PageWriter, Pointer, POINTER_BYTES}, serialize::SERIALIZABLE_BYTES, + utils::InlineVec, Result, }; @@ -49,7 +50,7 @@ impl LeafNode { let mut entries = Vec::with_capacity(len); for _ in 0..len { let l = scanner.read_u16()? as usize; - let key = scanner.read_n(l)?.to_vec(); + let key = scanner.read_n(l)?.into(); let ptr = scanner.read_u64()?; entries.push((key, ptr)); } @@ -145,7 +146,7 @@ impl<'a> LeafNodeView<'a> { pub fn writable(self) -> LeafNode { let mut entries = Vec::with_capacity(self.entries.len() + 1); for (s, e, p) in self.entries { - entries.push((self.page.copy_range(s..e), p)) + entries.push((InlineVec::from(self.page.range(s..e)), p)) } LeafNode::new(entries, self.next) } diff --git a/src/cursor/node.rs b/src/cursor/node.rs index 56b2ebf..b0e19d8 100644 --- a/src/cursor/node.rs +++ b/src/cursor/node.rs @@ -10,9 +10,7 @@ pub enum BTreeNodeView<'a> { Leaf(LeafNodeView<'a>), } impl<'a> TypedObject for BTreeNodeView<'a> { - fn get_type() -> SerializeType { - SerializeType::BTreeNode - } + const TYPE: SerializeType = BTreeNode::TYPE; } impl<'a> Viewable<'a> for BTreeNodeView<'a> { @@ -69,9 +67,7 @@ impl BTreeNode { } } impl TypedObject for BTreeNode { - fn get_type() -> SerializeType { - SerializeType::BTreeNode - } + const TYPE: SerializeType = SerializeType::BTreeNode; } impl Serializable for BTreeNode { fn write_at(&self, writer: &mut PageWriter) -> Result { diff --git a/src/cursor/tests/entry.rs b/src/cursor/tests/entry.rs index 4617e90..9228113 100644 --- a/src/cursor/tests/entry.rs +++ b/src/cursor/tests/entry.rs @@ -1,4 +1,4 @@ -use crate::{disk::Page, serialize::SerializeFrom}; +use crate::{disk::Page, inline_vec, serialize::SerializeFrom}; use super::*; @@ -47,7 +47,7 @@ fn test_entry_with_tombstone_roundtrip() { #[test] fn test_entry_with_chunked_roundtrip() { let mut page = Page::new(); - let pointers = vec![10, 20, 30, 500]; + let pointers = inline_vec![10, 20, 30, 500]; let owner = 2; let entry = DataEntry::init(VersionRecord::new( 2, diff --git a/src/cursor/tests/node.rs b/src/cursor/tests/node.rs index fffaa58..97d90ec 100644 --- a/src/cursor/tests/node.rs +++ b/src/cursor/tests/node.rs @@ -1,4 +1,4 @@ -use crate::{disk::Page, serialize::SerializeFrom}; +use crate::{disk::Page, inline_vec, serialize::SerializeFrom}; use super::*; @@ -25,7 +25,7 @@ fn test_serialize_internal() { fn test_serialize_leaf() { let mut page = Page::new(); - let entries = vec![(vec![49, 50, 51], 100)]; + let entries = vec![(inline_vec![49, 50, 51], 100)]; let next = Some(1100); let node = BTreeNode::Leaf(LeafNode::new(entries.clone(), next)); @@ -37,7 +37,7 @@ fn test_serialize_leaf() { .as_leaf() .expect("desirialize leaf error"); for (i, (k, p)) in d.get_entries().enumerate() { - assert_eq!(entries[i].0, k.to_vec()); + assert_eq!(entries[i].0, k.into()); assert_eq!(entries[i].1, p) } assert_eq!(d.get_next(), next) @@ -46,9 +46,9 @@ fn test_serialize_leaf() { #[test] fn test_serialize_internal_with_keys_and_right() { let mut page = Page::new(); - let keys = vec![vec![1, 2], vec![3, 4]]; + let keys = vec![inline_vec![1, 2], inline_vec![3, 4]]; let children = vec![10, 20, 30]; - let next = Some((99, vec![5, 6])); + let next = Some((99, inline_vec![5, 6])); let node = BTreeNode::Internal(InternalNode::new( keys.clone(), children.clone(), @@ -56,7 +56,7 @@ fn test_serialize_internal_with_keys_and_right() { )); page.serialize_from(&node).expect("serialize error"); - let d = match page.view::().expect("desiralize error") { + let d = match page.view::().expect("deserialize error") { BTreeNodeView::Internal(node) => node, BTreeNodeView::Leaf(_) => panic!("must be internal"), }; diff --git a/src/cursor/types.rs b/src/cursor/types.rs index 9c663fe..c795394 100644 --- a/src/cursor/types.rs +++ b/src/cursor/types.rs @@ -1,8 +1,8 @@ use std::ops::Deref; -use crate::cache::ReadonlySlot; +use crate::{cache::ReadonlySlot, utils::InlineVec}; -pub type StaticKey = Vec; +pub type StaticKey = InlineVec; pub type StaticKeyRef<'a> = &'a [u8]; enum Type { diff --git a/src/disk/page.rs b/src/disk/page.rs index 8d6d7b0..e5580a1 100644 --- a/src/disk/page.rs +++ b/src/disk/page.rs @@ -45,9 +45,8 @@ impl Page { #[inline] pub fn copy_range(&self, range: Range) -> Vec { let len = range.end - range.start; - let mut data = Vec::with_capacity(len); + let mut data = vec![0; len]; unsafe { copy_nonoverlapping(self.0.add(range.start), data.as_mut_ptr(), len) }; - unsafe { data.set_len(len) }; data } #[inline] diff --git a/src/serialize/mod.rs b/src/serialize/mod.rs index 6812397..5d747dd 100644 --- a/src/serialize/mod.rs +++ b/src/serialize/mod.rs @@ -29,7 +29,7 @@ impl SerializeType { pub const SERIALIZABLE_BYTES: usize = PAGE_SIZE - 1; // 1 byte reserved for SerializeType tag pub trait TypedObject { - fn get_type() -> SerializeType; + const TYPE: SerializeType; } pub trait Deserializable: Sized + TypedObject { @@ -37,7 +37,7 @@ pub trait Deserializable: Sized + TypedObject { fn deserialize(value: &Page) -> Result { let mut reader = value.scanner(); - let expected = Self::get_type().byte(); + let expected = Self::TYPE.byte(); let received = reader.read()?; if expected != received { return Err(Error::DeserializeError(expected, received)); @@ -50,7 +50,7 @@ pub trait Deserializable: Sized + TypedObject { pub trait Serializable: Sized + TypedObject { fn serialize_at(&self, page: &mut Page) -> Result { let mut writer = page.writer(); - writer.write(&[Self::get_type().byte()])?; + writer.write(&[Self::TYPE.byte()])?; self.write_at(&mut writer)?; Ok(writer.finalize()) } @@ -85,7 +85,7 @@ pub trait Viewable<'a>: Sized + TypedObject { fn view(page: &'a Page) -> Result { let mut scanner = page.scanner(); - let expected = Self::get_type().byte(); + let expected = Self::TYPE.byte(); let received = scanner.read()?; if expected != received { return Err(Error::DeserializeError(expected, received)); diff --git a/src/utils/inline_vec.rs b/src/utils/inline_vec.rs new file mode 100644 index 0000000..75f76df --- /dev/null +++ b/src/utils/inline_vec.rs @@ -0,0 +1,398 @@ +use std::{ + fmt::Debug, + marker::PhantomData, + mem::MaybeUninit, + ops::{Deref, DerefMut, Index, IndexMut}, + ptr::copy_nonoverlapping, + slice::{from_raw_parts, from_raw_parts_mut}, + vec::IntoIter, +}; + +#[macro_export] +macro_rules! inline_vec { + () => { + $crate::utils::InlineVec::new() + }; + ($elem:expr; $n:expr) => { + $crate::utils::InlineVec::from_elem($elem, $n) + }; + ($($x:expr),+ $(,)?) => { + $crate::utils::InlineVec::from([$($x),+].as_slice()) + }; +} + +struct Array { + data: [MaybeUninit; N], + len: usize, +} +impl Array { + const fn new() -> Self { + Self { + data: [const { MaybeUninit::uninit() }; N], + len: 0, + } + } + fn push(&mut self, value: T) -> std::result::Result<(), T> { + if self.len >= N { + return Err(value); + } + self.data[self.len].write(value); + self.len += 1; + Ok(()) + } + fn pop(&mut self) -> Option { + if self.len == 0 { + return None; + } + self.len -= 1; + Some(unsafe { self.data[self.len].assume_init_read() }) + } + #[inline] + const fn as_ptr(&self) -> *const T { + self.data.as_ptr() as _ + } + #[inline] + const fn as_mut_ptr(&mut self) -> *mut T { + self.data.as_mut_ptr() as _ + } + #[inline] + const fn as_slice(&self) -> &[T] { + unsafe { from_raw_parts(self.as_ptr(), self.len) } + } + #[inline] + const fn as_mut_slice(&mut self) -> &mut [T] { + unsafe { from_raw_parts_mut(self.as_mut_ptr(), self.len) } + } + #[inline] + const fn len(&self) -> usize { + self.len + } + fn into_vec(mut self) -> Vec { + let mut vector = Vec::with_capacity(self.len); + unsafe { vector.set_len(self.len) }; + unsafe { copy_nonoverlapping(self.as_ptr(), vector.as_mut_ptr(), self.len) }; + self.len = 0; + vector + } +} +impl Drop for Array { + fn drop(&mut self) { + for i in 0..self.len { + unsafe { self.data[i].assume_init_drop() }; + } + } +} +impl Clone for Array { + fn clone(&self) -> Self { + let mut data = [const { MaybeUninit::uninit() }; N]; + for i in 0..self.len { + data[i].write(unsafe { self.data[i].assume_init_ref() }.clone()); + } + Self { + data, + len: self.len, + } + } +} +impl IntoIterator for Array { + type Item = T; + + type IntoIter = ArrayIntoIter; + + fn into_iter(mut self) -> Self::IntoIter { + let mut data = [const { MaybeUninit::uninit() }; N]; + let len = self.len; + unsafe { copy_nonoverlapping(self.as_ptr(), data.as_mut_ptr() as *mut T, self.len) }; + self.len = 0; + Self::IntoIter { + data, + len, + current: 0, + } + } +} + +pub struct ArrayIntoIter { + data: [MaybeUninit; N], + len: usize, + current: usize, +} +impl Iterator for ArrayIntoIter { + type Item = T; + + fn next(&mut self) -> Option { + if self.len == self.current { + return None; + } + let value = unsafe { self.data[self.current].assume_init_read() }; + self.current += 1; + Some(value) + } +} +impl Drop for ArrayIntoIter { + fn drop(&mut self) { + for i in self.current..self.len { + unsafe { self.data[i].assume_init_drop() }; + } + } +} + +enum Type { + Inline(Array), + Heap(Vec), +} + +pub struct InlineVec(Type); +impl InlineVec { + #[doc(hidden)] + #[allow(unused)] + pub fn from_elem(elem: T, n: usize) -> Self + where + T: Clone, + { + if n > N { + return Self(Type::Heap(vec![elem; n])); + } + let mut array = Array::new(); + let ptr: *mut T = array.as_mut_ptr(); + for i in 0..(n - 1) { + unsafe { ptr.add(i).write(elem.clone()) }; + } + if n > 0 { + unsafe { ptr.add(n - 1).write(elem) }; + } + array.len = n; + Self(Type::Inline(array)) + } + + pub fn new() -> Self { + Self(Type::Inline(Array::new())) + } + + pub fn with_capacity(capacity: usize) -> Self { + if capacity > N { + Self(Type::Heap(Vec::with_capacity(capacity))) + } else { + Self::new() + } + } + + pub fn push(&mut self, value: T) { + let (array, value) = match &mut self.0 { + Type::Inline(array) => match array.push(value) { + Ok(_) => return, + Err(value) => (array, value), + }, + Type::Heap(vector) => return vector.push(value), + }; + + let mut grown = Vec::with_capacity(array.len << 1); + unsafe { grown.set_len(array.len + 1) }; + unsafe { copy_nonoverlapping(array.as_ptr(), grown.as_mut_ptr(), array.len()) }; + unsafe { grown.as_mut_ptr().add(array.len).write(value) }; + array.len = 0; + self.0 = Type::Heap(grown) + } + pub fn pop(&mut self) -> Option { + match &mut self.0 { + Type::Inline(array) => array.pop(), + Type::Heap(vector) => vector.pop(), + } + } + pub const fn len(&self) -> usize { + match &self.0 { + Type::Inline(array) => array.len(), + Type::Heap(vector) => vector.len(), + } + } + pub const fn as_ptr(&self) -> *const T { + match &self.0 { + Type::Inline(array) => array.as_ptr(), + Type::Heap(vector) => vector.as_ptr(), + } + } + pub const fn as_mut_ptr(&mut self) -> *mut T { + match &mut self.0 { + Type::Inline(array) => array.as_mut_ptr(), + Type::Heap(vector) => vector.as_mut_ptr(), + } + } + pub fn as_slice(&self) -> &[T] { + match &self.0 { + Type::Inline(array) => array.as_slice(), + Type::Heap(vector) => vector.as_slice(), + } + } + + pub fn iter(&self) -> InlineVecIter<'_, T, N> { + InlineVecIter { + ptr: self.as_ptr(), + size: self.len(), + current: 0, + _marker: PhantomData, + } + } +} +impl Deref for InlineVec { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} +impl DerefMut for InlineVec { + fn deref_mut(&mut self) -> &mut Self::Target { + match &mut self.0 { + Type::Inline(array) => array.as_mut_slice(), + Type::Heap(vector) => vector.as_mut_slice(), + } + } +} +impl Index for InlineVec { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + assert!(index < self.len()); + unsafe { &*self.as_ptr().add(index) } + } +} +impl IndexMut for InlineVec { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + assert!(index < self.len()); + unsafe { &mut *self.as_mut_ptr().add(index) } + } +} +impl Index> for InlineVec { + type Output = [T]; + + fn index(&self, index: std::ops::Range) -> &Self::Output { + assert!(index.start <= index.end); + let len = self.len(); + assert!(index.start < len); + assert!(index.end <= len); + unsafe { from_raw_parts(self.as_ptr().add(index.start), index.end - index.start) } + } +} +impl IndexMut> for InlineVec { + fn index_mut(&mut self, index: std::ops::Range) -> &mut Self::Output { + assert!(index.start <= index.end); + let len = self.len(); + assert!(index.start < len); + assert!(index.end <= len); + unsafe { + from_raw_parts_mut(self.as_mut_ptr().add(index.start), index.end - index.start) + } + } +} +impl From> for InlineVec { + fn from(value: Vec) -> Self { + Self(Type::Heap(value)) + } +} +impl From> for Vec { + fn from(value: InlineVec) -> Self { + match value.0 { + Type::Inline(array) => array.into_vec(), + Type::Heap(vector) => vector, + } + } +} +impl PartialEq for InlineVec { + fn eq(&self, other: &Self) -> bool { + self.as_slice().eq(other.as_slice()) + } +} +impl Eq for InlineVec {} +impl PartialOrd for InlineVec { + fn partial_cmp(&self, other: &Self) -> Option { + self.as_slice().partial_cmp(other.as_slice()) + } +} +impl Ord for InlineVec { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.as_slice().cmp(other.as_slice()) + } +} +impl From<&[T]> for InlineVec { + fn from(value: &[T]) -> Self { + if value.len() > N { + return Self(Type::Heap(value.to_vec())); + } + let mut array = Array::new(); + unsafe { copy_nonoverlapping(value.as_ptr(), array.as_mut_ptr(), value.len()) }; + array.len = value.len(); + Self(Type::Inline(array)) + } +} +impl Debug for InlineVec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.deref().fmt(f) + } +} +impl Clone for InlineVec { + fn clone(&self) -> Self { + match &self.0 { + Type::Inline(array) => Self(Type::Inline(Clone::clone(array))), + Type::Heap(vector) => Self(Type::Heap(Clone::clone(vector))), + } + } +} + +impl IntoIterator for InlineVec { + type Item = T; + + type IntoIter = InlineVecIntoIter; + + fn into_iter(self) -> Self::IntoIter { + match self.0 { + Type::Inline(array) => InlineVecIntoIter::Inline(array.into_iter()), + Type::Heap(vector) => InlineVecIntoIter::Heap(vector.into_iter()), + } + } +} +pub enum InlineVecIntoIter { + Inline(ArrayIntoIter), + Heap(IntoIter), +} +impl Iterator for InlineVecIntoIter { + type Item = T; + + fn next(&mut self) -> Option { + match self { + InlineVecIntoIter::Inline(iter) => iter.next(), + InlineVecIntoIter::Heap(iter) => iter.next(), + } + } +} +pub struct InlineVecIter<'a, T, const N: usize> { + ptr: *const T, + size: usize, + current: usize, + _marker: PhantomData<&'a InlineVec>, +} +impl<'a, T, const N: usize> Iterator for InlineVecIter<'a, T, N> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.current == self.size { + return None; + } + + let value = unsafe { &*self.ptr.add(self.current) }; + self.current += 1; + Some(value) + } +} +impl<'a, T, const N: usize> IntoIterator for &'a InlineVec { + type Item = &'a T; + + type IntoIter = InlineVecIter<'a, T, N>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[cfg(test)] +#[path = "tests/inline_vec.rs"] +mod tests; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 6881c13..a9e1687 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -26,3 +26,6 @@ mod log; mod atomic; pub use atomic::*; + +mod inline_vec; +pub use inline_vec::*; diff --git a/src/utils/tests/inline_vec.rs b/src/utils/tests/inline_vec.rs new file mode 100644 index 0000000..2a1bdd1 --- /dev/null +++ b/src/utils/tests/inline_vec.rs @@ -0,0 +1,354 @@ +use super::*; +use std::cell::Cell; +use std::rc::Rc; +struct Tracker { + counter: Rc>, +} +impl Tracker { + fn new(counter: Rc>) -> Self { + Self { counter } + } +} +impl Drop for Tracker { + fn drop(&mut self) { + self.counter.set(self.counter.get() + 1); + } +} +impl Clone for Tracker { + fn clone(&self) -> Self { + Self::new(self.counter.clone()) + } +} +#[test] +fn test_new_is_empty() { + let v: InlineVec = InlineVec::new(); + assert_eq!(v.len(), 0); +} +#[test] +fn test_push_pop_inline() { + let mut v: InlineVec = InlineVec::new(); + v.push(1); + v.push(2); + v.push(3); + assert_eq!(v.len(), 3); + assert_eq!(v.pop(), Some(3)); + assert_eq!(v.pop(), Some(2)); + assert_eq!(v.pop(), Some(1)); + assert_eq!(v.pop(), None); +} +#[test] +fn test_push_until_full_inline() { + let mut v: InlineVec = InlineVec::new(); + for i in 0..4 { + v.push(i); + } + assert_eq!(v.len(), 4); + assert_eq!(&*v, &[0, 1, 2, 3]); +} +#[test] +fn test_push_beyond_n() { + let mut v: InlineVec = InlineVec::new(); + for i in 0..6 { + v.push(i); + } + assert_eq!(v.len(), 6); + assert_eq!(&*v, &[0, 1, 2, 3, 4, 5]); +} +#[test] +fn test_pop_after_promotion() { + let mut v: InlineVec = InlineVec::new(); + for i in 0..5 { + v.push(i); + } + assert_eq!(v.pop(), Some(4)); + assert_eq!(v.pop(), Some(3)); + assert_eq!(v.pop(), Some(2)); + assert_eq!(v.pop(), Some(1)); + assert_eq!(v.pop(), Some(0)); + assert_eq!(v.pop(), None); +} +#[test] +fn test_index_usize() { + let mut v: InlineVec = InlineVec::new(); + for i in 0..3 { + v.push(i * 10); + } + assert_eq!(v[0], 0); + assert_eq!(v[1], 10); + assert_eq!(v[2], 20); +} +#[test] +#[should_panic] +fn test_index_oob() { + let v: InlineVec = InlineVec::new(); + let _ = v[0]; +} +#[test] +fn test_index_mut() { + let mut v: InlineVec = InlineVec::new(); + v.push(1); + v.push(2); + v[0] = 100; + assert_eq!(v[0], 100); + assert_eq!(v[1], 2); +} +#[test] +fn test_index_range() { + let mut v: InlineVec = InlineVec::new(); + for i in 0..4 { + v.push(i); + } + assert_eq!(&v[0..4], &[0, 1, 2, 3]); + assert_eq!(&v[1..3], &[1, 2]); +} +#[test] +fn test_index_mut_range() { + let mut v: InlineVec = inline_vec!(1, 2, 3, 4, 5); + v[2..4].copy_from_slice(&[6, 7]); + assert_eq!(&*v, &[1, 2, 6, 7, 5]) +} +#[test] +#[should_panic] +fn test_index_range_oob() { + let mut v: InlineVec = InlineVec::new(); + v.push(0); + let _ = &v[0..2]; +} +#[test] +fn test_from_slice_inline() { + let v: InlineVec = InlineVec::from(&[1, 2, 3][..]); + assert_eq!(v.len(), 3); + assert_eq!(&*v, &[1, 2, 3]); +} +#[test] +fn test_from_slice_overflow_to_heap() { + let v: InlineVec = InlineVec::from(&[1, 2, 3, 4, 5][..]); + assert_eq!(v.len(), 5); + assert_eq!(&*v, &[1, 2, 3, 4, 5]); +} +#[test] +fn test_from_vec() { + let v: InlineVec = InlineVec::from(vec![1, 2, 3]); + assert_eq!(&*v, &[1, 2, 3]); +} +#[test] +fn test_macro_empty() { + let v: InlineVec = inline_vec![]; + assert_eq!(v.len(), 0); +} +#[test] +fn test_macro_elements() { + let v: InlineVec = inline_vec![1, 2, 3]; + assert_eq!(&*v, &[1, 2, 3]); +} +#[test] +fn test_macro_repeated() { + let v: InlineVec = inline_vec![7; 3]; + assert_eq!(v.len(), 3); + assert_eq!(&*v, &[7, 7, 7]); +} +#[test] +fn test_into_iter_inline() { + let mut v: InlineVec = InlineVec::new(); + for i in 1..=3 { + v.push(i); + } + let collected: Vec<_> = v.into_iter().collect(); + assert_eq!(collected, vec![1, 2, 3]); +} +#[test] +fn test_into_iter_heap() { + let v: InlineVec = InlineVec::from(vec![1, 2, 3, 4]); + let collected: Vec<_> = v.into_iter().collect(); + assert_eq!(collected, vec![1, 2, 3, 4]); +} +#[test] +fn test_into_iter_empty() { + let v: InlineVec = InlineVec::new(); + let collected: Vec<_> = v.into_iter().collect(); + assert!(collected.is_empty()); +} +#[test] +fn test_drop_inline_all_elements() { + let counter = Rc::new(Cell::new(0)); + { + let mut v: InlineVec = InlineVec::new(); + v.push(Tracker::new(counter.clone())); + v.push(Tracker::new(counter.clone())); + v.push(Tracker::new(counter.clone())); + assert_eq!(counter.get(), 0); + } + assert_eq!(counter.get(), 3); +} +#[test] +fn test_drop_heap_all_elements() { + let counter = Rc::new(Cell::new(0)); + { + let mut v: InlineVec = InlineVec::new(); + for _ in 0..5 { + v.push(Tracker::new(counter.clone())); + } + assert_eq!(counter.get(), 0); + } + assert_eq!(counter.get(), 5); +} +#[test] +fn test_drop_pop_inline() { + let counter = Rc::new(Cell::new(0)); + let mut v: InlineVec = InlineVec::new(); + v.push(Tracker::new(counter.clone())); + v.push(Tracker::new(counter.clone())); + let popped = v.pop().unwrap(); + assert_eq!(counter.get(), 0); + drop(popped); + assert_eq!(counter.get(), 1); + drop(v); + assert_eq!(counter.get(), 2); +} +#[test] +fn test_drop_pop_heap() { + let counter = Rc::new(Cell::new(0)); + let mut v: InlineVec = InlineVec::new(); + for _ in 0..5 { + v.push(Tracker::new(counter.clone())); + } + let popped = v.pop().unwrap(); + assert_eq!(counter.get(), 0); + drop(popped); + assert_eq!(counter.get(), 1); + drop(v); + assert_eq!(counter.get(), 5); +} +#[test] +fn test_drop_no_double_drop_on_promotion() { + let counter = Rc::new(Cell::new(0)); + { + let mut v: InlineVec = InlineVec::new(); + v.push(Tracker::new(counter.clone())); + v.push(Tracker::new(counter.clone())); + v.push(Tracker::new(counter.clone())); + assert_eq!(v.len(), 3); + assert_eq!(counter.get(), 0); + } + assert_eq!(counter.get(), 3); +} +#[test] +fn test_drop_no_double_drop() { + let counter = Rc::new(Cell::new(0)); + { + let mut v: InlineVec = InlineVec::new(); + v.push(Tracker::new(counter.clone())); + v.push(Tracker::new(counter.clone())); + v.push(Tracker::new(counter.clone())); + assert_eq!(v.len(), 3); + assert_eq!(counter.get(), 0); + } + assert_eq!(counter.get(), 3); +} +#[test] +fn test_drop_no_leak_on_multiple_grows() { + let counter = Rc::new(Cell::new(0)); + { + let mut v: InlineVec = InlineVec::new(); + for _ in 0..50 { + v.push(Tracker::new(counter.clone())); + } + assert_eq!(counter.get(), 0); + } + assert_eq!(counter.get(), 50); +} +#[test] +fn test_drop_promotion_at_n_eq_1() { + let counter = Rc::new(Cell::new(0)); + { + let mut v: InlineVec = InlineVec::new(); + v.push(Tracker::new(counter.clone())); + v.push(Tracker::new(counter.clone())); + assert_eq!(counter.get(), 0); + } + assert_eq!(counter.get(), 2); +} +#[test] +fn test_drop_into_iter_full_consume() { + let counter = Rc::new(Cell::new(0)); + let mut v: InlineVec = InlineVec::new(); + for _ in 0..3 { + v.push(Tracker::new(counter.clone())); + } + let collected: Vec<_> = v.into_iter().collect(); + assert_eq!(counter.get(), 0); + drop(collected); + assert_eq!(counter.get(), 3); +} +#[test] +fn test_drop_into_iter_partial_inline() { + let counter = Rc::new(Cell::new(0)); + let mut v: InlineVec = InlineVec::new(); + for _ in 0..4 { + v.push(Tracker::new(counter.clone())); + } + let mut iter = v.into_iter(); + let first = iter.next().unwrap(); + let second = iter.next().unwrap(); + assert_eq!(counter.get(), 0); + drop(iter); + assert_eq!(counter.get(), 2); + drop(first); + drop(second); + assert_eq!(counter.get(), 4); +} +#[test] +fn test_drop_into_iter_partial_heap() { + let counter = Rc::new(Cell::new(0)); + let mut v: InlineVec = InlineVec::new(); + for _ in 0..5 { + v.push(Tracker::new(counter.clone())); + } + let mut iter = v.into_iter(); + let first = iter.next().unwrap(); + drop(iter); + assert_eq!(counter.get(), 4); + drop(first); + assert_eq!(counter.get(), 5); +} +#[test] +fn test_drop_from_vec() { + let counter = Rc::new(Cell::new(0)); + { + let trackers = (0..3) + .map(|_| Tracker::new(counter.clone())) + .collect::>(); + let _v: InlineVec = InlineVec::from(trackers); + assert_eq!(counter.get(), 0); + } + assert_eq!(counter.get(), 3); +} +#[test] +fn test_drop_empty_vec() { + let v: InlineVec = InlineVec::new(); + drop(v); +} +#[test] +fn test_clone_without_promotion() { + let counter = Rc::new(Cell::new(0)); + { + let mut v: InlineVec = inline_vec!(); + for _ in 0..5 { + v.push(Tracker::new(counter.clone())) + } + let _vv = v.clone(); + } + assert_eq!(counter.get(), 10); +} +#[test] +fn test_clone_on_promotion() { + let counter = Rc::new(Cell::new(0)); + { + let mut v: InlineVec = inline_vec!(); + for _ in 0..5 { + v.push(Tracker::new(counter.clone())) + } + let _vv = v.clone(); + } + assert_eq!(counter.get(), 10); +} diff --git a/tests/e2e.rs b/tests/e2e.rs index b4dbbc5..3c217cc 100644 --- a/tests/e2e.rs +++ b/tests/e2e.rs @@ -1710,8 +1710,8 @@ fn test_auto_compaction() { while let Some((k, v)) = iter.try_next().unwrap() { assert_eq!( - data.get(&k as &[_]).map(|v| v as &[_]), - Some(&v as &[_]), + data.get(&*k).map(|v| &**v), + Some(&*v), "table {name} key {k:?} not matched" ); c += 1 From 7ea8ce93faa23e06f8459e15f0e02ccbf853fec5 Mon Sep 17 00:00:00 2001 From: qwp0905 Date: Sun, 10 May 2026 09:53:33 +0900 Subject: [PATCH 2/2] fix typo --- src/disk/page.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/disk/page.rs b/src/disk/page.rs index e5580a1..8d6d7b0 100644 --- a/src/disk/page.rs +++ b/src/disk/page.rs @@ -45,8 +45,9 @@ impl Page { #[inline] pub fn copy_range(&self, range: Range) -> Vec { let len = range.end - range.start; - let mut data = vec![0; len]; + let mut data = Vec::with_capacity(len); unsafe { copy_nonoverlapping(self.0.add(range.start), data.as_mut_ptr(), len) }; + unsafe { data.set_len(len) }; data } #[inline]