Skip to main content

vector/serde/
centroid_stats.rs

1//! CentroidStats value encoding/decoding.
2//!
3//! Stores the number of vectors assigned to a centroid. Used as a merge-based
4//! counter: each write is a delta that gets summed by the merge operator.
5//!
6//! ## Value Layout
7//!
8//! ```text
9//! +-------------------------------+
10//! |  num_vectors: i32 LE (4B)     |
11//! +-------------------------------+
12//! ```
13
14use super::EncodingError;
15use bytes::{Bytes, BytesMut};
16
17/// Per-centroid vector count, stored as an i32 delta for merge-based summation.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct CentroidStatsValue {
20    pub num_vectors: i32,
21}
22
23impl CentroidStatsValue {
24    pub fn new(num_vectors: i32) -> Self {
25        Self { num_vectors }
26    }
27
28    pub fn encode_to_bytes(&self) -> Bytes {
29        let mut buf = BytesMut::with_capacity(4);
30        buf.extend_from_slice(&self.num_vectors.to_le_bytes());
31        buf.freeze()
32    }
33
34    pub fn decode_from_bytes(buf: &[u8]) -> Result<Self, EncodingError> {
35        if buf.len() < 4 {
36            return Err(EncodingError {
37                message: format!(
38                    "Buffer too short for CentroidStatsValue: expected 4 bytes, got {}",
39                    buf.len()
40                ),
41            });
42        }
43        let num_vectors = i32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
44        Ok(Self { num_vectors })
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn should_encode_and_decode_positive_value() {
54        // given
55        let value = CentroidStatsValue::new(42);
56
57        // when
58        let encoded = value.encode_to_bytes();
59        let decoded = CentroidStatsValue::decode_from_bytes(&encoded).unwrap();
60
61        // then
62        assert_eq!(decoded, value);
63        assert_eq!(encoded.len(), 4);
64    }
65
66    #[test]
67    fn should_encode_and_decode_zero() {
68        // given
69        let value = CentroidStatsValue::new(0);
70
71        // when
72        let encoded = value.encode_to_bytes();
73        let decoded = CentroidStatsValue::decode_from_bytes(&encoded).unwrap();
74
75        // then
76        assert_eq!(decoded, value);
77    }
78
79    #[test]
80    fn should_encode_and_decode_negative_value() {
81        // given
82        let value = CentroidStatsValue::new(-5);
83
84        // when
85        let encoded = value.encode_to_bytes();
86        let decoded = CentroidStatsValue::decode_from_bytes(&encoded).unwrap();
87
88        // then
89        assert_eq!(decoded, value);
90    }
91
92    #[test]
93    fn should_reject_short_buffer() {
94        // given
95        let buf = [0u8; 3];
96
97        // when
98        let result = CentroidStatsValue::decode_from_bytes(&buf);
99
100        // then
101        assert!(result.is_err());
102    }
103}