From af50de818990a1a7945f7ffa957fd681f2e4377e Mon Sep 17 00:00:00 2001 From: Tin Lai Date: Mon, 5 Jan 2026 02:10:56 +1100 Subject: [PATCH 1/3] put arbitary crate behind fuzzing feature Signed-off-by: Tin Lai --- Cargo.lock | 85 +------------------------------- crates/spz-pywrapper/Cargo.toml | 2 +- crates/spz-pywrapper/src/lib.rs | 63 ++++++++++++----------- crates/spz/Cargo.toml | 5 +- crates/spz/src/coord.rs | 7 +-- crates/spz/src/gaussian_splat.rs | 10 ++-- crates/spz/src/header.rs | 4 +- crates/spz/src/packed.rs | 10 ++-- crates/spz/src/unpacked.rs | 10 ++-- fuzz/Cargo.toml | 2 +- 10 files changed, 65 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b418c1..d164346 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,16 +690,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "matrixmultiply" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" -dependencies = [ - "autocfg", - "rawpointer", -] - [[package]] name = "memchr" version = "2.7.6" @@ -745,21 +735,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "ndarray" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7c9125e8f6f10c9da3aad044cc918cf8784fa34de857b1aa68038eb05a50a9" -dependencies = [ - "matrixmultiply", - "num-complex", - "num-integer", - "num-traits", - "portable-atomic", - "portable-atomic-util", - "rawpointer", -] - [[package]] name = "nix" version = "0.30.1" @@ -772,24 +747,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -799,22 +756,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "numpy" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aac2e6a6e4468ffa092ad43c39b81c79196c2bb773b8db4085f695efe3bba17" -dependencies = [ - "libc", - "ndarray", - "num-complex", - "num-integer", - "num-traits", - "pyo3", - "pyo3-build-config", - "rustc-hash", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -912,15 +853,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" -dependencies = [ - "portable-atomic", -] - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1053,12 +985,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - [[package]] name = "rayon" version = "1.11.0" @@ -1152,12 +1078,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - [[package]] name = "rustc_version" version = "0.4.1" @@ -1282,7 +1202,7 @@ dependencies = [ [[package]] name = "spz" -version = "0.0.7" +version = "0.0.6" dependencies = [ "anyhow", "approx", @@ -1316,9 +1236,8 @@ dependencies = [ [[package]] name = "spz-pywrapper" -version = "0.0.3" +version = "0.0.1" dependencies = [ - "numpy", "pyo3", "spz", ] diff --git a/crates/spz-pywrapper/Cargo.toml b/crates/spz-pywrapper/Cargo.toml index 3c5c364..9d29e82 100644 --- a/crates/spz-pywrapper/Cargo.toml +++ b/crates/spz-pywrapper/Cargo.toml @@ -33,7 +33,7 @@ name = "spz" crate-type = ["cdylib"] [dependencies] -spz = { version = "0.0.6", default-features = false, features = [] } +spz = { path = "../spz", default-features = false, features = [] } pyo3 = { version = "0.27", default-features = true, features = [] } [dev-dependencies] diff --git a/crates/spz-pywrapper/src/lib.rs b/crates/spz-pywrapper/src/lib.rs index 7167187..d424cb3 100644 --- a/crates/spz-pywrapper/src/lib.rs +++ b/crates/spz-pywrapper/src/lib.rs @@ -44,7 +44,7 @@ fn coord_sys_to_str(cs: &spz_rs::CoordinateSystem) -> &'static str { #[pyclass(eq, frozen)] #[derive(Clone, PartialEq)] pub struct CoordinateSystem { - inner: spz_rs::CoordinateSystem, + inner: spz_rs::coord::CoordinateSystem, } #[pymethods] @@ -62,7 +62,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn LDB() -> Self { Self { - inner: spz_rs::CoordinateSystem::LDB, + inner: spz_rs::coord::CoordinateSystem::LDB, } } @@ -71,7 +71,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn RDB() -> Self { Self { - inner: spz_rs::CoordinateSystem::RDB, + inner: spz_rs::coord::CoordinateSystem::RDB, } } @@ -80,7 +80,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn LUB() -> Self { Self { - inner: spz_rs::CoordinateSystem::LUB, + inner: spz_rs::coord::CoordinateSystem::LUB, } } @@ -89,7 +89,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn RUB() -> Self { Self { - inner: spz_rs::CoordinateSystem::RUB, + inner: spz_rs::coord::CoordinateSystem::RUB, } } @@ -98,7 +98,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn LDF() -> Self { Self { - inner: spz_rs::CoordinateSystem::LDF, + inner: spz_rs::coord::CoordinateSystem::LDF, } } @@ -107,7 +107,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn RDF() -> Self { Self { - inner: spz_rs::CoordinateSystem::RDF, + inner: spz_rs::coord::CoordinateSystem::RDF, } } @@ -116,7 +116,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn LUF() -> Self { Self { - inner: spz_rs::CoordinateSystem::LUF, + inner: spz_rs::coord::CoordinateSystem::LUF, } } @@ -125,7 +125,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn RUF() -> Self { Self { - inner: spz_rs::CoordinateSystem::RUF, + inner: spz_rs::coord::CoordinateSystem::RUF, } } @@ -134,7 +134,7 @@ impl CoordinateSystem { #[allow(non_snake_case)] fn UNSPECIFIED() -> Self { Self { - inner: spz_rs::CoordinateSystem::UNSPECIFIED, + inner: spz_rs::coord::CoordinateSystem::UNSPECIFIED, } } /* @@ -213,7 +213,7 @@ impl BoundingBox { #[pyclass] #[derive(Clone)] pub struct GaussianSplat { - inner: spz_rs::GaussianSplat, + inner: spz_rs::gaussian_splat::GaussianSplat, } #[pymethods] @@ -321,7 +321,7 @@ impl GaussianSplat { None => vec![0.0_f32; expected_sh_len], }; Ok(Self { - inner: spz_rs::GaussianSplat { + inner: spz_rs::gaussian_splat::GaussianSplat { num_points: num_points as i32, spherical_harmonics_degree: sh_degree, antialiased, @@ -352,15 +352,16 @@ impl GaussianSplat { fn load(path: &str, coordinate_system: Option) -> PyResult { let coord_sys = coordinate_system .map(|cs| cs.inner) - .unwrap_or(spz_rs::CoordinateSystem::UNSPECIFIED); + .unwrap_or(spz_rs::coord::CoordinateSystem::UNSPECIFIED); - let unpack_opts = spz_rs::UnpackOptions { + let unpack_opts = spz_rs::unpacked::UnpackOptions { to_coord_sys: coord_sys, }; - let inner = spz_rs::GaussianSplat::load_packed_from_file(path, &unpack_opts) - .map_err(|e| { - PyValueError::new_err(format!("Failed to load SPZ file: {}", e)) - })?; + let inner = spz_rs::gaussian_splat::GaussianSplat::load_packed_from_file( + path, + &unpack_opts, + ) + .map_err(|e| PyValueError::new_err(format!("Failed to load SPZ file: {}", e)))?; Ok(Self { inner }) } @@ -379,18 +380,20 @@ impl GaussianSplat { fn from_bytes(data: &[u8], coordinate_system: Option) -> PyResult { let coord_sys = coordinate_system .map(|cs| cs.inner) - .unwrap_or(spz_rs::CoordinateSystem::UNSPECIFIED); + .unwrap_or(spz_rs::coord::CoordinateSystem::UNSPECIFIED); - let unpack_opts = spz_rs::UnpackOptions { + let unpack_opts = spz_rs::unpacked::UnpackOptions { to_coord_sys: coord_sys, }; - let packed = spz_rs::GaussianSplat::load_packed(data).map_err(|e| { - PyValueError::new_err(format!("Failed to parse SPZ data: {}", e)) - })?; - let inner = spz_rs::GaussianSplat::new_from_packed_gaussians(&packed, &unpack_opts) - .map_err(|e| { - PyValueError::new_err(format!("Failed to unpack SPZ data: {}", e)) + let packed = + spz_rs::gaussian_splat::GaussianSplat::load_packed(data).map_err(|e| { + PyValueError::new_err(format!("Failed to parse SPZ data: {}", e)) })?; + let inner = spz_rs::gaussian_splat::GaussianSplat::new_from_packed_gaussians( + &packed, + &unpack_opts, + ) + .map_err(|e| PyValueError::new_err(format!("Failed to unpack SPZ data: {}", e)))?; Ok(Self { inner }) } @@ -409,9 +412,9 @@ impl GaussianSplat { ) -> PyResult<()> { let coord_sys = from_coordinate_system .map(|cs| cs.inner) - .unwrap_or(spz_rs::CoordinateSystem::UNSPECIFIED); + .unwrap_or(spz_rs::coord::CoordinateSystem::UNSPECIFIED); - let pack_opts = spz_rs::PackOptions { from: coord_sys }; + let pack_opts = spz_rs::packed::PackOptions { from: coord_sys }; self.inner.save_as_packed(path, &pack_opts).map_err(|e| { PyValueError::new_err(format!("Failed to save SPZ file: {}", e)) @@ -434,9 +437,9 @@ impl GaussianSplat { ) -> PyResult> { let coord_sys = from_coordinate_system .map(|cs| cs.inner) - .unwrap_or(spz_rs::CoordinateSystem::UNSPECIFIED); + .unwrap_or(spz_rs::coord::CoordinateSystem::UNSPECIFIED); - let pack_opts = spz_rs::PackOptions { from: coord_sys }; + let pack_opts = spz_rs::packed::PackOptions { from: coord_sys }; let bytes = self .inner diff --git a/crates/spz/Cargo.toml b/crates/spz/Cargo.toml index 4315002..b0b7cbd 100644 --- a/crates/spz/Cargo.toml +++ b/crates/spz/Cargo.toml @@ -41,7 +41,10 @@ static_assertions = { version = "1.1", default-features = true, features = [] } rayon = { version = "1.11", default-features = true, features = [] } tokio = { version = "1.48", default-features = true, features = ["full"] } likely_stable = { version = "0.1", default-features = true, features = [] } -arbitrary = { version = "1.4", default-features = true, features = ["derive"] } +arbitrary = { version = "1.4", default-features = true, features = ["derive"], optional = true } + +[features] +fuzzing = ["dep:arbitrary"] [dev-dependencies] criterion = { version = "0.8", default-features = true, features = [ diff --git a/crates/spz/src/coord.rs b/crates/spz/src/coord.rs index 948a813..5ec3fff 100644 --- a/crates/spz/src/coord.rs +++ b/crates/spz/src/coord.rs @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -use arbitrary::Arbitrary; use serde::{Deserialize, Serialize}; impl std::fmt::Display for CoordinateSystem { @@ -37,7 +36,8 @@ impl From<&str> for CoordinateSystem { } } -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum CoordinateSystem { #[default] UNSPECIFIED = 0, @@ -114,7 +114,8 @@ impl CoordinateSystem { } } -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct CoordinateConverter { pub flip_p: [f32; 3], pub flip_q: [f32; 3], diff --git a/crates/spz/src/gaussian_splat.rs b/crates/spz/src/gaussian_splat.rs index 997dc15..9c44b47 100644 --- a/crates/spz/src/gaussian_splat.rs +++ b/crates/spz/src/gaussian_splat.rs @@ -3,7 +3,6 @@ use std::{path::Path, path::PathBuf}; use anyhow::{Context, Result, bail}; -use arbitrary::Arbitrary; use likely_stable::unlikely; use serde::{Deserialize, Serialize}; use tokio::io::AsyncReadExt; @@ -18,7 +17,8 @@ use crate::{ unpacked::UnpackOptions, }; -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct GaussianSplat { /// The number of gaussians. pub num_points: i32, @@ -645,7 +645,8 @@ impl std::fmt::Display for GaussianSplat { } } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct BoundingBox { pub min_x: f32, pub max_x: f32, @@ -677,7 +678,8 @@ impl BoundingBox { } } -#[derive(Clone, Debug, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug)] pub struct GaussianSplatBuilder { filepath: Option, unpack_opts: UnpackOptions, diff --git a/crates/spz/src/header.rs b/crates/spz/src/header.rs index b1c9751..e2fa68e 100644 --- a/crates/spz/src/header.rs +++ b/crates/spz/src/header.rs @@ -3,14 +3,14 @@ use std::io::{Read, Write}; use anyhow::{Context, Result, bail}; -use arbitrary::Arbitrary; use serde::{Deserialize, Serialize}; use crate::consts; pub const HEADER_SIZE: usize = std::mem::size_of::(); -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[repr(C)] pub struct PackedGaussiansHeader { /// Always `0x5053474e`. "NGSP" = Niantic Gaussian SPlat. diff --git a/crates/spz/src/packed.rs b/crates/spz/src/packed.rs index 286717f..9e62f8c 100644 --- a/crates/spz/src/packed.rs +++ b/crates/spz/src/packed.rs @@ -6,7 +6,6 @@ use std::io::Write; use anyhow::bail; use anyhow::{Context, Result}; -use arbitrary::Arbitrary; use likely_stable::unlikely; use serde::{Deserialize, Serialize}; @@ -14,7 +13,8 @@ use crate::{consts, math}; use crate::{coord::CoordinateConverter, unpacked::UnpackedGaussian}; use crate::{coord::CoordinateSystem, header::PackedGaussiansHeader}; -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct PackOptions { pub from: CoordinateSystem, } @@ -26,7 +26,8 @@ impl PackOptions { } } -#[derive(Clone, Debug, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug)] pub struct PackOptionsBuilder { from: CoordinateSystem, } @@ -152,7 +153,8 @@ impl PackedGaussian { /// Each splat has at most 64 bytes, although splats with fewer spherical /// harmonics degrees will have less. /// The data is stored non-interleaved. -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct PackedGaussians { // Total number of points (gaussians). pub num_points: i32, diff --git a/crates/spz/src/unpacked.rs b/crates/spz/src/unpacked.rs index 7850cf4..ee25e57 100644 --- a/crates/spz/src/unpacked.rs +++ b/crates/spz/src/unpacked.rs @@ -1,11 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -use arbitrary::Arbitrary; use serde::{Deserialize, Serialize}; use crate::coord::CoordinateSystem; -#[derive(Clone, Debug, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug)] pub struct UnpackOptionsBuilder { to_coord_sys: CoordinateSystem, } @@ -34,7 +34,8 @@ impl Default for UnpackOptionsBuilder { } } -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct UnpackOptions { pub to_coord_sys: CoordinateSystem, } @@ -53,7 +54,8 @@ static_assertions::const_assert_eq!(std::mem::size_of::(), 236 /// Each gaussian has 236 bytes. /// Although the data is easier to intepret in this format, /// it is not more precise than the packed format, since it was inflated. -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct UnpackedGaussian { pub position: [f32; 3], // x, y, z pub rotation: [f32; 4], // x, y, z, w diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 8e27b94..53715b9 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -29,7 +29,7 @@ publish = false cargo-fuzz = true [dependencies] -spz = { path = "../crates/spz", default-features = false, features = [] } +spz = { path = "../crates/spz", default-features = false, features = ["fuzzing"] } libfuzzer-sys = { version = "0.4", default-features = true, features = [] } arbitrary = { version = "1.4", default-features = true, features = ["derive"] } From 5caa661ab7f420ccb251b74e8482524df3cdfdc2 Mon Sep 17 00:00:00 2001 From: Tin Lai Date: Mon, 5 Jan 2026 02:16:28 +1100 Subject: [PATCH 2/3] remove unused crates Signed-off-by: Tin Lai --- Cargo.lock | 122 +++++------------------------------------- crates/spz/Cargo.toml | 8 --- 2 files changed, 13 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d164346..f4f2395 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,56 +32,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - [[package]] name = "anstyle" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - [[package]] name = "anyhow" version = "1.0.100" @@ -194,7 +150,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", - "clap_derive", ] [[package]] @@ -203,24 +158,8 @@ version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ - "anstream", "anstyle", "clap_lex", - "strsim", - "unicase", - "unicode-width", -] - -[[package]] -name = "clap_derive" -version = "4.5.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", ] [[package]] @@ -291,12 +230,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - [[package]] name = "colored" version = "2.2.0" @@ -416,6 +349,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "find-msvc-tools" version = "0.1.5" @@ -600,12 +543,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - [[package]] name = "itertools" version = "0.10.5" @@ -762,12 +699,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - [[package]] name = "oorandom" version = "11.1.5" @@ -1165,10 +1096,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.7" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -1207,17 +1139,13 @@ dependencies = [ "anyhow", "approx", "arbitrary", - "clap", "codspeed-criterion-compat", "criterion", "flate2", - "libc", "likely_stable", "memmap2", "rand", - "rayon", "rstest", - "rustversion", "serde", "serde_json", "static_assertions", @@ -1258,12 +1186,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "syn" version = "2.0.111" @@ -1369,36 +1291,18 @@ dependencies = [ "winnow", ] -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - [[package]] name = "unindent" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "walkdir" version = "2.5.0" diff --git a/crates/spz/Cargo.toml b/crates/spz/Cargo.toml index b0b7cbd..5b48082 100644 --- a/crates/spz/Cargo.toml +++ b/crates/spz/Cargo.toml @@ -22,23 +22,15 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -rustversion = { version = "1.0", default-features = true, features = [] } anyhow = { version = "1.0", default-features = true, features = [] } thiserror = { version = "2.0", default-features = true, features = [] } -clap = { version = "4.5", default-features = true, features = [ - "env", - "unicode", - "derive", -] } memmap2 = { version = "0.9", default-features = true, features = [] } -libc = { version = "0.2", default-features = true, features = [] } flate2 = { version = "1.1", default-features = true, features = [] } serde_json = { version = "1.0", default-features = true, features = [ "float_roundtrip", ] } serde = { version = "1.0", default-features = true, features = ["derive"] } static_assertions = { version = "1.1", default-features = true, features = [] } -rayon = { version = "1.11", default-features = true, features = [] } tokio = { version = "1.48", default-features = true, features = ["full"] } likely_stable = { version = "0.1", default-features = true, features = [] } arbitrary = { version = "1.4", default-features = true, features = ["derive"], optional = true } From 0fa2ab8cea87850f6290e664ea49938df5cb70ce Mon Sep 17 00:00:00 2001 From: Tin Lai Date: Mon, 5 Jan 2026 10:40:25 +1100 Subject: [PATCH 3/3] remove tokio usage and use thiserror as lib Signed-off-by: Tin Lai --- Cargo.lock | 232 ++------------------------ crates/spz/Cargo.toml | 12 +- crates/spz/benches/benchmarks/load.rs | 6 - crates/spz/benches/util/mod.rs | 24 +-- crates/spz/examples/load_spz.rs | 20 +-- crates/spz/src/compression.rs | 17 +- crates/spz/src/errors.rs | 25 +++ crates/spz/src/gaussian_splat.rs | 163 ++++++++---------- crates/spz/src/header.rs | 18 +- crates/spz/src/lib.rs | 2 + crates/spz/src/mmap.rs | 11 +- crates/spz/src/packed.rs | 60 +++---- crates/spz/tests/test.rs | 3 +- 13 files changed, 173 insertions(+), 420 deletions(-) create mode 100644 crates/spz/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index f4f2395..c40d638 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,12 +80,6 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" -[[package]] -name = "bytes" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" - [[package]] name = "cast" version = "0.3.0" @@ -196,9 +190,7 @@ dependencies = [ "codspeed", "codspeed-criterion-compat-walltime", "colored", - "futures", "regex", - "tokio", ] [[package]] @@ -213,7 +205,6 @@ dependencies = [ "clap", "codspeed", "criterion-plot 0.5.0", - "futures", "is-terminal", "itertools 0.10.5", "num-traits", @@ -226,7 +217,6 @@ dependencies = [ "serde_derive", "serde_json", "tinytemplate", - "tokio", "walkdir", ] @@ -271,7 +261,6 @@ dependencies = [ "serde", "serde_json", "tinytemplate", - "tokio", "walkdir", ] @@ -349,16 +338,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "find-msvc-tools" version = "0.1.5" @@ -446,10 +425,13 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -618,15 +600,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "memchr" version = "2.7.6" @@ -661,17 +634,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - [[package]] name = "nix" version = "0.30.1" @@ -715,29 +677,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -936,15 +875,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.12.2" @@ -1033,12 +963,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.27" @@ -1094,16 +1018,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - [[package]] name = "simd-adler32" version = "0.3.8" @@ -1116,41 +1030,23 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - [[package]] name = "spz" version = "0.0.6" dependencies = [ - "anyhow", "approx", "arbitrary", "codspeed-criterion-compat", "criterion", "flate2", + "futures", "likely_stable", "memmap2", "rand", "rstest", "serde", - "serde_json", "static_assertions", "thiserror", - "tokio", ] [[package]] @@ -1233,34 +1129,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "tokio" -version = "1.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -1426,16 +1294,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -1453,31 +1312,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -1486,96 +1328,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.7.14" diff --git a/crates/spz/Cargo.toml b/crates/spz/Cargo.toml index 5b48082..286fedb 100644 --- a/crates/spz/Cargo.toml +++ b/crates/spz/Cargo.toml @@ -22,18 +22,14 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -anyhow = { version = "1.0", default-features = true, features = [] } thiserror = { version = "2.0", default-features = true, features = [] } memmap2 = { version = "0.9", default-features = true, features = [] } flate2 = { version = "1.1", default-features = true, features = [] } -serde_json = { version = "1.0", default-features = true, features = [ - "float_roundtrip", -] } serde = { version = "1.0", default-features = true, features = ["derive"] } static_assertions = { version = "1.1", default-features = true, features = [] } -tokio = { version = "1.48", default-features = true, features = ["full"] } likely_stable = { version = "0.1", default-features = true, features = [] } arbitrary = { version = "1.4", default-features = true, features = ["derive"], optional = true } +futures = { version = "0.3", default-features = false, features = ["std"] } [features] fuzzing = ["dep:arbitrary"] @@ -41,12 +37,8 @@ fuzzing = ["dep:arbitrary"] [dev-dependencies] criterion = { version = "0.8", default-features = true, features = [ "html_reports", - "async_tokio", -] } -codspeed-criterion-compat = { version = "4.1.0", default-features = true, features = [ - "async", - "async_tokio", ] } +codspeed-criterion-compat = { version = "4.1.0", default-features = true, features = [] } rstest = { version = "0.26", default-features = true, features = [] } approx = { version = "0.5", default-features = true, features = [] } rand = { version = "0.9", default-features = true, features = [] } diff --git a/crates/spz/benches/benchmarks/load.rs b/crates/spz/benches/benchmarks/load.rs index 0cdbe84..9a438fe 100644 --- a/crates/spz/benches/benchmarks/load.rs +++ b/crates/spz/benches/benchmarks/load.rs @@ -2,7 +2,6 @@ use codspeed_criterion_compat::Criterion; use spz::{packed::PackOptions, prelude::GaussianSplat, unpacked::UnpackOptions}; -use tokio::runtime::Runtime; use crate::util; @@ -24,12 +23,7 @@ pub fn bench_cloud_load_n(c: &mut Criterion) { } pub fn bench_load_packed_from_file(c: &mut Criterion) { - let rt = Runtime::new().unwrap(); - c.bench_function("load_packed_from_file", |b| { b.iter(|| util::load_packed_from_file()); }); - c.bench_function("load_packed_from_file_async", |b| { - b.to_async(&rt).iter(|| util::load_packed_from_file_async()); - }); } diff --git a/crates/spz/benches/util/mod.rs b/crates/spz/benches/util/mod.rs index bd41e2a..dd25c0d 100644 --- a/crates/spz/benches/util/mod.rs +++ b/crates/spz/benches/util/mod.rs @@ -1,8 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -use anyhow::Result; use rand::{Rng, SeedableRng, rngs::StdRng}; -use spz::{coord::CoordinateSystem, gaussian_splat::GaussianSplat, math, unpacked::UnpackOptions}; +use spz::{ + coord::CoordinateSystem, errors::SpzError, gaussian_splat::GaussianSplat, math, + unpacked::UnpackOptions, +}; pub fn create_splat(num_points: i32) -> GaussianSplat { let sh_degree = 2_i32; @@ -42,27 +44,13 @@ pub fn create_splat(num_points: i32) -> GaussianSplat { } } -pub fn load_packed_from_file() -> Result { +pub fn load_packed_from_file() -> Result { GaussianSplat::builder() - .filepath("../../assets/racoonfamily.spz") .packed(true)? .unpack_options( UnpackOptions::builder() .to_coord_system(CoordinateSystem::default()) .build(), ) - .load() -} - -pub async fn load_packed_from_file_async() -> Result { - GaussianSplat::builder() - .filepath("../../assets/racoonfamily.spz") - .packed(true)? - .unpack_options( - UnpackOptions::builder() - .to_coord_system(CoordinateSystem::default()) - .build(), - ) - .load_async() - .await + .load("../../assets/racoonfamily.spz") } diff --git a/crates/spz/examples/load_spz.rs b/crates/spz/examples/load_spz.rs index e6ff353..1a1310f 100644 --- a/crates/spz/examples/load_spz.rs +++ b/crates/spz/examples/load_spz.rs @@ -1,40 +1,40 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::path::{Path, PathBuf}; +use std::path::PathBuf; -use anyhow::Result; -use spz::{coord::CoordinateSystem, gaussian_splat::GaussianSplat, unpacked::UnpackOptions}; +use spz::{ + coord::CoordinateSystem, errors::SpzError, gaussian_splat::GaussianSplat, + unpacked::UnpackOptions, +}; -fn main() -> Result<()> { +fn main() -> Result<(), SpzError> { let mut sample_spz = PathBuf::from(env!("CARGO_MANIFEST_DIR")); sample_spz.push("assets/racoonfamily.spz"); let _gs = GaussianSplat::builder() - .filepath(sample_spz) .packed(true)? .unpack_options( UnpackOptions::builder() .to_coord_system(CoordinateSystem::default()) .build(), ) - .load()?; + .load(sample_spz)?; Ok(()) } #[allow(unused)] -async fn load_spz_async

(spz_file: P) -> Result +async fn load_spz_async(reader: R) -> Result where - P: AsRef, + R: futures::io::AsyncRead + Unpin, { GaussianSplat::builder() - .filepath(spz_file) .packed(true)? .unpack_options( UnpackOptions::builder() .to_coord_system(CoordinateSystem::default()) .build(), ) - .load_async() + .load_async(reader) .await } diff --git a/crates/spz/src/compression.rs b/crates/spz/src/compression.rs index cba6e23..fb3248d 100644 --- a/crates/spz/src/compression.rs +++ b/crates/spz/src/compression.rs @@ -2,16 +2,16 @@ use std::io::{BufRead, BufReader, Read}; -use anyhow::Context; -use anyhow::Result; use flate2::{ Compression, bufread::{GzDecoder, GzEncoder}, }; +use crate::errors::SpzError; + /// Compress data using gzip compression. #[inline] -pub fn to_gzip_bytes(decompressed: &[u8], compressed: &mut Vec) -> Result<()> { +pub fn to_gzip_bytes(decompressed: &[u8], compressed: &mut Vec) -> Result<(), SpzError> { compressed.clear(); compressed.reserve(decompressed.len() / 4); @@ -22,30 +22,27 @@ pub fn to_gzip_bytes(decompressed: &[u8], compressed: &mut Vec) -> Result<() /// Compress data using gzip compression. #[inline] -pub fn to_gzip(decompressed: B, compressed: &mut Vec) -> Result<()> +pub fn to_gzip(decompressed: B, compressed: &mut Vec) -> Result<(), SpzError> where B: BufRead, { let mut encoder = GzEncoder::new(decompressed, Compression::default()); - encoder.read_to_end(compressed) - .with_context(|| "unable to decompress")?; + encoder.read_to_end(compressed)?; Ok(()) } /// Decompress gzip-compressed data into the given buffer. #[inline] -pub fn gzip_to_bytes(compressed: C, mut decompressed: D) -> Result<()> +pub fn gzip_to_bytes(compressed: C, mut decompressed: D) -> Result<(), SpzError> where C: AsRef<[u8]>, D: AsMut>, { let mut gz_decoder = GzDecoder::new(compressed.as_ref()); - gz_decoder - .read_to_end(decompressed.as_mut()) - .with_context(|| "unable to decompress")?; + gz_decoder.read_to_end(decompressed.as_mut())?; Ok(()) } diff --git a/crates/spz/src/errors.rs b/crates/spz/src/errors.rs new file mode 100644 index 0000000..a970766 --- /dev/null +++ b/crates/spz/src/errors.rs @@ -0,0 +1,25 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum SpzError { + #[error("data is empty")] + DataIsEmpty, + #[error("invalid fractional bits (= {0}): {1}")] + InvalidFractionalBits(i32, i32), + #[error("index out of bounds: {0}")] + IndexOutOfBounds(usize), + #[error(transparent)] + IoError(#[from] std::io::Error), + #[error("invalid magic number in packed gaussians header")] + InvalidMagicNumber, + #[error("inconsistent sizes")] + InconsistentSizes, + #[error("unsupported version: {0}")] + UnsupportedVersion(i32), + #[error("unsupported spherical harmonics degree: {0}")] + UnsupportedSphericalHarmonicsDegree(u8), + #[error("{0}")] + LoadPackedError(String), + #[error("unsupported: {0}")] + UnsupportedFormat(String), +} diff --git a/crates/spz/src/gaussian_splat.rs b/crates/spz/src/gaussian_splat.rs index 9c44b47..25fd2e0 100644 --- a/crates/spz/src/gaussian_splat.rs +++ b/crates/spz/src/gaussian_splat.rs @@ -1,15 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::{path::Path, path::PathBuf}; +use std::path::Path; -use anyhow::{Context, Result, bail}; +use futures::io::AsyncReadExt; use likely_stable::unlikely; use serde::{Deserialize, Serialize}; -use tokio::io::AsyncReadExt; use crate::{ compression, consts, coord::{CoordinateConverter, CoordinateSystem}, + errors::SpzError, math::{self, dim_for_degree, half_to_float}, mmap, packed::PackOptions, @@ -83,47 +83,28 @@ impl GaussianSplat { GaussianSplatBuilder::default() } - /// Loads Gaussian splat from a file in packed format, async. + /// Loads Gaussian splat from an async reader containing packed data. /// - /// `filepath` - gzip compressed, packed gaussian data file. - pub async fn load_packed_from_file_into_buf_async( - filepath: F, - unpack_opts: &UnpackOptions, - contents: &mut Vec, - ) -> Result - where - F: AsRef, - { - let mut infile = tokio::fs::File::open(filepath).await?; - - infile.read_to_end(contents).await?; - - return Self::new_from_packed_gaussians( - &Self::load_packed(&contents)?, - unpack_opts, - ); - } - - /// Loads Gaussian splat from a file in packed format, async. - /// - /// `filepath` - gzip compressed, packed gaussian data file. - pub async fn load_packed_from_file_async( - filepath: F, + /// `reader` - gzip compressed, packed gaussian data stream. + pub async fn load_packed_from_reader( + mut reader: R, unpack_opts: &UnpackOptions, - ) -> Result + ) -> Result where - F: AsRef, + R: futures::io::AsyncRead + Unpin, { let mut contents = Vec::new(); - - Self::load_packed_from_file_into_buf_async(filepath, unpack_opts, &mut contents) - .await + reader.read_to_end(&mut contents).await?; + Self::new_from_packed_gaussians(&Self::load_packed(&contents)?, unpack_opts) } /// Loads Gaussian splat from a file in packed format. /// /// `filepath` - gzip compressed, packed gaussian data file. - pub fn load_packed_from_file(filepath: F, unpack_opts: &UnpackOptions) -> Result + pub fn load_packed_from_file( + filepath: F, + unpack_opts: &UnpackOptions, + ) -> Result where F: AsRef, { @@ -137,8 +118,7 @@ impl GaussianSplat { ); } let mmap = mmap::open(filepath)?; - let packed = Self::load_packed(mmap.as_ref()) - .with_context(|| "unable to load packed file")?; + let packed = Self::load_packed(mmap.as_ref())?; Self::new_from_packed_gaussians(&packed, unpack_opts) } @@ -146,65 +126,70 @@ impl GaussianSplat { /// Loads Gaussian splat from a slice of bytes in packed format. /// /// `data` - gzip compressed, packed gaussian data. - pub fn load_packed(data: D) -> Result + pub fn load_packed(data: D) -> Result where D: AsRef<[u8]>, { if unlikely(data.as_ref().is_empty()) { // we cannot return an empty struct as there is no header - bail!("data is empty"); + return Err(SpzError::DataIsEmpty); } let mut decompressed = Vec::::new(); - crate::compression::gzip_to_bytes(data, &mut decompressed) - .with_context(|| "unable to decompress gzip data")?; + crate::compression::gzip_to_bytes(data, &mut decompressed).map_err(|_| { + SpzError::LoadPackedError("unable to decompress gzip data".to_string()) + })?; - let packed: PackedGaussians = decompressed - .try_into() - .with_context(|| "unable to parse packed gaussian data")?; + let packed: PackedGaussians = decompressed.try_into().map_err(|_| { + SpzError::LoadPackedError( + "unable to parse packed gaussian data".to_string(), + ) + })?; Ok(packed) } #[inline] - pub async fn save_as_packed_async( + pub async fn save_as_packed_async( &self, - filepath: F, + mut writer: W, pack_opts: &PackOptions, - ) -> Result<()> + ) -> Result<(), SpzError> where - F: AsRef, + W: futures::io::AsyncWrite + Unpin, { - let compressed = self.serialize_as_packed_bytes(pack_opts)?; + use futures::io::AsyncWriteExt; - tokio::fs::create_dir_all( - filepath.as_ref() - .parent() - .ok_or_else(|| anyhow::anyhow!("recursive mkdir failed"))?, - ) - .await?; + let compressed = self.serialize_as_packed_bytes(pack_opts)?; + writer.write_all(&compressed).await?; + writer.flush().await?; - tokio::fs::write(filepath, compressed) - .await - .with_context(|| "unable to write to file") + Ok(()) } #[inline] - pub fn save_as_packed(&self, filepath: F, pack_opts: &PackOptions) -> Result<()> + pub fn save_as_packed( + &self, + filepath: F, + pack_opts: &PackOptions, + ) -> Result<(), SpzError> where F: AsRef, { let compressed = self.serialize_as_packed_bytes(pack_opts)?; - std::fs::create_dir_all( - filepath.as_ref() - .parent() - .ok_or_else(|| anyhow::anyhow!("recursive mkdir failed"))?, - )?; - std::fs::write(filepath, compressed).with_context(|| "unable to write to file") + if let Some(parent) = filepath.as_ref().parent() { + std::fs::create_dir_all(parent)?; + }; + std::fs::write(filepath, compressed)?; + + Ok(()) } - pub fn serialize_as_packed_bytes(&self, pack_opts: &PackOptions) -> Result> { + pub fn serialize_as_packed_bytes( + &self, + pack_opts: &PackOptions, + ) -> Result, SpzError> { let packed = self.to_packed_gaussians(pack_opts)?; let uncompressed = packed.as_bytes_vec()?; @@ -218,13 +203,13 @@ impl GaussianSplat { pub fn new_from_packed_gaussians( packed: &PackedGaussians, unpack_opts: &UnpackOptions, - ) -> Result { + ) -> Result { let num_points = packed.num_points as usize; let sh_dim = dim_for_degree(packed.sh_degree as u8); let uses_float16 = packed.uses_float16(); if unlikely(!packed.check_sizes(num_points, sh_dim, uses_float16)) { - bail!("inconsistent sizes"); + return Err(SpzError::InconsistentSizes); } let mut result = Self { num_points: packed.num_points, @@ -304,9 +289,12 @@ impl GaussianSplat { Ok(result) } - pub fn to_packed_gaussians(&self, pack_opts: &PackOptions) -> Result { + pub fn to_packed_gaussians( + &self, + pack_opts: &PackOptions, + ) -> Result { if unlikely(!self.check_sizes()) { - bail!("inconsistent sizes"); + return Err(SpzError::InconsistentSizes); } let num_points = self.num_points as usize; let sh_dim = math::dim_for_degree(self.spherical_harmonics_degree as u8) as usize; @@ -681,7 +669,6 @@ impl BoundingBox { #[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug)] pub struct GaussianSplatBuilder { - filepath: Option, unpack_opts: UnpackOptions, packed: bool, } @@ -690,7 +677,6 @@ impl Default for GaussianSplatBuilder { #[inline] fn default() -> Self { GaussianSplatBuilder { - filepath: None, unpack_opts: UnpackOptions::default(), packed: true, } @@ -698,17 +684,11 @@ impl Default for GaussianSplatBuilder { } impl GaussianSplatBuilder { - pub fn filepath(mut self, filepath: F) -> Self - where - F: AsRef, - { - self.filepath = Some(filepath.as_ref().to_path_buf()); - self - } - - pub fn packed(mut self, packed: bool) -> Result { + pub fn packed(mut self, packed: bool) -> Result { if !packed { - bail!("only packed format loading is supported currently"); + return Err(SpzError::UnsupportedFormat( + "only packed format loading is supported currently".to_string(), + )); } self.packed = packed; @@ -720,19 +700,18 @@ impl GaussianSplatBuilder { self } - pub fn load(self) -> Result { - GaussianSplat::load_packed_from_file( - self.filepath.as_ref().unwrap(), - &self.unpack_opts, - ) + pub fn load(self, filepath: F) -> Result + where + F: AsRef, + { + GaussianSplat::load_packed_from_file(filepath, &self.unpack_opts) } - pub async fn load_async(self) -> Result { - GaussianSplat::load_packed_from_file_async( - self.filepath.as_ref().unwrap(), - &self.unpack_opts, - ) - .await + pub async fn load_async(self, reader: R) -> Result + where + R: futures::io::AsyncRead + Unpin, + { + GaussianSplat::load_packed_from_reader(reader, &self.unpack_opts).await } } diff --git a/crates/spz/src/header.rs b/crates/spz/src/header.rs index e2fa68e..1298983 100644 --- a/crates/spz/src/header.rs +++ b/crates/spz/src/header.rs @@ -2,10 +2,10 @@ use std::io::{Read, Write}; -use anyhow::{Context, Result, bail}; use serde::{Deserialize, Serialize}; use crate::consts; +use crate::errors::SpzError; pub const HEADER_SIZE: usize = std::mem::size_of::(); @@ -35,23 +35,18 @@ pub struct PackedGaussiansHeader { impl PackedGaussiansHeader { #[inline] - pub fn read_from(reader: &mut R) -> Result + pub fn read_from(reader: &mut R) -> Result where R: Read, { let mut header_buf: [u8; HEADER_SIZE] = [0; HEADER_SIZE]; + reader.read_exact(&mut header_buf)?; - match reader.read_exact(&mut header_buf) { - Ok(_) => {}, - Err(err) => { - bail!(err); - }, - } Ok(unsafe { std::mem::transmute(header_buf) }) } #[inline] - pub fn serialize_to(&self, stream: &mut W) -> Result<()> + pub fn serialize_to(&self, stream: &mut W) -> Result<(), SpzError> where W: Write, { @@ -61,8 +56,9 @@ impl PackedGaussiansHeader { std::mem::size_of::(), ) }; - stream.write_all(b) - .with_context(|| "unable to write packed gaussians header to stream") + stream.write_all(b)?; + + Ok(()) } } diff --git a/crates/spz/src/lib.rs b/crates/spz/src/lib.rs index 78f896b..52f2df0 100644 --- a/crates/spz/src/lib.rs +++ b/crates/spz/src/lib.rs @@ -3,6 +3,7 @@ pub mod compression; pub mod consts; pub mod coord; +pub mod errors; pub mod gaussian_splat; pub mod header; pub mod math; @@ -14,6 +15,7 @@ pub mod prelude { pub use super::*; pub use super::coord::{CoordinateConverter, CoordinateSystem}; + pub use super::errors::SpzError; pub use super::gaussian_splat::{BoundingBox, GaussianSplat}; pub use super::header::PackedGaussiansHeader; pub use super::packed::{PackOptions, PackedGaussian, PackedGaussians}; diff --git a/crates/spz/src/mmap.rs b/crates/spz/src/mmap.rs index d99f698..2353292 100644 --- a/crates/spz/src/mmap.rs +++ b/crates/spz/src/mmap.rs @@ -2,15 +2,18 @@ use std::{fs::File, path::Path}; -use anyhow::Context; -use anyhow::Result; +use crate::errors::SpzError; use memmap2::Mmap; -pub fn open(file: F) -> Result +pub fn open(file: F) -> Result where F: AsRef, { let infile = File::open(&file)?; - unsafe { Mmap::map(&infile).with_context(|| "unable to open file with mmap()") } + unsafe { + Mmap::map(&infile).map_err(|_| { + SpzError::LoadPackedError("unable to open file with mmap()".to_string()) + }) + } } diff --git a/crates/spz/src/packed.rs b/crates/spz/src/packed.rs index 9e62f8c..751ea57 100644 --- a/crates/spz/src/packed.rs +++ b/crates/spz/src/packed.rs @@ -4,12 +4,10 @@ use std::io::BufReader; use std::io::Read; use std::io::Write; -use anyhow::bail; -use anyhow::{Context, Result}; use likely_stable::unlikely; use serde::{Deserialize, Serialize}; -use crate::{consts, math}; +use crate::{consts, errors::SpzError, math}; use crate::{coord::CoordinateConverter, unpacked::UnpackedGaussian}; use crate::{coord::CoordinateSystem, header::PackedGaussiansHeader}; @@ -79,7 +77,7 @@ impl PackedGaussian { uses_quaternion_smallest_three: bool, fractional_bits: i32, coord_flip: &CoordinateConverter, - ) -> Result { + ) -> Result { let mut result = UnpackedGaussian::default(); // positions @@ -96,7 +94,7 @@ impl PackedGaussian { let s = 1u32 << (fractional_bits as u32); if s == 0 { - bail!("invalid fractional bits (= 0): {}", fractional_bits); + return Err(SpzError::InvalidFractionalBits(0, fractional_bits)); } let scale = 1.0_f32 / s as f32; @@ -192,7 +190,7 @@ impl PackedGaussians { } } - pub fn as_bytes_vec(&self) -> Result> { + pub fn as_bytes_vec(&self) -> Result, SpzError> { let mut ret = Vec::new(); self.construct_header().serialize_to(&mut ret)?; @@ -207,7 +205,7 @@ impl PackedGaussians { Ok(ret) } - pub fn write_self_to(&self, stream: &mut W) -> Result<()> + pub fn write_self_to(&self, stream: &mut W) -> Result<(), SpzError> where W: Write, { @@ -228,9 +226,9 @@ impl PackedGaussians { self.positions.len() == self.num_points as usize * 3 * 2 } - pub fn at(&self, i: usize) -> Result { + pub fn at(&self, i: usize) -> Result { if i >= self.num_points as usize { - bail!("index out of bounds: {}", i); + return Err(SpzError::IndexOutOfBounds(i)); } let idx = i as usize; let mut result = PackedGaussian::default(); @@ -294,7 +292,7 @@ impl PackedGaussians { &self, i: usize, coord_flip: &CoordinateConverter, - ) -> Result { + ) -> Result { self.at(i)?.unpack( self.uses_float16(), self.uses_quaternion_smallest_three, @@ -330,7 +328,7 @@ impl PackedGaussians { } impl TryFrom> for PackedGaussians { - type Error = anyhow::Error; + type Error = SpzError; fn try_from(b: Vec) -> Result { Self::try_from(b.as_slice()) @@ -338,25 +336,23 @@ impl TryFrom> for PackedGaussians { } impl TryFrom<&[u8]> for PackedGaussians { - type Error = anyhow::Error; + type Error = SpzError; fn try_from(b: &[u8]) -> Result { let mut from_reader = BufReader::new(b); - let header = PackedGaussiansHeader::read_from(&mut from_reader) - .with_context(|| "unable to read packed gaussians header")?; + let header = PackedGaussiansHeader::read_from(&mut from_reader)?; if unlikely(header.magic != consts::HEADER_MAGIC) { - bail!("invalid magic number in packed gaussians header"); + return Err(SpzError::InvalidMagicNumber); } if unlikely(header.version < 1 || header.version > 3) { - bail!("unsupported version: {}", header.version); + return Err(SpzError::UnsupportedVersion(header.version)); } if header.spherical_harmonics_degree > 3 { - bail!( - "unsupported spherical harmonics degree: {}", - header.spherical_harmonics_degree - ); + return Err(SpzError::UnsupportedSphericalHarmonicsDegree( + header.spherical_harmonics_degree, + )); } let num_points = header.num_points; let uses_float16 = header.version == 1; @@ -387,24 +383,12 @@ impl TryFrom<&[u8]> for PackedGaussians { as usize * 3 ], }; - if let Err(err) = from_reader.read_exact(&mut result.positions) { - bail!("read error (positions): {}", err); - } - if let Err(err) = from_reader.read_exact(&mut result.alphas) { - bail!("read error (alphas): {}", err); - } - if let Err(err) = from_reader.read_exact(&mut result.colors) { - bail!("read error (colors): {}", err); - } - if let Err(err) = from_reader.read_exact(&mut result.scales) { - bail!("read error (scales): {}", err); - } - if let Err(err) = from_reader.read_exact(&mut result.rotations) { - bail!("read error (rotations): {}", err); - } - if let Err(err) = from_reader.read_exact(&mut result.spherical_harmonics) { - bail!("read error (sh): {}", err); - } + from_reader.read_exact(&mut result.positions)?; + from_reader.read_exact(&mut result.alphas)?; + from_reader.read_exact(&mut result.colors)?; + from_reader.read_exact(&mut result.scales)?; + from_reader.read_exact(&mut result.rotations)?; + from_reader.read_exact(&mut result.spherical_harmonics)?; Ok(result) } } diff --git a/crates/spz/tests/test.rs b/crates/spz/tests/test.rs index 8d8779d..eb5bc69 100644 --- a/crates/spz/tests/test.rs +++ b/crates/spz/tests/test.rs @@ -127,8 +127,7 @@ fn test_spz_values(#[case] filename: &str, #[case] spz_values: util::SpzValues) let spz_path = util::assets_dir().join(filename); let gs = GaussianSplat::builder() - .filepath(spz_path) - .load() + .load(spz_path) .expect("failed to load gaussian splat"); assert_eq!(gs.num_points, spz_values.num_points);