Skip to main content

gity_ipc/
bincode_serde.rs

1use bincode::Options;
2use thiserror::Error;
3
4/// Maximum allowed message size for IPC communication (1MB)
5pub const MAX_MESSAGE_SIZE: u64 = 1_048_576;
6
7/// Maximum allowed individual log entry size (1MB)
8#[allow(dead_code)]
9pub const MAX_LOG_ENTRY_SIZE: usize = 1_048_576;
10
11/// Returns bincode configuration matching bincode::serialize/deserialize behavior
12pub fn bounded_bincode() -> impl Options {
13    // Use bincode::options() to get the same configuration as bincode::serialize/deserialize
14    bincode::options()
15}
16
17/// Validates that a byte slice does not exceed the maximum allowed size
18pub fn validate_message_size(data: &[u8]) -> Result<(), MessageSizeError> {
19    if data.len() > MAX_MESSAGE_SIZE as usize {
20        Err(MessageSizeError::TooLarge {
21            actual: data.len(),
22            max: MAX_MESSAGE_SIZE,
23        })
24    } else {
25        Ok(())
26    }
27}
28
29/// Error returned when a message exceeds the allowed size limit.
30#[derive(Debug, Error)]
31pub enum MessageSizeError {
32    #[error("message too large: {actual} bytes exceeds maximum of {max} bytes")]
33    TooLarge { actual: usize, max: u64 },
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39    use crate::{DaemonCommand, JobKind, ValidatedPath};
40
41    #[test]
42    fn small_message_deserializes() {
43        let cmd = DaemonCommand::HealthCheck;
44        let bytes = bounded_bincode().serialize(&cmd).unwrap();
45        assert!(validate_message_size(&bytes).is_ok());
46    }
47
48    #[test]
49    fn oversized_message_rejected() {
50        let data = vec![0u8; 2_000_000]; // > 1MB
51        assert!(matches!(
52            validate_message_size(&data),
53            Err(MessageSizeError::TooLarge {
54                actual: 2_000_000,
55                max: 1_048_576
56            })
57        ));
58    }
59
60    #[test]
61    fn maximum_size_message_allowed() {
62        let data = vec![0u8; 1_048_576]; // Exactly 1MB
63        assert!(validate_message_size(&data).is_ok());
64    }
65
66    #[test]
67    fn bincode_roundtrip_with_limits() {
68        let cmd = DaemonCommand::QueueJob {
69            repo_path: ValidatedPath::new(std::path::PathBuf::from("/test/repo")).unwrap(),
70            job: JobKind::Prefetch,
71        };
72        // Use bounded_bincode for both serialize and deserialize
73        let bytes = bounded_bincode().serialize(&cmd).unwrap();
74        let decoded: DaemonCommand = bounded_bincode().deserialize(&bytes).unwrap();
75        assert_eq!(cmd, decoded);
76    }
77}