rs_pcd/decoder/
compressed.rs

1// Copyright 2025 bigpear0201
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7//     http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::error::{PcdError, Result};
16use crate::header::ValueType;
17use crate::layout::PcdLayout;
18use crate::storage::PointBlock;
19use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
20use lzf;
21use std::io::Read;
22
23pub struct CompressedReader<'a, R: Read> {
24    reader: &'a mut R,
25    layout: &'a PcdLayout,
26    points_to_read: usize,
27}
28
29impl<'a, R: Read> CompressedReader<'a, R> {
30    pub fn new(reader: &'a mut R, layout: &'a PcdLayout, points_to_read: usize) -> Self {
31        Self {
32            reader,
33            layout,
34            points_to_read,
35        }
36    }
37
38    pub fn decode(&mut self, output: &mut PointBlock) -> Result<()> {
39        let compressed_size = self.reader.read_u32::<LittleEndian>()? as usize;
40        let uncompressed_size = self.reader.read_u32::<LittleEndian>()? as usize;
41
42        let mut compressed_data = vec![0u8; compressed_size];
43        self.reader.read_exact(&mut compressed_data)?;
44
45        // Decompress
46        let decompressed = lzf::decompress(&compressed_data, uncompressed_size)
47            .map_err(|e| PcdError::Decompression(format!("{:?}", e)))?;
48
49        if decompressed.len() != uncompressed_size {
50            return Err(PcdError::Decompression(format!(
51                "Size mismatch: expected {}, got {}",
52                uncompressed_size,
53                decompressed.len()
54            )));
55        }
56
57        // Validate buffer size against layout
58        // SoA layout: sum(field.count * sizeof(type) * num_points)
59        // Wait, layout.total_size is per-point size (stride).
60        // Total bytes should be layout.total_size * points_to_read.
61        let expected_bytes = self.layout.total_size * self.points_to_read;
62        if uncompressed_size != expected_bytes {
63            return Err(PcdError::LayoutMismatch {
64                expected: expected_bytes,
65                got: uncompressed_size,
66            });
67        }
68
69        // Initialize output - verify all columns exist
70        for field in &self.layout.fields {
71            if output.get_column(&field.name).is_none() {
72                // Schema mismatch - column doesn't exist
73                // For now we just skip the check and let get_column_mut fail below
74            }
75        }
76        output.resize(self.points_to_read);
77
78        // Process fields (SoA in buffer: [Field1 All Points][Field2 All Points]...)
79        let mut offset = 0;
80
81        for field in &self.layout.fields {
82            let col = output
83                .get_column_mut(&field.name)
84                .ok_or(PcdError::InvalidDataFormat(format!(
85                    "Missing column {}",
86                    field.name
87                )))?;
88
89            let bytes_per_element = field.element_size; // e.g. 4 for f32
90            let elements_per_point = field.count; // e.g. 1
91            let bytes_per_field_block =
92                bytes_per_element * elements_per_point * self.points_to_read;
93
94            let end = offset + bytes_per_field_block;
95            let data_slice = &decompressed[offset..end];
96            offset = end;
97
98            match field.type_ {
99                ValueType::U8 => {
100                    let vec = col.as_u8_mut().unwrap();
101                    vec.copy_from_slice(data_slice);
102                }
103                ValueType::F32 => {
104                    let vec = col.as_f32_mut().unwrap();
105                    // Efficient copy using unsafe cast if alignment permits, or safely
106                    // Since standard lzf returns Vec<u8>, it might not be aligned to 4.
107                    // We iterate.
108                    // Optimizations: chunks_exact(4).
109
110                    // We must fill target vec. Target vec is flat for all points.
111                    // field.count > 1 means interleaved? No, SoA usually means:
112                    // If count=1: x1, x2, x3...
113                    // If count=3 (Normal): nx1, ny1, nz1, nx2, ny2... ?
114                    // OR: nx1..nxN, ny1..nyN ?
115                    // PCL spec: "The fields are stored sequentially... field_1_point_1, field_1_point_2... field_2_point_1..."
116                    // Wait. "binary_compressed format... The data is stored in a column-major order."
117                    // Does it mean field 1 for all points, then field 2?
118                    // Yes. "stored sequentially by field".
119                    // But if a field has count > 1 (e.g. viewpoint), how is it stored?
120                    // "dimensions corresponding to the same field are stored contiguously".
121                    // So if field is "normal" (count=3), is it nx1, ny1, nz1, nx2...?
122                    // OR nx1..nxN, ny1..nyN?
123                    // Usually PCL treats distinct Names as fields. "Normal" is usually split into "normal_x, normal_y, normal_z" in fields list.
124                    // If one field has count > 1 (e.g. FPFH signature 33 floats), it is stored as struct?
125                    // PCL generic: it is simply array of structs compressed? No.
126                    // It says "reorganized to column array".
127                    // I will assume for count > 1, it's (val_1_1, val_1_2... val_1_count), (val_2_1...)...
128                    // i.e. The unit being transposed is the whole field value (array).
129                    // So yes, loop over points, copy `count` elements.
130
131                    let mut i = 0;
132                    for chunk in data_slice.chunks_exact(4) {
133                        vec[i] = LittleEndian::read_f32(chunk);
134                        i += 1;
135                    }
136                }
137                ValueType::F64 => {
138                    let vec = col.as_f64_mut().unwrap();
139                    let mut i = 0;
140                    for chunk in data_slice.chunks_exact(8) {
141                        vec[i] = LittleEndian::read_f64(chunk);
142                        i += 1;
143                    }
144                }
145                ValueType::U16 => {
146                    let vec = col.as_u16_mut().unwrap();
147                    let mut i = 0;
148                    for chunk in data_slice.chunks_exact(2) {
149                        vec[i] = LittleEndian::read_u16(chunk);
150                        i += 1;
151                    }
152                }
153                ValueType::U32 => {
154                    let vec = col.as_u32_mut().unwrap();
155                    let mut i = 0;
156                    for chunk in data_slice.chunks_exact(4) {
157                        vec[i] = LittleEndian::read_u32(chunk);
158                        i += 1;
159                    }
160                }
161                ValueType::I8 => {
162                    let vec = col.as_i8_mut().unwrap();
163                    // Safe conversion from u8 to i8
164                    for (dest, &src) in vec.iter_mut().zip(data_slice.iter()) {
165                        *dest = src as i8;
166                    }
167                }
168                ValueType::I16 => {
169                    let vec = col.as_i16_mut().unwrap();
170                    let mut i = 0;
171                    for chunk in data_slice.chunks_exact(2) {
172                        vec[i] = LittleEndian::read_i16(chunk);
173                        i += 1;
174                    }
175                }
176                ValueType::I32 => {
177                    let vec = col.as_i32_mut().unwrap();
178                    let mut i = 0;
179                    for chunk in data_slice.chunks_exact(4) {
180                        vec[i] = LittleEndian::read_i32(chunk);
181                        i += 1;
182                    }
183                }
184            }
185        }
186
187        Ok(())
188    }
189}