Skip to content

Commit

Permalink
feat: add laz-parallel feature
Browse files Browse the repository at this point in the history
This adds the `laz-parallel` feature
(which enables laz-rs if not already enabled).

When enabled, las-rs will use laz-rs parallel decompressor when reading
many points at once (read_n_into, read_next_points) improving the
read speed
  • Loading branch information
tmontaigu authored and gadomski committed Apr 4, 2024
1 parent 7a79750 commit c55d851
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/continuous-integration.yml
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
features: ["", "--features laz"]
features: ["", "--features laz", "--features laz-parallel"]
steps:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v2
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Expand Up @@ -19,11 +19,15 @@ log = "0.4"
num-traits = "0.2"
thiserror = "1.0"
uuid = "1"
laz = { version = "0.9", optional = true }
laz = { version = "0.9.1", optional = true }

[dev-dependencies]
criterion = "0.5"

[features]
laz = ["dep:laz"]
laz-parallel = ["dep:laz", "laz/parallel"]

[[bench]]
name = "roundtrip"
harness = false
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -20,3 +20,11 @@ To include [laz](https://laszip.org/) support:
[dependencies]
las = { version = "0.8", features = ["laz"] }
```

To include `laz` support with parallel compression/decompression
to enhance speed:

```toml
[dependencies]
las = { version = "0.8", features = ["laz-parallel"] }
```
36 changes: 29 additions & 7 deletions src/compression/mod.rs
Expand Up @@ -29,33 +29,52 @@ fn create_laszip_vlr(laszip_vlr: &LazVlr) -> std::io::Result<Vlr> {
/// 1) call the decompressor that reads & decompress the next point
/// and put its data in an in-memory buffer
/// 2) read the buffer to get the decompress point
pub(crate) struct CompressedPointReader<'a, R: Read + Seek + Send> {
pub(crate) struct CompressedPointReader<Decompressor> {
/// decompressor that does the actual job
decompressor: laz::las::laszip::LasZipDecompressor<'a, R>,
decompressor: Decompressor,
header: Header,
/// in-memory buffer where the decompressor writes decompression result
decompressor_output: Cursor<Vec<u8>>,
last_point_idx: u64,
}

impl<'a, R: Read + Seek + Send> CompressedPointReader<'a, R> {
#[cfg(not(feature = "laz-parallel"))]
impl<'a, R: Read + Seek + Send> CompressedPointReader<laz::LasZipDecompressor<'a, R>> {
pub(crate) fn new(source: R, header: Header) -> Result<Self> {
let laszip_vlr = match header.vlrs().iter().find(|vlr| is_laszip_vlr(*vlr)) {
None => return Err(Error::LasZipVlrNotFound),
Some(vlr) => laz::las::laszip::LazVlr::from_buffer(&vlr.data)?,
Some(vlr) => LazVlr::from_buffer(&vlr.data)?,
};
let decompressor_output = Cursor::new(vec![0u8; header.point_format().len() as usize]);

Ok(Self {
decompressor: laz::las::laszip::LasZipDecompressor::new(source, laszip_vlr)?,
decompressor: laz::LasZipDecompressor::new(source, laszip_vlr)?,
header,
decompressor_output,
last_point_idx: 0,
})
}
}

impl<'a, R: Read + Seek + Send> Debug for CompressedPointReader<'a, R> {
#[cfg(feature = "laz-parallel")]
impl<R: Read + Seek + Send> CompressedPointReader<laz::ParLasZipDecompressor<R>> {
pub(crate) fn new(source: R, header: Header) -> Result<Self> {
let laszip_vlr = match header.vlrs().iter().find(|vlr| is_laszip_vlr(*vlr)) {
None => return Err(Error::LasZipVlrNotFound),
Some(vlr) => LazVlr::from_buffer(&vlr.data)?,
};
let decompressor_output = Cursor::new(vec![0u8; header.point_format().len() as usize]);

Ok(Self {
decompressor: laz::ParLasZipDecompressor::new(source, laszip_vlr)?,
header,
decompressor_output,
last_point_idx: 0,
})
}
}

impl<Decompressor> Debug for CompressedPointReader<Decompressor> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
Expand All @@ -65,7 +84,10 @@ impl<'a, R: Read + Seek + Send> Debug for CompressedPointReader<'a, R> {
}
}

impl<'a, R: Read + Seek + Send> PointReader for CompressedPointReader<'a, R> {
impl<Decompressor> PointReader for CompressedPointReader<Decompressor>
where
Decompressor: laz::LazDecompressor + Send,
{
fn read_next(&mut self) -> Option<Result<Point>> {
if self.last_point_idx < self.header.number_of_points() {
self.last_point_idx += 1;
Expand Down

0 comments on commit c55d851

Please sign in to comment.