Skip to main content

alopex_core/storage/format/
section.rs

1//! セクションエントリとセクションインデックスの定義。
2
3use std::convert::TryInto;
4use std::mem;
5
6use crate::storage::compression::CompressionAlgorithm;
7use crate::storage::format::{FormatError, SECTION_ENTRY_SIZE};
8
9/// セクションタイプ。
10#[repr(u8)]
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum SectionType {
13    /// メタデータセクション。
14    Metadata = 0x00,
15    /// SSTableセクション。
16    SSTable = 0x01,
17    /// ベクターインデックスセクション。
18    VectorIndex = 0x02,
19    /// カラムナーセグメントセクション。
20    ColumnarSegment = 0x03,
21    /// 大型値セクション。
22    LargeValue = 0x04,
23    /// インテントセクション。
24    Intent = 0x05,
25    /// ロックセクション。
26    Lock = 0x06,
27    /// Raftログセクション。
28    RaftLog = 0x07,
29}
30
31impl TryFrom<u8> for SectionType {
32    type Error = FormatError;
33
34    fn try_from(value: u8) -> Result<Self, Self::Error> {
35        match value {
36            0x00 => Ok(Self::Metadata),
37            0x01 => Ok(Self::SSTable),
38            0x02 => Ok(Self::VectorIndex),
39            0x03 => Ok(Self::ColumnarSegment),
40            0x04 => Ok(Self::LargeValue),
41            0x05 => Ok(Self::Intent),
42            0x06 => Ok(Self::Lock),
43            0x07 => Ok(Self::RaftLog),
44            _ => Err(FormatError::IncompleteWrite),
45        }
46    }
47}
48
49/// セクションエントリ(40バイト)。
50#[repr(C)]
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct SectionEntry {
53    /// セクションタイプ。
54    pub section_type: SectionType,
55    /// 圧縮アルゴリズム。
56    pub compression: CompressionAlgorithm,
57    /// パディング(常にゼロ)。
58    pub _padding: [u8; 2],
59    /// セクションID。
60    pub section_id: u32,
61    /// ファイル内オフセット。
62    pub offset: u64,
63    /// 圧縮後サイズ(ファイル上のバイト数)。
64    pub compressed_length: u64,
65    /// 解凍後サイズ。
66    pub uncompressed_length: u64,
67    /// 圧縮後データに対するチェックサム。
68    pub checksum: u32,
69    /// パディング(常にゼロ)。
70    pub _padding2: [u8; 4],
71}
72
73const _: () = assert!(mem::size_of::<SectionEntry>() == SECTION_ENTRY_SIZE);
74
75impl SectionEntry {
76    /// エントリサイズ(バイト)。
77    pub const SIZE: usize = SECTION_ENTRY_SIZE;
78
79    /// 新規エントリを生成する。
80    #[allow(clippy::too_many_arguments)]
81    pub fn new(
82        section_type: SectionType,
83        compression: CompressionAlgorithm,
84        section_id: u32,
85        offset: u64,
86        compressed_length: u64,
87        uncompressed_length: u64,
88        checksum: u32,
89    ) -> Self {
90        Self {
91            section_type,
92            compression,
93            _padding: [0u8; 2],
94            section_id,
95            offset,
96            compressed_length,
97            uncompressed_length,
98            checksum,
99            _padding2: [0u8; 4],
100        }
101    }
102}
103
104/// セクションインデックス。
105#[derive(Debug, Clone, PartialEq, Eq)]
106pub struct SectionIndex {
107    /// セクション数。
108    pub count: u32,
109    /// エントリ一覧。
110    pub entries: Vec<SectionEntry>,
111}
112
113impl Default for SectionIndex {
114    fn default() -> Self {
115        Self::new()
116    }
117}
118
119impl SectionIndex {
120    /// エントリの固定サイズ。
121    pub const ENTRY_SIZE: usize = SECTION_ENTRY_SIZE;
122
123    /// 空のインデックスを作成する。
124    pub fn new() -> Self {
125        Self {
126            count: 0,
127            entries: Vec::new(),
128        }
129    }
130
131    /// エントリを追加する。
132    pub fn add_entry(&mut self, entry: SectionEntry) {
133        self.entries.push(entry);
134        self.count = self.entries.len() as u32;
135    }
136
137    /// タイプでフィルタする。
138    pub fn filter_by_type(&self, section_type: SectionType) -> Vec<&SectionEntry> {
139        self.entries
140            .iter()
141            .filter(|entry| entry.section_type == section_type)
142            .collect()
143    }
144
145    /// IDで検索する。
146    pub fn find_by_id(&self, section_id: u32) -> Option<&SectionEntry> {
147        self.entries
148            .iter()
149            .find(|entry| entry.section_id == section_id)
150    }
151
152    /// バイト列からデシリアライズする。
153    pub fn from_bytes(bytes: &[u8]) -> Result<Self, FormatError> {
154        if bytes.len() < 4 {
155            return Err(FormatError::IncompleteWrite);
156        }
157
158        let count = u32::from_le_bytes(
159            bytes[0..4]
160                .try_into()
161                .expect("fixed slice length for count"),
162        );
163        let expected_size = 4 + count as usize * SECTION_ENTRY_SIZE;
164        if bytes.len() < expected_size {
165            return Err(FormatError::IncompleteWrite);
166        }
167
168        let mut entries = Vec::with_capacity(count as usize);
169        let mut cursor = 4;
170        for _ in 0..count {
171            let section_type = SectionType::try_from(bytes[cursor])?;
172            let compression = parse_compression(bytes[cursor + 1])?;
173
174            let padding = [bytes[cursor + 2], bytes[cursor + 3]];
175            if padding != [0, 0] {
176                return Err(FormatError::IncompleteWrite);
177            }
178
179            let section_id = u32::from_le_bytes(
180                bytes[cursor + 4..cursor + 8]
181                    .try_into()
182                    .expect("fixed slice length for section_id"),
183            );
184            let offset = u64::from_le_bytes(
185                bytes[cursor + 8..cursor + 16]
186                    .try_into()
187                    .expect("fixed slice length for offset"),
188            );
189            let compressed_length = u64::from_le_bytes(
190                bytes[cursor + 16..cursor + 24]
191                    .try_into()
192                    .expect("fixed slice length for compressed_length"),
193            );
194            let uncompressed_length = u64::from_le_bytes(
195                bytes[cursor + 24..cursor + 32]
196                    .try_into()
197                    .expect("fixed slice length for uncompressed_length"),
198            );
199            let checksum = u32::from_le_bytes(
200                bytes[cursor + 32..cursor + 36]
201                    .try_into()
202                    .expect("fixed slice length for checksum"),
203            );
204            let padding2: [u8; 4] = bytes[cursor + 36..cursor + 40]
205                .try_into()
206                .expect("fixed slice length for padding2");
207            if padding2 != [0; 4] {
208                return Err(FormatError::IncompleteWrite);
209            }
210
211            entries.push(SectionEntry {
212                section_type,
213                compression,
214                _padding: [0u8; 2],
215                section_id,
216                offset,
217                compressed_length,
218                uncompressed_length,
219                checksum,
220                _padding2: [0u8; 4],
221            });
222            cursor += SECTION_ENTRY_SIZE;
223        }
224
225        Ok(Self { count, entries })
226    }
227
228    /// バイト列へシリアライズする。
229    pub fn to_bytes(&self) -> Vec<u8> {
230        let count = self.entries.len() as u32;
231        let mut bytes = Vec::with_capacity(self.serialized_size());
232        bytes.extend_from_slice(&count.to_le_bytes());
233
234        for entry in &self.entries {
235            bytes.push(entry.section_type as u8);
236            bytes.push(entry.compression as u8);
237            bytes.extend_from_slice(&[0u8; 2]);
238            bytes.extend_from_slice(&entry.section_id.to_le_bytes());
239            bytes.extend_from_slice(&entry.offset.to_le_bytes());
240            bytes.extend_from_slice(&entry.compressed_length.to_le_bytes());
241            bytes.extend_from_slice(&entry.uncompressed_length.to_le_bytes());
242            bytes.extend_from_slice(&entry.checksum.to_le_bytes());
243            bytes.extend_from_slice(&[0u8; 4]);
244        }
245
246        bytes
247    }
248
249    /// シリアライズ後のサイズ(バイト)を返す。
250    pub fn serialized_size(&self) -> usize {
251        4 + self.entries.len() * SECTION_ENTRY_SIZE
252    }
253}
254
255fn parse_compression(byte: u8) -> Result<CompressionAlgorithm, FormatError> {
256    match byte {
257        0 => Ok(CompressionAlgorithm::None),
258        1 => Ok(CompressionAlgorithm::Snappy),
259        2 => Ok(CompressionAlgorithm::Zstd),
260        3 => Ok(CompressionAlgorithm::Lz4),
261        other => Err(FormatError::UnsupportedCompression { algorithm: other }),
262    }
263}