rs_pcd/io/
reader.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::decoder::ascii::AsciiReader;
16use crate::decoder::binary::BinaryReader;
17#[cfg(feature = "rayon")]
18use crate::decoder::binary_par::BinaryParallelDecoder;
19use crate::decoder::compressed::CompressedReader;
20use crate::error::Result;
21use crate::header::{DataFormat, PcdHeader, parse_header};
22use crate::layout::PcdLayout;
23use crate::storage::PointBlock;
24
25#[cfg(feature = "memmap2")]
26use memmap2::Mmap;
27use std::fs::File;
28use std::io::Cursor;
29use std::io::{BufRead, BufReader};
30use std::path::Path;
31
32pub enum InputSource<R: BufRead> {
33    Reader(R),
34    #[cfg(feature = "memmap2")]
35    Mmap(Mmap),
36}
37
38pub struct PcdReader<R: BufRead> {
39    source: InputSource<R>,
40    header: PcdHeader,
41    layout: PcdLayout,
42    #[cfg(feature = "memmap2")]
43    start_offset: usize, // Offset where data starts (after header)
44}
45
46impl<R: BufRead> PcdReader<R> {
47    pub fn new(mut reader: R) -> Result<Self> {
48        let header = parse_header(&mut reader)?;
49        let layout = PcdLayout::from_header(&header)?;
50
51        Ok(PcdReader {
52            source: InputSource::Reader(reader),
53            header,
54            layout,
55            #[cfg(feature = "memmap2")]
56            start_offset: 0,
57        })
58    }
59}
60
61/// Create a PcdReader directly from a byte slice.
62/// Useful for embedded resources, network data, or in-memory buffers.
63impl<'a> PcdReader<BufReader<Cursor<&'a [u8]>>> {
64    pub fn from_bytes(data: &'a [u8]) -> Result<Self> {
65        let cursor = Cursor::new(data);
66        let reader = BufReader::new(cursor);
67        Self::new(reader)
68    }
69}
70
71impl PcdReader<BufReader<File>> {
72    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
73        let file = File::open(path)?;
74        let reader = BufReader::new(file);
75        Self::new(reader)
76    }
77
78    #[cfg(feature = "memmap2")]
79    pub fn from_path_mmap<P: AsRef<Path>>(path: P) -> Result<Self> {
80        let file = File::open(path)?;
81        // We mmap the whole file
82        let mmap = unsafe { Mmap::map(&file)? };
83
84        // Parse header from mmap slice
85        let mut cursor = Cursor::new(&mmap[..]);
86        let header = parse_header(&mut cursor)?;
87        let pos = cursor.position() as usize; // This is the data start offset
88
89        let layout = PcdLayout::from_header(&header)?;
90
91        Ok(PcdReader {
92            source: InputSource::Mmap(mmap),
93            header,
94            layout,
95            start_offset: pos,
96        })
97    }
98}
99
100impl<R: BufRead> PcdReader<R> {
101    pub fn header(&self) -> &PcdHeader {
102        &self.header
103    }
104
105    pub fn read_all(mut self) -> Result<PointBlock> {
106        let points = self.header.points;
107        let mut block = PointBlock::new(
108            &self
109                .layout
110                .fields
111                .iter()
112                .map(|f| (f.name.clone(), f.type_))
113                .collect(),
114            points,
115        );
116
117        match &mut self.source {
118            InputSource::Reader(reader) => match self.header.data {
119                DataFormat::Binary => {
120                    let mut decoder = BinaryReader::new(reader, &self.layout, points);
121                    decoder.decode(&mut block)?;
122                }
123                DataFormat::BinaryCompressed => {
124                    let mut decoder = CompressedReader::new(reader, &self.layout, points);
125                    decoder.decode(&mut block)?;
126                }
127                DataFormat::Ascii => {
128                    let mut decoder = AsciiReader::new(reader, &self.layout, points);
129                    decoder.decode(&mut block)?;
130                }
131            },
132            #[cfg(feature = "memmap2")]
133            InputSource::Mmap(mmap) => {
134                let data_slice = &mmap[self.start_offset..];
135
136                match self.header.data {
137                    DataFormat::Binary => {
138                        #[cfg(feature = "rayon")]
139                        {
140                            // Use parallel decoder if enabled
141                            let decoder = BinaryParallelDecoder::new(&self.layout, points);
142                            decoder.decode_par(data_slice, &mut block)?;
143                        }
144                        #[cfg(not(feature = "rayon"))]
145                        {
146                            // Fallback to sequential using Cursor
147                            let mut cursor = Cursor::new(data_slice);
148                            let mut decoder = BinaryReader::new(&mut cursor, &self.layout, points);
149                            decoder.decode(&mut block)?;
150                        }
151                    }
152                    DataFormat::BinaryCompressed => {
153                        // Parallel Compressed not implemented yet (needs chunks processing)
154                        // Fallback to sequential
155                        let mut cursor = Cursor::new(data_slice);
156                        let mut decoder = CompressedReader::new(&mut cursor, &self.layout, points);
157                        decoder.decode(&mut block)?;
158                    }
159                    DataFormat::Ascii => {
160                        let mut cursor = Cursor::new(data_slice);
161                        let mut decoder = AsciiReader::new(&mut cursor, &self.layout, points);
162                        decoder.decode(&mut block)?;
163                    }
164                }
165            }
166        }
167        Ok(block)
168    }
169}
170
171pub fn read_pcd_file<P: AsRef<Path>>(path: P) -> Result<PointBlock> {
172    let file = File::open(path)?;
173    let reader = BufReader::new(file);
174    let pcd_reader = PcdReader::new(reader)?;
175    pcd_reader.read_all()
176}