Skip to main content

reddb_file/
bloom_segment.rs

1//! Persisted bloom-segment frame shared by storage/index owners.
2
3use std::fmt;
4
5use crate::BLOOM_SEGMENT_V2_MAGIC;
6
7pub const BLOOM_SEGMENT_HEADER_LEN: usize = 1 + 1 + 4;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum BloomSegmentFrameError {
11    TooShort,
12    BadMagic,
13    LengthMismatch,
14}
15
16impl fmt::Display for BloomSegmentFrameError {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        match self {
19            Self::TooShort => write!(f, "bloom header too short"),
20            Self::BadMagic => write!(f, "bloom header magic mismatch"),
21            Self::LengthMismatch => write!(f, "bloom header length mismatch"),
22        }
23    }
24}
25
26impl std::error::Error for BloomSegmentFrameError {}
27
28pub fn encode_bloom_segment_frame(inserted: u32, bloom_blob: &[u8]) -> Vec<u8> {
29    let mut out = Vec::with_capacity(BLOOM_SEGMENT_HEADER_LEN + bloom_blob.len());
30    out.push(BLOOM_SEGMENT_V2_MAGIC);
31    out.push(0);
32    out.extend_from_slice(&inserted.to_be_bytes());
33    out.extend_from_slice(bloom_blob);
34    out
35}
36
37pub fn decode_bloom_segment_frame(
38    bytes: &[u8],
39) -> Result<(u32, Vec<u8>, usize), BloomSegmentFrameError> {
40    if bytes.len() < BLOOM_SEGMENT_HEADER_LEN {
41        return Err(BloomSegmentFrameError::TooShort);
42    }
43    if bytes[0] != BLOOM_SEGMENT_V2_MAGIC {
44        return Err(BloomSegmentFrameError::BadMagic);
45    }
46
47    let inserted = u32::from_be_bytes(bytes[2..6].try_into().expect("len checked"));
48    if bytes.len() < BLOOM_SEGMENT_HEADER_LEN + 4 {
49        return Err(BloomSegmentFrameError::LengthMismatch);
50    }
51    let num_blocks = u32::from_le_bytes(bytes[6..10].try_into().expect("len checked")) as usize;
52    if num_blocks == 0 || !num_blocks.is_power_of_two() {
53        return Err(BloomSegmentFrameError::LengthMismatch);
54    }
55    let payload_len = 4 + num_blocks * 32;
56    let total = BLOOM_SEGMENT_HEADER_LEN + payload_len;
57    if bytes.len() < total {
58        return Err(BloomSegmentFrameError::LengthMismatch);
59    }
60
61    Ok((
62        inserted,
63        bytes[BLOOM_SEGMENT_HEADER_LEN..total].to_vec(),
64        total,
65    ))
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn bloom_segment_frame_round_trips_v2_header() {
74        let mut bloom_blob = Vec::new();
75        bloom_blob.extend_from_slice(&1u32.to_le_bytes());
76        bloom_blob.extend_from_slice(&[0xAB; 32]);
77
78        let encoded = encode_bloom_segment_frame(42, &bloom_blob);
79
80        assert_eq!(encoded[0], BLOOM_SEGMENT_V2_MAGIC);
81        assert_eq!(encoded[1], 0);
82        assert_eq!(&encoded[2..6], &42u32.to_be_bytes());
83
84        let (inserted, decoded_blob, consumed) =
85            decode_bloom_segment_frame(&encoded).expect("decode frame");
86        assert_eq!(inserted, 42);
87        assert_eq!(decoded_blob, bloom_blob);
88        assert_eq!(consumed, encoded.len());
89    }
90
91    #[test]
92    fn bloom_segment_frame_rejects_bad_inputs() {
93        assert_eq!(
94            decode_bloom_segment_frame(&[]).unwrap_err(),
95            BloomSegmentFrameError::TooShort
96        );
97
98        let mut bloom_blob = Vec::new();
99        bloom_blob.extend_from_slice(&1u32.to_le_bytes());
100        bloom_blob.extend_from_slice(&[0xAB; 32]);
101
102        let mut bad_magic = encode_bloom_segment_frame(1, &bloom_blob);
103        bad_magic[0] = 0;
104        assert_eq!(
105            decode_bloom_segment_frame(&bad_magic).unwrap_err(),
106            BloomSegmentFrameError::BadMagic
107        );
108
109        let mut truncated = encode_bloom_segment_frame(1, &bloom_blob);
110        truncated.pop();
111        assert_eq!(
112            decode_bloom_segment_frame(&truncated).unwrap_err(),
113            BloomSegmentFrameError::LengthMismatch
114        );
115    }
116}