pasture_core/layout/
point_layout.rs

1use std::{alloc::Layout, borrow::Cow, fmt::Display, iter::FromIterator, ops::Range};
2
3use itertools::Itertools;
4use nalgebra::{Vector3, Vector4};
5use static_assertions::const_assert;
6use uuid::Uuid;
7
8use crate::math::Alignable;
9
10/// Possible data types for individual point attributes
11///
12/// # Why no `bool` anymore?
13///
14/// Previous versions of pasture allowed `bool` as a `PointAttributeDataType`. This introduced undefined behavior (UB)
15/// into pasture since (contrary to C) there are only two valid bit patterns for `bool` in Rust (`0x0` and `0x1`). Hence,
16/// casting from a byte slice to a type containing `bool` values will result in UB unless we guarantee that the bytes
17/// are a valid bit pattern. In theory, these checks could be implemented, but they would have to be implemented everywhere
18/// where we deal with raw point bytes. Since pasture switched to the `bytemuck` crate for all byte casting, and `bytemuck`
19/// simply disallows slice-to-T casts for types that are not valid for any bit pattern, is was deemed that this is not worth
20/// the effort. If you need `bool`-like behavior, you can always use an `u8` type and check for `value != 0`.
21#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub enum PointAttributeDataType {
24    /// An unsigned 8-bit integer value, corresponding to Rusts `u8` type
25    U8,
26    /// A signed 8-bit integer value, corresponding to Rusts `i8` type
27    I8,
28    /// An unsigned 16-bit integer value, corresponding to Rusts `u16` type
29    U16,
30    /// A signed 16-bit integer value, corresponding to Rusts `i16` type
31    I16,
32    /// An unsigned 32-bit integer value, corresponding to Rusts `u32` type
33    U32,
34    /// A signed 32-bit integer value, corresponding to Rusts `i32` type
35    I32,
36    /// An unsigned 64-bit integer value, corresponding to Rusts `u64` type
37    U64,
38    /// A signed 64-bit integer value, corresponding to Rusts `i64` type
39    I64,
40    /// A single-precision floating point value, corresponding to Rusts `f32` type
41    F32,
42    /// A double-precision floating point value, corresponding to Rusts `f64` type
43    F64,
44    /// A 3-component vector storing unsigned 8-bit integer values. Corresponding to the `Vector3<u8>` type of the [nalgebra crate](https://crates.io/crates/nalgebra)
45    Vec3u8,
46    /// A 3-component vector storing unsigned 16-bit integer values. Corresponding to the `Vector3<u16>` type of the [nalgebra crate](https://crates.io/crates/nalgebra)
47    Vec3u16,
48    /// A 3-component vector storing single-precision floating point values. Corresponding to the `Vector3<f32>` type of the [nalgebra crate](https://crates.io/crates/nalgebra)
49    Vec3f32,
50    /// A 3-component vector storing singed 32-bit integer values. Corresponding to the `Vector3<i32>` type of the [nalgebra crate](https://crates.io/crates/nalgebra)
51    Vec3i32,
52    /// A 3-component vector storing double-precision floating point values. Corresponding to the `Vector3<f32>` type of the [nalgebra crate](https://crates.io/crates/nalgebra)
53    Vec3f64,
54    /// A 4-component vector storing unsigned 8-bit integer values. Corresponding to the `Vector4<u8>` type of the [nalgebra crate](https://crates.io/crates/nalgebra)
55    Vec4u8,
56    /// A raw byte array of a given size determined at runtime. This corresponds to the Rust type `[u8; N]`
57    ByteArray(u64),
58    /// A custom data type. This makes pasture extensible to types that it does not know. To use a custom type `T` with
59    /// pasture, implement the `PrimitiveType` trait for this type and have it return `PointAttributeDataType::Custom`
60    /// with the correct size and alignment
61    Custom {
62        size: u64,
63        min_alignment: u64,
64        name: Uuid,
65    }, //TODO REFACTOR Vector types should probably be Point3 instead, or at least use nalgebra::Point3 as their underlying type!
66       //TODO Instead of representing each VecN<T> type as a separate literal, might it be possible to do: Vec3(PointAttributeDataType)?
67       //Not in that way of course, because of recursive datastructures, but something like that?
68}
69
70impl PointAttributeDataType {
71    /// Size of the associated `PointAttributeDataType`
72    pub const fn size(&self) -> u64 {
73        match self {
74            PointAttributeDataType::U8 => 1,
75            PointAttributeDataType::I8 => 1,
76            PointAttributeDataType::U16 => 2,
77            PointAttributeDataType::I16 => 2,
78            PointAttributeDataType::U32 => 4,
79            PointAttributeDataType::I32 => 4,
80            PointAttributeDataType::U64 => 8,
81            PointAttributeDataType::I64 => 8,
82            PointAttributeDataType::F32 => 4,
83            PointAttributeDataType::F64 => 8,
84            PointAttributeDataType::Vec3u8 => 3,
85            PointAttributeDataType::Vec3u16 => 6,
86            PointAttributeDataType::Vec3i32 => 12,
87            PointAttributeDataType::Vec3f32 => 12,
88            PointAttributeDataType::Vec3f64 => 24,
89            PointAttributeDataType::Vec4u8 => 4,
90            PointAttributeDataType::ByteArray(length) => *length,
91            PointAttributeDataType::Custom {
92                size,
93                min_alignment: _,
94                name: _,
95            } => *size,
96        }
97    }
98
99    /// Minimum required alignment of the associated `PointAttributeDataType`
100    pub fn min_alignment(&self) -> u64 {
101        let align = match self {
102            PointAttributeDataType::U8 => std::mem::align_of::<u8>(),
103            PointAttributeDataType::I8 => std::mem::align_of::<i8>(),
104            PointAttributeDataType::U16 => std::mem::align_of::<u16>(),
105            PointAttributeDataType::I16 => std::mem::align_of::<i16>(),
106            PointAttributeDataType::U32 => std::mem::align_of::<u32>(),
107            PointAttributeDataType::I32 => std::mem::align_of::<i32>(),
108            PointAttributeDataType::U64 => std::mem::align_of::<u64>(),
109            PointAttributeDataType::I64 => std::mem::align_of::<i64>(),
110            PointAttributeDataType::F32 => std::mem::align_of::<f32>(),
111            PointAttributeDataType::F64 => std::mem::align_of::<f64>(),
112            PointAttributeDataType::Vec3u8 => std::mem::align_of::<Vector3<u8>>(),
113            PointAttributeDataType::Vec3u16 => std::mem::align_of::<Vector3<u16>>(),
114            PointAttributeDataType::Vec3i32 => std::mem::align_of::<Vector3<i32>>(),
115            PointAttributeDataType::Vec3f32 => std::mem::align_of::<Vector3<f32>>(),
116            PointAttributeDataType::Vec3f64 => std::mem::align_of::<Vector3<f64>>(),
117            PointAttributeDataType::Vec4u8 => std::mem::align_of::<Vector4<u8>>(),
118            PointAttributeDataType::ByteArray(_) => 1,
119            PointAttributeDataType::Custom {
120                size: _,
121                min_alignment,
122                name: _,
123            } => *min_alignment as usize,
124        };
125        align as u64
126    }
127}
128
129impl Display for PointAttributeDataType {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        match self {
132            PointAttributeDataType::U8 => write!(f, "U8"),
133            PointAttributeDataType::I8 => write!(f, "I8"),
134            PointAttributeDataType::U16 => write!(f, "U16"),
135            PointAttributeDataType::I16 => write!(f, "I16"),
136            PointAttributeDataType::U32 => write!(f, "U32"),
137            PointAttributeDataType::I32 => write!(f, "I32"),
138            PointAttributeDataType::U64 => write!(f, "U64"),
139            PointAttributeDataType::I64 => write!(f, "I64"),
140            PointAttributeDataType::F32 => write!(f, "F32"),
141            PointAttributeDataType::F64 => write!(f, "F64"),
142            PointAttributeDataType::Vec3u8 => write!(f, "Vec3<u8>"),
143            PointAttributeDataType::Vec3u16 => write!(f, "Vec3<u16>"),
144            PointAttributeDataType::Vec3i32 => write!(f, "Vec3<i32>"),
145            PointAttributeDataType::Vec3f32 => write!(f, "Vec3<f32>"),
146            PointAttributeDataType::Vec3f64 => write!(f, "Vec3<f64>"),
147            PointAttributeDataType::Vec4u8 => write!(f, "Vec4<u8>"),
148            PointAttributeDataType::ByteArray(length) => write!(f, "ByteArray[{length}]"),
149            PointAttributeDataType::Custom {
150                size: _,
151                min_alignment: _,
152                name,
153            } => write!(f, "{name}"),
154        }
155    }
156}
157
158/// Marker trait for all types that can be used as primitive types within a `PointAttributeDefinition`. It provides a mapping
159/// between Rust types and the `PointAttributeDataType` enum.
160pub trait PrimitiveType: Copy + bytemuck::Pod {
161    /// Returns the corresponding `PointAttributeDataType` for the implementing type
162    fn data_type() -> PointAttributeDataType;
163}
164
165impl PrimitiveType for u8 {
166    fn data_type() -> PointAttributeDataType {
167        PointAttributeDataType::U8
168    }
169}
170impl PrimitiveType for u16 {
171    fn data_type() -> PointAttributeDataType {
172        PointAttributeDataType::U16
173    }
174}
175impl PrimitiveType for u32 {
176    fn data_type() -> PointAttributeDataType {
177        PointAttributeDataType::U32
178    }
179}
180impl PrimitiveType for u64 {
181    fn data_type() -> PointAttributeDataType {
182        PointAttributeDataType::U64
183    }
184}
185impl PrimitiveType for i8 {
186    fn data_type() -> PointAttributeDataType {
187        PointAttributeDataType::I8
188    }
189}
190impl PrimitiveType for i16 {
191    fn data_type() -> PointAttributeDataType {
192        PointAttributeDataType::I16
193    }
194}
195impl PrimitiveType for i32 {
196    fn data_type() -> PointAttributeDataType {
197        PointAttributeDataType::I32
198    }
199}
200impl PrimitiveType for i64 {
201    fn data_type() -> PointAttributeDataType {
202        PointAttributeDataType::I64
203    }
204}
205impl PrimitiveType for f32 {
206    fn data_type() -> PointAttributeDataType {
207        PointAttributeDataType::F32
208    }
209}
210impl PrimitiveType for f64 {
211    fn data_type() -> PointAttributeDataType {
212        PointAttributeDataType::F64
213    }
214}
215impl PrimitiveType for Vector3<u8> {
216    fn data_type() -> PointAttributeDataType {
217        PointAttributeDataType::Vec3u8
218    }
219}
220impl PrimitiveType for Vector3<u16> {
221    fn data_type() -> PointAttributeDataType {
222        PointAttributeDataType::Vec3u16
223    }
224}
225impl PrimitiveType for Vector3<i32> {
226    fn data_type() -> PointAttributeDataType {
227        PointAttributeDataType::Vec3i32
228    }
229}
230impl PrimitiveType for Vector3<f32> {
231    fn data_type() -> PointAttributeDataType {
232        PointAttributeDataType::Vec3f32
233    }
234}
235impl PrimitiveType for Vector3<f64> {
236    fn data_type() -> PointAttributeDataType {
237        PointAttributeDataType::Vec3f64
238    }
239}
240
241impl PrimitiveType for Vector4<u8> {
242    fn data_type() -> PointAttributeDataType {
243        PointAttributeDataType::Vec4u8
244    }
245}
246
247// Assert sizes of vector types are as we expect. Primitive types always are the same size, but we don't know
248// what nalgebra does with the Vector3 types on the target machine...
249const_assert!(std::mem::size_of::<Vector3<u8>>() == 3);
250const_assert!(std::mem::size_of::<Vector3<u16>>() == 6);
251const_assert!(std::mem::size_of::<Vector3<f32>>() == 12);
252const_assert!(std::mem::size_of::<Vector3<f64>>() == 24);
253const_assert!(std::mem::size_of::<Vector4<u8>>() == 4);
254
255/// A definition for a single point attribute of a point cloud. Point attributes are things like the position,
256/// GPS time, intensity etc. In Pasture, attributes are identified by a unique name together with the data type
257/// that a single record of the attribute is stored in. Attributes can be grouped into two categories: Built-in
258/// attributes (e.g. POSITION_3D, INTENSITY, GPS_TIME etc.) and custom attributes.
259#[derive(Debug, Clone, PartialEq, Eq, Hash)]
260#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
261pub struct PointAttributeDefinition {
262    name: Cow<'static, str>,
263    datatype: PointAttributeDataType,
264}
265
266impl PointAttributeDefinition {
267    /// Creates a new custom PointAttributeDefinition with the given name and data type
268    /// ```
269    /// # use pasture_core::layout::*;
270    /// # use std::borrow::Cow;
271    /// let custom_attribute = PointAttributeDefinition::custom(Cow::Borrowed("Custom"), PointAttributeDataType::F32);
272    /// # assert_eq!(custom_attribute.name(), "Custom");
273    /// # assert_eq!(custom_attribute.datatype(), PointAttributeDataType::F32);
274    /// ```
275    pub const fn custom(name: Cow<'static, str>, datatype: PointAttributeDataType) -> Self {
276        Self { name, datatype }
277    }
278
279    /// Returns the name of this PointAttributeDefinition
280    /// ```
281    /// # use pasture_core::layout::*;
282    /// # use std::borrow::Cow;
283    /// let custom_attribute = PointAttributeDefinition::custom(Cow::Borrowed("Custom"), PointAttributeDataType::F32);
284    /// let name = custom_attribute.name();
285    /// # assert_eq!(name, "Custom");
286    /// ```
287    pub fn name(&self) -> &str {
288        &self.name
289    }
290
291    /// Returns the datatype of this PointAttributeDefinition
292    /// ```
293    /// # use pasture_core::layout::*;
294    /// # use std::borrow::Cow;
295    /// let custom_attribute = PointAttributeDefinition::custom(Cow::Borrowed("Custom"), PointAttributeDataType::F32);
296    /// let datatype = custom_attribute.datatype();
297    /// # assert_eq!(datatype, PointAttributeDataType::F32);
298    /// ```
299    #[inline]
300    pub const fn datatype(&self) -> PointAttributeDataType {
301        self.datatype
302    }
303
304    /// Returns the size in bytes of this attribute
305    #[inline]
306    pub const fn size(&self) -> u64 {
307        self.datatype.size()
308    }
309
310    /// Returns a new PointAttributeDefinition based on this PointAttributeDefinition, but with a different datatype
311    /// ```
312    /// # use pasture_core::layout::*;
313    /// let custom_position_attribute = attributes::POSITION_3D.with_custom_datatype(PointAttributeDataType::Vec3f32);
314    /// # assert_eq!(custom_position_attribute.name(), attributes::POSITION_3D.name());
315    /// # assert_eq!(custom_position_attribute.datatype(), PointAttributeDataType::Vec3f32);
316    /// ```
317    pub fn with_custom_datatype(&self, new_datatype: PointAttributeDataType) -> Self {
318        Self {
319            name: self.name.clone(),
320            datatype: new_datatype,
321        }
322    }
323
324    /// Creates a `PointAttributeMember` from the associated `PointAttributeDefinition` by specifying an offset
325    /// of the attribute within a `PointType`. This turns an abstract `PointAttributeDefinition` into a concrete
326    /// `PointAttributeMember`
327    /// ```
328    /// # use pasture_core::layout::*;
329    /// let custom_position_attribute = attributes::POSITION_3D.at_offset_in_type(8);
330    /// # assert_eq!(custom_position_attribute.name(), attributes::POSITION_3D.name());
331    /// # assert_eq!(custom_position_attribute.datatype(), attributes::POSITION_3D.datatype());
332    /// # assert_eq!(custom_position_attribute.offset(), 8);
333    /// ```
334    pub fn at_offset_in_type(&self, offset: u64) -> PointAttributeMember {
335        PointAttributeMember {
336            attribute_definition: self.clone(),
337            offset,
338            size: self.size(),
339        }
340    }
341}
342
343impl Display for PointAttributeDefinition {
344    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345        write!(f, "[{};{}]", self.name, self.datatype)
346    }
347}
348
349/// A point attribute within a `PointType` structure. This is similar to a `PointAttributeDefinition`, but includes the
350/// offset of the member within the structure
351#[derive(Debug, Clone, PartialEq, Eq, Hash)]
352#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
353pub struct PointAttributeMember {
354    attribute_definition: PointAttributeDefinition,
355    offset: u64,
356    size: u64,
357}
358
359impl PointAttributeMember {
360    /// Creates a new custom `PointAttributeMember` with the given name, datatype and byte offset
361    /// ```
362    /// # use pasture_core::layout::*;
363    /// let custom_attribute = PointAttributeMember::custom("Custom", PointAttributeDataType::F32, 8);
364    /// # assert_eq!(custom_attribute.name(), "Custom");
365    /// # assert_eq!(custom_attribute.datatype(), PointAttributeDataType::F32);
366    /// # assert_eq!(custom_attribute.offset(), 8);
367    /// ```
368    pub fn custom(name: &'static str, datatype: PointAttributeDataType, offset: u64) -> Self {
369        Self {
370            attribute_definition: PointAttributeDefinition {
371                name: Cow::Borrowed(name),
372                datatype,
373            },
374            offset,
375            size: datatype.size(),
376        }
377    }
378
379    /// Returns the name of the associated `PointAttributeMember`
380    /// ```
381    /// # use pasture_core::layout::*;
382    /// let custom_attribute = PointAttributeMember::custom("Custom", PointAttributeDataType::F32, 8);
383    /// let name = custom_attribute.name();
384    /// # assert_eq!(name, "Custom");
385    /// ```
386    pub fn name(&self) -> &str {
387        self.attribute_definition.name()
388    }
389
390    /// Returns the datatype of the associated `PointAttributeMember`
391    /// ```
392    /// # use pasture_core::layout::*;
393    /// let custom_attribute = PointAttributeMember::custom("Custom", PointAttributeDataType::F32, 0);
394    /// let datatype = custom_attribute.datatype();
395    /// # assert_eq!(datatype, PointAttributeDataType::F32);
396    /// ```
397    #[inline]
398    pub const fn datatype(&self) -> PointAttributeDataType {
399        self.attribute_definition.datatype()
400    }
401
402    /// Returns the byte offset of the associated `PointAttributeMember`
403    /// ```
404    /// # use pasture_core::layout::*;
405    /// let custom_attribute = PointAttributeMember::custom("Custom", PointAttributeDataType::F32, 8);
406    /// let offset = custom_attribute.offset();
407    /// # assert_eq!(offset, 8);
408    /// ```
409    #[inline]
410    pub const fn offset(&self) -> u64 {
411        self.offset
412    }
413
414    /// Returns the underlying `PointAttributeDefinition` for the associated `PointAttributeMember`
415    pub fn attribute_definition(&self) -> &PointAttributeDefinition {
416        &self.attribute_definition
417    }
418
419    /// Returns the size in bytes of the associated `PointAttributeMember`
420    #[inline]
421    pub const fn size(&self) -> u64 {
422        self.size
423    }
424
425    /// Returns the byte range within the `PointType` for this attribute
426    pub fn byte_range_within_point(&self) -> Range<usize> {
427        let start = self.offset as usize;
428        let end = start + self.size() as usize;
429        start..end
430    }
431}
432
433impl Display for PointAttributeMember {
434    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
435        write!(
436            f,
437            "[{};{} @ offset {}]",
438            self.name(),
439            self.datatype(),
440            self.offset
441        )
442    }
443}
444
445// impl PartialEq for PointAttributeMember {
446//     fn eq(&self, other: &Self) -> bool {
447//         self.name == other.name && self.datatype == other.datatype
448//     }
449// }
450
451// impl Eq for PointAttributeMember {}
452
453/// Module containing default attribute definitions
454pub mod attributes {
455    use std::borrow::Cow;
456
457    use super::{PointAttributeDataType, PointAttributeDefinition};
458
459    /// Attribute definition for a 3D position. Default datatype is Vec3f64
460    pub const POSITION_3D: PointAttributeDefinition = PointAttributeDefinition {
461        name: Cow::Borrowed("Position3D"),
462        datatype: PointAttributeDataType::Vec3f64,
463    };
464
465    /// Attribute definition for an intensity value. Default datatype is U16
466    pub const INTENSITY: PointAttributeDefinition = PointAttributeDefinition {
467        name: Cow::Borrowed("Intensity"),
468        datatype: PointAttributeDataType::U16,
469    };
470
471    /// Attribute definition for a return number. Default datatype is U8
472    pub const RETURN_NUMBER: PointAttributeDefinition = PointAttributeDefinition {
473        name: Cow::Borrowed("ReturnNumber"),
474        datatype: PointAttributeDataType::U8,
475    };
476
477    /// Attribute definition for the number of returns. Default datatype is U8
478    pub const NUMBER_OF_RETURNS: PointAttributeDefinition = PointAttributeDefinition {
479        name: Cow::Borrowed("NumberOfReturns"),
480        datatype: PointAttributeDataType::U8,
481    };
482
483    /// Attribute definition for the classification flags. Default datatype is U8
484    pub const CLASSIFICATION_FLAGS: PointAttributeDefinition = PointAttributeDefinition {
485        name: Cow::Borrowed("ClassificationFlags"),
486        datatype: PointAttributeDataType::U8,
487    };
488
489    /// Attribute definition for the scanner channel. Default datatype is U8
490    pub const SCANNER_CHANNEL: PointAttributeDefinition = PointAttributeDefinition {
491        name: Cow::Borrowed("ScannerChannel"),
492        datatype: PointAttributeDataType::U8,
493    };
494
495    /// Attribute definition for a scan direction flag. Default datatype is U8
496    pub const SCAN_DIRECTION_FLAG: PointAttributeDefinition = PointAttributeDefinition {
497        name: Cow::Borrowed("ScanDirectionFlag"),
498        datatype: PointAttributeDataType::U8,
499    };
500
501    /// Attribute definition for an edge of flight line flag. Default datatype is U8
502    pub const EDGE_OF_FLIGHT_LINE: PointAttributeDefinition = PointAttributeDefinition {
503        name: Cow::Borrowed("EdgeOfFlightLine"),
504        datatype: PointAttributeDataType::U8,
505    };
506
507    /// Attribute definition for a classification. Default datatype is U8
508    pub const CLASSIFICATION: PointAttributeDefinition = PointAttributeDefinition {
509        name: Cow::Borrowed("Classification"),
510        datatype: PointAttributeDataType::U8,
511    };
512
513    /// Attribute definition for a scan angle rank. Default datatype is I8
514    pub const SCAN_ANGLE_RANK: PointAttributeDefinition = PointAttributeDefinition {
515        name: Cow::Borrowed("ScanAngleRank"),
516        datatype: PointAttributeDataType::I8,
517    };
518
519    /// Attribute definition for a scan angle with extended precision (like in LAS format 1.4). Default datatype is I16
520    pub const SCAN_ANGLE: PointAttributeDefinition = PointAttributeDefinition {
521        name: Cow::Borrowed("ScanAngle"),
522        datatype: PointAttributeDataType::I16,
523    };
524
525    /// Attribute definition for a user data field. Default datatype is U8
526    pub const USER_DATA: PointAttributeDefinition = PointAttributeDefinition {
527        name: Cow::Borrowed("UserData"),
528        datatype: PointAttributeDataType::U8,
529    };
530
531    /// Attribute definition for a point source ID. Default datatype is U16
532    pub const POINT_SOURCE_ID: PointAttributeDefinition = PointAttributeDefinition {
533        name: Cow::Borrowed("PointSourceID"),
534        datatype: PointAttributeDataType::U16,
535    };
536
537    /// Attribute definition for an RGB color. Default datatype is Vec3u16
538    pub const COLOR_RGB: PointAttributeDefinition = PointAttributeDefinition {
539        name: Cow::Borrowed("ColorRGB"),
540        datatype: PointAttributeDataType::Vec3u16,
541    };
542
543    /// Attribute definition for a GPS timestamp. Default datatype is F64
544    pub const GPS_TIME: PointAttributeDefinition = PointAttributeDefinition {
545        name: Cow::Borrowed("GpsTime"),
546        datatype: PointAttributeDataType::F64,
547    };
548
549    /// Attribute definition for near-infrared records (NIR). Default datatype is U16
550    /// TODO NIR semantically belongs to the color attributes, so there should be a separate
551    /// attribute for 4-channel color that includes NIR!
552    pub const NIR: PointAttributeDefinition = PointAttributeDefinition {
553        name: Cow::Borrowed("NIR"),
554        datatype: PointAttributeDataType::U16,
555    };
556
557    /// Attribute definition for the wave packet descriptor index in the LAS format. Default datatype is U8
558    pub const WAVE_PACKET_DESCRIPTOR_INDEX: PointAttributeDefinition = PointAttributeDefinition {
559        name: Cow::Borrowed("WavePacketDescriptorIndex"),
560        datatype: PointAttributeDataType::U8,
561    };
562
563    /// Attribute definition for the offset to the waveform data in the LAS format. Default datatype is U64
564    pub const WAVEFORM_DATA_OFFSET: PointAttributeDefinition = PointAttributeDefinition {
565        name: Cow::Borrowed("WaveformDataOffset"),
566        datatype: PointAttributeDataType::U64,
567    };
568
569    /// Attribute definition for the size of a waveform data packet in the LAS format. Default datatype is U32
570    pub const WAVEFORM_PACKET_SIZE: PointAttributeDefinition = PointAttributeDefinition {
571        name: Cow::Borrowed("WaveformPacketSize"),
572        datatype: PointAttributeDataType::U32,
573    };
574
575    /// Attribute definition for the return point waveform location in the LAS format. Default datatype is F32
576    pub const RETURN_POINT_WAVEFORM_LOCATION: PointAttributeDefinition = PointAttributeDefinition {
577        name: Cow::Borrowed("ReturnPointWaveformLocation"),
578        datatype: PointAttributeDataType::F32,
579    };
580
581    /// Attribute definition for the waveform parameters in the LAS format. Default datatype is Vector3<f32>
582    pub const WAVEFORM_PARAMETERS: PointAttributeDefinition = PointAttributeDefinition {
583        name: Cow::Borrowed("WaveformParameters"),
584        datatype: PointAttributeDataType::Vec3f32,
585    };
586
587    /// Attribute definition for a point ID. Default datatype is U64
588    pub const POINT_ID: PointAttributeDefinition = PointAttributeDefinition {
589        name: Cow::Borrowed("PointID"),
590        datatype: PointAttributeDataType::U64,
591    };
592
593    /// Attribute definition for a 3D point normal. Default datatype is Vec3f32
594    pub const NORMAL: PointAttributeDefinition = PointAttributeDefinition {
595        name: Cow::Borrowed("Normal"),
596        datatype: PointAttributeDataType::Vec3f32,
597    };
598}
599
600/// How is a field within the associated in-memory type of a `PointLayout` aligned?
601pub enum FieldAlignment {
602    /// Use alignment as if the type is [`#[repr(C)]`](https://doc.rust-lang.org/reference/type-layout.html#reprc-structs)
603    Default,
604    /// Use alignment as if the type is [`#[repr(packed(N))]`](https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers)
605    Packed(u64),
606}
607
608/// Describes the data layout of a single point in a point cloud
609///
610/// # Detailed explanation
611///
612/// To understand `PointLayout`, it is necessary to understand the memory model of Pasture. Pasture is a library
613/// for handling point cloud data, so the first thing worth understanding is what 'point cloud data' means in the context
614/// of Pasture:
615///
616/// A point cloud in Pasture is modeled as a collection of tuples of attributes (a_1, a_2, ..., a_n). An
617/// *attribute* can be any datum associated with a point, such as its position in 3D space, an intensity value, an object
618/// classification etc. The set of all unique attributes in a point cloud make up the point clouds *point layout*, which
619/// is represented in Pasture by the `PointLayout` type. The Pasture memory model describes how the attributes for each
620/// point in a point cloud are layed out in memory. There are two major memory layouts supported by Pasture: *Interleaved*
621/// and *PerAttribute*. In an *Interleaved* layout, all attributes for a single point are stored together in memory:
622///
623/// \[a_1(p_1), a_2(p_1), ..., a_n(p_1), a_1(p_2), a_2(p_2), ..., a_n(p_2), ...\]
624///
625/// This layout is equivalent to storing a type `T` inside a `Vec`, where `T` has members a_1, a_2, ..., a_n and is packed
626/// tightly.
627///
628/// In a *PerAttribute* layout, all attributes of a single type are stored together in memory, often in separate memory regions:
629///
630/// \[a_1(p_1), a_1(p_2), ..., a_1(p_n)\]
631/// \[a_2(p_1), a_2(p_2), ..., a_2(p_n)\]
632/// ...
633/// \[a_n(p_1), a_n(p_2), ..., a_n(p_n)\]
634///
635/// These layouts are sometimes called 'Array of Structs' (Interleaved) and 'Struct of Arrays' (PerAttribute).
636///
637/// Most code in Pasture supports point clouds with either of these memory layouts. To correctly handle memory layout and access
638/// in both Interleaved and PerAttribute layout, each buffer in Pasture that stores point cloud data requires a piece of metadata
639/// that describes the attributes of the point cloud with their [respective Rust types](PointAttributeDataType), their order, their memory alignment
640/// and their potential offset within a single point entry in Interleaved format. All this information is stored inside the `PointLayout`
641/// structure.
642///
643/// To support the different memory layouts, Pasture buffers store point data as raw binary buffers internally. To work with the data,
644/// you will want to use strongly typed Rust structures. Any type `T` that you want to use for accessing point data in a strongly typed manner
645/// must implement the `PointType` trait and thus provide Pasture with a way of figuring out the attributes and memory layout of this type `T`.
646#[derive(Debug, Clone, PartialEq, Eq, Hash)]
647#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
648pub struct PointLayout {
649    attributes: Vec<PointAttributeMember>,
650    #[cfg_attr(feature = "serde", serde(with = "serde_layout"))]
651    memory_layout: Layout,
652}
653
654impl PointLayout {
655    /// Creates a new PointLayout from the given sequence of attributes. The attributes will be aligned using the
656    /// default alignments for their respective datatypes, in accordance with the [Rust alignment rules for `repr(C)` structs](https://doc.rust-lang.org/reference/type-layout.html#reprc-structs)
657    ///
658    /// #Panics
659    ///
660    /// If any two attributes within the sequence share the same attribute name.
661    ///
662    /// ```
663    /// # use pasture_core::layout::*;
664    /// let layout = PointLayout::from_attributes(&[attributes::POSITION_3D, attributes::INTENSITY]);
665    /// # assert_eq!(2, layout.attributes().count());
666    /// # assert_eq!(0, layout.at(0).offset());
667    /// # assert_eq!(attributes::POSITION_3D.size(), layout.at(1).offset());
668    /// ```
669    pub fn from_attributes(attributes: &[PointAttributeDefinition]) -> Self {
670        attributes.iter().cloned().collect()
671    }
672
673    /// Creates a new PointLayout from the given sequence of attributes. The attributes will be aligned to a 1 byte boundary
674    /// in accordance with the [Rust alignment rules for `repr(packed)` structs](https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers)
675    ///
676    /// #Panics
677    ///
678    /// If any two attributes within the sequence share the same attribute name.
679    ///
680    /// ```
681    /// # use pasture_core::layout::*;
682    /// // Default INTENSITY attribute uses u16 datatype. In a packed(1) struct, the next field will have offset 2
683    /// // even though the POSITION_3D attribute has an alignment requirement of 8
684    /// let layout_packed_1 = PointLayout::from_attributes_packed(&[attributes::INTENSITY, attributes::POSITION_3D], 1);
685    /// # assert_eq!(2, layout_packed_1.attributes().count());
686    /// assert_eq!(0, layout_packed_1.at(0).offset());
687    /// assert_eq!(2, layout_packed_1.at(1).offset());
688    ///
689    /// // If we use packed(4), POSITION_3D will start at byte 4:
690    /// let layout_packed_4 = PointLayout::from_attributes_packed(&[attributes::INTENSITY, attributes::POSITION_3D], 4);
691    /// assert_eq!(4, layout_packed_4.at(1).offset());
692    /// ```
693    pub fn from_attributes_packed(
694        attributes: &[PointAttributeDefinition],
695        max_alignment: u64,
696    ) -> Self {
697        let mut layout = Self::default();
698        for attribute in attributes {
699            layout.add_attribute(attribute.clone(), FieldAlignment::Packed(max_alignment));
700        }
701        layout
702    }
703
704    /// Creates a new PointLayout from the given `PointAttributeMember` sequence as well as the given `type_alignment`.
705    ///
706    /// #Panics
707    ///
708    /// If any two attributes within the sequence share the same attribute name, or if there is overlap between any two
709    /// attributes based on their sizes and offsets.
710    ///
711    /// ```
712    /// # use pasture_core::layout::*;
713    /// let layout = PointLayout::from_members_and_alignment(&[attributes::INTENSITY.at_offset_in_type(2), attributes::POSITION_3D.at_offset_in_type(8)], 8);
714    /// # assert_eq!(2, layout.attributes().count());
715    /// assert_eq!(2, layout.at(0).offset());
716    /// assert_eq!(8, layout.at(1).offset());
717    /// assert_eq!(32, layout.size_of_point_entry());
718    /// ```
719    pub fn from_members_and_alignment(
720        attributes: &[PointAttributeMember],
721        type_alignment: u64,
722    ) -> Self {
723        // Conduct extensive checks for uniqueness and non-overlap. The checks are a bit expensive, however
724        // they are absolutely necessary because this method is dangerous!
725        let unique_names = attributes.iter().map(|a| a.name()).unique();
726        if unique_names.count() != attributes.len() {
727            panic!(
728                "PointLayout::from_attributes_and_offsets: All attributes must have unique names!"
729            );
730        }
731
732        let mut unaligned_ranges = attributes
733            .iter()
734            .map(|a| (a.offset()..(a.offset() + a.size())))
735            .collect::<Vec<_>>();
736        unaligned_ranges.sort_by(|a, b| a.start.cmp(&b.start));
737        for next_idx in 1..unaligned_ranges.len() {
738            let this_range = &unaligned_ranges[next_idx - 1];
739            let next_range = &unaligned_ranges[next_idx];
740            if this_range.end > next_range.start {
741                panic!("PointLayout::from_attributes_and_offsets: All attributes must span non-overlapping memory regions!")
742            }
743        }
744
745        let unaligned_size = attributes
746            .iter()
747            .max_by(|a, b| a.offset().cmp(&b.offset()))
748            .map(|last_attribute| last_attribute.offset() + last_attribute.size())
749            .unwrap_or(0);
750
751        Self {
752            attributes: attributes.to_vec(),
753            memory_layout: Layout::from_size_align(
754                unaligned_size.align_to(type_alignment) as usize,
755                type_alignment as usize,
756            )
757            .expect("Could not create memory layout for PointLayout"),
758        }
759    }
760
761    /// Adds the given PointAttributeDefinition to this PointLayout. Sets the offset of the new attribute
762    /// within the `PointLayout` based on the given `FieldAlignment`
763    ///
764    /// #Panics
765    ///
766    /// If an attribute with the same name is already part of this PointLayout.
767    /// ```
768    /// # use pasture_core::layout::*;
769    /// let mut layout = PointLayout::default();
770    /// layout.add_attribute(attributes::INTENSITY, FieldAlignment::Default);
771    /// layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
772    /// # assert_eq!(2, layout.attributes().count());
773    /// # assert_eq!(&attributes::INTENSITY.at_offset_in_type(0), layout.at(0));
774    /// # assert_eq!(&attributes::POSITION_3D.at_offset_in_type(8), layout.at(1));
775    /// // Default field alignment respects the 8-byte alignment requirement of default POSITION_3D (Vector3<f64>), even though default INTENSITY takes only 2 bytes
776    /// assert_eq!(8, layout.at(1).offset());
777    /// ```
778    pub fn add_attribute(
779        &mut self,
780        point_attribute: PointAttributeDefinition,
781        field_alignment: FieldAlignment,
782    ) {
783        if let Some(old_attribute) = self.get_attribute_by_name(point_attribute.name()) {
784            panic!(
785                "Point attribute {} is already present in this PointLayout!",
786                old_attribute.name()
787            );
788        }
789
790        let alignment_requirement_of_field = match field_alignment {
791            FieldAlignment::Default => point_attribute.datatype().min_alignment(),
792            FieldAlignment::Packed(max_alignment) => {
793                std::cmp::min(max_alignment, point_attribute.datatype().min_alignment())
794            }
795        };
796        let offset = self
797            .packed_offset_of_next_field()
798            .align_to(alignment_requirement_of_field);
799
800        let current_max_alignment = self.memory_layout.align() as u64;
801        let new_max_alignment = match field_alignment {
802            FieldAlignment::Default => std::cmp::max(
803                current_max_alignment,
804                point_attribute.datatype().min_alignment(),
805            ),
806            FieldAlignment::Packed(max_alignment) => {
807                std::cmp::min(max_alignment, current_max_alignment)
808            }
809        };
810
811        self.attributes
812            .push(point_attribute.at_offset_in_type(offset));
813
814        let old_size = self.memory_layout.size() as u64;
815        let attribute_end = offset + point_attribute.size();
816        let new_size_unaligned = std::cmp::max(old_size, attribute_end);
817        self.memory_layout = Layout::from_size_align(
818            new_size_unaligned.align_to(new_max_alignment) as usize,
819            new_max_alignment as usize,
820        )
821        .expect("Could not create memory layout for PointLayout");
822    }
823
824    /// Returns true if an attribute with the given name is part of this PointLayout.
825    /// ```
826    /// # use pasture_core::layout::*;
827    /// let mut layout = PointLayout::default();
828    /// layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
829    /// assert!(layout.has_attribute_with_name(attributes::POSITION_3D.name()));
830    /// ```
831    pub fn has_attribute_with_name(&self, attribute_name: &str) -> bool {
832        self.attributes
833            .iter()
834            .any(|attribute| attribute.name() == attribute_name)
835    }
836
837    /// Returns `true` if the associated `PointLayout` contains the given `attribute`. Both the name of `attribute` as well as
838    /// its datatype must match for this method to return `true`. This is a more strict form of [`has_attribute_with_name`](Self::has_attribute_with_name)
839    ///
840    /// # Example
841    /// ```
842    /// # use pasture_core::layout::*;
843    /// let mut layout = PointLayout::default();
844    /// layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
845    /// assert!(layout.has_attribute(&attributes::POSITION_3D));
846    ///
847    /// layout.add_attribute(attributes::INTENSITY.with_custom_datatype(PointAttributeDataType::U32), FieldAlignment::Default);
848    /// assert!(!layout.has_attribute(&attributes::INTENSITY));
849    /// ```
850    pub fn has_attribute(&self, attribute: &PointAttributeDefinition) -> bool {
851        self.attributes.iter().any(|this_attribute| {
852            this_attribute.name() == attribute.name()
853                && this_attribute.datatype() == attribute.datatype()
854        })
855    }
856
857    /// Returns the attribute that matches the given `attribute` in name and datatype from the associated `PointLayout`. Returns `None` if
858    /// no attribute with the same name and datatype exists
859    /// ```
860    /// # use pasture_core::layout::*;
861    /// let mut layout = PointLayout::default();
862    /// layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
863    /// let attribute = layout.get_attribute(&attributes::POSITION_3D);
864    /// assert!(attribute.is_some());
865    /// let invalid_attribute = layout.get_attribute(&attributes::POSITION_3D.with_custom_datatype(PointAttributeDataType::U32));
866    /// assert!(invalid_attribute.is_none());
867    /// ```
868    pub fn get_attribute(
869        &self,
870        attribute: &PointAttributeDefinition,
871    ) -> Option<&PointAttributeMember> {
872        self.attributes.iter().find(|self_attribute| {
873            self_attribute.name() == attribute.name()
874                && self_attribute.datatype() == attribute.datatype()
875        })
876    }
877
878    /// Returns the attribute with the given name from this PointLayout. Returns None if no such attribute exists.
879    /// ```
880    /// # use pasture_core::layout::*;
881    /// let mut layout = PointLayout::default();
882    /// layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
883    /// let attribute = layout.get_attribute_by_name(attributes::POSITION_3D.name());
884    /// # assert!(attribute.is_some());
885    /// assert_eq!(attributes::POSITION_3D.at_offset_in_type(0), *attribute.unwrap());
886    /// ```
887    pub fn get_attribute_by_name(&self, attribute_name: &str) -> Option<&PointAttributeMember> {
888        self.attributes
889            .iter()
890            .find(|attribute| attribute.name() == attribute_name)
891    }
892
893    /// Returns the attribute at the given index from the associated `PointLayout`
894    ///
895    /// # Panics
896    ///
897    /// If `index` is out of bounds
898    pub fn at(&self, index: usize) -> &PointAttributeMember {
899        &self.attributes[index]
900    }
901
902    /// Returns an iterator over all attributes in this `PointLayout`. The attributes are returned in the order
903    /// in which they were added to this `PointLayout`:
904    /// ```
905    /// # use pasture_core::layout::*;
906    /// let mut layout = PointLayout::default();
907    /// layout.add_attribute(attributes::POSITION_3D, FieldAlignment::Default);
908    /// layout.add_attribute(attributes::INTENSITY, FieldAlignment::Default);
909    /// let attributes = layout.attributes().collect::<Vec<_>>();
910    /// # assert_eq!(2, attributes.len());
911    /// assert_eq!(attributes::POSITION_3D.at_offset_in_type(0), *attributes[0]);
912    /// assert_eq!(attributes::INTENSITY.at_offset_in_type(24), *attributes[1]);
913    /// ```
914    pub fn attributes(&self) -> impl Iterator<Item = &PointAttributeMember> {
915        self.attributes.iter()
916    }
917
918    /// Returns the size in bytes of a single point entry with the associated `PointLayout`. Note that the size can be
919    /// larger than the sum of the sizes of all attributes because of alignment requirements!
920    ///
921    /// # Example
922    /// ```
923    /// # use pasture_core::layout::*;
924    /// let layout = PointLayout::from_attributes(&[attributes::POSITION_3D, attributes::INTENSITY]);
925    /// // from_attributes respects the alignment requirements of each attribute. Default POSITION_3D uses Vector3<f64> and as such
926    /// // has an 8-byte minimum alignment, so the whole PointLayout is aligned to an 8-byte boundary. This is reflected in its size:
927    /// assert_eq!(32, layout.size_of_point_entry());
928    /// ```
929    #[inline]
930    pub const fn size_of_point_entry(&self) -> u64 {
931        self.memory_layout.size() as u64
932    }
933
934    /// Returns the index of the given attribute within the associated `PointLayout`, or `None` if the attribute is not
935    /// part of the `PointLayout`. The index depends on the order in which the attributes have been added to the associated
936    /// `PointLayout`, but does not necessarily reflect the order of the attributes in memory.
937    ///
938    /// # Example
939    /// ```
940    /// # use pasture_core::layout::*;
941    /// let layout = PointLayout::from_attributes(&[attributes::POSITION_3D, attributes::INTENSITY]);
942    /// assert_eq!(Some(0), layout.index_of(&attributes::POSITION_3D));
943    /// assert_eq!(Some(1), layout.index_of(&attributes::INTENSITY));
944    /// # assert_eq!(None, layout.index_of(&attributes::CLASSIFICATION));
945    ///
946    /// // Create a layout where we add INTENSITY as first attribute, however in memory, INTENSITY comes after POSITION_3D
947    /// let reordered_layout = PointLayout::from_members_and_alignment(&[attributes::INTENSITY.at_offset_in_type(24), attributes::POSITION_3D.at_offset_in_type(0)], 8);
948    /// assert_eq!(Some(0), reordered_layout.index_of(&attributes::INTENSITY));
949    /// assert_eq!(Some(1), reordered_layout.index_of(&attributes::POSITION_3D));
950    /// ```
951    pub fn index_of(&self, attribute: &PointAttributeDefinition) -> Option<usize> {
952        self.attributes.iter().position(|this_attribute| {
953            this_attribute.name() == attribute.name()
954                && this_attribute.datatype() == attribute.datatype()
955        })
956    }
957
958    /// Compares the associated `PointLayout` with the `other` layout, ignoring the attribute offsets. This way, only the names and datatypes
959    /// of the attributes are compared. This method is useful when dealing with data in a non-interleaved format, where offsets are irrelevant
960    pub fn compare_without_offsets(&self, other: &PointLayout) -> bool {
961        if self.attributes.len() != other.attributes.len() {
962            return false;
963        }
964
965        self.attributes.iter().all(|self_attribute| {
966            other
967                .get_attribute_by_name(self_attribute.name())
968                .map(|other_attribute| other_attribute.datatype() == self_attribute.datatype())
969                .unwrap_or(false)
970        })
971    }
972
973    /// Returns the offset from an attribute.
974    /// If the attribute don't exist in the layout this function returns None.
975    pub fn offset_of(&self, attribute: &PointAttributeDefinition) -> Option<u64> {
976        self.attributes
977            .iter()
978            .find(|this_attribute| {
979                this_attribute.name() == attribute.name()
980                    && this_attribute.datatype() == attribute.datatype()
981            })
982            .map(|member| member.offset())
983    }
984
985    /// Returns the offset of the next field that could be added to this `PointLayout`, without any alignment
986    /// requirements
987    fn packed_offset_of_next_field(&self) -> u64 {
988        if self.attributes.is_empty() {
989            0
990        } else {
991            // If there are previous attributes, the offset to this attribute is equal to the offset
992            // to the previous attribute plus the previous attribute's size
993            let last_attribute = self.attributes.last().unwrap();
994            last_attribute.offset() + last_attribute.size()
995        }
996    }
997}
998
999impl Display for PointLayout {
1000    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1001        writeln!(f, "PointLayout {{")?;
1002
1003        for attribute in self.attributes() {
1004            writeln!(f, "\t{}", attribute)?;
1005        }
1006
1007        writeln!(f, "}}")
1008    }
1009}
1010
1011impl Default for PointLayout {
1012    /// Creates a new empty PointLayout
1013    /// ```
1014    /// # use pasture_core::layout::*;
1015    /// let layout = PointLayout::default();
1016    /// # assert_eq!(0, layout.attributes().count());
1017    /// ```
1018    fn default() -> Self {
1019        Self {
1020            attributes: vec![],
1021            memory_layout: Layout::from_size_align(0, 1).unwrap(),
1022        }
1023    }
1024}
1025
1026impl FromIterator<PointAttributeDefinition> for PointLayout {
1027    fn from_iter<T: IntoIterator<Item = PointAttributeDefinition>>(iter: T) -> Self {
1028        let mut layout = Self::default();
1029        for attribute in iter.into_iter() {
1030            layout.add_attribute(attribute.clone(), FieldAlignment::Default);
1031        }
1032        layout
1033    }
1034}
1035
1036#[cfg(test)]
1037mod tests {
1038    use super::*;
1039    use crate::layout::{
1040        attributes::{COLOR_RGB, INTENSITY, POSITION_3D},
1041        PointType,
1042    };
1043    use pasture_derive::PointType;
1044
1045    #[derive(
1046        Debug, PointType, Copy, Clone, PartialEq, bytemuck::NoUninit, bytemuck::AnyBitPattern,
1047    )]
1048    #[repr(C, packed)]
1049    struct TestPoint1 {
1050        #[pasture(BUILTIN_POSITION_3D)]
1051        position: Vector3<f64>,
1052        #[pasture(BUILTIN_COLOR_RGB)]
1053        color: Vector3<u16>,
1054        #[pasture(BUILTIN_INTENSITY)]
1055        intensity: u16,
1056    }
1057
1058    #[test]
1059    fn test_derive_point_type() {
1060        let expected_layout_1 = PointLayout::from_attributes_packed(
1061            &[
1062                POSITION_3D.with_custom_datatype(PointAttributeDataType::Vec3f64),
1063                COLOR_RGB.with_custom_datatype(PointAttributeDataType::Vec3u16),
1064                INTENSITY.with_custom_datatype(PointAttributeDataType::U16),
1065            ],
1066            1,
1067        );
1068
1069        assert_eq!(expected_layout_1, TestPoint1::layout());
1070    }
1071
1072    #[test]
1073    #[cfg(feature = "serde")]
1074    fn test_point_layout_serde() {
1075        let original_value = PointLayout {
1076            attributes: vec![],
1077            memory_layout: Layout::from_size_align(20, 4).unwrap(),
1078        };
1079        let serialized = serde_json::to_value(original_value.clone()).unwrap();
1080        let expected = json!({
1081            "attributes": [],
1082            "memory_layout": {
1083                "align": 4,
1084                "size": 20
1085            },
1086        });
1087        assert_eq!(serialized, expected);
1088        let deserialized: PointLayout = serde_json::from_value(serialized).unwrap();
1089        assert_eq!(deserialized, original_value);
1090    }
1091}
1092
1093#[cfg(feature = "serde")]
1094mod serde_layout {
1095    use std::alloc::Layout;
1096
1097    use serde::{Deserialize, Deserializer, Serialize, Serializer};
1098
1099    #[derive(Serialize, Deserialize)]
1100    #[serde(rename = "Layout")]
1101    struct SizeAndAlignment {
1102        size: usize,
1103        align: usize,
1104    }
1105
1106    pub fn serialize<S>(layout: &Layout, serializer: S) -> Result<S::Ok, S::Error>
1107    where
1108        S: Serializer,
1109    {
1110        SizeAndAlignment {
1111            size: layout.size(),
1112            align: layout.align(),
1113        }
1114        .serialize(serializer)
1115    }
1116
1117    pub fn deserialize<'de, D>(de: D) -> Result<Layout, D::Error>
1118    where
1119        D: Deserializer<'de>,
1120    {
1121        let fields = SizeAndAlignment::deserialize(de)?;
1122        Layout::from_size_align(fields.size, fields.align).map_err(serde::de::Error::custom)
1123    }
1124}