diff --git a/Cargo.lock b/Cargo.lock index c4607ea..eea9172 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" @@ -124,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" @@ -194,7 +144,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", - "clap_derive", ] [[package]] @@ -203,24 +152,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]] @@ -257,9 +190,7 @@ dependencies = [ "codspeed", "codspeed-criterion-compat-walltime", "colored", - "futures", "regex", - "tokio", ] [[package]] @@ -274,7 +205,6 @@ dependencies = [ "clap", "codspeed", "criterion-plot 0.5.0", - "futures", "is-terminal", "itertools 0.10.5", "num-traits", @@ -287,16 +217,9 @@ dependencies = [ "serde_derive", "serde_json", "tinytemplate", - "tokio", "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" @@ -338,7 +261,6 @@ dependencies = [ "serde", "serde_json", "tinytemplate", - "tokio", "walkdir", ] @@ -503,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", @@ -600,12 +525,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" @@ -681,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" @@ -724,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" @@ -762,12 +661,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" @@ -784,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" @@ -1005,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" @@ -1102,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" @@ -1163,15 +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.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" -dependencies = [ - "libc", -] - [[package]] name = "simd-adler32" version = "0.3.8" @@ -1184,45 +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", - "clap", "codspeed-criterion-compat", "criterion", "flate2", - "libc", + "futures", "likely_stable", "memmap2", "rand", - "rayon", "rstest", - "rustversion", "serde", - "serde_json", "static_assertions", "thiserror", - "tokio", ] [[package]] @@ -1278,12 +1102,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" @@ -1331,34 +1149,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -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" @@ -1389,36 +1179,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" @@ -1542,16 +1314,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]] @@ -1569,31 +1332,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]] @@ -1602,96 +1348,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-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 44e2c63..286fedb 100644 --- a/crates/spz/Cargo.toml +++ b/crates/spz/Cargo.toml @@ -22,36 +22,23 @@ 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.49", 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 } +futures = { version = "0.3", default-features = false, features = ["std"] } + +[features] +fuzzing = ["dep:arbitrary"] [dev-dependencies] criterion = { version = "0.8", default-features = true, features = [ "html_reports", - "async_tokio", -] } -codspeed-criterion-compat = { version = "4.2.1", 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/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/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 997dc15..25fd2e0 100644 --- a/crates/spz/src/gaussian_splat.rs +++ b/crates/spz/src/gaussian_splat.rs @@ -1,16 +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 arbitrary::Arbitrary; +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, @@ -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, @@ -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; @@ -645,7 +633,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,9 +666,9 @@ impl BoundingBox { } } -#[derive(Clone, Debug, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug)] pub struct GaussianSplatBuilder { - filepath: Option, unpack_opts: UnpackOptions, packed: bool, } @@ -688,7 +677,6 @@ impl Default for GaussianSplatBuilder { #[inline] fn default() -> Self { GaussianSplatBuilder { - filepath: None, unpack_opts: UnpackOptions::default(), packed: true, } @@ -696,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; @@ -718,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 b1c9751..1298983 100644 --- a/crates/spz/src/header.rs +++ b/crates/spz/src/header.rs @@ -2,15 +2,15 @@ use std::io::{Read, Write}; -use anyhow::{Context, Result, bail}; -use arbitrary::Arbitrary; use serde::{Deserialize, Serialize}; use crate::consts; +use crate::errors::SpzError; 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. @@ -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 286717f..751ea57 100644 --- a/crates/spz/src/packed.rs +++ b/crates/spz/src/packed.rs @@ -4,17 +4,15 @@ use std::io::BufReader; use std::io::Read; use std::io::Write; -use anyhow::bail; -use anyhow::{Context, Result}; -use arbitrary::Arbitrary; 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}; -#[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 +24,8 @@ impl PackOptions { } } -#[derive(Clone, Debug, Arbitrary)] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug)] pub struct PackOptionsBuilder { from: CoordinateSystem, } @@ -78,7 +77,7 @@ impl PackedGaussian { uses_quaternion_smallest_three: bool, fractional_bits: i32, coord_flip: &CoordinateConverter, - ) -> Result { + ) -> Result { let mut result = UnpackedGaussian::default(); // positions @@ -95,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; @@ -152,7 +151,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, @@ -190,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)?; @@ -205,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, { @@ -226,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(); @@ -292,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, @@ -328,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()) @@ -336,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; @@ -385,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/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/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); 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"] }