use cyclonedds_sys::*;
use std::collections::HashMap;
use std::ffi::CString;
use std::sync::Arc;
use crate::dds_listener::DdsListener;
use crate::dds_participant::DdsParticipant;
use crate::dds_qos::DdsQos;
use crate::Entity;
pub use cyclonedds_sys::DdsEntity;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DynamicKind {
Bool,
I8,
U8,
I16,
U16,
I32,
U32,
I64,
U64,
F32,
F64,
Char8,
String,
}
impl DynamicKind {
fn dds_kind(self) -> dds_dynamic_type_kind {
match self {
DynamicKind::Bool => dds_dynamic_type_kind::DDS_DYNAMIC_BOOLEAN,
DynamicKind::I8 => dds_dynamic_type_kind::DDS_DYNAMIC_INT8,
DynamicKind::U8 => dds_dynamic_type_kind::DDS_DYNAMIC_UINT8,
DynamicKind::I16 => dds_dynamic_type_kind::DDS_DYNAMIC_INT16,
DynamicKind::U16 => dds_dynamic_type_kind::DDS_DYNAMIC_UINT16,
DynamicKind::I32 => dds_dynamic_type_kind::DDS_DYNAMIC_INT32,
DynamicKind::U32 => dds_dynamic_type_kind::DDS_DYNAMIC_UINT32,
DynamicKind::I64 => dds_dynamic_type_kind::DDS_DYNAMIC_INT64,
DynamicKind::U64 => dds_dynamic_type_kind::DDS_DYNAMIC_UINT64,
DynamicKind::F32 => dds_dynamic_type_kind::DDS_DYNAMIC_FLOAT32,
DynamicKind::F64 => dds_dynamic_type_kind::DDS_DYNAMIC_FLOAT64,
DynamicKind::Char8 => dds_dynamic_type_kind::DDS_DYNAMIC_CHAR8,
DynamicKind::String => dds_dynamic_type_kind::DDS_DYNAMIC_STRING8,
}
}
}
mod layout {
use super::DynamicKind;
use cyclonedds_sys::*;
const DDS_OP_MASK: u32 = 0xff000000;
const DDS_OP_TYPE_MASK: u32 = 0x007f0000;
#[derive(Debug, Clone)]
pub(crate) struct FieldLayout {
pub(crate) name: String,
pub(crate) offset: u32,
pub(crate) kind: DynamicKind,
pub(crate) is_key: bool,
}
fn expected_typecode(kind: DynamicKind) -> u32 {
(match kind {
DynamicKind::Bool => dds_stream_typecode_primary_DDS_OP_TYPE_BLN,
DynamicKind::I8 | DynamicKind::U8 | DynamicKind::Char8 => {
dds_stream_typecode_primary_DDS_OP_TYPE_1BY
}
DynamicKind::I16 | DynamicKind::U16 => dds_stream_typecode_primary_DDS_OP_TYPE_2BY,
DynamicKind::I32 | DynamicKind::U32 | DynamicKind::F32 => {
dds_stream_typecode_primary_DDS_OP_TYPE_4BY
}
DynamicKind::I64 | DynamicKind::U64 | DynamicKind::F64 => {
dds_stream_typecode_primary_DDS_OP_TYPE_8BY
}
DynamicKind::String => dds_stream_typecode_primary_DDS_OP_TYPE_STR,
}) as u32
}
pub(crate) fn parse(
m_ops: &[u32],
members: &[(String, DynamicKind, bool)],
) -> Result<Vec<FieldLayout>, DDSError> {
let mut fields = Vec::with_capacity(members.len());
let mut i = 0usize;
let mut member_idx = 0usize;
while i < m_ops.len() {
let op = m_ops[i];
let opcode = op & DDS_OP_MASK;
if opcode == dds_stream_opcode_DDS_OP_RTS {
break;
}
if opcode != dds_stream_opcode_DDS_OP_ADR {
return Err(DDSError::Unsupported);
}
let Some((name, kind, is_key)) = members.get(member_idx) else {
return Err(DDSError::Unsupported);
};
let typecode = op & DDS_OP_TYPE_MASK;
if typecode != expected_typecode(*kind) {
return Err(DDSError::Unsupported);
}
let offset = *m_ops.get(i + 1).ok_or(DDSError::Unsupported)?;
fields.push(FieldLayout {
name: name.clone(),
offset,
kind: *kind,
is_key: *is_key,
});
member_idx += 1;
i += 2; }
if member_idx != members.len() {
return Err(DDSError::Unsupported);
}
Ok(fields)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_flat_final_struct() {
const DDS_OP_ADR: u32 = 0x01 << 24;
const DDS_OP_RTS: u32 = 0x00 << 24;
const DDS_OP_TYPE_4BY: u32 = 0x03 << 16;
const DDS_OP_TYPE_STR: u32 = 0x05 << 16;
const DDS_OP_FLAG_KEY: u32 = 1;
let m_ops = vec![
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_KEY,
0, DDS_OP_ADR | DDS_OP_TYPE_STR,
8, DDS_OP_RTS,
];
let members = vec![
("id".to_string(), DynamicKind::U32, true),
("label".to_string(), DynamicKind::String, false),
];
let fields = parse(&m_ops, &members).expect("parse succeeds");
assert_eq!(fields.len(), 2);
assert_eq!(fields[0].name, "id");
assert_eq!(fields[0].offset, 0);
assert!(fields[0].is_key);
assert_eq!(fields[1].name, "label");
assert_eq!(fields[1].offset, 8);
assert!(!fields[1].is_key);
}
#[test]
fn rejects_kind_mismatch() {
const DDS_OP_ADR: u32 = 0x01 << 24;
const DDS_OP_TYPE_STR: u32 = 0x05 << 16;
let m_ops = vec![DDS_OP_ADR | DDS_OP_TYPE_STR, 0];
let members = vec![("id".to_string(), DynamicKind::U32, false)];
assert!(parse(&m_ops, &members).is_err());
}
#[test]
fn rejects_unsupported_opcode() {
const DDS_OP_JSR: u32 = 0x02 << 24;
let m_ops = vec![DDS_OP_JSR, 0];
let members = vec![("id".to_string(), DynamicKind::U32, false)];
assert!(parse(&m_ops, &members).is_err());
}
}
}
struct DynamicLayout {
type_name: String,
size: u32,
align: u32,
fields: Vec<layout::FieldLayout>,
field_index: HashMap<String, usize>,
type_info: *mut ddsi_typeinfo,
dtype: dds_dynamic_type_t,
}
unsafe impl Send for DynamicLayout {}
unsafe impl Sync for DynamicLayout {}
impl Drop for DynamicLayout {
fn drop(&mut self) {
unsafe {
dds_free_typeinfo(self.type_info);
dds_dynamic_type_unref(&mut self.dtype);
}
}
}
#[derive(Clone)]
pub struct DdsDynamicType(Arc<DynamicLayout>);
impl DdsDynamicType {
pub fn type_name(&self) -> &str {
&self.0.type_name
}
fn field(&self, name: &str) -> Option<&layout::FieldLayout> {
self.0.field_index.get(name).map(|&i| &self.0.fields[i])
}
pub fn is_key(&self, name: &str) -> bool {
self.field(name).is_some_and(|f| f.is_key)
}
pub fn size(&self) -> u32 {
self.0.size
}
pub fn align(&self) -> u32 {
self.0.align
}
fn alloc_layout(&self) -> std::alloc::Layout {
std::alloc::Layout::from_size_align(self.0.size as usize, self.0.align as usize)
.expect("CycloneDDS-provided size/align are always valid")
}
}
pub struct DynamicTypeBuilder {
type_name: String,
members: Vec<(String, DynamicKind, bool)>,
}
unsafe fn build_member_type_spec(
participant_entity: dds_entity_t,
kind: DynamicKind,
) -> Result<dds_dynamic_type_spec_t, DDSError> {
match kind {
DynamicKind::String => {
let descriptor = dds_dynamic_type_descriptor_t {
kind: dds_dynamic_type_kind::DDS_DYNAMIC_STRING8,
name: std::ptr::null(),
base_type: std::mem::zeroed(),
discriminator_type: std::mem::zeroed(),
num_bounds: 0,
bounds: std::ptr::null(),
element_type: std::mem::zeroed(),
key_element_type: std::mem::zeroed(),
};
let str_type = dds_dynamic_type_create(participant_entity, descriptor);
if str_type.ret != 0 {
return Err(DDSError::from(str_type.ret));
}
Ok(dds_dynamic_type_spec_t {
kind: dds_dynamic_type_spec_kind::DDS_DYNAMIC_TYPE_KIND_DEFINITION,
type_: dds_dynamic_type_spec__bindgen_ty_1 { type_: str_type },
})
}
_ => Ok(dds_dynamic_type_spec_t {
kind: dds_dynamic_type_spec_kind::DDS_DYNAMIC_TYPE_KIND_PRIMITIVE,
type_: dds_dynamic_type_spec__bindgen_ty_1 {
primitive: kind.dds_kind(),
},
}),
}
}
impl DynamicTypeBuilder {
pub fn new_struct(type_name: &str) -> Self {
Self {
type_name: type_name.to_string(),
members: Vec::new(),
}
}
pub fn member(mut self, name: &str, kind: DynamicKind) -> Self {
self.members.push((name.to_string(), kind, false));
self
}
pub fn key(mut self, name: &str) -> Self {
if let Some(m) = self.members.iter_mut().find(|(n, _, _)| n == name) {
m.2 = true;
}
self
}
pub fn build(self, participant: &DdsParticipant) -> Result<DdsDynamicType, DDSError> {
if self.members.is_empty() {
return Err(DDSError::BadParameter);
}
let participant_entity = unsafe { participant.entity().entity() };
let type_name_c = CString::new(self.type_name.as_str()).map_err(|_| DDSError::BadParameter)?;
let member_names_c: Vec<CString> = self
.members
.iter()
.map(|(name, _, _)| CString::new(name.as_str()))
.collect::<Result<_, _>>()
.map_err(|_| DDSError::BadParameter)?;
unsafe {
let descriptor = dds_dynamic_type_descriptor_t {
kind: dds_dynamic_type_kind::DDS_DYNAMIC_STRUCTURE,
name: type_name_c.as_ptr(),
base_type: std::mem::zeroed(),
discriminator_type: std::mem::zeroed(),
num_bounds: 0,
bounds: std::ptr::null(),
element_type: std::mem::zeroed(),
key_element_type: std::mem::zeroed(),
};
let mut dtype = dds_dynamic_type_create(participant_entity, descriptor);
if dtype.ret != 0 {
return Err(DDSError::from(dtype.ret));
}
let ret = dds_dynamic_type_set_extensibility(
&mut dtype,
dds_dynamic_type_extensibility::DDS_DYNAMIC_TYPE_EXT_FINAL,
);
if ret != 0 {
dds_dynamic_type_unref(&mut dtype);
return Err(DDSError::from(ret));
}
for (id, ((_, kind, _), name_c)) in
self.members.iter().zip(member_names_c.iter()).enumerate()
{
let type_spec = match build_member_type_spec(participant_entity, *kind) {
Ok(spec) => spec,
Err(e) => {
dds_dynamic_type_unref(&mut dtype);
return Err(e);
}
};
let member_descriptor = dds_dynamic_member_descriptor_t {
name: name_c.as_ptr(),
id: id as u32,
type_: type_spec,
default_value: std::ptr::null_mut(),
index: u32::MAX, num_labels: 0,
labels: std::ptr::null_mut(),
default_label: false,
};
let ret = dds_dynamic_type_add_member(&mut dtype, member_descriptor);
if ret != 0 {
dds_dynamic_type_unref(&mut dtype);
return Err(DDSError::from(ret));
}
}
for (id, (_, _, is_key)) in self.members.iter().enumerate() {
if *is_key {
let ret = dds_dynamic_member_set_key(&mut dtype, id as u32, true);
if ret != 0 {
dds_dynamic_type_unref(&mut dtype);
return Err(DDSError::from(ret));
}
}
}
let mut type_info: *mut ddsi_typeinfo = std::ptr::null_mut();
let ret = dds_dynamic_type_register(&mut dtype, &mut type_info);
if ret != 0 {
dds_dynamic_type_unref(&mut dtype);
return Err(DDSError::from(ret));
}
let mut descriptor: *mut dds_topic_descriptor_t = std::ptr::null_mut();
let ret = dds_create_topic_descriptor(
dds_find_scope::DDS_FIND_SCOPE_LOCAL_DOMAIN,
participant_entity,
type_info,
0,
&mut descriptor,
);
if ret != 0 {
dds_free_typeinfo(type_info);
dds_dynamic_type_unref(&mut dtype);
return Err(DDSError::from(ret));
}
let m_ops = std::slice::from_raw_parts((*descriptor).m_ops, (*descriptor).m_nops as usize);
let parse_result = layout::parse(m_ops, &self.members);
let (size, align) = ((*descriptor).m_size, (*descriptor).m_align);
dds_delete_topic_descriptor(descriptor);
let fields = match parse_result {
Ok(fields) => fields,
Err(e) => {
dds_free_typeinfo(type_info);
dds_dynamic_type_unref(&mut dtype);
return Err(e);
}
};
let field_index = fields
.iter()
.enumerate()
.map(|(i, f)| (f.name.clone(), i))
.collect();
Ok(DdsDynamicType(Arc::new(DynamicLayout {
type_name: self.type_name,
size,
align,
fields,
field_index,
dtype,
type_info,
})))
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DynamicValue {
Bool(bool),
I8(i8),
U8(u8),
I16(i16),
U16(u16),
I32(i32),
U32(u32),
I64(i64),
U64(u64),
F32(f32),
F64(f64),
Char8(u8),
String(String),
}
impl DynamicValue {
fn kind(&self) -> DynamicKind {
match self {
DynamicValue::Bool(_) => DynamicKind::Bool,
DynamicValue::I8(_) => DynamicKind::I8,
DynamicValue::U8(_) => DynamicKind::U8,
DynamicValue::I16(_) => DynamicKind::I16,
DynamicValue::U16(_) => DynamicKind::U16,
DynamicValue::I32(_) => DynamicKind::I32,
DynamicValue::U32(_) => DynamicKind::U32,
DynamicValue::I64(_) => DynamicKind::I64,
DynamicValue::U64(_) => DynamicKind::U64,
DynamicValue::F32(_) => DynamicKind::F32,
DynamicValue::F64(_) => DynamicKind::F64,
DynamicValue::Char8(_) => DynamicKind::Char8,
DynamicValue::String(_) => DynamicKind::String,
}
}
}
pub struct DynamicSample {
dtype: DdsDynamicType,
buf: std::ptr::NonNull<u8>,
}
unsafe impl Send for DynamicSample {}
impl DynamicSample {
pub fn new(dtype: &DdsDynamicType) -> Self {
let alloc_layout = dtype.alloc_layout();
let buf = unsafe { std::alloc::alloc_zeroed(alloc_layout) };
let buf = std::ptr::NonNull::new(buf).expect("DynamicSample allocation failed");
Self {
dtype: dtype.clone(),
buf,
}
}
pub fn set(&mut self, name: &str, value: DynamicValue) -> Result<(), DDSError> {
let field = self.dtype.field(name).ok_or(DDSError::BadParameter)?;
if field.kind != value.kind() {
return Err(DDSError::BadParameter);
}
let offset = field.offset as usize;
unsafe {
let p = self.buf.as_ptr().add(offset);
match value {
DynamicValue::Bool(v) => *(p as *mut bool) = v,
DynamicValue::I8(v) => *(p as *mut i8) = v,
DynamicValue::U8(v) => *(p as *mut u8) = v,
DynamicValue::I16(v) => *(p as *mut i16) = v,
DynamicValue::U16(v) => *(p as *mut u16) = v,
DynamicValue::I32(v) => *(p as *mut i32) = v,
DynamicValue::U32(v) => *(p as *mut u32) = v,
DynamicValue::I64(v) => *(p as *mut i64) = v,
DynamicValue::U64(v) => *(p as *mut u64) = v,
DynamicValue::F32(v) => *(p as *mut f32) = v,
DynamicValue::F64(v) => *(p as *mut f64) = v,
DynamicValue::Char8(v) => *(p as *mut u8) = v,
DynamicValue::String(s) => {
let ptr_slot = p as *mut *mut std::os::raw::c_char;
let old = *ptr_slot;
if !old.is_null() {
dds_free(old as *mut std::ffi::c_void);
}
*ptr_slot = alloc_dds_string(s.as_bytes())?;
}
}
}
Ok(())
}
pub fn get(&self, name: &str) -> Result<DynamicValue, DDSError> {
let field = self.dtype.field(name).ok_or(DDSError::BadParameter)?;
let offset = field.offset as usize;
unsafe {
let p = self.buf.as_ptr().add(offset);
Ok(match field.kind {
DynamicKind::Bool => DynamicValue::Bool(*(p as *const bool)),
DynamicKind::I8 => DynamicValue::I8(*(p as *const i8)),
DynamicKind::U8 => DynamicValue::U8(*(p as *const u8)),
DynamicKind::I16 => DynamicValue::I16(*(p as *const i16)),
DynamicKind::U16 => DynamicValue::U16(*(p as *const u16)),
DynamicKind::I32 => DynamicValue::I32(*(p as *const i32)),
DynamicKind::U32 => DynamicValue::U32(*(p as *const u32)),
DynamicKind::I64 => DynamicValue::I64(*(p as *const i64)),
DynamicKind::U64 => DynamicValue::U64(*(p as *const u64)),
DynamicKind::F32 => DynamicValue::F32(*(p as *const f32)),
DynamicKind::F64 => DynamicValue::F64(*(p as *const f64)),
DynamicKind::Char8 => DynamicValue::Char8(*(p as *const u8)),
DynamicKind::String => {
let ptr = *(p as *const *const std::os::raw::c_char);
if ptr.is_null() {
DynamicValue::String(String::new())
} else {
DynamicValue::String(
std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned(),
)
}
}
})
}
}
pub(crate) fn as_ptr(&self) -> *const std::ffi::c_void {
self.buf.as_ptr() as *const _
}
pub fn as_bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.buf.as_ptr(), self.dtype.0.size as usize) }
}
pub(crate) unsafe fn copy_from_raw(dtype: DdsDynamicType, src: *const std::ffi::c_void) -> Self {
let sample = Self::new(&dtype);
std::ptr::copy_nonoverlapping(src as *const u8, sample.buf.as_ptr(), dtype.0.size as usize);
for field in &dtype.0.fields {
if field.kind == DynamicKind::String {
let p = sample.buf.as_ptr().add(field.offset as usize)
as *mut *mut std::os::raw::c_char;
let src_ptr = *p;
if !src_ptr.is_null() {
let bytes = std::ffi::CStr::from_ptr(src_ptr).to_bytes();
*p = alloc_dds_string(bytes).expect("string field re-allocation");
}
}
}
sample
}
}
unsafe fn alloc_dds_string(bytes: &[u8]) -> Result<*mut std::os::raw::c_char, DDSError> {
let new_ptr = dds_string_alloc(bytes.len()) as *mut std::os::raw::c_char;
if new_ptr.is_null() {
return Err(DDSError::OutOfResources);
}
std::ptr::copy_nonoverlapping(bytes.as_ptr(), new_ptr as *mut u8, bytes.len());
*(new_ptr.add(bytes.len())) = 0; Ok(new_ptr)
}
impl Drop for DynamicSample {
fn drop(&mut self) {
unsafe {
for field in &self.dtype.0.fields {
if field.kind == DynamicKind::String {
let p = self.buf.as_ptr().add(field.offset as usize)
as *mut *mut std::os::raw::c_char;
let s = *p;
if !s.is_null() {
dds_free(s as *mut std::ffi::c_void);
}
}
}
std::alloc::dealloc(self.buf.as_ptr(), self.dtype.alloc_layout());
}
}
}
pub struct DdsDynamicTopic {
entity: DdsEntity,
dtype: DdsDynamicType,
_listener: Option<DdsListener>,
}
impl DdsDynamicTopic {
pub fn create(
participant: &DdsParticipant,
dtype: &DdsDynamicType,
topic_name: &str,
maybe_qos: Option<DdsQos>,
maybe_listener: Option<DdsListener>,
) -> Result<Self, DDSError> {
let participant_entity = unsafe { participant.entity().entity() };
let name_c = CString::new(topic_name).map_err(|_| DDSError::BadParameter)?;
unsafe {
let mut descriptor: *mut dds_topic_descriptor_t = std::ptr::null_mut();
let ret = dds_create_topic_descriptor(
dds_find_scope::DDS_FIND_SCOPE_LOCAL_DOMAIN,
participant_entity,
dtype.0.type_info,
0,
&mut descriptor,
);
if ret != 0 {
return Err(DDSError::from(ret));
}
let topic = dds_create_topic(
participant_entity,
descriptor,
name_c.as_ptr(),
maybe_qos.map_or(std::ptr::null(), |q| q.into()),
maybe_listener
.as_ref()
.map_or(std::ptr::null(), |l| l.into()),
);
dds_delete_topic_descriptor(descriptor);
if topic >= 0 {
Ok(DdsDynamicTopic {
entity: DdsEntity::new(topic),
dtype: dtype.clone(),
_listener: maybe_listener,
})
} else {
Err(DDSError::from(topic))
}
}
}
pub fn dynamic_type(&self) -> &DdsDynamicType {
&self.dtype
}
}
impl crate::Entity for DdsDynamicTopic {
fn entity(&self) -> &DdsEntity {
&self.entity
}
}
pub struct DdsDynamicWriter {
entity: DdsEntity,
dtype: DdsDynamicType,
_listener: Option<DdsListener>,
}
impl DdsDynamicWriter {
pub fn create(
entity: &dyn crate::DdsWritable,
topic: &DdsDynamicTopic,
maybe_qos: Option<DdsQos>,
maybe_listener: Option<DdsListener>,
) -> Result<Self, DDSError> {
unsafe {
let w = dds_create_writer(
entity.entity().entity(),
topic.entity.entity(),
maybe_qos.map_or(std::ptr::null(), |q| q.into()),
maybe_listener
.as_ref()
.map_or(std::ptr::null(), |l| l.into()),
);
if w >= 0 {
Ok(DdsDynamicWriter {
entity: DdsEntity::new(w),
dtype: topic.dtype.clone(),
_listener: maybe_listener,
})
} else {
Err(DDSError::from(w))
}
}
}
pub fn dynamic_type(&self) -> &DdsDynamicType {
&self.dtype
}
pub fn new_sample(&self) -> DynamicSample {
DynamicSample::new(&self.dtype)
}
pub fn write(&mut self, sample: &DynamicSample) -> Result<(), DDSError> {
let ret = unsafe { dds_write(self.entity.entity(), sample.as_ptr()) };
if ret >= 0 {
Ok(())
} else {
Err(DDSError::from(ret))
}
}
}
impl crate::Entity for DdsDynamicWriter {
fn entity(&self) -> &DdsEntity {
&self.entity
}
}
pub struct DdsDynamicReader {
entity: DdsEntity,
dtype: DdsDynamicType,
_listener: Option<DdsListener>,
}
impl DdsDynamicReader {
pub fn create(
entity: &dyn crate::DdsReadable,
topic: &DdsDynamicTopic,
maybe_qos: Option<DdsQos>,
maybe_listener: Option<DdsListener>,
) -> Result<Self, DDSError> {
unsafe {
let r = dds_create_reader(
entity.entity().entity(),
topic.entity.entity(),
maybe_qos.map_or(std::ptr::null(), |q| q.into()),
maybe_listener
.as_ref()
.map_or(std::ptr::null(), |l| l.into()),
);
if r >= 0 {
Ok(DdsDynamicReader {
entity: DdsEntity::new(r),
dtype: topic.dtype.clone(),
_listener: maybe_listener,
})
} else {
Err(DDSError::from(r))
}
}
}
pub fn dynamic_type(&self) -> &DdsDynamicType {
&self.dtype
}
pub fn take(&self, max: usize) -> Result<Vec<DynamicSample>, DDSError> {
let mut ptrs: Vec<*mut std::ffi::c_void> = vec![std::ptr::null_mut(); max];
let mut infos: Vec<dds_sample_info_t> = vec![unsafe { std::mem::zeroed() }; max];
unsafe {
let ret = dds_take(
self.entity.entity(),
ptrs.as_mut_ptr(),
infos.as_mut_ptr(),
max,
max as u32,
);
if ret < 0 {
return Err(DDSError::from(ret));
}
let mut out = Vec::with_capacity(ret as usize);
for i in 0..ret as usize {
if infos[i].valid_data {
out.push(DynamicSample::copy_from_raw(self.dtype.clone(), ptrs[i]));
}
}
Ok(out)
}
}
}
impl crate::Entity for DdsDynamicReader {
fn entity(&self) -> &DdsEntity {
&self.entity
}
}