logicaffeine_compile/concurrency/
stream.rs1pub fn frame_for_stream(body: &[u8], out: &mut Vec<u8>) {
15 let mut len = body.len() as u64;
16 loop {
17 let mut byte = (len & 0x7f) as u8;
18 len >>= 7;
19 if len != 0 {
20 byte |= 0x80;
21 }
22 out.push(byte);
23 if len == 0 {
24 break;
25 }
26 }
27 out.extend_from_slice(body);
28}
29
30#[derive(Default)]
34pub struct StreamDeframer {
35 buf: Vec<u8>,
36 start: usize,
37}
38
39impl StreamDeframer {
40 pub fn new() -> Self {
41 Self::default()
42 }
43
44 pub fn push(&mut self, bytes: &[u8]) {
46 self.buf.extend_from_slice(bytes);
47 }
48
49 pub fn is_empty(&self) -> bool {
51 self.start >= self.buf.len()
52 }
53
54 pub fn pending(&self) -> usize {
56 self.buf.len() - self.start
57 }
58
59 pub fn drain_frames(&mut self, mut f: impl FnMut(&[u8])) -> usize {
64 let mut count = 0;
65 loop {
66 let Some((len, header)) = read_uvarint(&self.buf[self.start..]) else {
67 break; };
69 let body_start = self.start + header;
70 let Some(body_end) = body_start.checked_add(len as usize) else {
71 break;
72 };
73 if body_end > self.buf.len() {
74 break; }
76 f(&self.buf[body_start..body_end]);
77 self.start = body_end;
78 count += 1;
79 }
80 if self.start > 0 {
81 self.buf.drain(0..self.start);
82 self.start = 0;
83 }
84 count
85 }
86}
87
88fn read_uvarint(buf: &[u8]) -> Option<(u64, usize)> {
91 let mut value = 0u64;
92 let mut shift = 0u32;
93 for (i, &b) in buf.iter().enumerate() {
94 value |= u64::from(b & 0x7f) << shift;
95 if b & 0x80 == 0 {
96 return Some((value, i + 1));
97 }
98 shift += 7;
99 if shift >= 64 {
100 return None; }
102 }
103 None
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 fn frame_all(bodies: &[&[u8]]) -> Vec<u8> {
111 let mut out = Vec::new();
112 for b in bodies {
113 frame_for_stream(b, &mut out);
114 }
115 out
116 }
117
118 fn collect(deframer: &mut StreamDeframer) -> Vec<Vec<u8>> {
119 let mut got = Vec::new();
120 deframer.drain_frames(|body| got.push(body.to_vec()));
121 got
122 }
123
124 #[test]
125 fn whole_stream_at_once() {
126 let stream = frame_all(&[b"hello", b"", b"world!!", &[0u8, 255, 7]]);
127 let mut d = StreamDeframer::new();
128 d.push(&stream);
129 let got = collect(&mut d);
130 assert_eq!(got, vec![b"hello".to_vec(), b"".to_vec(), b"world!!".to_vec(), vec![0, 255, 7]]);
131 assert!(d.is_empty(), "fully consumed at a frame boundary");
132 }
133
134 #[test]
135 fn byte_at_a_time_arrival() {
136 let bodies: Vec<Vec<u8>> = (0..20).map(|i| vec![i as u8; (i % 5) + 1]).collect();
139 let refs: Vec<&[u8]> = bodies.iter().map(|b| b.as_slice()).collect();
140 let stream = frame_all(&refs);
141
142 let mut d = StreamDeframer::new();
143 let mut got = Vec::new();
144 for &byte in &stream {
145 d.push(&[byte]);
146 d.drain_frames(|body| got.push(body.to_vec()));
147 }
148 assert_eq!(got, bodies, "every frame reassembled exactly, in order");
149 assert!(d.is_empty());
150 }
151
152 #[test]
153 fn frame_split_across_chunks_mid_body_and_mid_length() {
154 let big = vec![0xABu8; 5000]; let stream = frame_all(&[b"a", big.as_slice(), b"z"]);
157
158 let mut d = StreamDeframer::new();
159 let cut1 = 2 + 1; d.push(&stream[..cut1]);
162 assert_eq!(collect(&mut d), vec![b"a".to_vec()], "only the complete first frame");
163 let cut2 = cut1 + 2500;
165 d.push(&stream[cut1..cut2]);
166 assert_eq!(collect(&mut d), Vec::<Vec<u8>>::new(), "big body incomplete → nothing yet");
167 assert!(d.pending() > 0, "the partial big frame is buffered");
168 d.push(&stream[cut2..]);
170 assert_eq!(collect(&mut d), vec![big.clone(), b"z".to_vec()], "big + trailing frame complete");
171 assert!(d.is_empty());
172 }
173
174 #[test]
175 fn empty_and_no_complete_frame() {
176 let mut d = StreamDeframer::new();
177 assert_eq!(collect(&mut d), Vec::<Vec<u8>>::new(), "no bytes → nothing");
178 d.push(&[0x80]); assert_eq!(collect(&mut d), Vec::<Vec<u8>>::new(), "incomplete length → wait");
180 assert_eq!(d.pending(), 1);
181 d.push(&[0x01, b'X']); assert_eq!(collect(&mut d), Vec::<Vec<u8>>::new(), "length completes but body not arrived");
183 }
184
185 #[test]
186 fn end_to_end_zero_copy_stream_read() {
187 use crate::concurrency::marshal::{message_to_wire, view_message};
190 use crate::interpreter::RuntimeValue;
191
192 let m1 = message_to_wire("p", &RuntimeValue::Int(11)).unwrap();
193 let m2 = message_to_wire("p", &RuntimeValue::Int(22)).unwrap();
194 let m3 = message_to_wire("p", &RuntimeValue::Int(33)).unwrap();
195 let stream = frame_all(&[&m1, &m2, &m3]);
196
197 let mut d = StreamDeframer::new();
198 let mut ints = Vec::new();
199 for chunk in stream.chunks(7) {
201 d.push(chunk);
202 d.drain_frames(|frame| {
203 let v = view_message(frame).expect("frame opens as a view in place");
204 ints.push(v.as_int().expect("reads the int with no full decode"));
205 });
206 }
207 assert_eq!(ints, vec![11, 22, 33], "every streamed message read zero-copy, in order");
208 }
209}