1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use cyclonedds_rust_sys::dds_sample_info_t;
use std::ffi::c_void;
use crate::{DdsEntity as _, DdsResult}; // FFI-LIFE-011: Loan::drop chama reader.entity()
pub struct Sample<T> {
pub data: T,
pub info: dds_sample_info_t,
}
impl<T> Sample<T> {
pub fn is_valid(&self) -> bool {
self.info.valid_data
}
pub fn source_timestamp(&self) -> i64 {
self.info.source_timestamp
}
pub fn instance_handle(&self) -> u64 {
self.info.instance_handle
}
pub fn sample_state(&self) -> u32 {
self.info.sample_state
}
pub fn view_state(&self) -> u32 {
self.info.view_state
}
pub fn instance_state(&self) -> u32 {
self.info.instance_state
}
pub fn publication_handle(&self) -> u64 {
self.info.publication_handle
}
pub fn disposed_generation_count(&self) -> u32 {
self.info.disposed_generation_count
}
pub fn no_writers_generation_count(&self) -> u32 {
self.info.no_writers_generation_count
}
pub fn sample_rank(&self) -> u32 {
self.info.sample_rank
}
pub fn generation_rank(&self) -> u32 {
self.info.generation_rank
}
pub fn absolute_generation_rank(&self) -> u32 {
self.info.absolute_generation_rank
}
}
pub struct Loan<'a, T: crate::DdsType> {
samples: Vec<*mut c_void>,
infos: Vec<dds_sample_info_t>,
count: usize,
// FFI-LIFE-011: o loan agora SEGURA uma referência ao reader — o tipo
// garante que `dds_return_loan` nunca corre num handle morto e que as
// amostras não são lidas após o reader ser deletado (antes: `reader: i32`
// cru; o compilador não impedia reader cair antes do loan).
reader: &'a crate::DataReader<T>,
}
impl<'a, T: crate::DdsType> Loan<'a, T> {
pub(crate) fn new(
mut samples: Vec<*mut c_void>,
mut infos: Vec<dds_sample_info_t>,
count: usize,
reader: &'a crate::DataReader<T>,
) -> Self {
samples.truncate(count);
infos.truncate(count);
Self {
samples,
infos,
count,
reader,
}
}
/// Iterate over the loaned samples as owned `T` values.
///
/// The loaned memory holds `T::Native`, *not* `T` — for a type with
/// `String`/`Vec` fields those are `DdsString` (8 bytes) / `DdsSequence` on
/// the wire, where `T` expects `String` (24 bytes) / `Vec`. Handing out
/// `&T` over that buffer (as this method used to do) is an out-of-bounds
/// read, and cloning through such a reference corrupts the heap: the
/// resulting `String` carries a garbage capacity/length and is later freed
/// by Rust over memory it never allocated. Each sample is therefore
/// converted with [`crate::DdsType::clone_out`], the same path already used by
/// `read`/`take`/`read_next`.
///
/// For a genuinely zero-copy view, use [`iter_native`](Self::iter_native).
pub fn iter(&self) -> impl Iterator<Item = DdsResult<Sample<T>>> + '_ {
(0..self.count)
.filter(move |&i| self.infos[i].valid_data && !self.samples[i].is_null())
.map(move |i| unsafe {
Ok(Sample {
data: T::clone_out(self.samples[i] as *const T)?,
info: self.infos[i],
})
})
}
/// Zero-copy view over the loaned samples, borrowing the DDS-owned buffers
/// directly as [`crate::DdsType::Native`].
///
/// This is the wire representation: for a type with `String`/`Vec` fields
/// you get `DdsString`/`DdsSequence`, not `String`/`Vec`. Nothing is
/// copied, so this is the path to use when the sample is large and only a
/// few fields are read. The borrow ends when the `Loan` is dropped.
pub fn iter_native(&self) -> impl Iterator<Item = Sample<&T::Native>> + '_ {
(0..self.count)
.filter(move |&i| self.infos[i].valid_data && !self.samples[i].is_null())
.map(move |i| unsafe {
Sample {
data: &*(self.samples[i] as *const T::Native),
info: self.infos[i],
}
})
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
/// Copy every loaned sample out into owned values.
///
/// No longer requires `T: Clone` — the conversion goes through
/// [`crate::DdsType::clone_out`], which is the only sound way to read the native
/// buffer (see [`iter`](Self::iter)).
pub fn to_vec(&self) -> DdsResult<Vec<Sample<T>>> {
self.iter().collect()
}
}
impl<T: crate::DdsType> Drop for Loan<'_, T> {
fn drop(&mut self) {
if !self.samples.is_empty() && self.count > 0 {
unsafe {
cyclonedds_rust_sys::dds_return_loan(
self.reader.entity(),
self.samples.as_mut_ptr(),
self.count as i32,
);
}
}
}
}