1use std::ops::{Deref, Range};
18use std::sync::Arc;
19
20use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncReadExt};
22use harmos_domain::Message;
24use harmos_sdk::operations::{CHILD_METHOD, CallValue, OperationDeclaration, WIRE_METHOD, WireAnswer, WireChildCall, WireCommand};
25
26#[derive(Clone, Default)]
35pub struct FramePayload {
36 bytes: Arc<Vec<u8>>,
37 range: Range<usize>,
38}
39
40#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
41#[serde(deny_unknown_fields)]
42pub struct PluginCatalog {
43 pub registrations: Vec<crate::Registration>,
44 pub observers: Vec<crate::inventory::observers::Declaration>,
45 pub operations: Vec<crate::operations::OperationDefinition>,
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct Publication {
50 pub stream: String,
51 pub schema: u32,
52 pub sequence: u64,
53 pub published_unix_micros: Option<i64>,
54 pub context: Option<String>,
55 pub payload: Vec<u8>,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq)]
61pub enum Frame {
62 Call { id: u64, method: String, params: FramePayload },
63 Cancel { id: u64 },
64 Push { method: String, params: FramePayload },
65 Answer { id: u64, result: FramePayload },
66 Refusal { id: u64, refusal: harmos_domain::Refusal },
67}
68
69impl FramePayload {
74 pub fn as_slice(&self) -> &[u8] {
75 &self.bytes[self.range.clone()]
76 }
77
78 pub fn into_vec(self) -> Vec<u8> {
81 let Self { bytes, range } = self;
82 let mut bytes = match Arc::try_unwrap(bytes) {
83 Ok(bytes) => bytes,
84 Err(bytes) => return bytes[range].to_vec(),
85 };
86 let length = range.len();
87 if range.start != 0 {
88 bytes.copy_within(range, 0);
89 }
90 bytes.truncate(length);
91 bytes
92 }
93
94 pub(crate) fn from_range(bytes: Vec<u8>, range: Range<usize>) -> Self {
95 assert!(range.start <= range.end && range.end <= bytes.len());
96 Self { bytes: Arc::new(bytes), range }
97 }
98}
99
100impl From<Vec<u8>> for FramePayload {
101 fn from(bytes: Vec<u8>) -> Self {
102 let range = 0..bytes.len();
103 Self::from_range(bytes, range)
104 }
105}
106
107impl Deref for FramePayload {
108 type Target = [u8];
109 fn deref(&self) -> &[u8] {
110 self.as_slice()
111 }
112}
113
114impl AsRef<[u8]> for FramePayload {
115 fn as_ref(&self) -> &[u8] {
116 self.as_slice()
117 }
118}
119
120impl<T: AsRef<[u8]> + ?Sized> PartialEq<T> for FramePayload {
121 fn eq(&self, other: &T) -> bool {
122 self.as_slice() == other.as_ref()
123 }
124}
125
126impl Eq for FramePayload {}
127
128impl harmos_domain::MemorySize for FramePayload {
129 fn memory_size(&self) -> usize {
130 std::mem::size_of::<Self>()
131 .saturating_add(std::mem::size_of::<Vec<u8>>())
132 .saturating_add(2 * std::mem::size_of::<usize>())
133 .saturating_add(self.bytes.capacity())
134 }
135}
136
137impl std::fmt::Debug for FramePayload {
138 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 self.as_slice().fmt(formatter)
140 }
141}
142
143impl Frame {
144 pub const REGISTER: &'static str = "harmos.register";
145 pub const EXITING: &'static str = "harmos.exiting";
146 pub const LIMIT: usize = 16 * 1024 * 1024;
147 pub const OVERSIZED: i64 = -32_603;
148
149 pub fn request_encoded_len<O: OperationDeclaration>(request: &O::Request) -> usize {
153 Self::request_payload_encoded_len(O::ID, harmos_domain::Message::encoded_size(request))
154 }
155
156 pub fn request_payload_encoded_len(operation: &str, payload: usize) -> usize {
159 if operation.len() > Self::LIMIT {
160 return usize::MAX;
161 }
162 let prepare = WireCommand::prepare_encoded_len(u64::MAX, operation, [255; 32], u64::MAX, payload).unwrap_or(usize::MAX);
163 call_encoded_len(u64::MAX, WIRE_METHOD, prepare).max(Self::child_payload_encoded_len(operation, u64::MAX, payload, true, &[]))
164 }
165
166 pub fn child_payload_encoded_len(
169 operation: &str, generation: u64, payload: usize, generation_context: bool, values: &[CallValue],
170 ) -> usize {
171 let child = WireChildCall::payload_encoded_len(operation, generation, payload, generation_context, values).unwrap_or(usize::MAX);
172 call_encoded_len(u64::MAX, CHILD_METHOD, child)
173 }
174
175 pub(crate) fn child_encoded_from_empty(empty_envelope: usize, payload: usize) -> usize {
176 let growth = binary_bytes_len(payload).saturating_sub(1);
177 call_encoded_len(u64::MAX, CHILD_METHOD, empty_envelope.saturating_add(growth))
178 }
179
180 pub fn request_fits<O: OperationDeclaration>(request: &O::Request) -> bool {
182 Self::request_encoded_len::<O>(request) <= Self::LIMIT
183 }
184
185 pub fn reply_encoded_len<O: OperationDeclaration>(reply: &O::Response) -> usize {
186 let payload = WireAnswer::ready_encoded_len(reply.encoded_size()).unwrap_or(usize::MAX);
187 answer_encoded_len(u64::MAX, payload)
188 }
189
190 pub fn reply_fits<O: OperationDeclaration>(reply: &O::Response) -> bool {
191 Self::reply_encoded_len::<O>(reply) <= Self::LIMIT
192 }
193
194 pub fn encoded_len(&self) -> usize {
195 match self {
196 Self::Call { id, method, params } => call_encoded_len(*id, method, params.len()),
197 Self::Answer { id, result } => answer_encoded_len(*id, result.len()),
198 Self::Refusal { id, refusal } => refusal_encoded_len(*id, refusal),
199 Self::Cancel { id } => 2 + varint_len(*id) + varint_len(u64::MAX),
200 Self::Push { method, params } => {
201 2usize.saturating_add(binary_bytes_len(method.len())).saturating_add(binary_bytes_len(params.len()))
202 }
203 }
204 }
205
206 pub fn fits(&self) -> bool {
207 self.encoded_len() <= Self::LIMIT
208 }
209
210 #[cfg(feature = "kit")]
211 pub(crate) fn exiting(reason: String) -> Self {
212 Self::Push {
213 method: Self::EXITING.to_owned(),
214 params: reason.encode().into(),
215 }
216 }
217
218 pub fn exiting_reason(params: &[u8]) -> Option<String> {
219 String::decode(params).ok()
220 }
221
222 pub async fn diagnostic<R>(input: &mut R) -> std::io::Result<Option<String>>
223 where
224 R: AsyncBufRead + Unpin + Send,
225 {
226 loop {
227 let mut bytes = Vec::new();
228 let read = input.take(Self::LIMIT as u64 + 1).read_until(b'\n', &mut bytes).await?;
229 if read == 0 {
230 return Ok(None);
231 }
232 if bytes.len() > Self::LIMIT {
233 let mut rest = Vec::new();
234 input.read_until(b'\n', &mut rest).await?;
235 continue;
236 }
237
238 let text = String::from_utf8_lossy(&bytes);
239 return Ok(Some(text.trim_end_matches(['\n', '\r']).to_owned()));
240 }
241 }
242}
243
244#[cfg(feature = "kit")]
248pub(crate) fn publication_encoded_len(
249 stream: &str, schema: u32, sequence: u64, payload: usize, published_unix_micros: Option<i64>, context: Option<&str>,
250) -> usize {
251 binary_bytes_len(stream.len())
252 .saturating_add(varint_len(u64::from(schema)))
253 .saturating_add(1 + varint_len(sequence))
254 .saturating_add(1 + published_unix_micros.map_or(0, zigzag_len))
255 .saturating_add(1 + context.map_or(0, |value| binary_bytes_len(value.len())))
256 .saturating_add(binary_bytes_len(payload))
257}
258
259#[cfg(feature = "kit")]
261pub(crate) fn following_encoded_len(payload: usize, published_delta_micros: Option<i64>) -> usize {
262 binary_bytes_len(payload).saturating_add(1 + published_delta_micros.map_or(0, zigzag_len))
263}
264
265pub(crate) fn varint_len(value: u64) -> usize {
266 ((64 - value.leading_zeros()).max(1) as usize).div_ceil(7)
267}
268
269fn call_encoded_len(id: u64, method: &str, payload_len: usize) -> usize {
272 2usize.saturating_add(varint_len(id)).saturating_add(binary_bytes_len(method.len())).saturating_add(binary_bytes_len(payload_len))
273}
274
275fn answer_encoded_len(id: u64, payload_len: usize) -> usize {
276 2usize.saturating_add(varint_len(id)).saturating_add(binary_bytes_len(payload_len))
277}
278
279fn refusal_encoded_len(id: u64, refusal: &harmos_domain::Refusal) -> usize {
280 let refusal = postcard::experimental::serialized_size(refusal).unwrap_or(usize::MAX);
281 2usize.saturating_add(varint_len(id)).saturating_add(refusal)
282}
283
284#[cfg(feature = "kit")]
285fn zigzag_len(value: i64) -> usize {
286 varint_len(((value as u64) << 1) ^ ((value >> 63) as u64))
287}
288
289fn binary_bytes_len(length: usize) -> usize {
290 varint_len(length as u64).saturating_add(length)
291}
292
293#[cfg(test)]
296mod tests {
297 use super::*;
298 use harmos_domain::MemorySize;
299
300 #[test]
301 fn unique_payload_compacts() {
302 let mut bytes = Vec::with_capacity(128);
303 bytes.extend(0..32_u8);
304 let pointer = bytes.as_ptr();
305 let capacity = bytes.capacity();
306
307 let payload = FramePayload::from_range(bytes, 8..24);
308 let expected = std::mem::size_of::<FramePayload>() + std::mem::size_of::<Vec<u8>>() + 2 * std::mem::size_of::<usize>() + capacity;
309 assert_eq!(payload.memory_size(), expected);
310
311 let owned = payload.into_vec();
312 assert_eq!(owned.as_ptr(), pointer);
313 assert_eq!(owned.capacity(), capacity);
314 assert_eq!(owned, (8..24).collect::<Vec<u8>>());
315 }
316
317 #[test]
318 fn shared_payload_independent() {
319 let mut bytes = Vec::with_capacity(128);
320 bytes.extend(0..32_u8);
321 let payload = FramePayload::from_range(bytes, 8..24);
322 let alias = payload.clone();
323 let pointer = alias.as_ptr();
324 let memory = alias.memory_size();
325
326 let mut owned = payload.into_vec();
327 assert_ne!(owned.as_ptr(), pointer);
328 owned[0] = 99;
329 assert_eq!(alias.as_ptr(), pointer);
330 assert_eq!(alias.as_slice(), (8..24).collect::<Vec<u8>>());
331 assert_eq!(alias.memory_size(), memory);
332 }
333
334 #[test]
335 fn request_sizes_match() {
336 for size in [0, 127, 128, 16383, 16384, 2097151, 2097152] {
337 let operation = "operation.測定";
338 let prepare = WireCommand::Prepare {
339 invocation: u64::MAX,
340 operation: operation.into(),
341 fingerprint: [255; 32],
342 generation: u64::MAX,
343 payload: vec![255; size],
344 };
345 let prepare = Frame::Call {
346 id: u64::MAX,
347 method: WIRE_METHOD.into(),
348 params: prepare.encode().unwrap().into(),
349 };
350 let child = WireChildCall {
351 context: [255; 32],
352 generation_context: Some([255; 32]),
353 values: Vec::new(),
354 operation: operation.into(),
355 fingerprint: [1; 32],
356 generation: u64::MAX,
357 payload: vec![255; size],
358 submit: true,
359 };
360 let child = Frame::Call {
361 id: u64::MAX,
362 method: CHILD_METHOD.into(),
363 params: child.encode().unwrap().into(),
364 };
365 assert_eq!(Frame::request_payload_encoded_len(operation, size), prepare.encoded_len().max(child.encoded_len()));
366 }
367 assert_eq!(Frame::request_payload_encoded_len("op", usize::MAX), usize::MAX);
368 }
369
370 #[test]
371 fn typed_sizes_match() {
372 use harmos_domain::model::storage::files::{ByteRange, FileCompletion, FileSource};
373 use harmos_operations::storage::ReadFile;
374 let request = ReadFile {
375 source: FileSource::Local("data/測定.bin".into()),
376 range: ByteRange { offset: 127, limit: Some(128) },
377 };
378 assert_eq!(
379 Frame::request_encoded_len::<ReadFile>(&request),
380 Frame::request_payload_encoded_len(ReadFile::ID, request.encode().len())
381 );
382 let reply = FileCompletion { bytes: u64::MAX };
383 let envelope = WireAnswer::Ready { payload: reply.encode() }.encode().unwrap();
384 let frame = Frame::Answer { id: u64::MAX, result: envelope.into() };
385 assert_eq!(Frame::reply_encoded_len::<ReadFile>(&reply), frame.encoded_len());
386 assert!(Frame::request_fits::<ReadFile>(&request));
387 assert!(Frame::reply_fits::<ReadFile>(&reply));
388 }
389
390 #[test]
391 fn child_growth_matches() {
392 let values = vec![CallValue {
393 key: "context.測定".into(),
394 payload: vec![255; 16384],
395 }];
396 for generation in [false, true] {
397 let empty = WireChildCall::payload_encoded_len("op", u64::MAX, 0, generation, &values).unwrap();
398 for size in [0, 127, 128, 16383, 16384] {
399 let call = WireChildCall {
400 context: [255; 32],
401 generation_context: generation.then_some([255; 32]),
402 values: values.clone(),
403 operation: "op".into(),
404 fingerprint: [1; 32],
405 generation: u64::MAX,
406 payload: vec![0; size],
407 submit: false,
408 };
409 let frame = Frame::Call {
410 id: u64::MAX,
411 method: CHILD_METHOD.into(),
412 params: call.encode().unwrap().into(),
413 };
414 assert_eq!(Frame::child_payload_encoded_len("op", u64::MAX, size, generation, &values), frame.encoded_len());
415 assert_eq!(Frame::child_encoded_from_empty(empty, size), frame.encoded_len());
416 }
417 }
418 }
419
420 #[test]
421 fn frame_prefix_limits() {
422 let mut low = 0;
423 let mut high = Frame::LIMIT + 1;
424 while low + 1 < high {
425 let middle = low + (high - low) / 2;
426 if Frame::child_payload_encoded_len("op", u64::MAX, middle, true, &[]) <= Frame::LIMIT {
427 low = middle;
428 } else {
429 high = middle;
430 }
431 }
432 let mut call = WireChildCall {
433 context: [0; 32],
434 generation_context: Some([0; 32]),
435 values: Vec::new(),
436 operation: "op".into(),
437 fingerprint: [1; 32],
438 generation: u64::MAX,
439 payload: vec![0; low],
440 submit: true,
441 };
442 let frame = Frame::Call {
443 id: u64::MAX,
444 method: CHILD_METHOD.into(),
445 params: call.encode().unwrap().into(),
446 };
447 assert_eq!(frame.encoded_len(), Frame::LIMIT);
448 assert!(frame.fits());
449 let packet = crate::transport::Packet::Frame(frame);
450 assert_eq!(packet.encoded_len().unwrap(), Frame::LIMIT + 4);
451 assert_eq!(packet.encode().unwrap().len(), Frame::LIMIT + 4);
452 drop(packet);
453 call.payload.push(0);
454 let frame = Frame::Call {
455 id: u64::MAX,
456 method: CHILD_METHOD.into(),
457 params: call.encode().unwrap().into(),
458 };
459 assert_eq!(frame.encoded_len(), Frame::LIMIT + 1);
460 assert!(!frame.fits());
461 assert!(crate::transport::Packet::Frame(frame).encoded_len().is_err());
462 }
463}