Skip to main content

memlink_protocol/
request.rs

1//! Request message structure.
2//!
3//! Defines Request struct with module/method names, args, trace_id,
4//! and deadline. Includes serialization and deserialization methods.
5
6use alloc::string::{String, ToString};
7use alloc::vec::Vec;
8
9use crate::error::{ProtocolError, Result};
10use crate::header::MessageHeader;
11use crate::magic::MAX_PAYLOAD_SIZE;
12use crate::types::{Priority, RequestId, TraceId};
13
14#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
15pub struct Request {
16    pub header: MessageHeader,
17    pub module_name: String,
18    pub method_name: String,
19    pub args: Vec<u8>,
20    pub trace_id: TraceId,
21    pub deadline_ns: Option<u64>,
22}
23
24impl Request {
25    pub fn new(
26        request_id: RequestId,
27        _priority: Priority,
28        module: &str,
29        method: &str,
30        args: Vec<u8>,
31    ) -> Self {
32        let module_name = module.to_string();
33        let method_name = method.to_string();
34
35        let method_hash = compute_fnv1a_hash(method.as_bytes());
36        let module_id = compute_fnv1a_hash(module.as_bytes()) as u64;
37
38        let header = MessageHeader::new(
39            crate::types::MessageType::Request,
40            request_id,
41            module_id,
42            method_hash,
43            args.len() as u32,
44        );
45
46        Self {
47            header,
48            module_name,
49            method_name,
50            args,
51            trace_id: 0,
52            deadline_ns: None,
53        }
54    }
55
56    pub fn with_trace_id(mut self, trace_id: TraceId) -> Self {
57        self.trace_id = trace_id;
58        self
59    }
60
61    pub fn with_deadline_ns(mut self, deadline_ns: Option<u64>) -> Self {
62        self.deadline_ns = deadline_ns;
63        self
64    }
65
66    pub fn request_id(&self) -> RequestId {
67        self.header.request_id()
68    }
69
70    pub fn priority(&self) -> Priority {
71        Priority::Normal
72    }
73
74    pub fn module_name(&self) -> &str {
75        &self.module_name
76    }
77
78    pub fn method_name(&self) -> &str {
79        &self.method_name
80    }
81
82    pub fn args(&self) -> &[u8] {
83        &self.args
84    }
85
86    pub fn trace_id(&self) -> TraceId {
87        self.trace_id
88    }
89
90    pub fn deadline_ns(&self) -> Option<u64> {
91        self.deadline_ns
92    }
93
94    pub fn into_bytes(self) -> Result<Vec<u8>> {
95        let mut bytes = Vec::new();
96
97        let module_bytes = self.module_name.as_bytes();
98        bytes.extend_from_slice(&(module_bytes.len() as u32).to_le_bytes());
99        bytes.extend_from_slice(module_bytes);
100
101        let method_bytes = self.method_name.as_bytes();
102        bytes.extend_from_slice(&(method_bytes.len() as u32).to_le_bytes());
103        bytes.extend_from_slice(method_bytes);
104
105        bytes.extend_from_slice(&(self.args.len() as u32).to_le_bytes());
106        bytes.extend_from_slice(&self.args);
107
108        bytes.extend_from_slice(&self.trace_id.to_le_bytes());
109
110        let deadline = self.deadline_ns.unwrap_or(0);
111        bytes.extend_from_slice(&deadline.to_le_bytes());
112
113        if bytes.len() > MAX_PAYLOAD_SIZE {
114            return Err(ProtocolError::PayloadTooLarge(bytes.len(), MAX_PAYLOAD_SIZE));
115        }
116
117        Ok(bytes)
118    }
119
120    pub fn from_bytes(payload: &[u8], header: MessageHeader) -> Result<Self> {
121        let mut offset = 0;
122
123        if offset + 4 > payload.len() {
124            return Err(ProtocolError::InvalidHeader(
125                "insufficient data for module_name length".to_string(),
126            ));
127        }
128        let module_len =
129            u32::from_le_bytes([payload[offset], payload[offset + 1], payload[offset + 2], payload[offset + 3]])
130                as usize;
131        offset += 4;
132
133        if offset + module_len > payload.len() {
134            return Err(ProtocolError::InvalidHeader(
135                "insufficient data for module_name".to_string(),
136            ));
137        }
138        let module_bytes = &payload[offset..offset + module_len];
139        let module_name = String::from_utf8(module_bytes.to_vec()).map_err(|_| {
140            ProtocolError::InvalidHeader("module_name is not valid UTF-8".to_string())
141        })?;
142        offset += module_len;
143
144        if offset + 4 > payload.len() {
145            return Err(ProtocolError::InvalidHeader(
146                "insufficient data for method_name length".to_string(),
147            ));
148        }
149        let method_len =
150            u32::from_le_bytes([payload[offset], payload[offset + 1], payload[offset + 2], payload[offset + 3]])
151                as usize;
152        offset += 4;
153
154        if offset + method_len > payload.len() {
155            return Err(ProtocolError::InvalidHeader(
156                "insufficient data for method_name".to_string(),
157            ));
158        }
159        let method_bytes = &payload[offset..offset + method_len];
160        let method_name = String::from_utf8(method_bytes.to_vec()).map_err(|_| {
161            ProtocolError::InvalidHeader("method_name is not valid UTF-8".to_string())
162        })?;
163        offset += method_len;
164
165        if offset + 4 > payload.len() {
166            return Err(ProtocolError::InvalidHeader(
167                "insufficient data for args length".to_string(),
168            ));
169        }
170        let args_len =
171            u32::from_le_bytes([payload[offset], payload[offset + 1], payload[offset + 2], payload[offset + 3]])
172                as usize;
173        offset += 4;
174
175        if offset + args_len > payload.len() {
176            return Err(ProtocolError::InvalidHeader(
177                "insufficient data for args".to_string(),
178            ));
179        }
180        let args = payload[offset..offset + args_len].to_vec();
181        offset += args_len;
182
183        if offset + 16 > payload.len() {
184            return Err(ProtocolError::InvalidHeader(
185                "insufficient data for trace_id".to_string(),
186            ));
187        }
188        let trace_id = u128::from_le_bytes([
189            payload[offset],
190            payload[offset + 1],
191            payload[offset + 2],
192            payload[offset + 3],
193            payload[offset + 4],
194            payload[offset + 5],
195            payload[offset + 6],
196            payload[offset + 7],
197            payload[offset + 8],
198            payload[offset + 9],
199            payload[offset + 10],
200            payload[offset + 11],
201            payload[offset + 12],
202            payload[offset + 13],
203            payload[offset + 14],
204            payload[offset + 15],
205        ]);
206        offset += 16;
207
208        if offset + 8 > payload.len() {
209            return Err(ProtocolError::InvalidHeader(
210                "insufficient data for deadline_ns".to_string(),
211            ));
212        }
213        let deadline = u64::from_le_bytes([
214            payload[offset],
215            payload[offset + 1],
216            payload[offset + 2],
217            payload[offset + 3],
218            payload[offset + 4],
219            payload[offset + 5],
220            payload[offset + 6],
221            payload[offset + 7],
222        ]);
223        let deadline_ns = if deadline == 0 { None } else { Some(deadline) };
224
225        Ok(Self {
226            header,
227            module_name,
228            method_name,
229            args,
230            trace_id,
231            deadline_ns,
232        })
233    }
234}
235
236fn compute_fnv1a_hash(data: &[u8]) -> u32 {
237    const FNV_OFFSET_BASIS: u32 = 2166136261;
238    const FNV_PRIME: u32 = 16777619;
239
240    let mut hash = FNV_OFFSET_BASIS;
241    for byte in data {
242        hash ^= *byte as u32;
243        hash = hash.wrapping_mul(FNV_PRIME);
244    }
245    hash
246}