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}