oxihuman_core/
grpc_codec.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct GrpcFrameHeader {
10 pub compressed: bool,
12 pub message_len: u32,
14}
15
16#[derive(Debug, Clone, PartialEq)]
18pub enum GrpcError {
19 InsufficientData,
20 InvalidCompressionFlag(u8),
21 MessageTruncated { expected: u32, got: usize },
22}
23
24impl std::fmt::Display for GrpcError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Self::InsufficientData => write!(f, "insufficient data for gRPC frame"),
28 Self::InvalidCompressionFlag(b) => write!(f, "invalid gRPC compression flag: {b}"),
29 Self::MessageTruncated { expected, got } => {
30 write!(
31 f,
32 "gRPC message truncated: expected {expected} bytes, got {got}"
33 )
34 }
35 }
36 }
37}
38
39pub fn encode_frame(data: &[u8], compressed: bool, buf: &mut Vec<u8>) {
41 buf.push(if compressed { 1 } else { 0 });
42 buf.extend_from_slice(&(data.len() as u32).to_be_bytes());
43 buf.extend_from_slice(data);
44}
45
46pub fn decode_frame_header(buf: &[u8]) -> Result<GrpcFrameHeader, GrpcError> {
48 if buf.len() < 5 {
49 return Err(GrpcError::InsufficientData);
50 }
51 let flag = buf[0];
52 if flag > 1 {
53 return Err(GrpcError::InvalidCompressionFlag(flag));
54 }
55 let len_bytes: [u8; 4] = buf[1..5].try_into().unwrap_or_default();
56 Ok(GrpcFrameHeader {
57 compressed: flag == 1,
58 message_len: u32::from_be_bytes(len_bytes),
59 })
60}
61
62pub fn decode_frame(buf: &[u8]) -> Result<(GrpcFrameHeader, &[u8]), GrpcError> {
64 let header = decode_frame_header(buf)?;
65 let msg_len = header.message_len as usize;
66 if buf.len() < 5 + msg_len {
67 return Err(GrpcError::MessageTruncated {
68 expected: header.message_len,
69 got: buf.len().saturating_sub(5),
70 });
71 }
72 Ok((header, &buf[5..5 + msg_len]))
73}
74
75pub fn framed_length(msg_len: usize) -> usize {
77 5 + msg_len
78}
79
80pub fn is_complete_frame(buf: &[u8]) -> bool {
82 if buf.len() < 5 {
83 return false;
84 }
85 let len_bytes: [u8; 4] = buf[1..5].try_into().unwrap_or([0; 4]);
86 let msg_len = u32::from_be_bytes(len_bytes) as usize;
87 buf.len() >= 5 + msg_len
88}
89
90pub fn split_frames(mut buf: &[u8]) -> Vec<Vec<u8>> {
92 let mut frames = vec![];
93 while buf.len() >= 5 {
94 if let Ok((header, msg)) = decode_frame(buf) {
95 frames.push(msg.to_vec());
96 let consumed = 5 + header.message_len as usize;
97 if consumed > buf.len() {
98 break;
99 }
100 buf = &buf[consumed..];
101 } else {
102 break;
103 }
104 }
105 frames
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_encode_frame_length() {
114 let mut buf = vec![];
116 encode_frame(b"hello", false, &mut buf);
117 assert_eq!(buf.len(), 10);
118 }
119
120 #[test]
121 fn test_decode_header() {
122 let mut buf = vec![];
124 encode_frame(b"test", false, &mut buf);
125 let h = decode_frame_header(&buf).expect("should succeed");
126 assert!(!h.compressed);
127 assert_eq!(h.message_len, 4);
128 }
129
130 #[test]
131 fn test_decode_complete_frame() {
132 let mut buf = vec![];
134 encode_frame(b"abc", false, &mut buf);
135 let (_, msg) = decode_frame(&buf).expect("should succeed");
136 assert_eq!(msg, b"abc");
137 }
138
139 #[test]
140 fn test_insufficient_data() {
141 assert!(decode_frame_header(&[0, 0, 0]).is_err());
143 }
144
145 #[test]
146 fn test_is_complete_frame_true() {
147 let mut buf = vec![];
149 encode_frame(&[1, 2], false, &mut buf);
150 assert!(is_complete_frame(&buf));
151 }
152
153 #[test]
154 fn test_is_complete_frame_false() {
155 assert!(!is_complete_frame(&[0, 0, 0, 0, 5]));
157 }
158
159 #[test]
160 fn test_framed_length() {
161 assert_eq!(framed_length(10), 15);
163 }
164
165 #[test]
166 fn test_split_frames() {
167 let mut buf = vec![];
169 encode_frame(b"A", false, &mut buf);
170 encode_frame(b"BB", false, &mut buf);
171 let frames = split_frames(&buf);
172 assert_eq!(frames.len(), 2);
173 }
174
175 #[test]
176 fn test_compressed_flag() {
177 let mut buf = vec![];
179 encode_frame(b"x", true, &mut buf);
180 let h = decode_frame_header(&buf).expect("should succeed");
181 assert!(h.compressed);
182 }
183
184 #[test]
185 fn test_invalid_compression_flag() {
186 let buf = [2u8, 0, 0, 0, 0];
188 assert!(decode_frame_header(&buf).is_err());
189 }
190}