zerodds_rpc/
wire_codec.rs1extern crate alloc;
22
23use alloc::vec::Vec;
24
25use crate::common_types::{ReplyHeader, RequestHeader};
26use crate::error::{RpcError, RpcResult};
27
28#[must_use]
34pub fn encode_request_frame(header: &RequestHeader, user_payload: &[u8]) -> Vec<u8> {
35 let mut out = header.to_cdr_le();
36 out.extend_from_slice(user_payload);
37 out
38}
39
40pub fn decode_request_frame(bytes: &[u8]) -> RpcResult<(RequestHeader, &[u8])> {
46 let header = RequestHeader::from_cdr_le(bytes)?;
47 let consumed = encoded_request_header_len(&header);
48 if consumed > bytes.len() {
49 return Err(RpcError::codec("request frame truncated"));
50 }
51 Ok((header, &bytes[consumed..]))
52}
53
54#[must_use]
56pub fn encode_reply_frame(header: &ReplyHeader, user_payload: &[u8]) -> Vec<u8> {
57 let mut out = header.to_cdr_le();
58 out.extend_from_slice(user_payload);
59 out
60}
61
62pub fn decode_reply_frame(bytes: &[u8]) -> RpcResult<(ReplyHeader, &[u8])> {
68 let header = ReplyHeader::from_cdr_le(bytes)?;
69 let consumed = encoded_reply_header_len();
70 if consumed > bytes.len() {
71 return Err(RpcError::codec("reply frame truncated"));
72 }
73 Ok((header, &bytes[consumed..]))
74}
75
76fn encoded_request_header_len(header: &RequestHeader) -> usize {
77 header.to_cdr_le().len()
80}
81
82fn encoded_reply_header_len() -> usize {
83 28
85}
86
87#[cfg(test)]
88#[allow(clippy::unwrap_used, clippy::expect_used)]
89mod tests {
90 use super::*;
91 use crate::common_types::{RemoteExceptionCode, SampleIdentity};
92
93 fn id() -> SampleIdentity {
94 SampleIdentity::new([0x42; 16], 7)
95 }
96
97 #[test]
98 fn request_frame_roundtrip_empty_payload() {
99 let h = RequestHeader::new(id(), "");
100 let frame = encode_request_frame(&h, &[]);
101 let (back, payload) = decode_request_frame(&frame).unwrap();
102 assert_eq!(back, h);
103 assert!(payload.is_empty());
104 }
105
106 #[test]
107 fn request_frame_roundtrip_with_payload() {
108 let h = RequestHeader::new(id(), "calc-A");
109 let frame = encode_request_frame(&h, &[1, 2, 3, 4]);
110 let (back, payload) = decode_request_frame(&frame).unwrap();
111 assert_eq!(back, h);
112 assert_eq!(payload, &[1, 2, 3, 4]);
113 }
114
115 #[test]
116 fn reply_frame_roundtrip() {
117 let h = ReplyHeader::new(id(), RemoteExceptionCode::Ok);
118 let frame = encode_reply_frame(&h, &[9, 8, 7]);
119 let (back, payload) = decode_reply_frame(&frame).unwrap();
120 assert_eq!(back, h);
121 assert_eq!(payload, &[9, 8, 7]);
122 }
123
124 #[test]
125 fn reply_frame_carries_exception_code() {
126 let h = ReplyHeader::new(id(), RemoteExceptionCode::InvalidArgument);
127 let frame = encode_reply_frame(&h, &[]);
128 let (back, _) = decode_reply_frame(&frame).unwrap();
129 assert_eq!(back.remote_ex, RemoteExceptionCode::InvalidArgument);
130 }
131
132 #[test]
133 fn decode_request_frame_truncated_header_is_error() {
134 let bytes = [0u8; 8];
135 assert!(decode_request_frame(&bytes).is_err());
136 }
137
138 #[test]
139 fn decode_reply_frame_truncated_is_error() {
140 let bytes = [0u8; 4];
141 assert!(decode_reply_frame(&bytes).is_err());
142 }
143
144 #[test]
145 fn reply_header_consumed_28_bytes() {
146 let h = ReplyHeader::new(SampleIdentity::UNKNOWN, RemoteExceptionCode::Ok);
147 let bytes = h.to_cdr_le();
148 assert_eq!(bytes.len(), 28);
149 }
150}