pasture_core/layout/conversion/
attribute_conversion.rs

1//! Contains helper function and structures for raw binary point format conversions. This module contains a lot of unsafe
2//! code because it has to support conversions between various point formats at runtime. The conversions operate on binary
3//! buffers (`&[u8]` and `&mut [u8]`) that represent the binary layout of strongly typed `PointTypes`. Given two point
4//! types `A: PointType` and `B: PointType`, a runtime conversion from `A` to `B` works by first obtaining the binary
5//! representations of `A` and `B` through `view_raw_bytes`/`view_raw_bytes_mut`:
6//! ```ignore
7//! let point_a : A = Default::default();
8//! let point_b : B = Default::default();
9//!
10//! let buf_a = unsafe { view_raw_bytes(&a) };
11//! let buf_b = unsafe { view_raw_bytes_mut(&mut b) };
12//! ```
13//! The conversion then operates on these two buffers. As this is a *highly* unsafe operation where all sorts of things
14//! could go wrong, any conversion is only valid together with the *exact* `PointLayout` of both `A` and `B`!
15
16use lazy_static::lazy_static;
17use nalgebra::Vector3;
18use num_traits::AsPrimitive;
19use std::{collections::HashMap, ops::Range};
20
21use crate::layout::{PointAttributeDataType, PointAttributeDefinition, PointLayout};
22
23/// Helper structure that contains the relevant data to convert a single attribute from a source binary
24/// buffer to a target binary buffer.
25struct RawAttributeConverter {
26    conversion_fn: AttributeConversionFn,
27    source_range: Range<usize>,
28    target_range: Range<usize>,
29}
30
31impl RawAttributeConverter {
32    pub fn new(
33        conversion_fn: AttributeConversionFn,
34        source_offset: u64,
35        source_size: u64,
36        target_offset: u64,
37        target_size: u64,
38    ) -> Self {
39        Self {
40            conversion_fn,
41            source_range: Range {
42                start: source_offset as usize,
43                end: (source_offset + source_size) as usize,
44            },
45            target_range: Range {
46                start: target_offset as usize,
47                end: (target_offset + target_size) as usize,
48            },
49        }
50    }
51
52    /// Performs the conversion
53    unsafe fn convert(&self, source_point: &[u8], target_point: &mut [u8]) {
54        let source_slice = &source_point[self.source_range.clone()];
55        let target_slice = &mut target_point[self.target_range.clone()];
56
57        (self.conversion_fn)(source_slice, target_slice);
58    }
59}
60
61/// Helper struct that encapsulates all `RawAttributeConverter`s necessary for converting a point in a specific layout
62pub struct RawPointConverter {
63    attribute_converters: Vec<RawAttributeConverter>,
64}
65
66impl RawPointConverter {
67    /// Creates a new `RawPointConverter` that converts points `from_layout` to `to_layout`. The converter converts
68    /// all attributes that are present in both `from_layout` and `to_layout` and which can be converted.
69    pub fn from_to(from_layout: &PointLayout, to_layout: &PointLayout) -> RawPointConverter {
70        let converters = from_layout
71            .attributes()
72            .filter(|&from_attribute| to_layout.has_attribute_with_name(from_attribute.name()))
73            .filter_map(|from_attribute| {
74                let to_attribute = to_layout
75                    .get_attribute_by_name(from_attribute.name())
76                    .unwrap();
77                let conversion_fn = get_converter_for_attributes(
78                    from_attribute.attribute_definition(),
79                    to_attribute.attribute_definition(),
80                );
81                conversion_fn.map(|conversion_fn| {
82                    RawAttributeConverter::new(
83                        conversion_fn,
84                        from_attribute.offset(),
85                        from_attribute.size(),
86                        to_attribute.offset(),
87                        to_attribute.size(),
88                    )
89                })
90            })
91            .collect::<Vec<_>>();
92
93        Self {
94            attribute_converters: converters,
95        }
96    }
97
98    /// Converts the `source_point` into the `target_point`
99    ///
100    /// # Safety
101    ///
102    /// `source_point` must contain memory for an initialized `PointType` `T` that has the exact same
103    /// `PointLayout` as the one passed to [`Self::from_to`] as its first argument!
104    pub unsafe fn convert(&self, source_point: &[u8], target_point: &mut [u8]) {
105        for converter in self.attribute_converters.iter() {
106            converter.convert(source_point, target_point);
107        }
108    }
109}
110
111/// Function pointer type for functions that convert between attributes with different datatypes
112pub type AttributeConversionFn = unsafe fn(&[u8], &mut [u8]) -> ();
113
114/// Returns a conversion function for converting from `from_attribute` into `to_attribute`. Both attributes must have the
115/// same name but can have different datatypes. Conversion functions operate on raw byte buffers, where the first argument
116/// is a buffer that represents a single value of `from_attribute` and the second buffer is a single mutable value of
117/// `to_attribute`. If both attributes are equal, `None` is returned.
118///
119/// # Panics
120///
121/// If no conversion from `from_attribute` into `to_attribute` is possible
122pub fn get_converter_for_attributes(
123    from_attribute: &PointAttributeDefinition,
124    to_attribute: &PointAttributeDefinition,
125) -> Option<AttributeConversionFn> {
126    assert_eq!(from_attribute.name(), to_attribute.name());
127    if from_attribute.datatype() == to_attribute.datatype() {
128        return None;
129    }
130
131    get_generic_converter(from_attribute.datatype(), to_attribute.datatype())
132}
133
134macro_rules! insert_scalar_converter_using_as {
135    ($prim_from:ident, $prim_to:ident, $type_from:ident, $type_to:ident, $map:expr) => {
136        // Insert symmetric conversion function from<->to and assert that they are unique
137        assert!(($map)
138            .insert(
139                (
140                    PointAttributeDataType::$type_from,
141                    PointAttributeDataType::$type_to,
142                ),
143                convert_scalar_using_as::<$prim_from, $prim_to>,
144            )
145            .is_none());
146        assert!(($map)
147            .insert(
148                (
149                    PointAttributeDataType::$type_to,
150                    PointAttributeDataType::$type_from,
151                ),
152                convert_scalar_using_as::<$prim_to, $prim_from>,
153            )
154            .is_none());
155    };
156}
157
158macro_rules! insert_vec3_converter_using_as {
159    ($prim_from:ident, $prim_to:ident, $type_from:ident, $type_to:ident, $map:expr) => {
160        // Insert symmetric conversion function from<->to and assert that they are unique
161        assert!(($map)
162            .insert(
163                (
164                    PointAttributeDataType::$type_from,
165                    PointAttributeDataType::$type_to,
166                ),
167                convert_vec3_using_as::<$prim_from, $prim_to>,
168            )
169            .is_none());
170        assert!(($map)
171            .insert(
172                (
173                    PointAttributeDataType::$type_to,
174                    PointAttributeDataType::$type_from,
175                ),
176                convert_vec3_using_as::<$prim_to, $prim_from>,
177            )
178            .is_none());
179    };
180}
181
182/// Returns a generic converter that can convert between primitive types. These functions implement primitive type conversions
183/// as if using the `as` operator, using the [`num_traits::AsPrimitive`] trait
184pub fn get_generic_converter(
185    from_type: PointAttributeDataType,
186    to_type: PointAttributeDataType,
187) -> Option<AttributeConversionFn> {
188    lazy_static! {
189        static ref GENERIC_CONVERTERS: HashMap<(PointAttributeDataType, PointAttributeDataType), AttributeConversionFn> = {
190            let mut converters = HashMap::<
191                (PointAttributeDataType, PointAttributeDataType),
192                AttributeConversionFn,
193            >::new();
194            insert_scalar_converter_using_as!(u8, u16, U8, U16, converters);
195            insert_scalar_converter_using_as!(u8, u32, U8, U32, converters);
196            insert_scalar_converter_using_as!(u8, u64, U8, U64, converters);
197            insert_scalar_converter_using_as!(u8, i8, U8, I8, converters);
198            insert_scalar_converter_using_as!(u8, i16, U8, I16, converters);
199            insert_scalar_converter_using_as!(u8, i32, U8, I32, converters);
200            insert_scalar_converter_using_as!(u8, i64, U8, I64, converters);
201            insert_scalar_converter_using_as!(u8, f32, U8, F32, converters);
202            insert_scalar_converter_using_as!(u8, f64, U8, F64, converters);
203
204            insert_scalar_converter_using_as!(u16, u32, U16, U32, converters);
205            insert_scalar_converter_using_as!(u16, u64, U16, U64, converters);
206            insert_scalar_converter_using_as!(u16, i8, U16, I8, converters);
207            insert_scalar_converter_using_as!(u16, i16, U16, I16, converters);
208            insert_scalar_converter_using_as!(u16, i32, U16, I32, converters);
209            insert_scalar_converter_using_as!(u16, i64, U16, I64, converters);
210            insert_scalar_converter_using_as!(u16, f32, U16, F32, converters);
211            insert_scalar_converter_using_as!(u16, f64, U16, F64, converters);
212
213            insert_scalar_converter_using_as!(u32, u64, U32, U64, converters);
214            insert_scalar_converter_using_as!(u32, i8, U32, I8, converters);
215            insert_scalar_converter_using_as!(u32, i16, U32, I16, converters);
216            insert_scalar_converter_using_as!(u32, i32, U32, I32, converters);
217            insert_scalar_converter_using_as!(u32, i64, U32, I64, converters);
218            insert_scalar_converter_using_as!(u32, f32, U32, F32, converters);
219            insert_scalar_converter_using_as!(u32, f64, U32, F64, converters);
220
221            insert_scalar_converter_using_as!(u64, i8, U64, I8, converters);
222            insert_scalar_converter_using_as!(u64, i16, U64, I16, converters);
223            insert_scalar_converter_using_as!(u64, i32, U64, I32, converters);
224            insert_scalar_converter_using_as!(u64, i64, U64, I64, converters);
225            insert_scalar_converter_using_as!(u64, f32, U64, F32, converters);
226            insert_scalar_converter_using_as!(u64, f64, U64, F64, converters);
227
228            insert_scalar_converter_using_as!(i8, i16, I8, I16, converters);
229            insert_scalar_converter_using_as!(i8, i32, I8, I32, converters);
230            insert_scalar_converter_using_as!(i8, i64, I8, I64, converters);
231            insert_scalar_converter_using_as!(i8, f32, I8, F32, converters);
232            insert_scalar_converter_using_as!(i8, f64, I8, F64, converters);
233
234            insert_scalar_converter_using_as!(i16, i32, I16, I32, converters);
235            insert_scalar_converter_using_as!(i16, i64, I16, I64, converters);
236            insert_scalar_converter_using_as!(i16, f32, I16, F32, converters);
237            insert_scalar_converter_using_as!(i16, f64, I16, F64, converters);
238
239            insert_scalar_converter_using_as!(i32, i64, I32, I64, converters);
240            insert_scalar_converter_using_as!(i32, f32, I32, F32, converters);
241            insert_scalar_converter_using_as!(i32, f64, I32, F64, converters);
242
243            insert_scalar_converter_using_as!(i64, f32, I64, F32, converters);
244            insert_scalar_converter_using_as!(i64, f64, I64, F64, converters);
245
246            insert_scalar_converter_using_as!(f32, f64, F32, F64, converters);
247
248            insert_vec3_converter_using_as!(f32, f64, Vec3f32, Vec3f64, converters);
249
250            insert_vec3_converter_using_as!(u8, u16, Vec3u8, Vec3u16, converters);
251            insert_vec3_converter_using_as!(u8, i32, Vec3u8, Vec3i32, converters);
252            insert_vec3_converter_using_as!(u8, f32, Vec3u8, Vec3f32, converters);
253            insert_vec3_converter_using_as!(u8, f64, Vec3u8, Vec3f64, converters);
254
255            insert_vec3_converter_using_as!(u16, i32, Vec3u16, Vec3i32, converters);
256            insert_vec3_converter_using_as!(u16, f32, Vec3u16, Vec3f32, converters);
257            insert_vec3_converter_using_as!(u16, f64, Vec3u16, Vec3f64, converters);
258
259            insert_vec3_converter_using_as!(i32, f32, Vec3i32, Vec3f32, converters);
260            insert_vec3_converter_using_as!(i32, f64, Vec3i32, Vec3f64, converters);
261
262            converters
263        };
264    }
265
266    let key = (from_type, to_type);
267    let f = GENERIC_CONVERTERS
268        .get(&key)
269        .unwrap_or_else(|| panic!("Invalid conversion {} -> {}", from_type, to_type));
270    Some(*f)
271}
272
273/// Unit conversion function (when from and to represent the same datatype)
274///
275/// # Safety
276///
277/// Even though this function only performs a `memcpy`, it is only valid to call it if both
278/// `from` and `to` point to the same `PrimitiveType` (so `from` represents a `&T` and `to`
279/// a `&mut T`)
280///
281/// ```unsafe
282/// # use nalgebra::Vector3;
283/// # use pasture_core::layout::*;
284/// # use pasture_core::util::*;
285///
286/// let source : Vector3<f64> = Vector3::new(1.0, 2.0, 3.0);
287/// let mut dest : Vector3<f64> = Default::default();
288///
289/// let source_bytes = view_raw_bytes(&source);
290/// let dest_bytes = view_raw_bytes_mut(&mut dest);
291/// convert_unit(source_bytes, dest_bytes);
292///
293/// assert_eq!(1.0, dest.x);
294/// assert_eq!(2.0, dest.y);
295/// assert_eq!(3.0, dest.z);
296/// ```
297pub unsafe fn convert_unit(from: &[u8], to: &mut [u8]) {
298    to.copy_from_slice(from)
299}
300
301/// Generic conversion function from scalar values of type `From` to type `To`. Assumes that `From` and
302/// `To` are primitive types so that the conversion can happen by using `as`. Boils down to `*to_value = from_value as To;`
303/// where `from_value` comes from the bytes `from` interpreted as `From`, and `to_value` comes from the bytes
304/// `to` interpreted as `To`.
305///
306/// # Safety
307///
308/// `from` and `to` can be unaligned, but must point to valid initialized memory of the types `From` and
309/// `To`, respectively
310unsafe fn convert_scalar_using_as<From, To>(from: &[u8], to: &mut [u8])
311where
312    From: AsPrimitive<To> + Copy,
313    To: Copy + 'static,
314{
315    let from_ptr = from.as_ptr() as *const From;
316    let to_ptr = to.as_mut_ptr() as *mut To;
317
318    let from_value = from_ptr.read_unaligned();
319    let to_value = from_value.as_();
320    to_ptr.write_unaligned(to_value);
321}
322
323/// Generic conversion function from a `Vector3<From>` into a `Vector3<To>`. Assumes that `From` and `To`
324/// are primitive types so that the conversion can happen by using `as` for the components of the vector.
325/// Boils down to `to_vector.x = from_vector.x as To;` etc. where `from_vector` comes from the bytes `from`
326/// interpreted as `Vector3<From>` and `to_vector` comes from the bytes `to` interpreted as `Vector3<To>`.
327///
328/// # Safety
329///
330/// `from` and `to` can be unaligned, but must point to valid initialized memory of the type `Vector3<From>`
331/// and `Vector3<To>`, respectively.
332unsafe fn convert_vec3_using_as<From, To>(from: &[u8], to: &mut [u8])
333where
334    From: AsPrimitive<To> + Copy,
335    To: Copy + 'static,
336{
337    let from_ptr = from.as_ptr() as *const Vector3<From>;
338    let to_ptr = to.as_mut_ptr() as *mut Vector3<To>;
339
340    let from_vec = from_ptr.read_unaligned();
341    let to_vec = Vector3::<To>::new(from_vec[0].as_(), from_vec[1].as_(), from_vec[2].as_());
342    to_ptr.write_unaligned(to_vec);
343}