Skip to content

Commit 251ddba

Browse files
zero copy documentation added (#67)
1 parent e017b70 commit 251ddba

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

docs/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- [Typed Publisher](api/publisher.md)
2020
- [Typed Subscriber](api/subscriber.md)
2121
- [Supported Message Types](api/message_types.md)
22+
- [Zero Copy Messaging](api/zero_copy_messaging.md)
2223
- [Service Server](api/service_server.md)
2324
- [Service Client](api/service_client.md)
2425
- [Project Status](project_status.md)

docs/src/api/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ Explore the individual components in detail:
77
- [Supported Message Types](./message_types.md)
88
- [Typed Publisher](./publisher.md)
99
- [Typed Subscriber](./subscriber.md)
10+
- [Zero Copy Messaging](./zero_copy_messaging.md)
1011
- [Service Server](./service_server.md)
1112
- [Service Client](./service_client.md)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# eCAL Zero-Copy in Rustecal
2+
3+
This guide shows you how to publish and receive large binary payloads **without any memcpy** by using:
4+
5+
- **`TypedPublisher<BytesMessage>`** - a built-in byte-array message type
6+
- **`PayloadWriter`** - for direct in-place writes into eCAL's shared memory
7+
8+
---
9+
10+
## 1. How eCAL Zero-Copy Works
11+
12+
eCAL zero-copy uses shared memory to avoid copying data between publisher and subscriber.
13+
- **Publisher** allocates a memory file and writes directly into it via `PayloadWriter`.
14+
- **Subscriber** maps the same memory file and reads the buffer in place.
15+
- Handshake and buffer management are handled by eCAL's SHM layer.
16+
17+
---
18+
19+
## 2. `PayloadWriter` API
20+
21+
A `PayloadWriter` lets you fill the shared-memory buffer in place:
22+
23+
```rust
24+
pub trait PayloadWriter {
25+
/// Called once on first allocation or resize.
26+
fn write_full(&mut self, buf: &mut [u8]) -> bool;
27+
28+
/// Called on subsequent sends to modify only parts of the buffer.
29+
fn write_modified(&mut self, buf: &mut [u8]) -> bool {
30+
self.write_full(buf)
31+
}
32+
33+
/// Returns the exact number of bytes you will write.
34+
fn get_size(&self) -> usize;
35+
}
36+
````
37+
38+
Implement these methods for your payload type, then pass a mutable reference to `send_payload_writer`.
39+
40+
---
41+
42+
## 3. Publisher Sample
43+
44+
```rust
45+
use rustecal::{Configuration, Ecal, EcalComponents, TypedPublisher};
46+
use rustecal_pubsub::PayloadWriter;
47+
use rustecal_pubsub::publisher::Timestamp;
48+
use rustecal_types_bytes::BytesMessage;
49+
50+
/// A simple zero-copy writer that fills a buffer with a repeating pattern.
51+
pub struct CustomWriter {
52+
size: usize,
53+
counter: u8,
54+
}
55+
56+
impl CustomWriter {
57+
pub fn new(size: usize) -> Self {
58+
Self { size, counter: 0 }
59+
}
60+
}
61+
62+
impl PayloadWriter for CustomWriter {
63+
fn write_full(&mut self, buf: &mut [u8]) -> bool {
64+
if buf.len() < self.size { return false; }
65+
// fill entire buffer with 0xAA
66+
buf[..self.size].fill(0xAA);
67+
true
68+
}
69+
70+
fn write_modified(&mut self, buf: &mut [u8]) -> bool {
71+
if buf.len() < self.size { return false; }
72+
// flip one byte each time
73+
let idx = (self.counter as usize) % self.size;
74+
buf[idx] ^= 0xFF;
75+
self.counter = self.counter.wrapping_add(1);
76+
true
77+
}
78+
79+
fn get_size(&self) -> usize {
80+
self.size
81+
}
82+
}
83+
84+
fn main() -> Result<(), Box<dyn std::error::Error>> {
85+
// configure eCAL
86+
let mut cfg = Configuration::new()?;
87+
cfg.publisher.layer.shm.zero_copy_mode = true as i32;
88+
cfg.publisher.layer.shm.acknowledge_timeout_ms = 50;
89+
Ecal::initialize(
90+
Some("zero copy publisher"),
91+
EcalComponents::DEFAULT,
92+
Some(&cfg),
93+
)?;
94+
95+
// create typed publisher
96+
let publisher: TypedPublisher<BytesMessage> =
97+
TypedPublisher::new("buffer")?;
98+
99+
// prepare zero-copy payload writer
100+
let mut writer = CustomWriter::new(8 * 1024 * 1024); // 8 MB
101+
102+
// send loop
103+
while Ecal::ok() {
104+
publisher.send_payload_writer(&mut writer, Timestamp::Auto);
105+
}
106+
107+
// finalize ecal and clean up
108+
Ecal::finalize();
109+
Ok(())
110+
}
111+
```
112+
113+
---
114+
115+
## 4. Subscriber Sample
116+
117+
```rust
118+
use std::{thread, time::Duration};
119+
use rustecal::{Ecal, EcalComponents, TypedSubscriber};
120+
use rustecal_types_bytes::BytesMessage;
121+
122+
fn main() -> Result<(), Box<dyn std::error::Error>> {
123+
// initialize eCAL
124+
Ecal::initialize(
125+
Some("zero copy subscriber"),
126+
EcalComponents::DEFAULT,
127+
None,
128+
)?;
129+
130+
// create typed subscriber
131+
let mut sub: TypedSubscriber<BytesMessage> =
132+
TypedSubscriber::new("buffer")?;
133+
134+
// register zero-copy callback
135+
sub.set_callback(|received| {
136+
// borrow shared-memory payload
137+
let buffer: &[u8] = received.payload.data.as_ref();
138+
// this line is just to demonstrate usage (it will kill the performance)
139+
println!("Received {} bytes", buffer.len());
140+
});
141+
142+
// keep alive for callbacks
143+
while Ecal::ok() {
144+
thread::sleep(Duration::from_millis(100));
145+
}
146+
147+
// finalize ecal and clean up
148+
Ecal::finalize();
149+
Ok(())
150+
}
151+
```

0 commit comments

Comments
 (0)