From dd0a186174779e94177d8193ee4764aba74beadd Mon Sep 17 00:00:00 2001 From: mattcieslak Date: Sun, 22 Feb 2026 15:11:44 -0500 Subject: [PATCH 1/7] refactor repeated lines, address todos --- .gitignore | 4 +- examples/load_trx.cpp | 16 +- include/trx/detail/dtype_helpers.h | 29 + include/trx/detail/exceptions.h | 41 ++ include/trx/detail/zip_raii.h | 102 +++ include/trx/trx.h | 89 ++- include/trx/trx.tpp | 1012 ++++++++++------------------ src/detail/dtype_helpers.cpp | 11 +- src/trx.cpp | 376 ++++------- tests/test_trx_anytrxfile.cpp | 52 +- tests/test_trx_io.cpp | 46 +- tests/test_trx_mmap.cpp | 32 +- tests/test_trx_trxfile.cpp | 6 +- 13 files changed, 815 insertions(+), 1001 deletions(-) create mode 100644 include/trx/detail/exceptions.h create mode 100644 include/trx/detail/zip_raii.h diff --git a/.gitignore b/.gitignore index 66a6152..2490d42 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ docs/api/ test_package/build test_package/CMakeUserPresets.json test-data/* -bench/results* \ No newline at end of file +bench/results* +**build-tests +*.jsonl diff --git a/examples/load_trx.cpp b/examples/load_trx.cpp index 1b7fec2..ef72aa5 100644 --- a/examples/load_trx.cpp +++ b/examples/load_trx.cpp @@ -8,23 +8,23 @@ int main(int argc, char **argv) { // check_syntax off auto trx = trx::TrxFile::load_from_zip(argv[1]); - std::cout << "Vertices: " << trx->streamlines->_data.size() / 3 << "\n"; - std::cout << "First vertex (x,y,z): " << trx->streamlines->_data(0, 0) << "," << trx->streamlines->_data(0, 1) << "," - << trx->streamlines->_data(0, 2) << "\n"; - std::cout << "Streamlines: " << trx->streamlines->_offsets.size() << "\n"; - std::cout << "Vertices in first streamline: " << trx->streamlines->_offsets(1) - trx->streamlines->_offsets(0) + std::cout << "Vertices: " << trx->streamlines->data().size() / 3 << "\n"; + std::cout << "First vertex (x,y,z): " << trx->streamlines->data()(0, 0) << "," << trx->streamlines->data()(0, 1) + << "," << trx->streamlines->data()(0, 2) << "\n"; + std::cout << "Streamlines: " << trx->streamlines->offsets().size() << "\n"; + std::cout << "Vertices in first streamline: " << trx->streamlines->offsets()(1) - trx->streamlines->offsets()(0) << "\n"; std::cout << "dpg (data_per_group) items: " << trx->data_per_group.size() << "\n"; std::cout << "dps (data_per_streamline) items: " << trx->data_per_streamline.size() << "\n"; for (auto const &x : trx->data_per_streamline) { - std::cout << "'" << x.first << "' items: " << x.second->_matrix.size() << "\n"; + std::cout << "'" << x.first << "' items: " << x.second->matrix().size() << "\n"; } std::cout << "dpv (data_per_vertex) items:" << trx->data_per_vertex.size() << "\n"; for (auto const &x : trx->data_per_vertex) { - std::cout << "'" << x.first << "' items: " << x.second->_data.size() << "\n"; + std::cout << "'" << x.first << "' items: " << x.second->data().size() << "\n"; } std::cout << *trx << std::endl; -} \ No newline at end of file +} diff --git a/include/trx/detail/dtype_helpers.h b/include/trx/detail/dtype_helpers.h index 703edb2..e1a0d96 100644 --- a/include/trx/detail/dtype_helpers.h +++ b/include/trx/detail/dtype_helpers.h @@ -3,12 +3,41 @@ #include +#include #include #include namespace trx { namespace detail { +// Central helper that performs the ONE placement-new + reinterpret_cast needed +// to (re)bind an Eigen::Map to a new memory region. All other code should call +// this instead of scattering placement-new across the codebase. +// +// MapType must be an Eigen::Map> type. +template +inline void remap(MapType &map, void *data, int rows, int cols) { + using Scalar = typename MapType::Scalar; + new (&map) MapType(reinterpret_cast(data), rows, cols); // NOLINT +} + +// Overload for const data pointers (read-only maps). +template +inline void remap(MapType &map, const void *data, int rows, int cols) { + using Scalar = typename MapType::Scalar; + new (&map) MapType(const_cast(reinterpret_cast(data)), rows, cols); // NOLINT +} + +// Convenience overloads that unpack a (rows, cols) shape tuple. +template +inline void remap(MapType &map, void *data, const std::tuple &shape) { + remap(map, data, std::get<0>(shape), std::get<1>(shape)); +} +template +inline void remap(MapType &map, const void *data, const std::tuple &shape) { + remap(map, data, std::get<0>(shape), std::get<1>(shape)); +} + int _sizeof_dtype(const std::string &dtype); std::string _get_dtype(const std::string &dtype); bool _is_dtype_valid(const std::string &ext); diff --git a/include/trx/detail/exceptions.h b/include/trx/detail/exceptions.h new file mode 100644 index 0000000..54ee822 --- /dev/null +++ b/include/trx/detail/exceptions.h @@ -0,0 +1,41 @@ +#ifndef TRX_DETAIL_EXCEPTIONS_H +#define TRX_DETAIL_EXCEPTIONS_H + +#include +#include + +namespace trx { + +/// Base exception for all TRX library errors. +class TrxError : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +/// I/O errors: zip failures, file not found, mmap errors, write failures. +class TrxIOError : public TrxError { +public: + using TrxError::TrxError; +}; + +/// Format errors: wrong sizes, missing fields, corrupt data, structural issues. +class TrxFormatError : public TrxError { +public: + using TrxError::TrxError; +}; + +/// Dtype errors: unsupported or mismatched data types. +class TrxDTypeError : public TrxError { +public: + using TrxError::TrxError; +}; + +/// Argument errors: invalid API arguments. +class TrxArgumentError : public TrxError { +public: + using TrxError::TrxError; +}; + +} // namespace trx + +#endif // TRX_DETAIL_EXCEPTIONS_H diff --git a/include/trx/detail/zip_raii.h b/include/trx/detail/zip_raii.h new file mode 100644 index 0000000..ef2eb2c --- /dev/null +++ b/include/trx/detail/zip_raii.h @@ -0,0 +1,102 @@ +#ifndef TRX_DETAIL_ZIP_RAII_H +#define TRX_DETAIL_ZIP_RAII_H + +#include +#include + +#include + +namespace trx { +namespace detail { + +/// RAII wrapper for zip_t*. Calls zip_discard() on destruction unless commit() is called. +class ZipArchive { +public: + ZipArchive() = default; + explicit ZipArchive(zip_t *z) : z_(z) {} + + ~ZipArchive() { + if (z_) { + if (committed_) { + zip_close(z_); + } else { + zip_discard(z_); + } + } + } + + ZipArchive(const ZipArchive &) = delete; + ZipArchive &operator=(const ZipArchive &) = delete; + ZipArchive(ZipArchive &&o) noexcept : z_(o.z_), committed_(o.committed_) { o.z_ = nullptr; } + ZipArchive &operator=(ZipArchive &&o) noexcept { + if (this != &o) { + if (z_) + zip_discard(z_); + z_ = o.z_; + committed_ = o.committed_; + o.z_ = nullptr; + } + return *this; + } + + /// Commit changes (calls zip_close on destruction instead of zip_discard). + void commit(const std::string &path = "") { + committed_ = true; + if (z_ && zip_close(z_) != 0) { + auto err = zip_strerror(z_); + z_ = nullptr; // prevent double-close + throw TrxIOError("Unable to close archive " + path + ": " + err); + } + z_ = nullptr; + } + + zip_t *get() const { return z_; } + explicit operator bool() const { return z_ != nullptr; } + + /// Release ownership without closing. + zip_t *release() { + auto *p = z_; + z_ = nullptr; + return p; + } + +private: + zip_t *z_ = nullptr; + bool committed_ = false; +}; + +/// RAII wrapper for zip_file_t*. Calls zip_fclose() on destruction. +class ZipFile { +public: + ZipFile() = default; + explicit ZipFile(zip_file_t *f) : f_(f) {} + + ~ZipFile() { + if (f_) + zip_fclose(f_); + } + + ZipFile(const ZipFile &) = delete; + ZipFile &operator=(const ZipFile &) = delete; + ZipFile(ZipFile &&o) noexcept : f_(o.f_) { o.f_ = nullptr; } + ZipFile &operator=(ZipFile &&o) noexcept { + if (this != &o) { + if (f_) + zip_fclose(f_); + f_ = o.f_; + o.f_ = nullptr; + } + return *this; + } + + zip_file_t *get() const { return f_; } + explicit operator bool() const { return f_ != nullptr; } + +private: + zip_file_t *f_ = nullptr; +}; + +} // namespace detail +} // namespace trx + +#endif // TRX_DETAIL_ZIP_RAII_H diff --git a/include/trx/trx.h b/include/trx/trx.h index 34c3bde..92b2c11 100644 --- a/include/trx/trx.h +++ b/include/trx/trx.h @@ -32,6 +32,9 @@ #include #include +#include +#include + namespace trx { namespace fs = std::filesystem; } @@ -205,6 +208,14 @@ inline const std::array dtypes = {"float16", "float64"}; template struct ArraySequence { + // Public accessors + auto &data() { return _data; } + const auto &data() const { return _data; } + auto &offsets() { return _offsets; } + const auto &offsets() const { return _offsets; } + auto &lengths() { return _lengths; } + const auto &lengths() const { return _lengths; } + Eigen::Map> _data; Eigen::Map> _offsets; Eigen::Matrix _lengths; @@ -216,6 +227,10 @@ template struct ArraySequence { }; template struct MMappedMatrix { + // Public accessor + auto &matrix() { return _matrix; } + const auto &matrix() const { return _matrix; } + Eigen::Map> _matrix; mio::shared_mmap_sink mmap; @@ -363,6 +378,10 @@ template class TrxFile { return 0; } + /// Returns an empty TrxFile that inherits this file's header metadata but has + /// NB_VERTICES and NB_STREAMLINES reset to zero. + std::unique_ptr> make_empty_like() const; + /** * @brief Build per-streamline axis-aligned bounding boxes (AABB). * @@ -455,35 +474,18 @@ template class TrxFile { */ void remove_dpg_group(const std::string &group); -private: - mutable std::vector> aabb_cache_; - /** - * @brief Load a TrxFile from a zip archive. - * - * Internal: prefer TrxReader / with_trx_reader in public API. - */ + /// Load a TrxFile from a zip archive. static std::unique_ptr> load_from_zip(const std::string &path); - /** - * @brief Load a TrxFile from an on-disk directory. - * - * Internal: prefer TrxReader / with_trx_reader in public API. - */ + /// Load a TrxFile from an on-disk directory. static std::unique_ptr> load_from_directory(const std::string &path); - /** - * @brief Load a TrxFile from either a zip archive or directory. - * - * Internal: prefer TrxReader / with_trx_reader in public API. - */ + /// Load a TrxFile from either a zip archive or directory. static std::unique_ptr> load(const std::string &path); - /** - * @brief Get the real size of data (ignoring zeros of preallocation) - * - * @return std::tuple A tuple representing the index of the last streamline and the - * total length of all the streamlines - */ - std::tuple _get_real_len(); + + /// Access the backing directory path. + const std::string &uncompressed_folder_handle() const { return _uncompressed_folder_handle; } + std::string &uncompressed_folder_handle() { return _uncompressed_folder_handle; } /** * @brief Fill a TrxFile using another and start indexes (preallocation) @@ -498,6 +500,16 @@ template class TrxFile { std::tuple _copy_fixed_arrays_from(TrxFile
*trx, int strs_start = 0, int pts_start = 0, int nb_strs_to_copy = -1); int len(); + +private: + mutable std::vector> aabb_cache_; + /** + * @brief Get the real size of data (ignoring zeros of preallocation) + * + * @return std::tuple A tuple representing the index of the last streamline and the + * total length of all the streamlines + */ + std::tuple _get_real_len(); }; namespace detail { @@ -638,6 +650,14 @@ class AnyTrxFile { static AnyTrxFile load_from_zip(const std::string &path); static AnyTrxFile load_from_directory(const std::string &path); + /// Access the backing directory path. + const std::string &backing_directory() const { return _backing_directory; } + std::string &backing_directory() { return _backing_directory; } + + /// Access the uncompressed folder handle. + const std::string &uncompressed_folder_handle() const { return _uncompressed_folder_handle; } + std::string &uncompressed_folder_handle() { return _uncompressed_folder_handle; } + private: std::string _uncompressed_folder_handle; bool _owns_uncompressed_folder = false; @@ -703,24 +723,24 @@ class TrxStream { * InMemory keeps metadata in RAM until finalize (default). * OnDisk writes metadata to temp files and copies them at finalize. */ - void set_metadata_mode(MetadataMode mode); + TrxStream &set_metadata_mode(MetadataMode mode); /** * @brief Set max in-memory buffer size for metadata writes (bytes). * * Applies when MetadataMode::OnDisk. Larger buffers reduce write calls. */ - void set_metadata_buffer_max_bytes(std::size_t max_bytes); + TrxStream &set_metadata_buffer_max_bytes(std::size_t max_bytes); /** * @brief Set the VOXEL_TO_RASMM affine matrix in the header. */ - void set_voxel_to_rasmm(const Eigen::Matrix4f &affine); + TrxStream &set_voxel_to_rasmm(const Eigen::Matrix4f &affine); /** * @brief Set DIMENSIONS in the header. */ - void set_dimensions(const std::array &dims); + TrxStream &set_dimensions(const std::array &dims); /** * @brief Add per-streamline values (DPS) from an in-memory vector. @@ -836,11 +856,11 @@ class TrxStream { }; /** - * TODO: This function might be completely unecessary + * @brief Copy header fields from a JSON root (currently unused; candidate for removal). * - * @param[in] root a Json::Value root obtained from reading a header file with JsonCPP - * @param[out] header a header containing the same elements as the original root - * */ + * @param[in] root a Json::Value root obtained from reading a header file + * @return header a header containing the same elements as the original root + */ json assignHeader(const json &root); /** @@ -1020,9 +1040,8 @@ void allocate_file(const std::string &path, std::size_t size); * @param offset offset of the data within the file * @return mio::shared_mmap_sink */ -// TODO: ADD order?? -// TODO: change tuple to vector to support ND arrays? -// TODO: remove data type as that's done outside of this function +// Known limitations: only row-major order supported; shape uses tuple (sufficient for 2D); +// dtype parameter is used only for byte-size computation. mio::shared_mmap_sink _create_memmap(std::string filename, const std::tuple &shape, const std::string &mode = "r", diff --git a/include/trx/trx.tpp b/include/trx/trx.tpp index fff2f05..67d405a 100644 --- a/include/trx/trx.tpp +++ b/include/trx/trx.tpp @@ -12,6 +12,48 @@ using Eigen::Index; using Eigen::Map; using Eigen::Matrix; using Eigen::RowMajor; +inline void mkdir_or_throw(const std::string &path) { + std::error_code ec; + trx::fs::create_directories(path, ec); + if (ec) { + throw TrxIOError("Could not create directory " + path); + } +} + +inline json default_header() { + std::vector> affine(4, std::vector(4, 0.0f)); + for (int i = 0; i < 4; i++) { + affine[i][i] = 1.0f; + } + json::object obj; + obj["VOXEL_TO_RASMM"] = affine; + obj["DIMENSIONS"] = std::vector{1, 1, 1}; + obj["NB_VERTICES"] = 0; + obj["NB_STREAMLINES"] = 0; + return json(obj); +} + +inline std::string folder_from_path(const std::string &elem_filename, const std::string &root) { + trx::fs::path elem_path(elem_filename); + trx::fs::path folder_path = elem_path.parent_path(); + std::string folder; + if (!root.empty()) { + trx::fs::path rel_path = elem_path.lexically_relative(trx::fs::path(root)); + std::string rel_str = rel_path.string(); + if (!rel_str.empty() && rel_str.rfind("..", 0) != 0) { + folder = rel_path.parent_path().string(); + } else { + folder = folder_path.string(); + } + } else { + folder = folder_path.string(); + } + if (folder == ".") { + folder.clear(); + } + return folder; +} + template void write_binary(const std::string &filename, const Matrix &matrix) { std::ofstream out(filename, std::ios::out | std::ios::binary | std::ios::trunc); typename Matrix::Index rows = matrix.rows(), cols = matrix.cols(); @@ -73,6 +115,14 @@ std::string _generate_filename_from_data(const Eigen::MatrixBase
&arr, std:: return new_filename; } +template +std::unique_ptr> TrxFile
::make_empty_like() const { + auto empty = std::make_unique>(); + empty->header = _json_set(this->header, "NB_VERTICES", 0); + empty->header = _json_set(empty->header, "NB_STREAMLINES", 0); + return empty; +} + template TrxFile
::TrxFile(int nb_vertices, int nb_streamlines, const TrxFile
*init_as, std::string reference) { std::vector> affine(4); @@ -104,7 +154,7 @@ TrxFile
::TrxFile(int nb_vertices, int nb_streamlines, const TrxFile
*ini if (nb_vertices == 0 && nb_streamlines == 0) { if (init_as != nullptr) { // raise error here - throw std::invalid_argument("Can't us init_as without declaring nb_vertices and nb_streamlines"); + throw TrxArgumentError("Can't use init_as without declaring nb_vertices and nb_streamlines"); } // will remove as completely unecessary. using as placeholders @@ -132,7 +182,7 @@ TrxFile
::TrxFile(int nb_vertices, int nb_streamlines, const TrxFile
*ini trx->_owns_uncompressed_folder = false; trx->_uncompressed_folder_handle.clear(); } else { - throw std::invalid_argument("You must declare both NB_VERTICES AND NB_STREAMLINES"); + throw TrxArgumentError("You must declare both NB_VERTICES AND NB_STREAMLINES"); } json::object header_obj; @@ -181,17 +231,7 @@ std::unique_ptr> _initialize_empty_trx(int nb_streamlines, int nb_ve trx->streamlines = std::make_unique>(); trx->streamlines->mmap_pos = trx::_create_memmap(positions_filename, shape, "w+", positions_dtype); - // TODO: find a better way to get the dtype than using all these switch cases. - if (positions_dtype.compare("float16") == 0) { - new (&(trx->streamlines->_data)) Map>( - reinterpret_cast(trx->streamlines->mmap_pos.data()), std::get<0>(shape), std::get<1>(shape)); - } else if (positions_dtype.compare("float32") == 0) { - new (&(trx->streamlines->_data)) Map>( - reinterpret_cast(trx->streamlines->mmap_pos.data()), std::get<0>(shape), std::get<1>(shape)); - } else { - new (&(trx->streamlines->_data)) Map>( - reinterpret_cast(trx->streamlines->mmap_pos.data()), std::get<0>(shape), std::get<1>(shape)); - } + trx::detail::remap(trx->streamlines->_data, trx->streamlines->mmap_pos.data(), shape); std::string offsets_filename(tmp_dir); offsets_filename += "/offsets." + offsets_dtype; @@ -199,8 +239,7 @@ std::unique_ptr> _initialize_empty_trx(int nb_streamlines, int nb_ve std::tuple shape_off = std::make_tuple(nb_streamlines + 1, 1); trx->streamlines->mmap_off = trx::_create_memmap(offsets_filename, shape_off, "w+", offsets_dtype); - new (&(trx->streamlines->_offsets)) Map>( - reinterpret_cast(trx->streamlines->mmap_off.data()), std::get<0>(shape_off), std::get<1>(shape_off)); + trx::detail::remap(trx->streamlines->_offsets, trx->streamlines->mmap_off.data(), shape_off); trx->streamlines->_lengths.resize(nb_streamlines); trx->streamlines->_lengths.setZero(); @@ -210,19 +249,11 @@ std::unique_ptr> _initialize_empty_trx(int nb_streamlines, int nb_ve std::string dps_dirname; if (init_as->data_per_vertex.size() > 0) { dpv_dirname = tmp_dir + "/dpv/"; - std::error_code ec; - trx::fs::create_directories(dpv_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpv_dirname); - } + mkdir_or_throw(dpv_dirname); } if (init_as->data_per_streamline.size() > 0) { dps_dirname = tmp_dir + "/dps/"; - std::error_code ec; - trx::fs::create_directories(dps_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dps_dirname); - } + mkdir_or_throw(dps_dirname); } for (auto const &x : init_as->data_per_vertex) { @@ -245,21 +276,11 @@ std::unique_ptr> _initialize_empty_trx(int nb_streamlines, int nb_ve std::tuple dpv_shape = std::make_tuple(rows, cols); trx->data_per_vertex[x.first] = std::make_unique>(); trx->data_per_vertex[x.first]->mmap_pos = trx::_create_memmap(dpv_filename, dpv_shape, "w+", dpv_dtype); - if (dpv_dtype.compare("float16") == 0) { - new (&(trx->data_per_vertex[x.first]->_data)) Map>( - reinterpret_cast(trx->data_per_vertex[x.first]->mmap_pos.data()), rows, cols); - } else if (dpv_dtype.compare("float32") == 0) { - new (&(trx->data_per_vertex[x.first]->_data)) Map>( - reinterpret_cast(trx->data_per_vertex[x.first]->mmap_pos.data()), rows, cols); - } else { - new (&(trx->data_per_vertex[x.first]->_data)) Map>( - reinterpret_cast(trx->data_per_vertex[x.first]->mmap_pos.data()), rows, cols); - } + trx::detail::remap(trx->data_per_vertex[x.first]->_data, trx->data_per_vertex[x.first]->mmap_pos.data(), rows, + cols); - new (&(trx->data_per_vertex[x.first]->_offsets)) - Map>(trx->streamlines->_offsets.data(), - int(trx->streamlines->_offsets.rows()), - int(trx->streamlines->_offsets.cols())); + trx::detail::remap(trx->data_per_vertex[x.first]->_offsets, trx->streamlines->_offsets.data(), + int(trx->streamlines->_offsets.rows()), int(trx->streamlines->_offsets.cols())); trx->data_per_vertex[x.first]->_lengths = trx->streamlines->_lengths; } @@ -285,16 +306,8 @@ std::unique_ptr> _initialize_empty_trx(int nb_streamlines, int nb_ve trx->data_per_streamline[x.first]->mmap = trx::_create_memmap(dps_filename, dps_shape, std::string("w+"), dps_dtype); - if (dps_dtype.compare("float16") == 0) { - new (&(trx->data_per_streamline[x.first]->_matrix)) Map>( - reinterpret_cast(trx->data_per_streamline[x.first]->mmap.data()), rows, cols); - } else if (dps_dtype.compare("float32") == 0) { - new (&(trx->data_per_streamline[x.first]->_matrix)) Map>( - reinterpret_cast(trx->data_per_streamline[x.first]->mmap.data()), rows, cols); - } else { - new (&(trx->data_per_streamline[x.first]->_matrix)) Map>( - reinterpret_cast(trx->data_per_streamline[x.first]->mmap.data()), rows, cols); - } + trx::detail::remap(trx->data_per_streamline[x.first]->_matrix, trx->data_per_streamline[x.first]->mmap.data(), + rows, cols); } } @@ -317,7 +330,8 @@ TrxFile
::_create_trx_from_pointer(json header, std::string filename; - // TODO: Fix this hack of iterating through dictionary in reverse to get main files read first + // Iterate in reverse so that "positions" and "offsets" (which sort after "dpv"/"dps"/"groups") + // are processed first, before DPS/DPV entries that depend on them being initialized. for (auto x = dict_pointer_size.rbegin(); x != dict_pointer_size.rend(); ++x) { std::string elem_filename = x->first; @@ -327,38 +341,18 @@ TrxFile
::_create_trx_from_pointer(json header, filename = elem_filename; } - trx::fs::path elem_path(elem_filename); - trx::fs::path folder_path = elem_path.parent_path(); - std::string folder; - if (!root.empty()) { - trx::fs::path rel_path = elem_path.lexically_relative(trx::fs::path(root)); - std::string rel_str = rel_path.string(); - if (!rel_str.empty() && rel_str.rfind("..", 0) != 0) { - folder = rel_path.parent_path().string(); - } else { - folder = folder_path.string(); - } - } else { - folder = folder_path.string(); - } - if (folder == ".") { - folder.clear(); - } + std::string folder = folder_from_path(elem_filename, root); - // _split_ext_with_dimensionality - std::tuple base_tuple = trx::detail::_split_ext_with_dimensionality(elem_filename); - std::string base(std::get<0>(base_tuple)); - int dim = std::get<1>(base_tuple); - std::string ext(std::get<2>(base_tuple)); + auto [base, dim, ext] = trx::detail::_split_ext_with_dimensionality(elem_filename); long long mem_adress = std::get<0>(x->second); long long size = std::get<1>(x->second); - if (base.compare("positions") == 0 && (folder.compare("") == 0 || folder.compare(".") == 0)) { + if (base == "positions" && (folder.empty() || folder == ".")) { const auto nb_vertices = static_cast(trx->header["NB_VERTICES"].int_value()); const auto expected = nb_vertices * 3; if (size != expected || dim != 3) { - throw std::invalid_argument("Wrong data size/dimensionality: size=" + std::to_string(size) + + throw TrxFormatError("Wrong data size/dimensionality: size=" + std::to_string(size) + " expected=" + std::to_string(expected) + " dim=" + std::to_string(dim) + " filename=" + elem_filename); } @@ -367,25 +361,14 @@ TrxFile
::_create_trx_from_pointer(json header, trx->streamlines->mmap_pos = trx::_create_memmap(filename, shape, "r+", ext.substr(1, ext.size() - 1), mem_adress); - // TODO: find a better way to get the dtype than using all these switch cases. Also - // refactor into function as per specifications, positions can only be floats - if (ext.compare("float16") == 0) { - new (&(trx->streamlines->_data)) Map>( - reinterpret_cast(trx->streamlines->mmap_pos.data()), std::get<0>(shape), std::get<1>(shape)); - } else if (ext.compare("float32") == 0) { - new (&(trx->streamlines->_data)) Map>( - reinterpret_cast(trx->streamlines->mmap_pos.data()), std::get<0>(shape), std::get<1>(shape)); - } else { - new (&(trx->streamlines->_data)) Map>( - reinterpret_cast(trx->streamlines->mmap_pos.data()), std::get<0>(shape), std::get<1>(shape)); - } + trx::detail::remap(trx->streamlines->_data, trx->streamlines->mmap_pos.data(), shape); } - else if (base.compare("offsets") == 0 && (folder.compare("") == 0 || folder.compare(".") == 0)) { + else if (base == "offsets" && (folder.empty() || folder == ".")) { const auto nb_streamlines = static_cast(trx->header["NB_STREAMLINES"].int_value()); const auto expected = nb_streamlines + 1; if (size != expected || dim != 1) { - throw std::invalid_argument("Wrong offsets size/dimensionality: size=" + std::to_string(size) + + throw TrxFormatError("Wrong offsets size/dimensionality: size=" + std::to_string(size) + " expected=" + std::to_string(expected) + " dim=" + std::to_string(dim) + " filename=" + elem_filename); } @@ -394,18 +377,16 @@ TrxFile
::_create_trx_from_pointer(json header, std::tuple shape = std::make_tuple(nb_str + 1, 1); trx->streamlines->mmap_off = trx::_create_memmap(filename, shape, "r+", ext, mem_adress); - if (ext.compare("uint64") == 0) { - new (&(trx->streamlines->_offsets)) Map>( - reinterpret_cast(trx->streamlines->mmap_off.data()), std::get<0>(shape), std::get<1>(shape)); - } else if (ext.compare("uint32") == 0) { + if (ext == "uint64") { + trx::detail::remap(trx->streamlines->_offsets, trx->streamlines->mmap_off.data(), shape); + } else if (ext == "uint32") { trx->streamlines->_offsets_owned.resize(std::get<0>(shape)); - auto *src = reinterpret_cast(trx->streamlines->mmap_off.data()); + auto *src = reinterpret_cast(trx->streamlines->mmap_off.data()); // NOLINT for (int i = 0; i < std::get<0>(shape); ++i) trx->streamlines->_offsets_owned[static_cast(i)] = static_cast(src[i]); - new (&(trx->streamlines->_offsets)) Map>( - trx->streamlines->_offsets_owned.data(), std::get<0>(shape), std::get<1>(shape)); + trx::detail::remap(trx->streamlines->_offsets, trx->streamlines->_offsets_owned.data(), shape); } else { - throw std::invalid_argument("Unsupported offsets datatype: " + ext); + throw TrxDTypeError("Unsupported offsets datatype: " + ext); } Matrix offsets = trx->streamlines->_offsets; @@ -413,69 +394,33 @@ TrxFile
::_create_trx_from_pointer(json header, trx::detail::_compute_lengths(offsets, static_cast(trx->header["NB_VERTICES"].int_value())); } - else if (folder.compare("dps") == 0) { + else if (folder == "dps") { std::tuple shape; trx->data_per_streamline[base] = std::make_unique>(); int nb_scalar = size / static_cast(trx->header["NB_STREAMLINES"].int_value()); if (size % static_cast(trx->header["NB_STREAMLINES"].int_value()) != 0 || nb_scalar != dim) { - - throw std::invalid_argument("Wrong dps size/dimensionality"); + throw TrxFormatError("Wrong dps size/dimensionality"); } else { shape = std::make_tuple(static_cast(trx->header["NB_STREAMLINES"].int_value()), nb_scalar); } trx->data_per_streamline[base]->mmap = trx::_create_memmap(filename, shape, "r+", ext, mem_adress); - - if (ext.compare("float16") == 0) { - new (&(trx->data_per_streamline[base]->_matrix)) - Map>(reinterpret_cast(trx->data_per_streamline[base]->mmap.data()), - std::get<0>(shape), - std::get<1>(shape)); - } else if (ext.compare("float32") == 0) { - new (&(trx->data_per_streamline[base]->_matrix)) - Map>(reinterpret_cast(trx->data_per_streamline[base]->mmap.data()), - std::get<0>(shape), - std::get<1>(shape)); - } else { - new (&(trx->data_per_streamline[base]->_matrix)) Map>( - reinterpret_cast(trx->data_per_streamline[base]->mmap.data()), - std::get<0>(shape), - std::get<1>(shape)); - } + trx::detail::remap(trx->data_per_streamline[base]->_matrix, trx->data_per_streamline[base]->mmap.data(), shape); } - else if (folder.compare("dpv") == 0) { + else if (folder == "dpv") { std::tuple shape; trx->data_per_vertex[base] = std::make_unique>(); int nb_scalar = size / static_cast(trx->header["NB_VERTICES"].int_value()); if (size % static_cast(trx->header["NB_VERTICES"].int_value()) != 0 || nb_scalar != dim) { - - throw std::invalid_argument("Wrong dpv size/dimensionality"); + throw TrxFormatError("Wrong dpv size/dimensionality"); } else { shape = std::make_tuple(static_cast(trx->header["NB_VERTICES"].int_value()), nb_scalar); } trx->data_per_vertex[base]->mmap_pos = trx::_create_memmap(filename, shape, "r+", ext, mem_adress); - - if (ext.compare("float16") == 0) { - new (&(trx->data_per_vertex[base]->_data)) - Map>(reinterpret_cast(trx->data_per_vertex[base]->mmap_pos.data()), - std::get<0>(shape), - std::get<1>(shape)); - } else if (ext.compare("float32") == 0) { - new (&(trx->data_per_vertex[base]->_data)) - Map>(reinterpret_cast(trx->data_per_vertex[base]->mmap_pos.data()), - std::get<0>(shape), - std::get<1>(shape)); - } else { - new (&(trx->data_per_vertex[base]->_data)) Map>( - reinterpret_cast(trx->data_per_vertex[base]->mmap_pos.data()), - std::get<0>(shape), - std::get<1>(shape)); - } - - new (&(trx->data_per_vertex[base]->_offsets)) - Map>(trx->streamlines->_offsets.data(), std::get<0>(shape), std::get<1>(shape)); + trx::detail::remap(trx->data_per_vertex[base]->_data, trx->data_per_vertex[base]->mmap_pos.data(), shape); + trx::detail::remap(trx->data_per_vertex[base]->_offsets, trx->streamlines->_offsets.data(), shape); trx->data_per_vertex[base]->_lengths = trx->streamlines->_lengths; } @@ -484,7 +429,7 @@ TrxFile
::_create_trx_from_pointer(json header, if (size != dim) { - throw std::invalid_argument("Wrong dpg size/dimensionality"); + throw TrxFormatError("Wrong dpg size/dimensionality"); } else { shape = std::make_tuple(1, static_cast(size)); } @@ -495,188 +440,142 @@ TrxFile
::_create_trx_from_pointer(json header, trx->data_per_group[sub_folder][data_name] = std::make_unique>(); trx->data_per_group[sub_folder][data_name]->mmap = trx::_create_memmap(filename, shape, "r+", ext, mem_adress); - if (ext.compare("float16") == 0) { - new (&(trx->data_per_group[sub_folder][data_name]->_matrix)) Map>( - reinterpret_cast(trx->data_per_group[sub_folder][data_name]->mmap.data()), - std::get<0>(shape), - std::get<1>(shape)); - } else if (ext.compare("float32") == 0) { - new (&(trx->data_per_group[sub_folder][data_name]->_matrix)) Map>( - reinterpret_cast(trx->data_per_group[sub_folder][data_name]->mmap.data()), - std::get<0>(shape), - std::get<1>(shape)); - } else { - new (&(trx->data_per_group[sub_folder][data_name]->_matrix)) Map>( - reinterpret_cast(trx->data_per_group[sub_folder][data_name]->mmap.data()), - std::get<0>(shape), - std::get<1>(shape)); - } + trx::detail::remap(trx->data_per_group[sub_folder][data_name]->_matrix, + trx->data_per_group[sub_folder][data_name]->mmap.data(), shape); } - else if (folder.compare("groups") == 0) { + else if (folder == "groups") { std::tuple shape; if (dim != 1) { - throw std::invalid_argument("Wrong group dimensionality"); + throw TrxFormatError("Wrong group dimensionality"); } else { shape = std::make_tuple(static_cast(size), 1); } trx->groups[base] = std::make_unique>(); trx->groups[base]->mmap = trx::_create_memmap(filename, shape, "r+", ext, mem_adress); - new (&(trx->groups[base]->_matrix)) Map>( - reinterpret_cast(trx->groups[base]->mmap.data()), std::get<0>(shape), std::get<1>(shape)); + trx::detail::remap(trx->groups[base]->_matrix, trx->groups[base]->mmap.data(), shape); } else { - throw std::invalid_argument("Entry is not part of a valid TRX structure: " + elem_filename); + throw TrxFormatError("Entry is not part of a valid TRX structure: " + elem_filename); } } if (trx->streamlines->_data.size() == 0 || trx->streamlines->_offsets.size() == 0) { - throw std::invalid_argument("Missing essential data."); + throw TrxFormatError("Missing essential data."); } return trx; } -// TODO: Major refactoring template std::unique_ptr> TrxFile
::deepcopy() { if (!this->streamlines || this->streamlines->_data.size() == 0 || this->streamlines->_offsets.size() == 0) { auto empty_copy = std::make_unique>(); empty_copy->header = this->header; return empty_copy; } - std::string tmp_dir = make_temp_dir("trx"); - std::string header = tmp_dir + SEPARATOR + "header.json"; - std::ofstream out_json(header); - - // TODO: Definitely a better way to deepcopy + // Determine effective counts (handle sliced/non-copy-safe data) json tmp_header = this->header; - - auto to_dump = std::make_unique>(); - // TODO: Verify that this is indeed a deep copy - new (&(to_dump->_data)) Matrix(this->streamlines->_data); - new (&(to_dump->_offsets)) Matrix(this->streamlines->_offsets); - new (&(to_dump->_lengths)) Matrix(this->streamlines->_lengths); - + int nb_streamlines, nb_vertices; if (!this->_copy_safe) { - const int nb_streamlines = to_dump->_offsets.size() > 0 ? static_cast(to_dump->_offsets.size() - 1) : 0; - const int nb_vertices = static_cast(to_dump->_data.size() / 3); + nb_streamlines = static_cast(this->num_streamlines()); + nb_vertices = static_cast(this->streamlines->_data.size() / 3); tmp_header = _json_set(tmp_header, "NB_STREAMLINES", nb_streamlines); tmp_header = _json_set(tmp_header, "NB_VERTICES", nb_vertices); - } - // Ensure sentinel is correct before persisting - if (to_dump->_offsets.size() > 0) { - to_dump->_offsets(to_dump->_offsets.size() - 1) = static_cast(tmp_header["NB_VERTICES"].int_value()); - } - if (out_json.is_open()) { - out_json << tmp_header.dump() << std::endl; - out_json.close(); + } else { + nb_streamlines = tmp_header["NB_STREAMLINES"].int_value(); + nb_vertices = tmp_header["NB_VERTICES"].int_value(); } - std::string pos_rootfn = tmp_dir + SEPARATOR + "positions"; - std::string positions_filename = _generate_filename_from_data(to_dump->_data, pos_rootfn); + // Allocate a fresh TrxFile with memory-mapped storage + auto copy = _initialize_empty_trx
(nb_streamlines, nb_vertices, this); - write_binary(positions_filename, to_dump->_data); + // Copy header + copy->header = tmp_header; - std::string off_rootfn = tmp_dir + SEPARATOR + "offsets"; - std::string offsets_filename = _generate_filename_from_data(to_dump->_offsets, off_rootfn); + // Copy positions + copy->streamlines->_data = this->streamlines->_data; - write_binary(offsets_filename, to_dump->_offsets); + // Copy offsets + copy->streamlines->_offsets = this->streamlines->_offsets; + // Ensure sentinel is correct + if (copy->streamlines->_offsets.size() > 0) { + copy->streamlines->_offsets(copy->streamlines->_offsets.size() - 1) = static_cast(nb_vertices); + } - if (this->data_per_vertex.size() > 0) { - std::string dpv_dirname = tmp_dir + SEPARATOR + "dpv" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(dpv_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpv_dirname); - } + // Copy lengths + copy->streamlines->_lengths = this->streamlines->_lengths; + + // Copy DPS + for (auto const &kv : this->data_per_streamline) { + auto it = copy->data_per_streamline.find(kv.first); + if (it != copy->data_per_streamline.end()) { + it->second->_matrix = kv.second->_matrix; } - for (auto const &x : this->data_per_vertex) { - Matrix dpv_todump = x.second->_data; - std::string dpv_filename = dpv_dirname + x.first; - dpv_filename = _generate_filename_from_data(dpv_todump, dpv_filename); + } - write_binary(dpv_filename, dpv_todump); + // Copy DPV + for (auto const &kv : this->data_per_vertex) { + auto it = copy->data_per_vertex.find(kv.first); + if (it != copy->data_per_vertex.end()) { + it->second->_data = kv.second->_data; + it->second->_offsets = kv.second->_offsets; + it->second->_lengths = kv.second->_lengths; } } - if (this->data_per_streamline.size() > 0) { - std::string dps_dirname = tmp_dir + SEPARATOR + "dps" + SEPARATOR; + // Copy groups (not covered by _initialize_empty_trx) + std::string tmp_dir = copy->_uncompressed_folder_handle; + if (!this->groups.empty()) { + std::string groups_dirname = tmp_dir + SEPARATOR + "groups" + SEPARATOR; { std::error_code ec; - trx::fs::create_directories(dps_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dps_dirname); - } + trx::fs::create_directories(groups_dirname, ec); } - for (auto const &x : this->data_per_streamline) { - Matrix dps_todump = x.second->_matrix; - std::string dps_filename = dps_dirname + x.first; - dps_filename = _generate_filename_from_data(dps_todump, dps_filename); + for (auto const &kv : this->groups) { + std::string group_dtype = dtype_from_scalar(); + int rows = static_cast(kv.second->_matrix.rows()); + int cols = static_cast(kv.second->_matrix.cols()); + std::string group_filename = groups_dirname + kv.first; + group_filename = _generate_filename_from_data(kv.second->_matrix, group_filename); - write_binary(dps_filename, dps_todump); + std::tuple group_shape = std::make_tuple(rows, cols); + copy->groups[kv.first] = std::make_unique>(); + copy->groups[kv.first]->mmap = _create_memmap(group_filename, group_shape, "w+", group_dtype); + trx::detail::remap(copy->groups[kv.first]->_matrix, copy->groups[kv.first]->mmap.data(), rows, cols); + copy->groups[kv.first]->_matrix = kv.second->_matrix; } } - if (this->groups.size() > 0) { - std::string groups_dirname = tmp_dir + SEPARATOR + "groups" + SEPARATOR; + // Copy DPG (not covered by _initialize_empty_trx) + for (auto const &group_kv : this->data_per_group) { + std::string dpg_dirname = tmp_dir + SEPARATOR + "dpg" + SEPARATOR; + std::string dpg_subdirname = dpg_dirname + group_kv.first; { std::error_code ec; - trx::fs::create_directories(groups_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + groups_dirname); - } + trx::fs::create_directories(dpg_subdirname, ec); } + for (auto const &field : group_kv.second) { + std::string dpg_dtype = dtype_from_scalar
(); + int rows = static_cast(field.second->_matrix.rows()); + int cols = static_cast(field.second->_matrix.cols()); + std::string dpg_filename = dpg_subdirname + SEPARATOR + field.first; + dpg_filename = _generate_filename_from_data(field.second->_matrix, dpg_filename); - for (auto const &x : this->groups) { - Matrix group_todump = x.second->_matrix; - std::string group_filename = groups_dirname + x.first; - group_filename = _generate_filename_from_data(group_todump, group_filename); - - write_binary(group_filename, group_todump); - - if (this->data_per_group.find(x.first) == this->data_per_group.end()) { - continue; - } - - for (auto const &y : this->data_per_group[x.first]) { - std::string dpg_dirname = tmp_dir + SEPARATOR + "dpg" + SEPARATOR; - std::string dpg_subdirname = dpg_dirname + x.first; - std::error_code ec; - if (!trx::fs::exists(dpg_dirname, ec)) { - ec.clear(); - trx::fs::create_directories(dpg_dirname, ec); - } - if (ec) { - throw std::runtime_error("Could not create directory " + dpg_dirname); - } - ec.clear(); - if (!trx::fs::exists(dpg_subdirname, ec)) { - ec.clear(); - trx::fs::create_directories(dpg_subdirname, ec); - } - if (ec) { - throw std::runtime_error("Could not create directory " + dpg_subdirname); - } - - Matrix dpg_todump = this->data_per_group[x.first][y.first]->_matrix; - std::string dpg_filename = dpg_subdirname + SEPARATOR + y.first; - dpg_filename = _generate_filename_from_data(dpg_todump, dpg_filename); - - write_binary(dpg_filename, dpg_todump); - } + std::tuple dpg_shape = std::make_tuple(rows, cols); + copy->data_per_group[group_kv.first][field.first] = std::make_unique>(); + copy->data_per_group[group_kv.first][field.first]->mmap = + _create_memmap(dpg_filename, dpg_shape, "w+", dpg_dtype); + trx::detail::remap(copy->data_per_group[group_kv.first][field.first]->_matrix, + copy->data_per_group[group_kv.first][field.first]->mmap.data(), rows, cols); + copy->data_per_group[group_kv.first][field.first]->_matrix = field.second->_matrix; } } - auto copy_trx = TrxFile
::load_from_directory(tmp_dir); - copy_trx->_uncompressed_folder_handle = tmp_dir; - copy_trx->_owns_uncompressed_folder = true; - - return copy_trx; + return copy; } -// TODO: verify that this function is actually necessary (there should not be preallocation zeros -// afaik) +/// Compute the used range in a preallocated TrxFile by finding the last non-zero length. +/// Returns (nb_streamlines_used, nb_vertices_used). template std::tuple TrxFile
::_get_real_len() { if (this->streamlines->_lengths.size() == 0) return std::make_tuple(0, 0); @@ -733,10 +632,9 @@ TrxFile
::_copy_fixed_arrays_from(TrxFile
*trx, int strs_start, int pts_s this->data_per_vertex[x.first]->_data.block( pts_start, 0, curr_pts_len, this->data_per_vertex[x.first]->_data.cols()) = trx->data_per_vertex[x.first]->_data.block(0, 0, curr_pts_len, trx->data_per_vertex[x.first]->_data.cols()); - new (&(this->data_per_vertex[x.first]->_offsets)) - Map>(trx->data_per_vertex[x.first]->_offsets.data(), - trx->data_per_vertex[x.first]->_offsets.rows(), - trx->data_per_vertex[x.first]->_offsets.cols()); + trx::detail::remap(this->data_per_vertex[x.first]->_offsets, trx->data_per_vertex[x.first]->_offsets.data(), + static_cast(trx->data_per_vertex[x.first]->_offsets.rows()), + static_cast(trx->data_per_vertex[x.first]->_offsets.cols())); this->data_per_vertex[x.first]->_lengths = trx->data_per_vertex[x.first]->_lengths; } @@ -792,7 +690,7 @@ template // resize is a no-op. void TrxFile
::resize(int nb_streamlines, int nb_vertices, bool delete_dpg) { if (!this->_copy_safe) { - throw std::invalid_argument("Cannot resize a sliced dataset."); + throw TrxArgumentError("Cannot resize a sliced dataset."); } std::tuple sp_end = this->_get_real_len(); @@ -831,13 +729,7 @@ void TrxFile
::resize(int nb_streamlines, int nb_vertices, bool delete_dpg) { if (this->groups.size() > 0) { std::string group_dir = tmp_dir + SEPARATOR + "groups" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(group_dir, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + group_dir); - } - } + mkdir_or_throw(group_dir); for (auto const &x : this->groups) { std::string group_dtype = dtype_from_scalar(); @@ -863,10 +755,7 @@ void TrxFile
::resize(int nb_streamlines, int nb_vertices, bool delete_dpg) { trx->groups[x.first] = std::make_unique>(); trx->groups[x.first]->mmap = trx::_create_memmap(group_name, group_shape, "w+", group_dtype); - new (&(trx->groups[x.first]->_matrix)) - Map>(reinterpret_cast(trx->groups[x.first]->mmap.data()), - std::get<0>(group_shape), - std::get<1>(group_shape)); + trx::detail::remap(trx->groups[x.first]->_matrix, trx->groups[x.first]->mmap.data(), group_shape); // update values for (int i = 0; i < trx->groups[x.first]->_matrix.rows(); ++i) { @@ -883,25 +772,12 @@ void TrxFile
::resize(int nb_streamlines, int nb_vertices, bool delete_dpg) { } if (this->data_per_group.size() > 0) { - // really need to refactor all these mkdirs std::string dpg_dir = tmp_dir + SEPARATOR + "dpg" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(dpg_dir, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpg_dir); - } - } + mkdir_or_throw(dpg_dir); for (auto const &x : this->data_per_group) { std::string dpg_subdir = dpg_dir + x.first; - { - std::error_code ec; - trx::fs::create_directories(dpg_subdir, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpg_subdir); - } - } + mkdir_or_throw(dpg_subdir); if (trx->data_per_group.find(x.first) == trx->data_per_group.end()) { trx->data_per_group.emplace(x.first, std::map>>{}); @@ -922,10 +798,8 @@ void TrxFile
::resize(int nb_streamlines, int nb_vertices, bool delete_dpg) { } trx->data_per_group[x.first][y.first]->mmap = _create_memmap(dpg_filename, dpg_shape, "w+", dpg_dtype); - new (&(trx->data_per_group[x.first][y.first]->_matrix)) Map>( - reinterpret_cast(trx->data_per_group[x.first][y.first]->mmap.data()), - std::get<0>(dpg_shape), - std::get<1>(dpg_shape)); + trx::detail::remap(trx->data_per_group[x.first][y.first]->_matrix, + trx->data_per_group[x.first][y.first]->mmap.data(), dpg_shape); // update values for (int i = 0; i < trx->data_per_group[x.first][y.first]->_matrix.rows(); ++i) { @@ -942,13 +816,12 @@ void TrxFile
::resize(int nb_streamlines, int nb_vertices, bool delete_dpg) { template std::unique_ptr> TrxFile
::load_from_zip(const std::string &filename) { int errorp = 0; - zip_t *zf = open_zip_for_read(filename, errorp); - if (zf == nullptr) { - throw std::runtime_error("Could not open zip file: " + filename); + detail::ZipArchive zf(open_zip_for_read(filename, errorp)); + if (!zf) { + throw TrxIOError("Could not open zip file: " + filename); } - std::string temp_dir = extract_zip_to_directory(zf); - zip_close(zf); + std::string temp_dir = extract_zip_to_directory(zf.get()); auto trx = TrxFile
::load_from_directory(temp_dir); trx->_uncompressed_folder_handle = temp_dir; @@ -1003,14 +876,14 @@ template std::unique_ptr> TrxFile
::load_from_direc detail += "]"; } } - throw std::runtime_error(detail); + throw TrxIOError(detail); } std::string jstream((std::istreambuf_iterator(header_file)), std::istreambuf_iterator()); header_file.close(); std::string err; json header = json::parse(jstream, err); if (!err.empty()) { - throw std::runtime_error("Failed to parse header.json: " + err); + throw TrxIOError("Failed to parse header.json: " + err); } std::map> files_pointer_size; @@ -1025,7 +898,7 @@ template std::unique_ptr> TrxFile
::load_from_direc template std::unique_ptr> TrxFile
::load(const std::string &path) { trx::fs::path input(path); if (!trx::fs::exists(input)) { - throw std::runtime_error("Input path does not exist: " + path); + throw TrxIOError("Input path does not exist: " + path); } std::error_code ec; if (trx::fs::is_directory(input, ec) && !ec) { @@ -1078,15 +951,15 @@ template void TrxFile
::save(const std::string &filename, zip_u template void TrxFile
::normalize_for_save() { if (!this->streamlines) { - throw std::runtime_error("Cannot normalize TRX without streamline data"); + throw TrxFormatError("Cannot normalize TRX without streamline data"); } if (this->streamlines->_offsets.size() == 0) { - throw std::runtime_error("Cannot normalize TRX without offsets data"); + throw TrxFormatError("Cannot normalize TRX without offsets data"); } const size_t offsets_count = static_cast(this->streamlines->_offsets.size()); if (offsets_count < 1) { - throw std::runtime_error("Invalid offsets array"); + throw TrxFormatError("Invalid offsets array"); } const size_t total_streamlines = offsets_count - 1; const uint64_t data_rows = static_cast(this->streamlines->_data.rows()); @@ -1104,73 +977,69 @@ template void TrxFile
::normalize_for_save() { const uint64_t used_vertices = static_cast(this->streamlines->_offsets(static_cast(used_streamlines))); if (used_vertices > data_rows) { - throw std::runtime_error("TRX offsets exceed positions row count"); + throw TrxFormatError("TRX offsets exceed positions row count"); } if (used_vertices > static_cast(std::numeric_limits::max()) || used_streamlines > static_cast(std::numeric_limits::max())) { - throw std::runtime_error("TRX normalize_for_save exceeds supported int range"); + throw TrxFormatError("TRX normalize_for_save exceeds supported int range"); } if (used_streamlines < total_streamlines || used_vertices < data_rows) { this->resize(static_cast(used_streamlines), static_cast(used_vertices)); } - const size_t normalized_streamlines = static_cast(this->streamlines->_offsets.size()) - 1; + const size_t normalized_streamlines = this->num_streamlines(); for (size_t i = 0; i < normalized_streamlines; ++i) { const uint64_t curr = static_cast(this->streamlines->_offsets(static_cast(i))); const uint64_t next = static_cast(this->streamlines->_offsets(static_cast(i + 1))); if (next < curr) { - throw std::runtime_error("TRX offsets must be monotonically increasing"); + throw TrxFormatError("TRX offsets must be monotonically increasing"); } const uint64_t diff = next - curr; if (diff > static_cast(std::numeric_limits::max())) { - throw std::runtime_error("TRX streamline length exceeds uint32 range"); + throw TrxFormatError("TRX streamline length exceeds uint32 range"); } this->streamlines->_lengths(static_cast(i)) = static_cast(diff); } - const uint64_t sentinel = static_cast( - this->streamlines->_offsets(static_cast(this->streamlines->_offsets.size() - 1))); this->header = _json_set(this->header, "NB_STREAMLINES", static_cast(normalized_streamlines)); - this->header = _json_set(this->header, "NB_VERTICES", static_cast(sentinel)); + this->header = _json_set(this->header, "NB_VERTICES", static_cast(this->num_vertices())); } template void TrxFile
::save(const std::string &filename, const TrxSaveOptions &options) { std::string ext = get_ext(filename); if (ext.size() > 0 && ext != "zip" && ext != "trx") { - throw std::invalid_argument("Unsupported extension: " + ext); + throw TrxDTypeError("Unsupported extension: " + ext); } TrxFile
*save_trx = this; if (!save_trx->streamlines || save_trx->streamlines->_offsets.size() == 0) { - throw std::runtime_error("Cannot save TRX without offsets data"); + throw TrxFormatError("Cannot save TRX without offsets data"); } if (save_trx->header["NB_STREAMLINES"].is_number()) { const auto nb_streamlines = static_cast(save_trx->header["NB_STREAMLINES"].int_value()); if (save_trx->streamlines->_offsets.size() != static_cast(nb_streamlines + 1)) { - throw std::runtime_error("TRX offsets size does not match NB_STREAMLINES"); + throw TrxFormatError("TRX offsets size does not match NB_STREAMLINES"); } } if (save_trx->header["NB_VERTICES"].is_number()) { const auto nb_vertices = static_cast(save_trx->header["NB_VERTICES"].int_value()); - const auto last = - static_cast(save_trx->streamlines->_offsets(save_trx->streamlines->_offsets.size() - 1)); + const auto last = static_cast(save_trx->num_vertices()); if (last != nb_vertices) { - throw std::runtime_error("TRX offsets sentinel does not match NB_VERTICES"); + throw TrxFormatError("TRX offsets sentinel does not match NB_VERTICES"); } } for (Eigen::Index i = 1; i < save_trx->streamlines->_offsets.size(); ++i) { if (save_trx->streamlines->_offsets(i) < save_trx->streamlines->_offsets(i - 1)) { - throw std::runtime_error("TRX offsets must be monotonically increasing"); + throw TrxFormatError("TRX offsets must be monotonically increasing"); } } if (save_trx->streamlines->_data.size() > 0) { - const auto last = - static_cast(save_trx->streamlines->_offsets(save_trx->streamlines->_offsets.size() - 1)); + const auto last = static_cast(save_trx->num_vertices()); if (last != static_cast(save_trx->streamlines->_data.rows())) { - throw std::runtime_error("TRX positions row count does not match offsets sentinel"); + throw TrxFormatError("TRX positions row count does not match offsets sentinel"); } } std::string tmp_dir_name = save_trx->_uncompressed_folder_handle; @@ -1179,7 +1048,7 @@ template void TrxFile
::save(const std::string &filename, const const std::string header_path = tmp_dir_name + SEPARATOR + "header.json"; std::ofstream out_json(header_path, std::ios::out | std::ios::trunc); if (!out_json.is_open()) { - throw std::runtime_error("Failed to write header.json to: " + header_path); + throw TrxIOError("Failed to write header.json to: " + header_path); } out_json << save_trx->header.dump() << std::endl; out_json.close(); @@ -1221,44 +1090,37 @@ template void TrxFile
::save(const std::string &filename, const } int errorp; - zip_t *zf; - if ((zf = zip_open(filename.c_str(), ZIP_CREATE + ZIP_TRUNCATE, &errorp)) == nullptr) { - throw std::runtime_error("Could not open archive " + filename + ": " + strerror(errorp)); - } else { - zip_from_folder(zf, tmp_dir_name, tmp_dir_name, options.compression_standard, nullptr); - if (zip_close(zf) != 0) { - throw std::runtime_error("Unable to close archive " + filename + ": " + zip_strerror(zf)); - } + detail::ZipArchive zf(zip_open(filename.c_str(), ZIP_CREATE + ZIP_TRUNCATE, &errorp)); + if (!zf) { + throw TrxIOError("Could not open archive " + filename + ": " + strerror(errorp)); } + zip_from_folder(zf.get(), tmp_dir_name, tmp_dir_name, options.compression_standard, nullptr); + zf.commit(filename); } else { std::error_code ec; if (!trx::fs::exists(tmp_dir_name, ec) || !trx::fs::is_directory(tmp_dir_name, ec)) { - throw std::runtime_error("Temporary TRX directory does not exist: " + tmp_dir_name); + throw TrxIOError("Temporary TRX directory does not exist: " + tmp_dir_name); } if (trx::fs::exists(filename, ec) && trx::fs::is_directory(filename, ec)) { if (!options.overwrite_existing) { - throw std::runtime_error("Output directory already exists: " + filename); + throw TrxIOError("Output directory already exists: " + filename); } if (rm_dir(filename) != 0) { - throw std::runtime_error("Could not remove existing directory " + filename); + throw TrxIOError("Could not remove existing directory " + filename); } } trx::fs::path dest_path(filename); if (dest_path.has_parent_path()) { - std::error_code ec; - trx::fs::create_directories(dest_path.parent_path(), ec); - if (ec) { - throw std::runtime_error("Could not create output parent directory: " + dest_path.parent_path().string()); - } + mkdir_or_throw(dest_path.parent_path().string()); } copy_dir(tmp_dir_name, filename); ec.clear(); if (!trx::fs::exists(filename, ec) || !trx::fs::is_directory(filename, ec)) { - throw std::runtime_error("Failed to create output directory: " + filename); + throw TrxIOError("Failed to create output directory: " + filename); } const trx::fs::path header_path = dest_path / "header.json"; if (!trx::fs::exists(header_path)) { - throw std::runtime_error("Missing header.json in output directory: " + header_path.string()); + throw TrxFormatError("Missing header.json in output directory: " + header_path.string()); } } } @@ -1267,7 +1129,7 @@ template void TrxFile
::add_dps_from_text(const std::string &name, const std::string &dtype, const std::string &path) { std::ifstream input(path); if (!input.is_open()) { - throw std::runtime_error("Failed to open DPS text file: " + path); + throw TrxIOError("Failed to open DPS text file: " + path); } std::vector values; @@ -1276,7 +1138,7 @@ void TrxFile
::add_dps_from_text(const std::string &name, const std::string & values.push_back(value); } if (!input.eof() && input.fail()) { - throw std::runtime_error("Failed to parse DPS text file: " + path); + throw TrxFormatError("Failed to parse DPS text file: " + path); } add_dps_from_vector(name, dtype, values); @@ -1286,7 +1148,7 @@ template template void TrxFile
::add_dps_from_vector(const std::string &name, const std::string &dtype, const std::vector &values) { if (name.empty()) { - throw std::invalid_argument("DPS name cannot be empty"); + throw TrxArgumentError("DPS name cannot be empty"); } std::string dtype_norm = dtype; @@ -1295,14 +1157,14 @@ void TrxFile
::add_dps_from_vector(const std::string &name, const std::string }); if (!trx::detail::_is_dtype_valid(dtype_norm)) { - throw std::invalid_argument("Unsupported DPS dtype: " + dtype); + throw TrxDTypeError("Unsupported DPS dtype: " + dtype); } if (dtype_norm != "float16" && dtype_norm != "float32" && dtype_norm != "float64") { - throw std::invalid_argument("Unsupported DPS dtype: " + dtype); + throw TrxDTypeError("Unsupported DPS dtype: " + dtype); } if (this->_uncompressed_folder_handle.empty()) { - throw std::runtime_error("TRX file has no backing directory to store DPS data"); + throw TrxIOError("TRX file has no backing directory to store DPS data"); } size_t nb_streamlines = 0; @@ -1313,18 +1175,12 @@ void TrxFile
::add_dps_from_vector(const std::string &name, const std::string } if (values.size() != nb_streamlines) { - throw std::runtime_error("DPS values (" + std::to_string(values.size()) + ") do not match number of streamlines (" + + throw TrxFormatError("DPS values (" + std::to_string(values.size()) + ") do not match number of streamlines (" + std::to_string(nb_streamlines) + ")"); } std::string dps_dirname = this->_uncompressed_folder_handle + SEPARATOR + "dps" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(dps_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dps_dirname); - } - } + mkdir_or_throw(dps_dirname); std::string dps_filename = dps_dirname + name + "." + dtype_norm; { @@ -1346,27 +1202,9 @@ void TrxFile
::add_dps_from_vector(const std::string &name, const std::string auto matrix = std::make_unique>(); matrix->mmap = trx::_create_memmap(dps_filename, shape, "w+", dtype_norm); - if (dtype_norm == "float16") { - auto *data = reinterpret_cast(matrix->mmap.data()); - Map> mapped(data, rows, cols); - new (&(matrix->_matrix)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } - } else if (dtype_norm == "float32") { - auto *data = reinterpret_cast(matrix->mmap.data()); - Map> mapped(data, rows, cols); - new (&(matrix->_matrix)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } - } else { - auto *data = reinterpret_cast(matrix->mmap.data()); - Map> mapped(data, rows, cols); - new (&(matrix->_matrix)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } + trx::detail::remap(matrix->_matrix, matrix->mmap.data(), rows, cols); + for (int i = 0; i < rows; ++i) { + matrix->_matrix(i, 0) = static_cast
(values[static_cast(i)]); } this->data_per_streamline[name] = std::move(matrix); @@ -1376,7 +1214,7 @@ template template void TrxFile
::add_dpv_from_vector(const std::string &name, const std::string &dtype, const std::vector &values) { if (name.empty()) { - throw std::invalid_argument("DPV name cannot be empty"); + throw TrxArgumentError("DPV name cannot be empty"); } std::string dtype_norm = dtype; @@ -1385,14 +1223,14 @@ void TrxFile
::add_dpv_from_vector(const std::string &name, const std::string }); if (!trx::detail::_is_dtype_valid(dtype_norm)) { - throw std::invalid_argument("Unsupported DPV dtype: " + dtype); + throw TrxDTypeError("Unsupported DPV dtype: " + dtype); } if (dtype_norm != "float16" && dtype_norm != "float32" && dtype_norm != "float64") { - throw std::invalid_argument("Unsupported DPV dtype: " + dtype); + throw TrxDTypeError("Unsupported DPV dtype: " + dtype); } if (this->_uncompressed_folder_handle.empty()) { - throw std::runtime_error("TRX file has no backing directory to store DPV data"); + throw TrxIOError("TRX file has no backing directory to store DPV data"); } size_t nb_vertices = 0; @@ -1403,18 +1241,12 @@ void TrxFile
::add_dpv_from_vector(const std::string &name, const std::string } if (values.size() != nb_vertices) { - throw std::runtime_error("DPV values (" + std::to_string(values.size()) + ") do not match number of vertices (" + + throw TrxFormatError("DPV values (" + std::to_string(values.size()) + ") do not match number of vertices (" + std::to_string(nb_vertices) + ")"); } std::string dpv_dirname = this->_uncompressed_folder_handle + SEPARATOR + "dpv" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(dpv_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpv_dirname); - } - } + mkdir_or_throw(dpv_dirname); std::string dpv_filename = dpv_dirname + name + "." + dtype_norm; { @@ -1436,33 +1268,14 @@ void TrxFile
::add_dpv_from_vector(const std::string &name, const std::string auto seq = std::make_unique>(); seq->mmap_pos = trx::_create_memmap(dpv_filename, shape, "w+", dtype_norm); - if (dtype_norm == "float16") { - auto *data = reinterpret_cast(seq->mmap_pos.data()); - Map> mapped(data, rows, cols); - new (&(seq->_data)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } - } else if (dtype_norm == "float32") { - auto *data = reinterpret_cast(seq->mmap_pos.data()); - Map> mapped(data, rows, cols); - new (&(seq->_data)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } - } else { - auto *data = reinterpret_cast(seq->mmap_pos.data()); - Map> mapped(data, rows, cols); - new (&(seq->_data)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } + trx::detail::remap(seq->_data, seq->mmap_pos.data(), rows, cols); + for (int i = 0; i < rows; ++i) { + seq->_data(i, 0) = static_cast
(values[static_cast(i)]); } if (this->streamlines && this->streamlines->_offsets.size() > 0) { - new (&(seq->_offsets)) Map>(this->streamlines->_offsets.data(), - int(this->streamlines->_offsets.rows()), - int(this->streamlines->_offsets.cols())); + trx::detail::remap(seq->_offsets, this->streamlines->_offsets.data(), int(this->streamlines->_offsets.rows()), + int(this->streamlines->_offsets.cols())); seq->_lengths = this->streamlines->_lengths; } @@ -1472,10 +1285,10 @@ void TrxFile
::add_dpv_from_vector(const std::string &name, const std::string template void TrxFile
::add_group_from_indices(const std::string &name, const std::vector &indices) { if (name.empty()) { - throw std::invalid_argument("Group name cannot be empty"); + throw TrxArgumentError("Group name cannot be empty"); } if (this->_uncompressed_folder_handle.empty()) { - throw std::runtime_error("TRX file has no backing directory to store groups"); + throw TrxIOError("TRX file has no backing directory to store groups"); } size_t nb_streamlines = 0; @@ -1487,18 +1300,12 @@ void TrxFile
::add_group_from_indices(const std::string &name, const std::vec for (const auto idx : indices) { if (idx >= nb_streamlines) { - throw std::runtime_error("Group index out of range: " + std::to_string(idx)); + throw TrxArgumentError("Group index out of range: " + std::to_string(idx)); } } std::string groups_dirname = this->_uncompressed_folder_handle + SEPARATOR + "groups" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(groups_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + groups_dirname); - } - } + mkdir_or_throw(groups_dirname); std::string group_filename = groups_dirname + name + ".uint32"; { @@ -1519,8 +1326,7 @@ void TrxFile
::add_group_from_indices(const std::string &name, const std::vec auto group = std::make_unique>(); group->mmap = trx::_create_memmap(group_filename, shape, "w+", "uint32"); - new (&(group->_matrix)) Map>( - reinterpret_cast(group->mmap.data()), std::get<0>(shape), std::get<1>(shape)); + trx::detail::remap(group->_matrix, group->mmap.data(), shape); for (int i = 0; i < rows; ++i) { group->_matrix(i, 0) = indices[static_cast(i)]; } @@ -1543,7 +1349,7 @@ inline TrxStream::TrxStream(std::string positions_dtype) : positions_dtype_(std: return static_cast(std::tolower(c)); }); if (positions_dtype_ != "float32" && positions_dtype_ != "float16") { - throw std::invalid_argument("TrxStream only supports float16/float32 positions for now"); + throw TrxArgumentError("TrxStream only supports float16/float32 positions for now"); } tmp_dir_ = make_temp_dir("trx_proto"); positions_path_ = tmp_dir_ + SEPARATOR + "positions.tmp"; @@ -1552,39 +1358,36 @@ inline TrxStream::TrxStream(std::string positions_dtype) : positions_dtype_(std: inline TrxStream::~TrxStream() { cleanup_tmp(); } -inline void TrxStream::set_metadata_mode(MetadataMode mode) { +inline TrxStream &TrxStream::set_metadata_mode(MetadataMode mode) { if (finalized_) { - throw std::runtime_error("Cannot adjust metadata mode after finalize"); + throw TrxArgumentError("Cannot adjust metadata mode after finalize"); } metadata_mode_ = mode; + return *this; } -inline void TrxStream::set_metadata_buffer_max_bytes(std::size_t max_bytes) { +inline TrxStream &TrxStream::set_metadata_buffer_max_bytes(std::size_t max_bytes) { if (finalized_) { - throw std::runtime_error("Cannot adjust metadata buffer after finalize"); + throw TrxArgumentError("Cannot adjust metadata buffer after finalize"); } metadata_buffer_max_bytes_ = max_bytes; + return *this; } inline void TrxStream::ensure_positions_stream() { if (!positions_out_.is_open()) { positions_out_.open(positions_path_, std::ios::binary | std::ios::out | std::ios::trunc); if (!positions_out_.is_open()) { - throw std::runtime_error("Failed to open TrxStream temp positions file: " + positions_path_); + throw TrxIOError("Failed to open TrxStream temp positions file: " + positions_path_); } } } inline void TrxStream::ensure_metadata_dir(const std::string &subdir) { if (tmp_dir_.empty()) { - throw std::runtime_error("TrxStream temp directory not initialized"); - } - const std::string dir = tmp_dir_ + SEPARATOR + subdir + SEPARATOR; - std::error_code ec; - trx::fs::create_directories(dir, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dir); + throw TrxIOError("TrxStream temp directory not initialized"); } + mkdir_or_throw(tmp_dir_ + SEPARATOR + subdir + SEPARATOR); } inline void TrxStream::flush_positions_buffer() { @@ -1597,7 +1400,7 @@ inline void TrxStream::flush_positions_buffer() { positions_out_.write(reinterpret_cast(positions_buffer_half_.data()), static_cast(byte_count)); if (!positions_out_) { - throw std::runtime_error("Failed to write TrxStream positions buffer"); + throw TrxIOError("Failed to write TrxStream positions buffer"); } positions_buffer_half_.clear(); return; @@ -1611,7 +1414,7 @@ inline void TrxStream::flush_positions_buffer() { positions_out_.write(reinterpret_cast(positions_buffer_float_.data()), static_cast(byte_count)); if (!positions_out_) { - throw std::runtime_error("Failed to write TrxStream positions buffer"); + throw TrxIOError("Failed to write TrxStream positions buffer"); } positions_buffer_float_.clear(); } @@ -1630,7 +1433,7 @@ inline void TrxStream::cleanup_tmp() { inline void TrxStream::push_streamline(const float *xyz, size_t point_count) { if (finalized_) { - throw std::runtime_error("TrxStream already finalized"); + throw TrxArgumentError("TrxStream already finalized"); } if (point_count == 0) { lengths_.push_back(0); @@ -1647,13 +1450,13 @@ inline void TrxStream::push_streamline(const float *xyz, size_t point_count) { const size_t byte_count = tmp.size() * sizeof(half); positions_out_.write(reinterpret_cast(tmp.data()), static_cast(byte_count)); if (!positions_out_) { - throw std::runtime_error("Failed to write TrxStream positions"); + throw TrxIOError("Failed to write TrxStream positions"); } } else { const size_t byte_count = point_count * 3 * sizeof(float); positions_out_.write(reinterpret_cast(xyz), static_cast(byte_count)); if (!positions_out_) { - throw std::runtime_error("Failed to write TrxStream positions"); + throw TrxIOError("Failed to write TrxStream positions"); } } } else { @@ -1679,7 +1482,7 @@ inline void TrxStream::push_streamline(const float *xyz, size_t point_count) { inline void TrxStream::push_streamline(const std::vector &xyz_flat) { if (xyz_flat.size() % 3 != 0) { - throw std::invalid_argument("TrxStream streamline buffer must be a multiple of 3"); + throw TrxArgumentError("TrxStream streamline buffer must be a multiple of 3"); } push_streamline(xyz_flat.data(), xyz_flat.size() / 3); } @@ -1699,7 +1502,7 @@ inline void TrxStream::push_streamline(const std::vector> & push_streamline(xyz_flat); } -inline void TrxStream::set_voxel_to_rasmm(const Eigen::Matrix4f &affine) { +inline TrxStream &TrxStream::set_voxel_to_rasmm(const Eigen::Matrix4f &affine) { std::vector> matrix(4, std::vector(4, 0.0f)); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { @@ -1707,34 +1510,36 @@ inline void TrxStream::set_voxel_to_rasmm(const Eigen::Matrix4f &affine) { } } header = _json_set(header, "VOXEL_TO_RASMM", matrix); + return *this; } -inline void TrxStream::set_dimensions(const std::array &dims) { +inline TrxStream &TrxStream::set_dimensions(const std::array &dims) { header = _json_set(header, "DIMENSIONS", std::vector{dims[0], dims[1], dims[2]}); + return *this; } template inline void TrxStream::push_dps_from_vector(const std::string &name, const std::string &dtype, const std::vector &values) { if (name.empty()) { - throw std::invalid_argument("DPS name cannot be empty"); + throw TrxArgumentError("DPS name cannot be empty"); } std::string dtype_norm = dtype; std::transform(dtype_norm.begin(), dtype_norm.end(), dtype_norm.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); if (!trx::detail::_is_dtype_valid(dtype_norm)) { - throw std::invalid_argument("Unsupported DPS dtype: " + dtype); + throw TrxDTypeError("Unsupported DPS dtype: " + dtype); } if (dtype_norm != "float16" && dtype_norm != "float32" && dtype_norm != "float64") { - throw std::invalid_argument("Unsupported DPS dtype: " + dtype); + throw TrxDTypeError("Unsupported DPS dtype: " + dtype); } if (metadata_mode_ == MetadataMode::OnDisk) { ensure_metadata_dir("dps"); const std::string filename = tmp_dir_ + SEPARATOR + "dps" + SEPARATOR + name + "." + dtype_norm; std::ofstream out(filename, std::ios::binary | std::ios::out | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to open DPS file: " + filename); + throw TrxIOError("Failed to open DPS file: " + filename); } if (dtype_norm == "float16") { const size_t chunk_elems = std::max(1, metadata_buffer_max_bytes_ / sizeof(half)); @@ -1796,24 +1601,24 @@ template inline void TrxStream::push_dpv_from_vector(const std::string &name, const std::string &dtype, const std::vector &values) { if (name.empty()) { - throw std::invalid_argument("DPV name cannot be empty"); + throw TrxArgumentError("DPV name cannot be empty"); } std::string dtype_norm = dtype; std::transform(dtype_norm.begin(), dtype_norm.end(), dtype_norm.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); if (!trx::detail::_is_dtype_valid(dtype_norm)) { - throw std::invalid_argument("Unsupported DPV dtype: " + dtype); + throw TrxDTypeError("Unsupported DPV dtype: " + dtype); } if (dtype_norm != "float16" && dtype_norm != "float32" && dtype_norm != "float64") { - throw std::invalid_argument("Unsupported DPV dtype: " + dtype); + throw TrxDTypeError("Unsupported DPV dtype: " + dtype); } if (metadata_mode_ == MetadataMode::OnDisk) { ensure_metadata_dir("dpv"); const std::string filename = tmp_dir_ + SEPARATOR + "dpv" + SEPARATOR + name + "." + dtype_norm; std::ofstream out(filename, std::ios::binary | std::ios::out | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to open DPV file: " + filename); + throw TrxIOError("Failed to open DPV file: " + filename); } if (dtype_norm == "float16") { const size_t chunk_elems = std::max(1, metadata_buffer_max_bytes_ / sizeof(half)); @@ -1873,7 +1678,7 @@ TrxStream::push_dpv_from_vector(const std::string &name, const std::string &dtyp inline void TrxStream::set_positions_buffer_max_bytes(std::size_t max_bytes) { if (finalized_) { - throw std::runtime_error("Cannot adjust buffer after finalize"); + throw TrxArgumentError("Cannot adjust buffer after finalize"); } if (max_bytes == 0) { positions_buffer_max_entries_ = 0; @@ -1893,14 +1698,14 @@ inline void TrxStream::set_positions_buffer_max_bytes(std::size_t max_bytes) { inline void TrxStream::push_group_from_indices(const std::string &name, const std::vector &indices) { if (name.empty()) { - throw std::invalid_argument("Group name cannot be empty"); + throw TrxArgumentError("Group name cannot be empty"); } if (metadata_mode_ == MetadataMode::OnDisk) { ensure_metadata_dir("groups"); const std::string filename = tmp_dir_ + SEPARATOR + "groups" + SEPARATOR + name + ".uint32"; std::ofstream out(filename, std::ios::binary | std::ios::out | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to open group file: " + filename); + throw TrxIOError("Failed to open group file: " + filename); } const size_t chunk_elems = std::max(1, metadata_buffer_max_bytes_ / sizeof(uint32_t)); size_t offset = 0; @@ -1919,7 +1724,7 @@ inline void TrxStream::push_group_from_indices(const std::string &name, const st template void TrxStream::finalize(const std::string &filename, zip_uint32_t compression_standard) { if (finalized_) { - throw std::runtime_error("TrxStream already finalized"); + throw TrxArgumentError("TrxStream already finalized"); } finalized_ = true; @@ -1951,14 +1756,14 @@ template void TrxStream::finalize(const std::string &filename, zip std::ifstream in(positions_path_, std::ios::binary); if (!in.is_open()) { - throw std::runtime_error("Failed to open TrxStream temp positions file for read: " + positions_path_); + throw TrxIOError("Failed to open TrxStream temp positions file for read: " + positions_path_); } for (size_t i = 0; i < nb_vertices; ++i) { if (positions_dtype_ == "float16") { half xyz[3]; in.read(reinterpret_cast(xyz), sizeof(xyz)); if (!in) { - throw std::runtime_error("Failed to read TrxStream positions"); + throw TrxIOError("Failed to read TrxStream positions"); } positions(static_cast(i), 0) = static_cast
(xyz[0]); positions(static_cast(i), 1) = static_cast
(xyz[1]); @@ -1967,7 +1772,7 @@ template void TrxStream::finalize(const std::string &filename, zip float xyz[3]; in.read(reinterpret_cast(xyz), sizeof(xyz)); if (!in) { - throw std::runtime_error("Failed to read TrxStream positions"); + throw TrxIOError("Failed to read TrxStream positions"); } positions(static_cast(i), 0) = static_cast
(xyz[0]); positions(static_cast(i), 1) = static_cast
(xyz[1]); @@ -1996,7 +1801,7 @@ template void TrxStream::finalize(const std::string &filename, zip std::error_code copy_ec; trx::fs::copy_file(meta.absolute_path, dest, trx::fs::copy_options::overwrite_existing, copy_ec); if (copy_ec) { - throw std::runtime_error("Failed to copy metadata file: " + meta.absolute_path + " -> " + dest); + throw TrxIOError("Failed to copy metadata file: " + meta.absolute_path + " -> " + dest); } } } @@ -2027,7 +1832,7 @@ inline void TrxStream::finalize(const std::string &filename, inline void TrxStream::finalize(const std::string &filename, const TrxSaveOptions &options) { if (options.mode == TrxSaveMode::Directory) { if (finalized_) { - throw std::runtime_error("TrxStream already finalized"); + throw TrxArgumentError("TrxStream already finalized"); } if (options.overwrite_existing) { finalize_directory(filename); @@ -2048,7 +1853,7 @@ inline void TrxStream::finalize(const std::string &filename, const TrxSaveOption inline void TrxStream::finalize_directory_impl(const std::string &directory, bool remove_existing) { if (finalized_) { - throw std::runtime_error("TrxStream already finalized"); + throw TrxArgumentError("TrxStream already finalized"); } finalized_ = true; @@ -2069,10 +1874,7 @@ inline void TrxStream::finalize_directory_impl(const std::string &directory, boo // Create directory if it doesn't exist if (!trx::fs::exists(directory, ec)) { - trx::fs::create_directories(directory, ec); - if (ec) { - throw std::runtime_error("Failed to create output directory: " + directory); - } + mkdir_or_throw(directory); } ec.clear(); @@ -2082,7 +1884,7 @@ inline void TrxStream::finalize_directory_impl(const std::string &directory, boo const std::string header_path = directory + SEPARATOR + "header.json"; std::ofstream out_header(header_path, std::ios::out | std::ios::trunc); if (!out_header.is_open()) { - throw std::runtime_error("Failed to write header.json to: " + header_path); + throw TrxIOError("Failed to write header.json to: " + header_path); } out_header << header_out.dump() << std::endl; out_header.close(); @@ -2094,14 +1896,14 @@ inline void TrxStream::finalize_directory_impl(const std::string &directory, boo ec.clear(); trx::fs::copy_file(positions_path_, positions_dst, trx::fs::copy_options::overwrite_existing, ec); if (ec) { - throw std::runtime_error("Failed to copy positions file to: " + positions_dst); + throw TrxIOError("Failed to copy positions file to: " + positions_dst); } } const std::string offsets_dst = directory + SEPARATOR + "offsets.uint64"; std::ofstream offsets_out(offsets_dst, std::ios::binary | std::ios::out | std::ios::trunc); if (!offsets_out.is_open()) { - throw std::runtime_error("Failed to open offsets file for write: " + offsets_dst); + throw TrxIOError("Failed to open offsets file for write: " + offsets_dst); } uint64_t offset = 0; offsets_out.write(reinterpret_cast(&offset), sizeof(offset)); @@ -2115,7 +1917,7 @@ inline void TrxStream::finalize_directory_impl(const std::string &directory, boo auto write_field_values = [&](const std::string &path, const FieldValues &values) { std::ofstream out(path, std::ios::binary | std::ios::out | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to open metadata file: " + path); + throw TrxIOError("Failed to open metadata file: " + path); } const size_t count = values.values.size(); if (values.dtype == "float16") { @@ -2161,7 +1963,7 @@ inline void TrxStream::finalize_directory_impl(const std::string &directory, boo idx += n; } } else { - throw std::runtime_error("Unsupported metadata dtype: " + values.dtype); + throw TrxDTypeError("Unsupported metadata dtype: " + values.dtype); } out.close(); }; @@ -2177,31 +1979,31 @@ inline void TrxStream::finalize_directory_impl(const std::string &directory, boo std::error_code copy_ec; trx::fs::copy_file(meta.absolute_path, dest, trx::fs::copy_options::overwrite_existing, copy_ec); if (copy_ec) { - throw std::runtime_error("Failed to copy metadata file: " + meta.absolute_path + " -> " + dest); + throw TrxIOError("Failed to copy metadata file: " + meta.absolute_path + " -> " + dest); } } } else { if (!dps_.empty()) { - trx::fs::create_directories(directory + SEPARATOR + "dps", ec); + mkdir_or_throw(directory + SEPARATOR + "dps"); for (const auto &kv : dps_) { const std::string path = directory + SEPARATOR + "dps" + SEPARATOR + kv.first + "." + kv.second.dtype; write_field_values(path, kv.second); } } if (!dpv_.empty()) { - trx::fs::create_directories(directory + SEPARATOR + "dpv", ec); + mkdir_or_throw(directory + SEPARATOR + "dpv"); for (const auto &kv : dpv_) { const std::string path = directory + SEPARATOR + "dpv" + SEPARATOR + kv.first + "." + kv.second.dtype; write_field_values(path, kv.second); } } if (!groups_.empty()) { - trx::fs::create_directories(directory + SEPARATOR + "groups", ec); + mkdir_or_throw(directory + SEPARATOR + "groups"); for (const auto &kv : groups_) { const std::string path = directory + SEPARATOR + "groups" + SEPARATOR + kv.first + ".uint32"; std::ofstream out(path, std::ios::binary | std::ios::out | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to open group file: " + path); + throw TrxIOError("Failed to open group file: " + path); } if (!kv.second.empty()) { out.write(reinterpret_cast(kv.second.data()), @@ -2226,7 +2028,7 @@ inline void TrxStream::finalize_directory_persistent(const std::string &director template void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &dtype, const std::string &path) { if (name.empty()) { - throw std::invalid_argument("DPV name cannot be empty"); + throw TrxArgumentError("DPV name cannot be empty"); } std::string dtype_norm = dtype; @@ -2235,17 +2037,17 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d }); if (!trx::detail::_is_dtype_valid(dtype_norm)) { - throw std::invalid_argument("Unsupported DPV dtype: " + dtype); + throw TrxDTypeError("Unsupported DPV dtype: " + dtype); } if (dtype_norm != "float16" && dtype_norm != "float32" && dtype_norm != "float64") { - throw std::invalid_argument("Unsupported DPV dtype for TSF input: " + dtype); + throw TrxDTypeError("Unsupported DPV dtype for TSF input: " + dtype); } if (!this->streamlines) { - throw std::runtime_error("TRX file has no streamlines to attach DPV data"); + throw TrxFormatError("TRX file has no streamlines to attach DPV data"); } if (this->_uncompressed_folder_handle.empty()) { - throw std::runtime_error("TRX file has no backing directory to store DPV data"); + throw TrxIOError("TRX file has no backing directory to store DPV data"); } const auto &lengths = this->streamlines->_lengths; @@ -2254,7 +2056,7 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d std::ifstream input(path); if (!input.is_open()) { - throw std::runtime_error("Failed to open TSF file: " + path); + throw TrxIOError("Failed to open TSF file: " + path); } auto trim = [](std::string note) { @@ -2299,14 +2101,14 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d } } if (!found_end) { - throw std::runtime_error("Failed to parse TSF header: missing END"); + throw TrxFormatError("Failed to parse TSF header: missing END"); } } else { input.clear(); input.seekg(start_pos); } } else { - throw std::runtime_error("Failed to parse TSF file: " + path); + throw TrxFormatError("Failed to parse TSF file: " + path); } std::vector values; @@ -2317,7 +2119,7 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d if (binary_mode) { if (datatype != "Float32LE" && datatype != "Float32BE" && datatype != "Float64LE" && datatype != "Float64BE") { - throw std::runtime_error("Unsupported TSF datatype: " + datatype); + throw TrxDTypeError("Unsupported TSF datatype: " + datatype); } auto is_little_endian = []() { @@ -2365,7 +2167,7 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d } if (std::isnan(value)) { if (current_vertices != expected_vertices) { - throw std::runtime_error("TSF streamline length does not match TRX streamlines"); + throw TrxFormatError("TSF streamline length does not match TRX streamlines"); } if (streamline_index + 1 < nb_streamlines) { ++streamline_index; @@ -2386,7 +2188,7 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d }); if (token_norm.rfind("nan", 0) == 0) { if (current_vertices != expected_vertices) { - throw std::runtime_error("TSF streamline length does not match TRX streamlines"); + throw TrxFormatError("TSF streamline length does not match TRX streamlines"); } if (streamline_index + 1 < nb_streamlines) { ++streamline_index; @@ -2403,17 +2205,17 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d size_t idx = 0; value = std::stod(token, &idx); if (idx != token.size()) { - throw std::invalid_argument("invalid token"); + throw TrxArgumentError("invalid token"); } } catch (const std::exception &) { - throw std::runtime_error("Failed to parse TSF file: " + path); + throw TrxFormatError("Failed to parse TSF file: " + path); } if (std::isinf(value)) { break; } if (std::isnan(value)) { if (current_vertices != expected_vertices) { - throw std::runtime_error("TSF streamline length does not match TRX streamlines"); + throw TrxFormatError("TSF streamline length does not match TRX streamlines"); } if (streamline_index + 1 < nb_streamlines) { ++streamline_index; @@ -2426,27 +2228,21 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d ++current_vertices; } if (!input.eof() && input.fail()) { - throw std::runtime_error("Failed to parse TSF file: " + path); + throw TrxFormatError("Failed to parse TSF file: " + path); } } if (nb_streamlines > 0) { if (streamline_index != nb_streamlines - 1 || current_vertices != expected_vertices) { - throw std::runtime_error("TSF streamline count does not match TRX streamlines"); + throw TrxFormatError("TSF streamline count does not match TRX streamlines"); } } if (values.size() != nb_vertices) { - throw std::runtime_error("TSF values (" + std::to_string(values.size()) + ") do not match number of vertices (" + + throw TrxFormatError("TSF values (" + std::to_string(values.size()) + ") do not match number of vertices (" + std::to_string(nb_vertices) + ")"); } std::string dpv_dirname = this->_uncompressed_folder_handle + SEPARATOR + "dpv" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(dpv_dirname, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpv_dirname); - } - } + mkdir_or_throw(dpv_dirname); std::string dpv_filename = dpv_dirname + name + "." + dtype_norm; { @@ -2468,32 +2264,14 @@ void TrxFile
::add_dpv_from_tsf(const std::string &name, const std::string &d auto seq = std::make_unique>(); seq->mmap_pos = trx::_create_memmap(dpv_filename, shape, "w+", dtype_norm); - if (dtype_norm == "float16") { - auto *data = reinterpret_cast(seq->mmap_pos.data()); - Map> mapped(data, rows, cols); - new (&(seq->_data)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } - } else if (dtype_norm == "float32") { - auto *data = reinterpret_cast(seq->mmap_pos.data()); - Map> mapped(data, rows, cols); - new (&(seq->_data)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } - } else { - auto *data = reinterpret_cast(seq->mmap_pos.data()); - Map> mapped(data, rows, cols); - new (&(seq->_data)) Map>(data, rows, cols); - for (int i = 0; i < rows; ++i) { - mapped(i, 0) = static_cast(values[static_cast(i)]); - } + trx::detail::remap(seq->_data, seq->mmap_pos.data(), rows, cols); + for (int i = 0; i < rows; ++i) { + seq->_data(i, 0) = static_cast
(values[static_cast(i)]); } - new (&(seq->_offsets)) Map>(this->streamlines->_offsets.data(), - static_cast(this->streamlines->_offsets.rows()), - static_cast(this->streamlines->_offsets.cols())); + trx::detail::remap(seq->_offsets, this->streamlines->_offsets.data(), + static_cast(this->streamlines->_offsets.rows()), + static_cast(this->streamlines->_offsets.cols())); seq->_lengths = this->streamlines->_lengths; this->data_per_vertex[name] = std::move(seq); @@ -2505,10 +2283,10 @@ void TrxFile
::export_dpv_to_tsf(const std::string &name, const std::string ×tamp, const std::string &dtype) const { if (name.empty()) { - throw std::invalid_argument("DPV name cannot be empty"); + throw TrxArgumentError("DPV name cannot be empty"); } if (timestamp.empty()) { - throw std::invalid_argument("TSF timestamp cannot be empty"); + throw TrxArgumentError("TSF timestamp cannot be empty"); } std::string dtype_norm = dtype; @@ -2517,34 +2295,34 @@ void TrxFile
::export_dpv_to_tsf(const std::string &name, }); if (!trx::detail::_is_dtype_valid(dtype_norm)) { - throw std::invalid_argument("Unsupported TSF dtype: " + dtype); + throw TrxDTypeError("Unsupported TSF dtype: " + dtype); } if (dtype_norm != "float32" && dtype_norm != "float64") { - throw std::invalid_argument("Unsupported TSF dtype for output: " + dtype); + throw TrxDTypeError("Unsupported TSF dtype for output: " + dtype); } if (!this->streamlines) { - throw std::runtime_error("TRX file has no streamlines to export DPV data"); + throw TrxFormatError("TRX file has no streamlines to export DPV data"); } const auto dpv_it = this->data_per_vertex.find(name); if (dpv_it == this->data_per_vertex.end()) { - throw std::runtime_error("DPV entry not found: " + name); + throw TrxFormatError("DPV entry not found: " + name); } const auto *seq = dpv_it->second.get(); if (!seq) { - throw std::runtime_error("DPV entry is null: " + name); + throw TrxFormatError("DPV entry is null: " + name); } if (seq->_data.cols() != 1) { - throw std::runtime_error("DPV must be 1D to export as TSF: " + name); + throw TrxFormatError("DPV must be 1D to export as TSF: " + name); } const auto &lengths = this->streamlines->_lengths; const size_t nb_streamlines = static_cast(lengths.size()); const size_t nb_vertices = static_cast(seq->_data.rows()); if (nb_vertices != static_cast(this->streamlines->_data.rows())) { - throw std::runtime_error("DPV vertex count does not match streamlines data"); + throw TrxFormatError("DPV vertex count does not match streamlines data"); } const auto is_little_endian = []() { @@ -2582,7 +2360,7 @@ void TrxFile
::export_dpv_to_tsf(const std::string &name, std::ofstream out(path, std::ios::binary | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to open TSF file for writing: " + path); + throw TrxIOError("Failed to open TSF file for writing: " + path); } out.write(header.data(), static_cast(header.size())); const size_t pad = (4 - (header.size() % 4)) % 4; @@ -2606,13 +2384,13 @@ void TrxFile
::export_dpv_to_tsf(const std::string &name, for (size_t s = 0; s < nb_streamlines; ++s) { const uint32_t len = lengths(static_cast(s)); if (offset > total_vertices) { - throw std::runtime_error("DPV length metadata exceeds vertex count"); + throw TrxFormatError("DPV length metadata exceeds vertex count"); } if (len > std::numeric_limits::max() - offset) { - throw std::runtime_error("DPV length metadata exceeds vertex count"); + throw TrxFormatError("DPV length metadata exceeds vertex count"); } if (offset + static_cast(len) > total_vertices) { - throw std::runtime_error("DPV length metadata exceeds vertex count"); + throw TrxFormatError("DPV length metadata exceeds vertex count"); } offset += static_cast(len); } @@ -2622,7 +2400,7 @@ void TrxFile
::export_dpv_to_tsf(const std::string &name, for (uint32_t i = 0; i < len; ++i) { const size_t idx = offset + static_cast(i); if (idx > static_cast(std::numeric_limits::max())) { - throw std::runtime_error("DPV length metadata exceeds vertex count"); + throw TrxFormatError("DPV length metadata exceeds vertex count"); } write_value(static_cast(seq->_data(static_cast(idx), 0))); } @@ -2634,7 +2412,7 @@ void TrxFile
::export_dpv_to_tsf(const std::string &name, write_value(std::numeric_limits::infinity()); if (!out.good()) { - throw std::runtime_error("Failed to write TSF file: " + path); + throw TrxIOError("Failed to write TSF file: " + path); } } @@ -2723,22 +2501,12 @@ std::unique_ptr> TrxFile
::query_aabb( const std::vector> *precomputed_aabbs, bool build_cache_for_result) const { if (!this->streamlines) { - auto empty = std::make_unique>(); - empty->header = _json_set(this->header, "NB_VERTICES", 0); - empty->header = _json_set(empty->header, "NB_STREAMLINES", 0); - return empty; + return this->make_empty_like(); } - size_t nb_streamlines = 0; - if (this->streamlines->_offsets.size() > 0) { - nb_streamlines = static_cast(this->streamlines->_offsets.size() - 1); - } else if (this->streamlines->_lengths.size() > 0) { - nb_streamlines = static_cast(this->streamlines->_lengths.size()); - } else { - auto empty = std::make_unique>(); - empty->header = _json_set(this->header, "NB_VERTICES", 0); - empty->header = _json_set(empty->header, "NB_STREAMLINES", 0); - return empty; + const size_t nb_streamlines = this->num_streamlines(); + if (nb_streamlines == 0) { + return this->make_empty_like(); } std::vector> aabbs_local; @@ -2746,7 +2514,7 @@ std::unique_ptr> TrxFile
::query_aabb( ? *precomputed_aabbs : (!this->aabb_cache_.empty() ? this->aabb_cache_ : (aabbs_local = this->build_streamline_aabbs())); if (aabbs.size() != nb_streamlines) { - throw std::invalid_argument("AABB size does not match streamlines count"); + throw TrxArgumentError("AABB size does not match streamlines count"); } const float min_x = min_corner[0]; @@ -2804,9 +2572,9 @@ const ArraySequence
*TrxFile
::get_dpv(const std::string &name) const { template std::vector> TrxFile
::get_streamline(size_t streamline_index) const { if (!this->streamlines || this->streamlines->_offsets.size() == 0) { - throw std::runtime_error("TRX streamlines are not available"); + throw TrxFormatError("TRX streamlines are not available"); } - const size_t n_streamlines = static_cast(this->streamlines->_offsets.size() - 1); + const size_t n_streamlines = this->num_streamlines(); if (streamline_index >= n_streamlines) { throw std::out_of_range("Streamline index out of range"); } @@ -2833,7 +2601,7 @@ void TrxFile
::for_each_streamline(Fn &&fn) const { if (!this->streamlines || this->streamlines->_offsets.size() == 0) { return; } - const size_t n_streamlines = static_cast(this->streamlines->_offsets.size() - 1); + const size_t n_streamlines = this->num_streamlines(); for (size_t i = 0; i < n_streamlines; ++i) { const uint64_t start = static_cast(this->streamlines->_offsets(static_cast(i), 0)); const uint64_t end = static_cast(this->streamlines->_offsets(static_cast(i + 1), 0)); @@ -2850,56 +2618,44 @@ void TrxFile
::add_dpg_from_vector(const std::string &group, int rows, int cols) { if (group.empty()) { - throw std::invalid_argument("DPG group cannot be empty"); + throw TrxArgumentError("DPG group cannot be empty"); } if (name.empty()) { - throw std::invalid_argument("DPG name cannot be empty"); + throw TrxArgumentError("DPG name cannot be empty"); } std::string dtype_norm = dtype; std::transform(dtype_norm.begin(), dtype_norm.end(), dtype_norm.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); if (!trx::detail::_is_dtype_valid(dtype_norm)) { - throw std::invalid_argument("Unsupported DPG dtype: " + dtype); + throw TrxDTypeError("Unsupported DPG dtype: " + dtype); } if (dtype_norm != "float16" && dtype_norm != "float32" && dtype_norm != "float64") { - throw std::invalid_argument("Unsupported DPG dtype: " + dtype); + throw TrxDTypeError("Unsupported DPG dtype: " + dtype); } if (this->_uncompressed_folder_handle.empty()) { - throw std::runtime_error("TRX file has no backing directory to store DPG data"); + throw TrxIOError("TRX file has no backing directory to store DPG data"); } if (rows <= 0) { - throw std::invalid_argument("DPG rows must be positive"); + throw TrxArgumentError("DPG rows must be positive"); } if (cols < 0) { if (values.size() % static_cast(rows) != 0) { - throw std::invalid_argument("DPG values size does not match rows"); + throw TrxArgumentError("DPG values size does not match rows"); } cols = static_cast(values.size() / static_cast(rows)); } if (cols <= 0) { - throw std::invalid_argument("DPG cols must be positive"); + throw TrxArgumentError("DPG cols must be positive"); } if (static_cast(rows) * static_cast(cols) != values.size()) { - throw std::invalid_argument("DPG values size does not match rows*cols"); + throw TrxArgumentError("DPG values size does not match rows*cols"); } std::string dpg_dir = this->_uncompressed_folder_handle + SEPARATOR + "dpg" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(dpg_dir, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpg_dir); - } - } + mkdir_or_throw(dpg_dir); std::string dpg_subdir = dpg_dir + group; - { - std::error_code ec; - trx::fs::create_directories(dpg_subdir, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpg_subdir); - } - } + mkdir_or_throw(dpg_subdir); std::string dpg_filename = dpg_subdir + SEPARATOR + name + "." + dtype_norm; { @@ -2916,27 +2672,9 @@ void TrxFile
::add_dpg_from_vector(const std::string &group, group_map[name] = std::make_unique>(); group_map[name]->mmap = _create_memmap(dpg_filename, shape, "w+", dtype_norm); - if (dtype_norm == "float16") { - auto *data = reinterpret_cast(group_map[name]->mmap.data()); - Map> mapped(data, rows, cols); - new (&(group_map[name]->_matrix)) Map>(data, rows, cols); - for (int i = 0; i < rows * cols; ++i) { - data[i] = static_cast(values[static_cast(i)]); - } - } else if (dtype_norm == "float32") { - auto *data = reinterpret_cast(group_map[name]->mmap.data()); - Map> mapped(data, rows, cols); - new (&(group_map[name]->_matrix)) Map>(data, rows, cols); - for (int i = 0; i < rows * cols; ++i) { - data[i] = static_cast(values[static_cast(i)]); - } - } else { - auto *data = reinterpret_cast(group_map[name]->mmap.data()); - Map> mapped(data, rows, cols); - new (&(group_map[name]->_matrix)) Map>(data, rows, cols); - for (int i = 0; i < rows * cols; ++i) { - data[i] = static_cast(values[static_cast(i)]); - } + trx::detail::remap(group_map[name]->_matrix, group_map[name]->mmap.data(), rows, cols); + for (int i = 0; i < rows * cols; ++i) { + group_map[name]->_matrix(i) = static_cast
(values[static_cast(i)]); } } @@ -2947,7 +2685,7 @@ void TrxFile
::add_dpg_from_matrix(const std::string &group, const std::string &dtype, const Eigen::MatrixBase &matrix) { if (matrix.size() == 0) { - throw std::invalid_argument("DPG matrix cannot be empty"); + throw TrxArgumentError("DPG matrix cannot be empty"); } std::vector values; values.reserve(static_cast(matrix.size())); @@ -3018,10 +2756,7 @@ template std::unique_ptr> TrxFile
::subset_streamlines(const std::vector &streamline_ids, bool build_cache_for_result) const { if (!this->streamlines) { - auto empty = std::make_unique>(); - empty->header = _json_set(this->header, "NB_VERTICES", 0); - empty->header = _json_set(empty->header, "NB_STREAMLINES", 0); - return empty; + return this->make_empty_like(); } std::vector offsets; @@ -3038,18 +2773,12 @@ std::unique_ptr> TrxFile
::subset_streamlines(const std::vector(this->streamlines->_lengths(static_cast(i))); } } else { - auto empty = std::make_unique>(); - empty->header = _json_set(this->header, "NB_VERTICES", 0); - empty->header = _json_set(empty->header, "NB_STREAMLINES", 0); - return empty; + return this->make_empty_like(); } const size_t nb_streamlines = offsets.size() > 0 ? offsets.size() - 1 : 0; if (nb_streamlines == 0) { - auto empty = std::make_unique>(); - empty->header = _json_set(this->header, "NB_VERTICES", 0); - empty->header = _json_set(empty->header, "NB_STREAMLINES", 0); - return empty; + return this->make_empty_like(); } std::vector selected; @@ -3057,7 +2786,7 @@ std::unique_ptr> TrxFile
::subset_streamlines(const std::vector seen(nb_streamlines, 0); for (uint32_t id : streamline_ids) { if (id >= nb_streamlines) { - throw std::invalid_argument("Streamline id out of range"); + throw TrxArgumentError("Streamline id out of range"); } if (!seen[id]) { selected.push_back(id); @@ -3066,10 +2795,7 @@ std::unique_ptr> TrxFile
::subset_streamlines(const std::vector>(); - empty->header = _json_set(this->header, "NB_VERTICES", 0); - empty->header = _json_set(empty->header, "NB_STREAMLINES", 0); - return empty; + return this->make_empty_like(); } std::vector old_to_new(nb_streamlines, -1); @@ -3163,13 +2889,7 @@ std::unique_ptr> TrxFile
::subset_streamlines(const std::vectordata_per_group.empty() && !out->groups.empty()) { std::string dpg_dir = out->_uncompressed_folder_handle + SEPARATOR + "dpg" + SEPARATOR; - { - std::error_code ec; - trx::fs::create_directories(dpg_dir, ec); - if (ec) { - throw std::runtime_error("Could not create directory " + dpg_dir); - } - } + mkdir_or_throw(dpg_dir); for (const auto &group_kv : out->groups) { const std::string &group_name = group_kv.first; @@ -3179,13 +2899,7 @@ std::unique_ptr> TrxFile
::subset_streamlines(const std::vectordata_per_group.find(group_name) == out->data_per_group.end()) { out->data_per_group.emplace(group_name, std::map>>{}); @@ -3206,19 +2920,9 @@ std::unique_ptr> TrxFile
::subset_streamlines(const std::vectordata_per_group[group_name][field_name]->mmap = _create_memmap(dpg_filename, dpg_shape, "w+", dpg_dtype); - if (dpg_dtype.compare("float16") == 0) { - new (&(out->data_per_group[group_name][field_name]->_matrix)) Map>( - reinterpret_cast(out->data_per_group[group_name][field_name]->mmap.data()), - std::get<0>(dpg_shape), std::get<1>(dpg_shape)); - } else if (dpg_dtype.compare("float32") == 0) { - new (&(out->data_per_group[group_name][field_name]->_matrix)) Map>( - reinterpret_cast(out->data_per_group[group_name][field_name]->mmap.data()), - std::get<0>(dpg_shape), std::get<1>(dpg_shape)); - } else { - new (&(out->data_per_group[group_name][field_name]->_matrix)) Map>( - reinterpret_cast(out->data_per_group[group_name][field_name]->mmap.data()), - std::get<0>(dpg_shape), std::get<1>(dpg_shape)); - } + trx::detail::remap(out->data_per_group[group_name][field_name]->_matrix, + out->data_per_group[group_name][field_name]->mmap.data(), + std::get<0>(dpg_shape), std::get<1>(dpg_shape)); for (int i = 0; i < out->data_per_group[group_name][field_name]->_matrix.rows(); ++i) { for (int j = 0; j < out->data_per_group[group_name][field_name]->_matrix.cols(); ++j) { diff --git a/src/detail/dtype_helpers.cpp b/src/detail/dtype_helpers.cpp index 197e5b7..8bf056c 100644 --- a/src/detail/dtype_helpers.cpp +++ b/src/detail/dtype_helpers.cpp @@ -61,22 +61,22 @@ std::string _get_dtype(const std::string &dtype) { } bool _is_dtype_valid(const std::string &ext) { - if (std::find(::trx::dtypes.begin(), ::trx::dtypes.end(), ext) != ::trx::dtypes.end()) + if (std::find(dtypes.begin(), dtypes.end(), ext) != dtypes.end()) return true; return false; } std::tuple _split_ext_with_dimensionality(const std::string &filename) { - std::string base = ::trx::path_basename(filename); + std::string base = path_basename(filename); const size_t num_splits = std::count(base.begin(), base.end(), '.'); int dim = 0; if (num_splits != 1 && num_splits != 2) { - throw std::invalid_argument("Invalid filename"); + throw TrxFormatError("Invalid filename"); } - const std::string ext = ::trx::get_ext(base); + const std::string ext = get_ext(base); base = base.substr(0, base.length() - ext.length() - 1); @@ -91,8 +91,7 @@ std::tuple _split_ext_with_dimensionality(const s const bool is_valid = _is_dtype_valid(ext); if (!is_valid) { - // TODO: make formatted string and include provided extension name - throw std::invalid_argument("Unsupported file extension"); + throw TrxDTypeError("Unsupported file extension: " + ext); } std::tuple output{base, dim, ext}; diff --git a/src/trx.cpp b/src/trx.cpp index 67f0a58..f875488 100644 --- a/src/trx.cpp +++ b/src/trx.cpp @@ -31,6 +31,7 @@ #include #include +#include // #define ZIP_DD_SIG 0x08074b50 // #define ZIP_CD_SIG 0x06054b50 @@ -59,13 +60,13 @@ std::string normalize_slashes(std::string path) { return path; } + bool parse_positions_dtype(const std::string &filename, std::string &out_dtype) { const std::string normalized = normalize_slashes(filename); try { - const auto tuple = trx::detail::_split_ext_with_dimensionality(normalized); - const std::string &base = std::get<0>(tuple); + auto [base, dim, dtype] = trx::detail::_split_ext_with_dimensionality(normalized); if (base == "positions") { - out_dtype = std::get<2>(tuple); + out_dtype = dtype; return true; } } catch (const std::exception &) { @@ -107,7 +108,7 @@ TrxSaveMode resolve_save_mode(const std::string &filename, TrxSaveMode requested std::array read_xyz_as_double(const TypedArray &positions, size_t row_index) { if (positions.cols != 3) { - throw std::runtime_error("Positions must have 3 columns."); + throw TrxFormatError("Positions must have 3 columns."); } if (row_index >= static_cast(positions.rows)) { throw std::out_of_range("Position row index out of range"); @@ -130,7 +131,7 @@ std::array read_xyz_as_double(const TypedArray &positions, size_t row view(static_cast(row_index), 1), view(static_cast(row_index), 2)}; } - throw std::runtime_error("Unsupported positions dtype for streamline extraction: " + positions.dtype); + throw TrxDTypeError("Unsupported positions dtype for streamline extraction: " + positions.dtype); } TypedArray make_typed_array(const std::string &filename, int rows, int cols, const std::string &dtype) { @@ -148,7 +149,7 @@ TypedArray make_typed_array(const std::string &filename, int rows, int cols, con std::string detect_positions_dtype(const std::string &path) { const trx::fs::path input(path); if (!trx::fs::exists(input)) { - throw std::runtime_error("Input path does not exist: " + path); + throw TrxIOError("Input path does not exist: " + path); } std::error_code ec; @@ -165,14 +166,14 @@ std::string detect_positions_dtype(const std::string &path) { } int err = 0; - zip_t *zf = open_zip_for_read(path, err); - if (zf == nullptr) { - throw std::runtime_error("Could not open zip file: " + path); + detail::ZipArchive zf(open_zip_for_read(path, err)); + if (!zf) { + throw TrxIOError("Could not open zip file: " + path); } std::string dtype; - const zip_int64_t count = zip_get_num_entries(zf, 0); + const zip_int64_t count = zip_get_num_entries(zf.get(), 0); for (zip_int64_t i = 0; i < count; ++i) { - const auto *name = zip_get_name(zf, i, 0); + const auto *name = zip_get_name(zf.get(), i, 0); if (name == nullptr) { continue; } @@ -180,7 +181,6 @@ std::string detect_positions_dtype(const std::string &path) { break; } } - zip_close(zf); return dtype; } @@ -201,7 +201,7 @@ TrxScalarType detect_positions_scalar_type(const std::string &path, TrxScalarTyp bool is_trx_directory(const std::string &path) { const trx::fs::path input(path); if (!trx::fs::exists(input)) { - throw std::runtime_error("Input path does not exist: " + path); + throw TrxIOError("Input path does not exist: " + path); } std::error_code ec; return trx::fs::is_directory(input, ec) && !ec; @@ -257,7 +257,7 @@ const TypedArray *AnyTrxFile::get_dpv(const std::string &name) const { std::vector> AnyTrxFile::get_streamline(size_t streamline_index) const { if (offsets_u64.empty()) { - throw std::runtime_error("TRX offsets are empty."); + throw TrxFormatError("TRX offsets are empty."); } const size_t n_streamlines = offsets_u64.size() - 1; if (streamline_index >= n_streamlines) { @@ -286,17 +286,7 @@ void AnyTrxFile::close() { _uncompressed_folder_handle.clear(); _owns_uncompressed_folder = false; - std::vector> affine(4, std::vector(4, 0.0f)); - for (int i = 0; i < 4; i++) { - affine[i][i] = 1.0f; - } - std::vector dimensions{1, 1, 1}; - json::object header_obj; - header_obj["VOXEL_TO_RASMM"] = affine; - header_obj["DIMENSIONS"] = dimensions; - header_obj["NB_VERTICES"] = 0; - header_obj["NB_STREAMLINES"] = 0; - header = json(header_obj); + header = default_header(); } void AnyTrxFile::_cleanup_temporary_directory() { @@ -311,7 +301,7 @@ void AnyTrxFile::_cleanup_temporary_directory() { AnyTrxFile AnyTrxFile::load(const std::string &path) { trx::fs::path input(path); if (!trx::fs::exists(input)) { - throw std::runtime_error("Input path does not exist: " + path); + throw TrxIOError("Input path does not exist: " + path); } std::error_code ec; if (trx::fs::is_directory(input, ec) && !ec) { @@ -322,13 +312,12 @@ AnyTrxFile AnyTrxFile::load(const std::string &path) { AnyTrxFile AnyTrxFile::load_from_zip(const std::string &filename) { int errorp = 0; - zip_t *zf = open_zip_for_read(filename, errorp); - if (zf == nullptr) { - throw std::runtime_error("Could not open zip file: " + filename); + detail::ZipArchive zf(open_zip_for_read(filename, errorp)); + if (!zf) { + throw TrxIOError("Could not open zip file: " + filename); } - std::string temp_dir = extract_zip_to_directory(zf); - zip_close(zf); + std::string temp_dir = extract_zip_to_directory(zf.get()); auto trx = AnyTrxFile::load_from_directory(temp_dir); trx._uncompressed_folder_handle = temp_dir; @@ -382,14 +371,14 @@ AnyTrxFile AnyTrxFile::load_from_directory(const std::string &path) { detail += "]"; } } - throw std::runtime_error(detail); + throw TrxIOError(detail); } std::string jstream((std::istreambuf_iterator(header_file)), std::istreambuf_iterator()); header_file.close(); std::string err; json header = json::parse(jstream, err); if (!err.empty()) { - throw std::runtime_error("Failed to parse header.json: " + err); + throw TrxIOError("Failed to parse header.json: " + err); } std::map> files_pointer_size; @@ -408,7 +397,7 @@ AnyTrxFile::_create_from_pointer(json header, trx.header = header; if (!header["NB_VERTICES"].is_number() || !header["NB_STREAMLINES"].is_number()) { - throw std::invalid_argument("Missing NB_VERTICES or NB_STREAMLINES in header.json"); + throw TrxFormatError("Missing NB_VERTICES or NB_STREAMLINES in header.json"); } const int nb_vertices = header["NB_VERTICES"].int_value(); @@ -417,28 +406,9 @@ AnyTrxFile::_create_from_pointer(json header, for (auto x = dict_pointer_size.rbegin(); x != dict_pointer_size.rend(); ++x) { const std::string elem_filename = x->first; - trx::fs::path elem_path(elem_filename); - trx::fs::path folder_path = elem_path.parent_path(); - std::string folder; - if (!root.empty()) { - trx::fs::path rel_path = elem_path.lexically_relative(trx::fs::path(root)); - std::string rel_str = rel_path.string(); - if (!rel_str.empty() && rel_str.rfind("..", 0) != 0) { - folder = rel_path.parent_path().string(); - } else { - folder = folder_path.string(); - } - } else { - folder = folder_path.string(); - } - if (folder == ".") { - folder.clear(); - } + std::string folder = folder_from_path(elem_filename, root); - std::tuple base_tuple = trx::detail::_split_ext_with_dimensionality(elem_filename); - std::string base(std::get<0>(base_tuple)); - int dim = std::get<1>(base_tuple); - std::string ext(std::get<2>(base_tuple)); + auto [base, dim, ext] = trx::detail::_split_ext_with_dimensionality(elem_filename); ext = _normalize_dtype(ext); @@ -446,35 +416,35 @@ AnyTrxFile::_create_from_pointer(json header, if (base == "positions" && (folder.empty() || folder == ".")) { if (size != static_cast(nb_vertices) * 3 || dim != 3) { - throw std::invalid_argument("Wrong positions size/dimensionality"); + throw TrxFormatError("Wrong positions size/dimensionality"); } if (ext != "float16" && ext != "float32" && ext != "float64") { - throw std::invalid_argument("Unsupported positions dtype: " + ext); + throw TrxDTypeError("Unsupported positions dtype: " + ext); } trx.positions = make_typed_array(elem_filename, nb_vertices, 3, ext); } else if (base == "offsets" && (folder.empty() || folder == ".")) { if (size != static_cast(nb_streamlines) + 1 || dim != 1) { - throw std::invalid_argument("Wrong offsets size/dimensionality"); + throw TrxFormatError("Wrong offsets size/dimensionality"); } if (ext != "uint32" && ext != "uint64") { - throw std::invalid_argument("Unsupported offsets dtype: " + ext); + throw TrxDTypeError("Unsupported offsets dtype: " + ext); } trx.offsets = make_typed_array(elem_filename, nb_streamlines + 1, 1, ext); } else if (folder == "dps") { const int nb_scalar = nb_streamlines > 0 ? static_cast(size / nb_streamlines) : 0; if (nb_streamlines == 0 || size % nb_streamlines != 0 || nb_scalar != dim) { - throw std::invalid_argument("Wrong dps size/dimensionality"); + throw TrxFormatError("Wrong dps size/dimensionality"); } trx.data_per_streamline.emplace(base, make_typed_array(elem_filename, nb_streamlines, nb_scalar, ext)); } else if (folder == "dpv") { const int nb_scalar = nb_vertices > 0 ? static_cast(size / nb_vertices) : 0; if (nb_vertices == 0 || size % nb_vertices != 0 || nb_scalar != dim) { - throw std::invalid_argument("Wrong dpv size/dimensionality"); + throw TrxFormatError("Wrong dpv size/dimensionality"); } trx.data_per_vertex.emplace(base, make_typed_array(elem_filename, nb_vertices, nb_scalar, ext)); } else if (folder.rfind("dpg", 0) == 0) { if (size != dim) { - throw std::invalid_argument("Wrong dpg size/dimensionality"); + throw TrxFormatError("Wrong dpg size/dimensionality"); } std::string data_name = path_basename(base); std::string sub_folder = path_basename(folder); @@ -482,19 +452,19 @@ AnyTrxFile::_create_from_pointer(json header, make_typed_array(elem_filename, 1, static_cast(size), ext)); } else if (folder == "groups") { if (dim != 1) { - throw std::invalid_argument("Wrong group dimensionality"); + throw TrxFormatError("Wrong group dimensionality"); } if (ext != "uint32") { - throw std::invalid_argument("Unsupported group dtype: " + ext); + throw TrxDTypeError("Unsupported group dtype: " + ext); } trx.groups.emplace(base, make_typed_array(elem_filename, static_cast(size), 1, ext)); } else { - throw std::invalid_argument("Entry is not part of a valid TRX structure: " + elem_filename); + throw TrxFormatError("Entry is not part of a valid TRX structure: " + elem_filename); } } if (trx.positions.empty() || trx.offsets.empty()) { - throw std::invalid_argument("Missing essential data."); + throw TrxFormatError("Missing essential data."); } const size_t offsets_count = trx.offsets.size(); @@ -512,7 +482,7 @@ AnyTrxFile::_create_from_pointer(json header, trx.offsets_u64[i] = static_cast(src[i]); } } else { - throw std::invalid_argument("Unsupported offsets datatype: " + trx.offsets.dtype); + throw TrxDTypeError("Unsupported offsets datatype: " + trx.offsets.dtype); } } @@ -521,7 +491,7 @@ AnyTrxFile::_create_from_pointer(json header, for (size_t i = 0; i + 1 < offsets_count; ++i) { const uint64_t diff = trx.offsets_u64[i + 1] - trx.offsets_u64[i]; if (diff > std::numeric_limits::max()) { - throw std::runtime_error("Offset difference exceeds uint32 range"); + throw TrxFormatError("Offset difference exceeds uint32 range"); } trx.lengths[i] = static_cast(diff); } @@ -540,94 +510,87 @@ void AnyTrxFile::save(const std::string &filename, const TrxSaveOptions &options const std::string ext = get_ext(filename); const TrxSaveMode save_mode = resolve_save_mode(filename, options.mode); if (ext.size() > 0 && ext != "zip" && ext != "trx") { - throw std::invalid_argument("Unsupported extension: " + ext); + throw TrxDTypeError("Unsupported extension: " + ext); } if (offsets.empty()) { - throw std::runtime_error("Cannot save TRX without offsets data"); + throw TrxFormatError("Cannot save TRX without offsets data"); } if (offsets_u64.empty()) { - throw std::runtime_error("Cannot save TRX without decoded offsets"); + throw TrxFormatError("Cannot save TRX without decoded offsets"); } if (header["NB_STREAMLINES"].is_number()) { const auto nb_streamlines = static_cast(header["NB_STREAMLINES"].int_value()); if (offsets_u64.size() != nb_streamlines + 1) { - throw std::runtime_error("TRX offsets size does not match NB_STREAMLINES"); + throw TrxFormatError("TRX offsets size does not match NB_STREAMLINES"); } } if (header["NB_VERTICES"].is_number()) { const auto nb_vertices = static_cast(header["NB_VERTICES"].int_value()); const auto last = offsets_u64.back(); if (last != nb_vertices) { - throw std::runtime_error("TRX offsets sentinel does not match NB_VERTICES"); + throw TrxFormatError("TRX offsets sentinel does not match NB_VERTICES"); } } for (size_t i = 1; i < offsets_u64.size(); ++i) { if (offsets_u64[i] < offsets_u64[i - 1]) { - throw std::runtime_error("TRX offsets must be monotonically increasing"); + throw TrxFormatError("TRX offsets must be monotonically increasing"); } } if (!positions.empty()) { const auto last = offsets_u64.back(); if (last != static_cast(positions.rows)) { - throw std::runtime_error("TRX positions row count does not match offsets sentinel"); + throw TrxFormatError("TRX positions row count does not match offsets sentinel"); } } const std::string source_dir = !_uncompressed_folder_handle.empty() ? _uncompressed_folder_handle : _backing_directory; if (source_dir.empty()) { - throw std::runtime_error("TRX file has no backing directory to save from"); + throw TrxIOError("TRX file has no backing directory to save from"); } if (save_mode == TrxSaveMode::Archive) { int errorp; - zip_t *zf; - if ((zf = zip_open(filename.c_str(), ZIP_CREATE + ZIP_TRUNCATE, &errorp)) == nullptr) { - throw std::runtime_error("Could not open archive " + filename + ": " + strerror(errorp)); + detail::ZipArchive zf(zip_open(filename.c_str(), ZIP_CREATE + ZIP_TRUNCATE, &errorp)); + if (!zf) { + throw TrxIOError("Could not open archive " + filename + ": " + strerror(errorp)); } const std::string header_payload = header.dump() + "\n"; zip_source_t *header_source = - zip_source_buffer(zf, header_payload.data(), header_payload.size(), 0 /* do not free */); + zip_source_buffer(zf.get(), header_payload.data(), header_payload.size(), 0 /* do not free */); if (header_source == nullptr) { - zip_close(zf); - throw std::runtime_error("Failed to create zip source for header.json: " + std::string(zip_strerror(zf))); + throw TrxIOError("Failed to create zip source for header.json: " + + std::string(zip_strerror(zf.get()))); } - const zip_int64_t header_idx = zip_file_add(zf, "header.json", header_source, ZIP_FL_ENC_UTF_8 | ZIP_FL_OVERWRITE); + const zip_int64_t header_idx = + zip_file_add(zf.get(), "header.json", header_source, ZIP_FL_ENC_UTF_8 | ZIP_FL_OVERWRITE); if (header_idx < 0) { - zip_source_free(header_source); - zip_close(zf); - throw std::runtime_error("Failed to add header.json to archive: " + std::string(zip_strerror(zf))); + throw TrxIOError("Failed to add header.json to archive: " + std::string(zip_strerror(zf.get()))); } const zip_int32_t compression = static_cast(options.compression_standard); - if (zip_set_file_compression(zf, header_idx, compression, 0) < 0) { - zip_close(zf); - throw std::runtime_error("Failed to set compression for header.json: " + std::string(zip_strerror(zf))); + if (zip_set_file_compression(zf.get(), header_idx, compression, 0) < 0) { + throw TrxIOError("Failed to set compression for header.json: " + + std::string(zip_strerror(zf.get()))); } const std::unordered_set skip = {"header.json"}; - zip_from_folder(zf, source_dir, source_dir, options.compression_standard, &skip); - if (zip_close(zf) != 0) { - throw std::runtime_error("Unable to close archive " + filename + ": " + zip_strerror(zf)); - } + zip_from_folder(zf.get(), source_dir, source_dir, options.compression_standard, &skip); + zf.commit(filename); } else { std::error_code ec; if (trx::fs::exists(filename, ec) && trx::fs::is_directory(filename, ec)) { if (!options.overwrite_existing) { - throw std::runtime_error("Output directory already exists: " + filename); + throw TrxIOError("Output directory already exists: " + filename); } if (rm_dir(filename) != 0) { - throw std::runtime_error("Could not remove existing directory " + filename); + throw TrxIOError("Could not remove existing directory " + filename); } } trx::fs::path dest_path(filename); if (dest_path.has_parent_path()) { - std::error_code parent_ec; - trx::fs::create_directories(dest_path.parent_path(), parent_ec); - if (parent_ec) { - throw std::runtime_error("Could not create output parent directory: " + dest_path.parent_path().string()); - } + mkdir_or_throw(dest_path.parent_path().string()); } std::error_code source_ec; const trx::fs::path source_path = trx::fs::weakly_canonical(trx::fs::path(source_dir), source_ec); @@ -642,17 +605,17 @@ void AnyTrxFile::save(const std::string &filename, const TrxSaveOptions &options const trx::fs::path final_header_path = dest_path / "header.json"; std::ofstream out_json(final_header_path, std::ios::out | std::ios::trunc); if (!out_json.is_open()) { - throw std::runtime_error("Failed to write header.json to: " + final_header_path.string()); + throw TrxIOError("Failed to write header.json to: " + final_header_path.string()); } out_json << header.dump() << std::endl; out_json.close(); ec.clear(); if (!trx::fs::exists(filename, ec) || !trx::fs::is_directory(filename, ec)) { - throw std::runtime_error("Failed to create output directory: " + filename); + throw TrxIOError("Failed to create output directory: " + filename); } if (!trx::fs::exists(final_header_path)) { - throw std::runtime_error("Missing header.json in output directory: " + final_header_path.string()); + throw TrxFormatError("Missing header.json in output directory: " + final_header_path.string()); } } } @@ -666,7 +629,7 @@ void populate_fps(const string &name, std::mappath(); const std::string filename = entry_path.filename().string(); @@ -696,14 +659,14 @@ void populate_fps(const string &name, std::map(dtype_size) == 0) { @@ -712,7 +675,7 @@ void populate_fps(const string &name, std::map 0) { + while ((nbytes = zip_fread(zh.get(), buffer.data(), buff_len - 1)) > 0) { jstream.append(buffer.data(), static_cast(nbytes)); } - zip_fclose(zh); - // convert jstream data into Json. std::string err; auto root = json::parse(jstream, err); if (!err.empty()) { - throw std::runtime_error("Failed to parse header.json: " + err); + throw TrxIOError("Failed to parse header.json: " + err); } return root; } @@ -808,7 +769,7 @@ mio::shared_mmap_sink _create_memmap(std::string filename, return rw_mmap; } -// TODO: support FORTRAN ORDERING +// Known limitation: only C (row-major) ordering is supported; Fortran ordering is not. // template json assignHeader(const json &root) { @@ -842,16 +803,14 @@ void get_reference_info( const Eigen::RowVectorXi &dimensions) { // NOLINT(misc-use-internal-linkage,misc-include-cleaner) static_cast(affine); static_cast(dimensions); - // TODO: find a library to use for nifti and trk (MRtrix??) + // Known limitation: NIfTI support is partially addressed by nifti_io.cpp; TRK is not yet supported. // if (reference.find(".nii") != std::string::npos) // { // } if (reference.find(".trk") != std::string::npos) { - // TODO: Create exception class - throw std::runtime_error("Trk reference not implemented"); + throw TrxError("Trk reference not implemented"); } - // TODO: Create exception class - throw std::runtime_error("Trk reference not implemented"); + throw TrxError("Trk reference not implemented"); } void copy_dir(const string &src, const string &dst) { @@ -862,16 +821,14 @@ void copy_dir(const string &src, const string &dst) { return; } - if (!trx::fs::create_directories(dst_path, ec) && ec) { - throw std::runtime_error(std::string("Could not create directory ") + dst); - } + mkdir_or_throw(dst); ec.clear(); const auto options = trx::fs::copy_options::recursive | trx::fs::copy_options::overwrite_existing | trx::fs::copy_options::skip_symlinks; trx::fs::copy(src_path, dst_path, options, ec); if (ec) { - throw std::runtime_error("Failed to copy directory: " + ec.message()); + throw TrxIOError("Failed to copy directory: " + ec.message()); } } @@ -879,7 +836,7 @@ void copy_file(const string &src, const string &dst) { std::error_code ec; trx::fs::copy_file(src, dst, trx::fs::copy_options::overwrite_existing, ec); if (ec) { - throw std::runtime_error(std::string("Failed to copy file ") + src + ": " + ec.message()); + throw TrxIOError(std::string("Failed to copy file ") + src + ": " + ec.message()); } } int rm_dir(const string &d) { @@ -936,11 +893,7 @@ std::string make_temp_dir(const std::string &prefix) { const trx::fs::path base_path(base_dir); std::error_code ec; if (!trx::fs::exists(base_path, ec)) { - ec.clear(); - trx::fs::create_directories(base_path, ec); - if (ec) { - throw std::runtime_error("Failed to create base temp directory: " + base_dir); - } + mkdir_or_throw(base_path.string()); } static std::mt19937_64 rng(std::random_device{}()); @@ -959,15 +912,15 @@ std::string make_temp_dir(const std::string &prefix) { return candidate.string(); } if (ec && ec != std::errc::file_exists) { - throw std::runtime_error("Failed to create temporary directory: " + ec.message()); + throw TrxIOError("Failed to create temporary directory: " + ec.message()); } } - throw std::runtime_error("Failed to create temporary directory"); + throw TrxIOError("Failed to create temporary directory"); } std::string extract_zip_to_directory(zip_t *zfolder) { if (zfolder == nullptr) { - throw std::invalid_argument("Zip archive pointer is null"); + throw TrxArgumentError("Zip archive pointer is null"); } const std::string root_dir = make_temp_dir("trx_zip"); const trx::fs::path normalized_root = trx::fs::path(root_dir).lexically_normal(); @@ -982,7 +935,7 @@ std::string extract_zip_to_directory(zip_t *zfolder) { const trx::fs::path entry_path(entry); if (entry_path.is_absolute()) { - throw std::runtime_error("Zip entry has absolute path: " + entry); + throw TrxIOError("Zip entry has absolute path: " + entry); } const trx::fs::path normalized_entry = entry_path.lexically_normal(); @@ -990,60 +943,42 @@ std::string extract_zip_to_directory(zip_t *zfolder) { const trx::fs::path normalized_out = out_path.lexically_normal(); if (!is_path_within(normalized_out, normalized_root)) { - throw std::runtime_error("Zip entry escapes temporary directory: " + entry); + throw TrxIOError("Zip entry escapes temporary directory: " + entry); } if (!entry.empty() && entry.back() == '/') { - std::error_code ec; - trx::fs::create_directories(normalized_out, ec); - if (ec) { - throw std::runtime_error("Failed to create directory: " + normalized_out.string()); - } + mkdir_or_throw(normalized_out.string()); continue; } - std::error_code ec; - trx::fs::create_directories(normalized_out.parent_path(), ec); - if (ec) { - throw std::runtime_error("Failed to create parent directory: " + normalized_out.parent_path().string()); - } + mkdir_or_throw(normalized_out.parent_path().string()); - zip_file_t *zf = zip_fopen_index(zfolder, i, ZIP_FL_UNCHANGED); - if (zf == nullptr) { - throw std::runtime_error("Failed to open zip entry: " + entry); + detail::ZipFile zf(zip_fopen_index(zfolder, i, ZIP_FL_UNCHANGED)); + if (!zf) { + throw TrxIOError("Failed to open zip entry: " + entry); } std::ofstream out(normalized_out.string(), std::ios::binary); if (!out.is_open()) { - zip_fclose(zf); - throw std::runtime_error("Failed to open output file: " + normalized_out.string()); + throw TrxIOError("Failed to open output file: " + normalized_out.string()); } std::array buffer{}; zip_int64_t nbytes = 0; - while ((nbytes = zip_fread(zf, buffer.data(), buffer.size())) > 0) { + while ((nbytes = zip_fread(zf.get(), buffer.data(), buffer.size())) > 0) { out.write(buffer.data(), nbytes); if (!out) { - out.close(); - zip_fclose(zf); - throw std::runtime_error("Failed to write to output file: " + normalized_out.string()); + throw TrxIOError("Failed to write to output file: " + normalized_out.string()); } } if (nbytes < 0) { - out.close(); - zip_fclose(zf); - throw std::runtime_error("Failed to read data from zip entry: " + entry); + throw TrxIOError("Failed to read data from zip entry: " + entry); } out.flush(); if (!out) { - out.close(); - zip_fclose(zf); - throw std::runtime_error("Failed to flush output file: " + normalized_out.string()); + throw TrxIOError("Failed to flush output file: " + normalized_out.string()); } - - out.close(); - zip_fclose(zf); } return root_dir; @@ -1057,7 +992,7 @@ void zip_from_folder(zip_t *zf, std::error_code ec; for (trx::fs::recursive_directory_iterator it(directory, ec), end; it != end; it.increment(ec)) { if (ec) { - throw std::runtime_error("Failed to read directory: " + directory); + throw TrxIOError("Failed to read directory: " + directory); } const trx::fs::path current = it->path(); const std::string zip_fname = rm_root(root, current.string()); @@ -1073,7 +1008,7 @@ void zip_from_folder(zip_t *zf, const std::string fullpath = current.string(); zip_source_t *source = zip_source_file(zf, fullpath.c_str(), 0, 0); if (source == nullptr) { - throw std::runtime_error(std::string("Error adding file ") + zip_fname + ": " + zip_strerror(zf)); + throw TrxIOError(std::string("Error adding file ") + zip_fname + ": " + zip_strerror(zf)); } if (skip && skip->find(zip_fname) != skip->end()) { zip_source_free(source); @@ -1082,11 +1017,11 @@ void zip_from_folder(zip_t *zf, const zip_int64_t file_idx = zip_file_add(zf, zip_fname.c_str(), source, ZIP_FL_ENC_UTF_8); if (file_idx < 0) { zip_source_free(source); - throw std::runtime_error(std::string("Error adding file ") + zip_fname + ": " + zip_strerror(zf)); + throw TrxIOError(std::string("Error adding file ") + zip_fname + ": " + zip_strerror(zf)); } const zip_int32_t compression = static_cast(compression_standard); if (zip_set_file_compression(zf, file_idx, compression, 0) < 0) { - throw std::runtime_error(std::string("Error setting compression for ") + zip_fname + ": " + zip_strerror(zf)); + throw TrxIOError(std::string("Error setting compression for ") + zip_fname + ": " + zip_strerror(zf)); } } } @@ -1126,7 +1061,7 @@ void write_typed_array_file(const std::string &path, const TypedArray &arr) { const auto bytes = arr.to_bytes(); std::ofstream out(path, std::ios::binary | std::ios::out | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to open output file: " + path); + throw TrxIOError("Failed to open output file: " + path); } if (bytes.data && bytes.size > 0) { out.write(reinterpret_cast(bytes.data), static_cast(bytes.size)); @@ -1138,10 +1073,10 @@ void write_typed_array_file(const std::string &path, const TypedArray &arr) { void AnyTrxFile::for_each_positions_chunk(size_t chunk_bytes, const PositionsChunkCallback &fn) const { if (positions.empty()) { - throw std::runtime_error("TRX positions are empty."); + throw TrxFormatError("TRX positions are empty."); } if (positions.cols != 3) { - throw std::runtime_error("Positions must have 3 columns."); + throw TrxFormatError("Positions must have 3 columns."); } if (!fn) { return; @@ -1167,10 +1102,10 @@ void AnyTrxFile::for_each_positions_chunk(size_t chunk_bytes, const PositionsChu void AnyTrxFile::for_each_positions_chunk_mutable(size_t chunk_bytes, const PositionsChunkMutableCallback &fn) { if (positions.empty()) { - throw std::runtime_error("TRX positions are empty."); + throw TrxFormatError("TRX positions are empty."); } if (positions.cols != 3) { - throw std::runtime_error("Positions must have 3 columns."); + throw TrxFormatError("Positions must have 3 columns."); } if (!fn) { return; @@ -1198,10 +1133,10 @@ PositionsOutputInfo prepare_positions_output(const AnyTrxFile &input, const std::string &output_directory, const PrepareOutputOptions &options) { if (input.positions.empty() || input.offsets.empty()) { - throw std::runtime_error("Input TRX missing positions/offsets."); + throw TrxFormatError("Input TRX missing positions/offsets."); } if (input.positions.cols != 3) { - throw std::runtime_error("Positions must have 3 columns."); + throw TrxFormatError("Positions must have 3 columns."); } std::error_code ec; @@ -1209,20 +1144,16 @@ PositionsOutputInfo prepare_positions_output(const AnyTrxFile &input, if (options.overwrite_existing) { trx::fs::remove_all(output_directory, ec); } else { - throw std::runtime_error("Output directory already exists: " + output_directory); + throw TrxIOError("Output directory already exists: " + output_directory); } } - ec.clear(); - trx::fs::create_directories(output_directory, ec); - if (ec) { - throw std::runtime_error("Failed to create output directory: " + output_directory); - } + mkdir_or_throw(output_directory); const std::string header_path = output_directory + SEPARATOR + "header.json"; { std::ofstream out(header_path, std::ios::out | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to write header.json to: " + header_path); + throw TrxIOError("Failed to write header.json to: " + header_path); } out << input.header.dump() << std::endl; } @@ -1275,21 +1206,21 @@ PositionsOutputInfo prepare_positions_output(const AnyTrxFile &input, void merge_trx_shards(const MergeTrxShardsOptions &options) { if (options.shard_directories.empty()) { - throw std::invalid_argument("merge_trx_shards requires at least one shard directory"); + throw TrxArgumentError("merge_trx_shards requires at least one shard directory"); } auto read_header = [](const std::string &dir) { const std::string path = dir + SEPARATOR + "header.json"; std::ifstream in(path); if (!in.is_open()) { - throw std::runtime_error("Failed to open shard header: " + path); + throw TrxIOError("Failed to open shard header: " + path); } std::stringstream ss; ss << in.rdbuf(); std::string err; json parsed = json::parse(ss.str(), err); if (!err.empty()) { - throw std::runtime_error("Failed to parse shard header " + path + ": " + err); + throw TrxIOError("Failed to parse shard header " + path + ": " + err); } return parsed; }; @@ -1314,11 +1245,11 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { auto append_binary = [](const std::string &dst, const std::string &src) { std::ifstream in(src, std::ios::binary); if (!in.is_open()) { - throw std::runtime_error("Failed to open source for append: " + src); + throw TrxIOError("Failed to open source for append: " + src); } std::ofstream out(dst, std::ios::binary | std::ios::app); if (!out.is_open()) { - throw std::runtime_error("Failed to open destination for append: " + dst); + throw TrxIOError("Failed to open destination for append: " + dst); } std::vector buffer(1 << 20); while (in) { @@ -1333,11 +1264,11 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { auto append_offsets_with_base = [](const std::string &dst, const std::string &src, uint64_t base_vertices, bool skip_first) { std::ifstream in(src, std::ios::binary); if (!in.is_open()) { - throw std::runtime_error("Failed to open source offsets: " + src); + throw TrxIOError("Failed to open source offsets: " + src); } std::ofstream out(dst, std::ios::binary | std::ios::app); if (!out.is_open()) { - throw std::runtime_error("Failed to open destination offsets: " + dst); + throw TrxIOError("Failed to open destination offsets: " + dst); } constexpr size_t kChunkElems = (8 * 1024 * 1024) / sizeof(uint64_t); std::vector buffer(kChunkElems); @@ -1350,7 +1281,7 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { break; } if (bytes % static_cast(sizeof(uint64_t)) != 0) { - throw std::runtime_error("Offsets file has invalid byte count: " + src); + throw TrxFormatError("Offsets file has invalid byte count: " + src); } const size_t count = static_cast(bytes) / sizeof(uint64_t); size_t start_index = 0; @@ -1374,11 +1305,11 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { auto append_group_indices_with_base = [](const std::string &dst, const std::string &src, uint32_t base_streamlines) { std::ifstream in(src, std::ios::binary); if (!in.is_open()) { - throw std::runtime_error("Failed to open source group file: " + src); + throw TrxIOError("Failed to open source group file: " + src); } std::ofstream out(dst, std::ios::binary | std::ios::app); if (!out.is_open()) { - throw std::runtime_error("Failed to open destination group file: " + dst); + throw TrxIOError("Failed to open destination group file: " + dst); } constexpr size_t kChunkElems = (8 * 1024 * 1024) / sizeof(uint32_t); std::vector buffer(kChunkElems); @@ -1390,7 +1321,7 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { break; } if (bytes % static_cast(sizeof(uint32_t)) != 0) { - throw std::runtime_error("Group file has invalid byte count: " + src); + throw TrxFormatError("Group file has invalid byte count: " + src); } const size_t count = static_cast(bytes) / sizeof(uint32_t); for (size_t i = 0; i < count; ++i) { @@ -1408,11 +1339,11 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { return files; } if (!trx::fs::is_directory(path, ec)) { - throw std::runtime_error("Expected directory for subdir: " + path.string()); + throw TrxFormatError("Expected directory for subdir: " + path.string()); } for (trx::fs::directory_iterator it(path, ec), end; it != end; it.increment(ec)) { if (ec) { - throw std::runtime_error("Failed to read directory: " + path.string()); + throw TrxIOError("Failed to read directory: " + path.string()); } if (!it->is_regular_file()) { continue; @@ -1426,32 +1357,29 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { auto ensure_schema_match = [&](const std::string &subdir, const std::vector &schema_files, const std::string &shard) { const auto shard_files = list_subdir_files(shard, subdir); if (shard_files != schema_files) { - throw std::runtime_error("Shard schema mismatch for subdir '" + subdir + "': " + shard); + throw TrxFormatError("Shard schema mismatch for subdir '" + subdir + "': " + shard); } }; std::error_code ec; for (const auto &dir : options.shard_directories) { if (!trx::fs::exists(dir, ec) || !trx::fs::is_directory(dir, ec)) { - throw std::runtime_error("Shard directory does not exist: " + dir); + throw TrxFormatError("Shard directory does not exist: " + dir); } } const std::string output_dir = options.output_directory ? options.output_path : make_temp_dir("trx_merge"); if (trx::fs::exists(output_dir, ec)) { if (!options.overwrite_existing) { - throw std::runtime_error("Output already exists: " + output_dir); + throw TrxIOError("Output already exists: " + output_dir); } trx::fs::remove_all(output_dir, ec); } - trx::fs::create_directories(output_dir, ec); - if (ec) { - throw std::runtime_error("Failed to create output directory: " + output_dir); - } + mkdir_or_throw(output_dir); for (const auto &dir : options.shard_directories) { if (trx::fs::exists(dir + SEPARATOR + "dpg", ec)) { - throw std::runtime_error("merge_trx_shards currently does not support dpg/ merges"); + throw TrxArgumentError("merge_trx_shards currently does not support dpg/ merges"); } } @@ -1459,10 +1387,10 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { const std::string first_positions = find_file_with_prefix(options.shard_directories.front(), "positions."); const std::string first_offsets = find_file_with_prefix(options.shard_directories.front(), "offsets."); if (first_positions.empty() || first_offsets.empty()) { - throw std::runtime_error("Shard missing positions/offsets: " + options.shard_directories.front()); + throw TrxFormatError("Shard missing positions/offsets: " + options.shard_directories.front()); } if (get_ext(first_offsets) != "uint64") { - throw std::runtime_error("merge_trx_shards currently requires offsets.uint64"); + throw TrxArgumentError("merge_trx_shards currently requires offsets.uint64"); } const std::string positions_filename = trx::fs::path(first_positions).filename().string(); @@ -1472,13 +1400,13 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { { std::ofstream clear_positions(positions_out, std::ios::binary | std::ios::out | std::ios::trunc); if (!clear_positions.is_open()) { - throw std::runtime_error("Failed to create output positions file: " + positions_out); + throw TrxIOError("Failed to create output positions file: " + positions_out); } } { std::ofstream clear_offsets(offsets_out, std::ios::binary | std::ios::out | std::ios::trunc); if (!clear_offsets.is_open()) { - throw std::runtime_error("Failed to create output offsets file: " + offsets_out); + throw TrxIOError("Failed to create output offsets file: " + offsets_out); } } @@ -1497,20 +1425,20 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { for (const auto &name : dps_schema) { std::ofstream clear_file(output_dir + SEPARATOR + "dps" + SEPARATOR + name, std::ios::binary | std::ios::out | std::ios::trunc); if (!clear_file.is_open()) { - throw std::runtime_error("Failed to create merged dps file: " + name); + throw TrxIOError("Failed to create merged dps file: " + name); } } for (const auto &name : dpv_schema) { std::ofstream clear_file(output_dir + SEPARATOR + "dpv" + SEPARATOR + name, std::ios::binary | std::ios::out | std::ios::trunc); if (!clear_file.is_open()) { - throw std::runtime_error("Failed to create merged dpv file: " + name); + throw TrxIOError("Failed to create merged dpv file: " + name); } } for (const auto &name : groups_schema) { std::ofstream clear_file(output_dir + SEPARATOR + "groups" + SEPARATOR + name, std::ios::binary | std::ios::out | std::ios::trunc); if (!clear_file.is_open()) { - throw std::runtime_error("Failed to create merged group file: " + name); + throw TrxIOError("Failed to create merged group file: " + name); } } @@ -1529,13 +1457,13 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { const std::string shard_positions = find_file_with_prefix(shard_dir, "positions."); const std::string shard_offsets = find_file_with_prefix(shard_dir, "offsets."); if (shard_positions.empty() || shard_offsets.empty()) { - throw std::runtime_error("Shard missing positions/offsets: " + shard_dir); + throw TrxFormatError("Shard missing positions/offsets: " + shard_dir); } if (trx::fs::path(shard_positions).filename().string() != positions_filename) { - throw std::runtime_error("Shard positions dtype mismatch: " + shard_dir); + throw TrxFormatError("Shard positions dtype mismatch: " + shard_dir); } if (trx::fs::path(shard_offsets).filename().string() != offsets_filename) { - throw std::runtime_error("Shard offsets dtype mismatch: " + shard_dir); + throw TrxFormatError("Shard offsets dtype mismatch: " + shard_dir); } append_binary(positions_out, shard_positions); @@ -1549,7 +1477,7 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { } for (const auto &name : groups_schema) { if (total_streamlines > static_cast(std::numeric_limits::max())) { - throw std::runtime_error("Group index offset exceeds uint32 range during merge"); + throw TrxFormatError("Group index offset exceeds uint32 range during merge"); } append_group_indices_with_base( output_dir + SEPARATOR + "groups" + SEPARATOR + name, @@ -1567,7 +1495,7 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { const std::string merged_header_path = output_dir + SEPARATOR + "header.json"; std::ofstream out(merged_header_path, std::ios::out | std::ios::trunc); if (!out.is_open()) { - throw std::runtime_error("Failed to write merged header: " + merged_header_path); + throw TrxIOError("Failed to write merged header: " + merged_header_path); } out << merged_header.dump() << std::endl; } @@ -1578,22 +1506,16 @@ void merge_trx_shards(const MergeTrxShardsOptions &options) { const trx::fs::path archive_path(options.output_path); if (archive_path.has_parent_path()) { - std::error_code parent_ec; - trx::fs::create_directories(archive_path.parent_path(), parent_ec); - if (parent_ec) { - throw std::runtime_error("Could not create archive parent directory: " + archive_path.parent_path().string()); - } + mkdir_or_throw(archive_path.parent_path().string()); } int errorp = 0; - zip_t *zf = zip_open(options.output_path.c_str(), ZIP_CREATE + ZIP_TRUNCATE, &errorp); - if (zf == nullptr) { - throw std::runtime_error("Could not open archive " + options.output_path + ": " + strerror(errorp)); - } - zip_from_folder(zf, output_dir, output_dir, options.compression_standard, nullptr); - if (zip_close(zf) != 0) { - throw std::runtime_error("Unable to close archive " + options.output_path + ": " + zip_strerror(zf)); + detail::ZipArchive zf(zip_open(options.output_path.c_str(), ZIP_CREATE + ZIP_TRUNCATE, &errorp)); + if (!zf) { + throw TrxIOError("Could not open archive " + options.output_path + ": " + strerror(errorp)); } + zip_from_folder(zf.get(), output_dir, output_dir, options.compression_standard, nullptr); + zf.commit(options.output_path); rm_dir(output_dir); } }; // namespace trx \ No newline at end of file diff --git a/tests/test_trx_anytrxfile.cpp b/tests/test_trx_anytrxfile.cpp index 9803d53..a9d51f7 100644 --- a/tests/test_trx_anytrxfile.cpp +++ b/tests/test_trx_anytrxfile.cpp @@ -1,8 +1,6 @@ #include #include -#define private public #include -#undef private #include #include @@ -419,7 +417,7 @@ TEST(AnyTrxFile, MissingHeaderCountsThrows) { header_obj.erase("NB_VERTICES"); write_header_file(corrupt_dir, json(header_obj)); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -433,7 +431,7 @@ TEST(AnyTrxFile, WrongPositionsDimThrows) { const fs::path positions = find_file_with_prefix(corrupt_dir, "positions"); rename_with_new_dim(positions, 4); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -448,7 +446,7 @@ TEST(AnyTrxFile, UnsupportedPositionsDtypeThrows) { const std::string ext = get_ext(positions.string()); rename_with_new_ext(positions, pick_int_dtype_same_size(ext)); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxDTypeError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -462,7 +460,7 @@ TEST(AnyTrxFile, WrongOffsetsDimThrows) { const fs::path offsets = find_file_with_prefix(corrupt_dir, "offsets"); rename_with_new_dim(offsets, 2); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -477,7 +475,7 @@ TEST(AnyTrxFile, UnsupportedOffsetsDtypeThrows) { const std::string ext = get_ext(offsets.string()); rename_with_new_ext(offsets, pick_int_dtype_same_size(ext)); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxDTypeError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -499,7 +497,7 @@ TEST(AnyTrxFile, WrongDpsDimThrows) { const fs::path dps_file = find_first_file_recursive(dps_dir); rename_with_new_dim(dps_file, 2); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -521,7 +519,7 @@ TEST(AnyTrxFile, WrongDpvDimThrows) { const fs::path dpv_file = find_first_file_recursive(dpv_dir); rename_with_new_dim(dpv_file, 2); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -542,7 +540,7 @@ TEST(AnyTrxFile, WrongDpgDimThrows) { const fs::path dpg_file = find_first_file_recursive(dpg_dir); rename_with_new_dim(dpg_file, 2); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -564,7 +562,7 @@ TEST(AnyTrxFile, UnsupportedGroupDtypeThrows) { const fs::path group_file = find_first_file_recursive(groups_dir); rename_with_new_ext(group_file, "int32"); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxDTypeError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -583,7 +581,7 @@ TEST(AnyTrxFile, InvalidEntryThrows) { out.write(value_bytes.data(), static_cast(value_bytes.size())); out.close(); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -598,7 +596,7 @@ TEST(AnyTrxFile, MissingEssentialDataThrows) { std::error_code ec; fs::remove(positions, ec); - EXPECT_THROW(load_any(corrupt_dir.string()), std::invalid_argument); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); fs::remove_all(temp_root, ec); } @@ -631,7 +629,7 @@ TEST(AnyTrxFile, OffsetsOverflowThrows) { out.write(data_bytes.data(), static_cast(data_bytes.size())); out.close(); - EXPECT_THROW(load_any(corrupt_dir.string()), std::runtime_error); + EXPECT_THROW(load_any(corrupt_dir.string()), trx::TrxFormatError); std::error_code ec; fs::remove_all(temp_root, ec); @@ -644,7 +642,7 @@ TEST(AnyTrxFile, SaveRejectsUnsupportedExtension) { const auto temp_dir = make_temp_test_dir("trx_any_save_badext"); const fs::path out_path = temp_dir / "bad.txt"; - EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), std::invalid_argument); + EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), trx::TrxDTypeError); trx.close(); std::error_code ec; @@ -659,7 +657,7 @@ TEST(AnyTrxFile, SaveRejectsMissingOffsets) { trx.offsets = TypedArray(); const auto temp_dir = make_temp_test_dir("trx_any_save_no_offsets"); const fs::path out_path = temp_dir / "missing_offsets.trx"; - EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), std::runtime_error); + EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), trx::TrxFormatError); trx.close(); std::error_code ec; @@ -674,7 +672,7 @@ TEST(AnyTrxFile, SaveRejectsMissingDecodedOffsets) { trx.offsets_u64.clear(); const auto temp_dir = make_temp_test_dir("trx_any_save_no_offsets_u64"); const fs::path out_path = temp_dir / "missing_offsets_u64.trx"; - EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), std::runtime_error); + EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), trx::TrxFormatError); trx.close(); std::error_code ec; @@ -692,7 +690,7 @@ TEST(AnyTrxFile, SaveRejectsStreamlineCountMismatch) { const auto temp_dir = make_temp_test_dir("trx_any_save_bad_streamlines"); const fs::path out_path = temp_dir / "bad_streamlines.trx"; - EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), std::runtime_error); + EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), trx::TrxFormatError); trx.close(); std::error_code ec; @@ -710,7 +708,7 @@ TEST(AnyTrxFile, SaveRejectsVertexCountMismatch) { const auto temp_dir = make_temp_test_dir("trx_any_save_bad_vertices"); const fs::path out_path = temp_dir / "bad_vertices.trx"; - EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), std::runtime_error); + EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), trx::TrxFormatError); trx.close(); std::error_code ec; @@ -728,7 +726,7 @@ TEST(AnyTrxFile, SaveRejectsNonMonotonicOffsets) { const auto temp_dir = make_temp_test_dir("trx_any_save_non_mono"); const fs::path out_path = temp_dir / "non_mono.trx"; - EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), std::runtime_error); + EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), trx::TrxFormatError); trx.close(); std::error_code ec; @@ -746,7 +744,7 @@ TEST(AnyTrxFile, SaveRejectsPositionsRowMismatch) { const auto temp_dir = make_temp_test_dir("trx_any_save_bad_positions"); const fs::path out_path = temp_dir / "bad_positions.trx"; - EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), std::runtime_error); + EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), trx::TrxFormatError); trx.close(); std::error_code ec; @@ -758,12 +756,12 @@ TEST(AnyTrxFile, SaveRejectsMissingBackingDirectory) { const fs::path gs_trx = gs_dir / "gs_fldr.trx"; auto trx = load_any(gs_trx.string()); - trx._backing_directory.clear(); - trx._uncompressed_folder_handle.clear(); + trx.backing_directory().clear(); + trx.uncompressed_folder_handle().clear(); const auto temp_dir = make_temp_test_dir("trx_any_save_no_backing"); const fs::path out_path = temp_dir / "no_backing.trx"; - EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), std::runtime_error); + EXPECT_THROW(trx.save(out_path.string(), ZIP_CM_STORE), trx::TrxIOError); trx.close(); std::error_code ec; @@ -858,7 +856,7 @@ TEST(AnyTrxFile, MergeTrxShardsSchemaMismatchThrows) { options.shard_directories = {shard1.string(), shard2.string()}; options.output_path = output_dir.string(); options.output_directory = true; - EXPECT_THROW(merge_trx_shards(options), std::runtime_error); + EXPECT_THROW(merge_trx_shards(options), trx::TrxFormatError); fs::remove_all(temp_root, ec); } @@ -999,7 +997,7 @@ TEST(AnyTrxFile, MergeTrxShardsRejectsDpg) { options.shard_directories = {shard1.string(), shard2.string()}; options.output_path = (temp_root / "merged").string(); options.output_directory = true; - EXPECT_THROW(merge_trx_shards(options), std::runtime_error); + EXPECT_THROW(merge_trx_shards(options), trx::TrxArgumentError); fs::remove_all(temp_root, ec); } @@ -1021,7 +1019,7 @@ TEST(AnyTrxFile, PreparePositionsOutputOverwriteFalseThrows) { PrepareOutputOptions options; options.overwrite_existing = false; - EXPECT_THROW(prepare_positions_output(input, output_dir.string(), options), std::runtime_error); + EXPECT_THROW(prepare_positions_output(input, output_dir.string(), options), trx::TrxIOError); input.close(); fs::remove_all(temp_root, ec); diff --git a/tests/test_trx_io.cpp b/tests/test_trx_io.cpp index 46c492a..2c5bac0 100644 --- a/tests/test_trx_io.cpp +++ b/tests/test_trx_io.cpp @@ -711,20 +711,20 @@ TEST(TrxFileIo, add_dps_from_text_errors) { const fs::path input_path = tmp_dir / "dps.txt"; write_text_file(input_path, "1.0"); - EXPECT_THROW(trx.add_dps_from_text("", "float32", input_path.string()), std::invalid_argument); - EXPECT_THROW(trx.add_dps_from_text("weight", "badtype", input_path.string()), std::invalid_argument); - EXPECT_THROW(trx.add_dps_from_text("weight", "int32", input_path.string()), std::invalid_argument); + EXPECT_THROW(trx.add_dps_from_text("", "float32", input_path.string()), trx::TrxArgumentError); + EXPECT_THROW(trx.add_dps_from_text("weight", "badtype", input_path.string()), trx::TrxDTypeError); + EXPECT_THROW(trx.add_dps_from_text("weight", "int32", input_path.string()), trx::TrxDTypeError); - EXPECT_THROW(trx.add_dps_from_text("weight", "float32", (tmp_dir / "missing.txt").string()), std::runtime_error); + EXPECT_THROW(trx.add_dps_from_text("weight", "float32", (tmp_dir / "missing.txt").string()), trx::TrxIOError); write_text_file(input_path, "1.0 abc"); - EXPECT_THROW(trx.add_dps_from_text("weight", "float32", input_path.string()), std::runtime_error); + EXPECT_THROW(trx.add_dps_from_text("weight", "float32", input_path.string()), trx::TrxFormatError); write_text_file(input_path, "1.0"); - EXPECT_THROW(trx.add_dps_from_text("weight", "float32", input_path.string()), std::runtime_error); + EXPECT_THROW(trx.add_dps_from_text("weight", "float32", input_path.string()), trx::TrxFormatError); trx::TrxFile empty; - EXPECT_THROW(empty.add_dps_from_text("weight", "float32", input_path.string()), std::runtime_error); + EXPECT_THROW(empty.add_dps_from_text("weight", "float32", input_path.string()), trx::TrxIOError); } TEST(TrxFileIo, add_dpv_from_tsf_success) { @@ -763,34 +763,34 @@ TEST(TrxFileIo, add_dpv_from_tsf_errors) { const fs::path input_path = tmp_dir / "dpv.tsf"; write_tsf_text_file(input_path, build_tsf_contents({{0.1, 0.2}, {0.3}})); - EXPECT_THROW(trx.add_dpv_from_tsf("", "float32", input_path.string()), std::invalid_argument); - EXPECT_THROW(trx.add_dpv_from_tsf("signal", "badtype", input_path.string()), std::invalid_argument); - EXPECT_THROW(trx.add_dpv_from_tsf("signal", "int32", input_path.string()), std::invalid_argument); + EXPECT_THROW(trx.add_dpv_from_tsf("", "float32", input_path.string()), trx::TrxArgumentError); + EXPECT_THROW(trx.add_dpv_from_tsf("signal", "badtype", input_path.string()), trx::TrxDTypeError); + EXPECT_THROW(trx.add_dpv_from_tsf("signal", "int32", input_path.string()), trx::TrxDTypeError); - EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", (tmp_dir / "missing.tsf").string()), std::runtime_error); + EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", (tmp_dir / "missing.tsf").string()), trx::TrxIOError); write_tsf_text_file(input_path, "0.1 0.2 abc"); - EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", input_path.string()), std::runtime_error); + EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", input_path.string()), trx::TrxFormatError); write_tsf_text_file(input_path, build_tsf_contents({{0.1}, {0.2, 0.3}})); - EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", input_path.string()), std::runtime_error); + EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", input_path.string()), trx::TrxFormatError); write_tsf_text_file(input_path, build_tsf_contents({{0.1, 0.2}, {0.3}})); - EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", input_path.string()), std::runtime_error); + EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", input_path.string()), trx::TrxFormatError); write_text_file(input_path, "mrtrix track scalars\nfile: . 0\ndatatype: Float32LE\ntimestamp: 0\n0.1 0.2"); - EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", input_path.string()), std::runtime_error); + EXPECT_THROW(trx.add_dpv_from_tsf("signal", "float32", input_path.string()), trx::TrxFormatError); trx::TrxFile empty; - EXPECT_THROW(empty.add_dpv_from_tsf("signal", "float32", input_path.string()), std::runtime_error); + EXPECT_THROW(empty.add_dpv_from_tsf("signal", "float32", input_path.string()), trx::TrxFormatError); trx::TrxFile no_dir(4, 2); set_streamline_lengths(no_dir.streamlines.get(), {2, 2}); // Intentional white-box access: there is no public API to construct a TrxFile // with valid streamlines but without an uncompressed folder. This test verifies // that add_dpv_from_tsf fails in that specific internal state. - no_dir._uncompressed_folder_handle.clear(); - EXPECT_THROW(no_dir.add_dpv_from_tsf("signal", "float32", input_path.string()), std::runtime_error); + no_dir.uncompressed_folder_handle().clear(); + EXPECT_THROW(no_dir.add_dpv_from_tsf("signal", "float32", input_path.string()), trx::TrxIOError); } TEST(TrxFileIo, export_dpv_to_tsf_success) { @@ -831,11 +831,11 @@ TEST(TrxFileIo, export_dpv_to_tsf_errors) { const fs::path tmp_dir = make_temp_test_dir("trx_export_tsf_err"); const fs::path output_path = tmp_dir / "signal.tsf"; - EXPECT_THROW(trx.export_dpv_to_tsf("", output_path.string(), "1"), std::invalid_argument); - EXPECT_THROW(trx.export_dpv_to_tsf("signal", output_path.string(), ""), std::invalid_argument); - EXPECT_THROW(trx.export_dpv_to_tsf("signal", output_path.string(), "1", "int32"), std::invalid_argument); - EXPECT_THROW(trx.export_dpv_to_tsf("missing", output_path.string(), "1"), std::runtime_error); + EXPECT_THROW(trx.export_dpv_to_tsf("", output_path.string(), "1"), trx::TrxArgumentError); + EXPECT_THROW(trx.export_dpv_to_tsf("signal", output_path.string(), ""), trx::TrxArgumentError); + EXPECT_THROW(trx.export_dpv_to_tsf("signal", output_path.string(), "1", "int32"), trx::TrxDTypeError); + EXPECT_THROW(trx.export_dpv_to_tsf("missing", output_path.string(), "1"), trx::TrxFormatError); trx::TrxFile empty; - EXPECT_THROW(empty.export_dpv_to_tsf("signal", output_path.string(), "1"), std::runtime_error); + EXPECT_THROW(empty.export_dpv_to_tsf("signal", output_path.string(), "1"), trx::TrxFormatError); } diff --git a/tests/test_trx_mmap.cpp b/tests/test_trx_mmap.cpp index 7eb443b..09e8224 100644 --- a/tests/test_trx_mmap.cpp +++ b/tests/test_trx_mmap.cpp @@ -351,12 +351,12 @@ TEST(TrxFileMemmap, detect_positions_scalar_type_fallback) { ASSERT_TRUE(out.is_open()); out.close(); - EXPECT_THROW(trx::detect_positions_scalar_type(invalid_dir.string(), TrxScalarType::Float64), std::invalid_argument); + EXPECT_THROW(trx::detect_positions_scalar_type(invalid_dir.string(), TrxScalarType::Float64), trx::TrxError); } TEST(TrxFileMemmap, detect_positions_scalar_type_missing_path) { const fs::path missing = fs::path(make_temp_test_dir("trx_scalar_missing")) / "nope"; - EXPECT_THROW(trx::detect_positions_scalar_type(missing.string(), TrxScalarType::Float32), std::runtime_error); + EXPECT_THROW(trx::detect_positions_scalar_type(missing.string(), TrxScalarType::Float32), trx::TrxError); } TEST(TrxFileMemmap, open_zip_for_read_generic_fallback) { @@ -416,36 +416,36 @@ TEST(TrxFileMemmap, __split_ext_with_dimensionality) { { try { output = trx::detail::_split_ext_with_dimensionality(fn3); - } catch (const std::invalid_argument &e) { + } catch (const trx::TrxError &e) { EXPECT_STREQ("Invalid filename", e.what()); throw; } }, - std::invalid_argument); + trx::TrxFormatError); const std::string fn4 = "mean_fa.5.4.int32"; EXPECT_THROW( { try { output = trx::detail::_split_ext_with_dimensionality(fn4); - } catch (const std::invalid_argument &e) { + } catch (const trx::TrxError &e) { EXPECT_STREQ("Invalid filename", e.what()); throw; } }, - std::invalid_argument); + trx::TrxFormatError); const std::string fn5 = "mean_fa.fa"; EXPECT_THROW( { try { output = trx::detail::_split_ext_with_dimensionality(fn5); - } catch (const std::invalid_argument &e) { - EXPECT_STREQ("Unsupported file extension", e.what()); + } catch (const trx::TrxDTypeError &e) { + EXPECT_TRUE(std::string(e.what()).find("Unsupported file extension") != std::string::npos); throw; } }, - std::invalid_argument); + trx::TrxDTypeError); } // Mirrors trx/tests/test_memmap.py::test__compute_lengths. @@ -711,7 +711,7 @@ TEST(TrxFileMemmap, load_missing_trx_throws) { const auto memmap_dir = resolve_memmap_test_data_dir(root); const fs::path missing_trx = memmap_dir / "dontexist.trx"; - EXPECT_THROW(trx::TrxReader(missing_trx.string()), std::runtime_error); + EXPECT_THROW(trx::TrxReader(missing_trx.string()), trx::TrxError); } // validates C++ TrxFile initialization. @@ -841,7 +841,7 @@ TEST(TrxFileMemmap, query_aabb_rejects_bad_aabb_size) { std::array max_corner{1.0f, 1.0f, 1.0f}; std::vector> bad_aabbs(1); - EXPECT_THROW(trx->query_aabb(min_corner, max_corner, &bad_aabbs), std::invalid_argument); + EXPECT_THROW(trx->query_aabb(min_corner, max_corner, &bad_aabbs), trx::TrxError); trx->close(); } @@ -888,7 +888,7 @@ TEST(TrxFileMemmap, subset_streamlines_empty) { TEST(TrxFileMemmap, subset_streamlines_out_of_range) { auto trx = create_small_trx(); std::vector ids{99}; - EXPECT_THROW(trx->subset_streamlines(ids), std::invalid_argument); + EXPECT_THROW(trx->subset_streamlines(ids), trx::TrxError); trx->close(); } @@ -926,10 +926,10 @@ TEST(TrxFileMemmap, dpg_api_invalid_inputs) { auto trx = create_small_trx(); std::vector values{1.0f, 2.0f, 3.0f}; - EXPECT_THROW(trx->add_dpg_from_vector("", "dpg", "float32", values), std::invalid_argument); - EXPECT_THROW(trx->add_dpg_from_vector("g1", "", "float32", values), std::invalid_argument); - EXPECT_THROW(trx->add_dpg_from_vector("g1", "dpg", "int8", values), std::invalid_argument); - EXPECT_THROW(trx->add_dpg_from_vector("g1", "dpg", "float32", values, 2, 2), std::invalid_argument); + EXPECT_THROW(trx->add_dpg_from_vector("", "dpg", "float32", values), trx::TrxError); + EXPECT_THROW(trx->add_dpg_from_vector("g1", "", "float32", values), trx::TrxError); + EXPECT_THROW(trx->add_dpg_from_vector("g1", "dpg", "int8", values), trx::TrxError); + EXPECT_THROW(trx->add_dpg_from_vector("g1", "dpg", "float32", values, 2, 2), trx::TrxError); trx->close(); } diff --git a/tests/test_trx_trxfile.cpp b/tests/test_trx_trxfile.cpp index 2336535..28a9fb8 100644 --- a/tests/test_trx_trxfile.cpp +++ b/tests/test_trx_trxfile.cpp @@ -2,9 +2,7 @@ #include #include -#define private public #include -#undef private #include #include @@ -417,7 +415,7 @@ TEST(TrxFileTpp, NormalizeForSaveRejectsNonMonotonicOffsets) { src->streamlines->_offsets(1) = 5; src->streamlines->_offsets(2) = 4; - EXPECT_THROW(src->normalize_for_save(), std::runtime_error); + EXPECT_THROW(src->normalize_for_save(), trx::TrxError); src->close(); @@ -456,7 +454,7 @@ TEST(TrxFileTpp, LoadFromDirectoryMissingHeader) { std::ofstream f(dummy.string(), std::ios::binary); f.close(); - EXPECT_THROW(TrxFile::load_from_directory(tmp_dir.string()), std::runtime_error); + EXPECT_THROW(TrxFile::load_from_directory(tmp_dir.string()), trx::TrxError); std::error_code ec; fs::remove_all(tmp_dir, ec); From 8de6a88b886d6b7f90b9740d04e3470c847d7ee7 Mon Sep 17 00:00:00 2001 From: mattcieslak Date: Sun, 22 Feb 2026 15:53:32 -0500 Subject: [PATCH 2/7] use streamlines offsets dimensions for DPV _offsets --- include/trx/trx.tpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/trx/trx.tpp b/include/trx/trx.tpp index 67d405a..44dfe42 100644 --- a/include/trx/trx.tpp +++ b/include/trx/trx.tpp @@ -420,7 +420,8 @@ TrxFile
::_create_trx_from_pointer(json header, } trx->data_per_vertex[base]->mmap_pos = trx::_create_memmap(filename, shape, "r+", ext, mem_adress); trx::detail::remap(trx->data_per_vertex[base]->_data, trx->data_per_vertex[base]->mmap_pos.data(), shape); - trx::detail::remap(trx->data_per_vertex[base]->_offsets, trx->streamlines->_offsets.data(), shape); + trx::detail::remap(trx->data_per_vertex[base]->_offsets, trx->streamlines->_offsets.data(), + int(trx->streamlines->_offsets.rows()), int(trx->streamlines->_offsets.cols())); trx->data_per_vertex[base]->_lengths = trx->streamlines->_lengths; } @@ -518,7 +519,7 @@ template std::unique_ptr> TrxFile
::deepcopy() { auto it = copy->data_per_vertex.find(kv.first); if (it != copy->data_per_vertex.end()) { it->second->_data = kv.second->_data; - it->second->_offsets = kv.second->_offsets; + // _offsets is already correctly bound to copy->streamlines->_offsets by _initialize_empty_trx it->second->_lengths = kv.second->_lengths; } } From e028770cb280421635e6ff924bdb06bdb5105611 Mon Sep 17 00:00:00 2001 From: mattcieslak Date: Sun, 22 Feb 2026 16:06:28 -0500 Subject: [PATCH 3/7] chocolatey paths problem --- .github/workflows/ci.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45118c6..b78680a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,11 +88,9 @@ jobs: - name: Test with coverage (Windows) if: runner.os == 'Windows' shell: pwsh - run: > - & "$env:ProgramFiles\OpenCppCoverage\OpenCppCoverage.exe" - --export_type cobertura:coverage.xml - --sources ${{ github.workspace }} - -- ctest --test-dir build --output-on-failure -C Release + run: | + $occ = (Get-Command OpenCppCoverage -ErrorAction Stop).Path + & $occ --export_type cobertura:coverage.xml --sources ${{ github.workspace }} -- ctest --test-dir build --output-on-failure -C Release - name: Generate coverage summary (macOS) if: runner.os == 'macOS' From bf176b5286b22587d62d95d4b4c6dce0b585870a Mon Sep 17 00:00:00 2001 From: mattcieslak Date: Sun, 22 Feb 2026 16:13:10 -0500 Subject: [PATCH 4/7] use official opencppcoverage from github --- .github/workflows/ci.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b78680a..1b63f0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,6 @@ jobs: shell: pwsh run: | choco install ninja -y - choco install opencppcoverage -y New-Item -ItemType Directory -Force "$env:GITHUB_WORKSPACE\vcpkg_cache" | Out-Null git clone https://github.com/microsoft/vcpkg "$env:GITHUB_WORKSPACE\vcpkg" & "$env:GITHUB_WORKSPACE\vcpkg\bootstrap-vcpkg.bat" @@ -48,6 +47,15 @@ jobs: zlib ` --triplet x64-windows + - name: Install OpenCppCoverage (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $installer = "$env:TEMP\OpenCppCoverageSetup.exe" + Invoke-WebRequest -Uri "https://github.com/OpenCppCoverage/OpenCppCoverage/releases/download/release-0.9.9.0/OpenCppCoverageSetup-x64-0.9.9.0.exe" -OutFile $installer + Start-Process -FilePath $installer -ArgumentList "/VERYSILENT" -Wait + echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH + - name: Setup MSVC environment (Windows) if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 @@ -88,9 +96,11 @@ jobs: - name: Test with coverage (Windows) if: runner.os == 'Windows' shell: pwsh - run: | - $occ = (Get-Command OpenCppCoverage -ErrorAction Stop).Path - & $occ --export_type cobertura:coverage.xml --sources ${{ github.workspace }} -- ctest --test-dir build --output-on-failure -C Release + run: > + & "C:\Program Files\OpenCppCoverage\OpenCppCoverage.exe" + --export_type cobertura:coverage.xml + --sources ${{ github.workspace }} + -- ctest --test-dir build --output-on-failure -C Release - name: Generate coverage summary (macOS) if: runner.os == 'macOS' From 98e007e6d29696f465ae6b13be3346e0422a42f8 Mon Sep 17 00:00:00 2001 From: mattcieslak Date: Sun, 22 Feb 2026 22:20:16 -0500 Subject: [PATCH 5/7] fill out benchmarks --- bench/bench_trx_realdata.cpp | 222 ++++++++++++++---- .../benchmarks/trx_query_slab_timings.png | Bin 42841 -> 43139 bytes .../benchmarks/trx_size_vs_streamlines.png | Bin 70328 -> 178718 bytes .../benchmarks/trx_translate_write_rss.png | Bin 64086 -> 66045 bytes .../benchmarks/trx_translate_write_time.png | Bin 67147 -> 107467 bytes 5 files changed, 177 insertions(+), 45 deletions(-) diff --git a/bench/bench_trx_realdata.cpp b/bench/bench_trx_realdata.cpp index 035916d..6af97f1 100644 --- a/bench/bench_trx_realdata.cpp +++ b/bench/bench_trx_realdata.cpp @@ -35,6 +35,8 @@ namespace { using Eigen::half; std::string g_reference_trx_path; +bool g_reference_has_dpv = false; +size_t g_reference_streamline_count = 0; constexpr float kMinLengthMm = 20.0f; constexpr float kMaxLengthMm = 500.0f; @@ -554,6 +556,48 @@ struct TrxOnDisk { size_t shard_processes = 1; }; +// Parse the per-element column count and byte size encoded in a TRX filename. +// TRX convention: .. or . +// e.g. "positions.3.float16" -> {3, 2}, "sift_weights.float32" -> {1, 4} +static std::pair parse_trx_array_dims(const std::string &filename) { + std::vector parts; + std::istringstream ss(filename); + std::string tok; + while (std::getline(ss, tok, '.')) { + if (!tok.empty()) parts.push_back(tok); + } + if (parts.size() < 2) return {1, 4}; + const std::string dtype_str = parts.back(); + const size_t elem_size = static_cast(trx::detail::_sizeof_dtype(dtype_str)); + if (parts.size() >= 3) { + const std::string &maybe_ncols = parts[parts.size() - 2]; + if (!maybe_ncols.empty() && + std::all_of(maybe_ncols.begin(), maybe_ncols.end(), [](unsigned char c) { return std::isdigit(c); })) { + return {static_cast(std::stoul(maybe_ncols)), elem_size}; + } + } + return {1, elem_size}; +} + +#if defined(__unix__) || defined(__APPLE__) +static void truncate_file_to(const std::string &path, off_t byte_size) { + if (::truncate(path.c_str(), byte_size) != 0) { + throw std::runtime_error("truncate " + path + ": " + std::strerror(errno)); + } +} +#endif + +// Truncate every regular file in dir to row_count rows based on the per-file dtype/ncols. +static void truncate_array_dir(const std::string &dir_path, size_t row_count) { + std::error_code ec; + if (!trx::fs::exists(dir_path, ec)) return; + for (const auto &entry : trx::fs::directory_iterator(dir_path, ec)) { + if (ec || !entry.is_regular_file()) continue; + const auto [ncols, elem_size] = parse_trx_array_dims(entry.path().filename().string()); + truncate_file_to(entry.path().string(), static_cast(row_count * ncols * elem_size)); + } +} + TrxOnDisk build_trx_file_on_disk_single(size_t streamlines, GroupScenario scenario, bool add_dps, @@ -562,66 +606,144 @@ TrxOnDisk build_trx_file_on_disk_single(size_t streamlines, const std::string &out_path_override = "") { const size_t progress_every = parse_env_size("TRX_BENCH_LOG_PROGRESS_EVERY", 0); - // Fast path: For 10M (full reference) WITHOUT DPV, just copy and add groups - // DPV requires 1.3B vertices × 8 bytes (vector + internal copy) = 10 GB extra memory! if (g_reference_trx_path.empty()) { throw std::runtime_error("Reference TRX path not set."); } - auto ref_trx_check = trx::load(g_reference_trx_path); - const size_t ref_count = ref_trx_check->num_streamlines(); - const bool is_full_reference = (streamlines == ref_count); - ref_trx_check.reset(); // Release mmap - - // Fast path for full reference WITHOUT DPV: work with reference directly - // DPV + 10M requires 40-50 GB due to: mmap(7.6) + dpv_vec(4.8) + dpv_mmap(4.8) + save() overhead(20+) - if (is_full_reference && !add_dpv) { - log_bench_start("build_trx_copy_fast", "streamlines=" + std::to_string(streamlines)); - - // Filesystem copy reference (disk I/O only) - const std::string temp_copy = make_temp_path("trx_ref_copy"); - std::error_code copy_ec; - std::filesystem::copy_file(g_reference_trx_path, temp_copy, - std::filesystem::copy_options::overwrite_existing, copy_ec); - if (copy_ec) { - throw std::runtime_error("Failed to copy reference: " + copy_ec.message()); + const bool is_full_reference = (streamlines == g_reference_streamline_count); + + // Fast path: load the reference (extracts to a fresh unique temp dir each call), optionally + // ftruncate the positions/offsets/dps files to a prefix boundary in O(1), then assign + // groups and save. Avoids the large intermediate positions write from subset_streamlines(). + // + // Used whenever dpv isn't needed, or when the full reference already has dpv (preserved + // through the copy without any vector allocation). + // Falls back to build_prefix_subset_trx only for synthetic-dpv at small sizes. + if (!add_dpv || (is_full_reference && g_reference_has_dpv)) { + log_bench_start("build_trx_prefix_fast", "streamlines=" + std::to_string(streamlines)); + + // Load reference into a fresh temp dir; each call gets its own unique extraction. + auto ref = trx::load(g_reference_trx_path); + const std::string temp_dir = ref->_uncompressed_folder_handle; + + // Locate positions and offsets files while the mmap is still open. + std::string pos_path, off_path; + { + std::error_code ec; + for (const auto &entry : trx::fs::directory_iterator(temp_dir, ec)) { + if (ec) break; + const std::string fn = entry.path().filename().string(); + if (fn.rfind("positions", 0) == 0) pos_path = entry.path().string(); + else if (fn.rfind("offsets", 0) == 0) off_path = entry.path().string(); + } + } + if (pos_path.empty() || off_path.empty()) { + throw std::runtime_error("positions/offsets not found in " + temp_dir); } - - // Load and modify (just groups, no DPV) - auto trx = trx::load(temp_copy); - - // Add groups + + // Read vertex_cutoff directly from the offsets file so the dtype (uint32 vs uint64) + // is always respected, regardless of how the Eigen map interprets the mmap width. + const auto [off_ncols, off_elem] = + parse_trx_array_dims(trx::fs::path(off_path).filename().string()); + size_t vertex_cutoff = 0; + { + std::ifstream ofs(off_path, std::ios::binary); + ofs.seekg(static_cast(streamlines * off_ncols * off_elem)); + if (off_elem == 4) { + uint32_t v = 0; + ofs.read(reinterpret_cast(&v), 4); + vertex_cutoff = static_cast(v); + } else { + uint64_t v = 0; + ofs.read(reinterpret_cast(&v), 8); + vertex_cutoff = static_cast(v); + } + } + + // Release mmaps without deleting temp_dir — we own it from here. + ref->_owns_uncompressed_folder = false; + ref.reset(); + + if (!is_full_reference) { + // Truncate positions and offsets to the prefix boundary. + const auto [pos_ncols, pos_elem] = + parse_trx_array_dims(trx::fs::path(pos_path).filename().string()); + truncate_file_to(pos_path, static_cast(vertex_cutoff * pos_ncols * pos_elem)); + truncate_file_to(off_path, static_cast((streamlines + 1) * off_ncols * off_elem)); + + // Truncate DPS arrays to the prefix streamline count. + truncate_array_dir(temp_dir + trx::SEPARATOR + "dps", streamlines); + + // DPV is absent at this point (condition above guarantees !add_dpv for non-full ref). + std::error_code ec2; + std::filesystem::remove_all(trx::fs::path(temp_dir) / "dpv", ec2); + + // Patch NB_STREAMLINES and NB_VERTICES in header.json. + const std::string header_path = temp_dir + trx::SEPARATOR + "header.json"; + { + std::ifstream in(header_path); + std::string raw((std::istreambuf_iterator(in)), {}); + std::string parse_err; + json hdr = json::parse(raw, parse_err); + if (!parse_err.empty()) throw std::runtime_error("header.json parse error: " + parse_err); + hdr = trx::_json_set(hdr, "NB_STREAMLINES", static_cast(streamlines)); + hdr = trx::_json_set(hdr, "NB_VERTICES", static_cast(vertex_cutoff)); + std::ofstream out(header_path, std::ios::trunc); + out << hdr.dump(); + } + } + + // Strip DPS if not wanted (applies to both full and prefix). + if (!add_dps) { + std::error_code ec; + std::filesystem::remove_all(trx::fs::path(temp_dir) / "dps", ec); + } + + // Clear any groups the reference may carry; the benchmark owns grouping. + { + std::error_code ec; + std::filesystem::remove_all(trx::fs::path(temp_dir) / "groups", ec); + } + + auto trx = trx::TrxFile::load_from_directory(temp_dir); + + // Add synthetic DPS if requested but the reference didn't have it. + if (add_dps && trx->data_per_streamline.empty()) { + std::vector ones(streamlines, 1.0f); + trx->add_dps_from_vector("sift_weights", "float32", ones); + } + assign_groups_to_trx(*trx, scenario, streamlines); - - // Note: DPV for 10M handled by sampling path (skipped by default due to 40-50 GB memory) - - // Save to output - const std::string out_path = out_path_override.empty() ? make_temp_path("trx_input") : out_path_override; + + const std::string out_path = + out_path_override.empty() ? make_temp_path("trx_input") : out_path_override; trx::TrxSaveOptions save_opts; save_opts.compression_standard = compression; trx->save(out_path, save_opts); const size_t total_vertices = trx->num_vertices(); trx->close(); - - std::filesystem::remove(temp_copy, copy_ec); - + trx::rm_dir(temp_dir); + if (out_path_override.empty()) { register_cleanup(out_path); } - - log_bench_end("build_trx_copy_fast", "streamlines=" + std::to_string(streamlines)); + + log_bench_end("build_trx_prefix_fast", "streamlines=" + std::to_string(streamlines)); return {out_path, streamlines, total_vertices, 0.0, 1}; } - - // Prefix-subset path: copy first N streamlines into a TrxFile and save. + + // Fallback: prefix-subset path for synthetic dpv at smaller streamline counts. auto trx_subset = build_prefix_subset_trx(streamlines, scenario, add_dps, add_dpv); const size_t total_vertices = trx_subset->num_vertices(); - const std::string out_path = out_path_override.empty() ? make_temp_path("trx_input") : out_path_override; + const std::string out_path = + out_path_override.empty() ? make_temp_path("trx_input") : out_path_override; trx::TrxSaveOptions save_opts; save_opts.compression_standard = compression; trx_subset->save(out_path, save_opts); trx_subset->close(); - if (progress_every > 0 && (parse_env_bool("TRX_BENCH_CHILD_LOG", false) || parse_env_bool("TRX_BENCH_LOG", false))) { - std::cerr << "[trx-bench] progress build_trx streamlines=" << streamlines << " / " << streamlines << std::endl; + if (progress_every > 0 && + (parse_env_bool("TRX_BENCH_CHILD_LOG", false) || parse_env_bool("TRX_BENCH_LOG", false))) { + std::cerr << "[trx-bench] progress build_trx streamlines=" << streamlines << " / " + << streamlines << std::endl; } if (out_path_override.empty()) { register_cleanup(out_path); @@ -732,19 +854,19 @@ static void BM_TrxFileSize_Float16(benchmark::State &state) { const auto compression = use_zip ? ZIP_CM_DEFLATE : ZIP_CM_STORE; const size_t skip_zip_at = parse_env_size("TRX_BENCH_SKIP_ZIP_AT", 5000000); const size_t skip_dpv_at = parse_env_size("TRX_BENCH_SKIP_DPV_AT", 10000000); - const size_t skip_connectome_at = parse_env_size("TRX_BENCH_SKIP_CONNECTOME_AT", 5000000); if (use_zip && streamlines >= skip_zip_at) { state.SkipWithMessage("zip compression skipped for large streamlines"); return; } - if (add_dpv && streamlines >= skip_dpv_at) { + // Skip synthetic dpv at high streamline counts due to memory cost (~40-50 GB). + // Exception: if the reference already has dpv and this is the full-reference count, the + // fast path handles it via ftruncate without allocating a dpv vector. + const bool fast_path_covers_dpv = + (streamlines == g_reference_streamline_count && g_reference_has_dpv); + if (add_dpv && streamlines >= skip_dpv_at && !fast_path_covers_dpv) { state.SkipWithMessage("dpv skipped: requires 40-50 GB memory (set TRX_BENCH_SKIP_DPV_AT=0 to force)"); return; } - if (scenario == GroupScenario::Connectome && streamlines >= skip_connectome_at) { - state.SkipWithMessage("connectome groups skipped for large streamlines (set TRX_BENCH_SKIP_CONNECTOME_AT=0 to force)"); - return; - } log_bench_start("BM_TrxFileSize_Float16", "streamlines=" + std::to_string(streamlines) + " group_case=" + std::to_string(state.range(1)) + @@ -1104,6 +1226,16 @@ int main(int argc, char **argv) { // Set global reference path g_reference_trx_path = reference_trx; std::cerr << "[trx-bench] Using reference TRX: " << g_reference_trx_path << std::endl; + + // Pre-inspect reference to avoid repeated loads and to inform skip decisions + { + auto ref = trx::load(g_reference_trx_path); + g_reference_streamline_count = ref->num_streamlines(); + g_reference_has_dpv = !ref->data_per_vertex.empty(); + ref->close(); + std::cerr << "[trx-bench] Reference: " << g_reference_streamline_count + << " streamlines, dpv=" << (g_reference_has_dpv ? "yes" : "no") << std::endl; + } // Enable verbose logging if requested if (verbose) { diff --git a/docs/_static/benchmarks/trx_query_slab_timings.png b/docs/_static/benchmarks/trx_query_slab_timings.png index b950715fb3ef0f4133a3f483e5b695b12eae4450..faffaf2c2d9c00f72f79cc41932d0935d4c522f8 100644 GIT binary patch literal 43139 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 F|38%IVuSzy diff --git a/docs/_static/benchmarks/trx_size_vs_streamlines.png b/docs/_static/benchmarks/trx_size_vs_streamlines.png index f43a93f68b9d575a0d37dcd3bc83bfa9625c0a88..97965196c0f5a475c3661135d14a19596263cfed 100644 GIT binary patch literal 178718 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
%fsB!93|m!35N-k=n3S6z-dph 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 Iui5`U00j%xr2qf` diff --git a/docs/_static/benchmarks/trx_translate_write_rss.png b/docs/_static/benchmarks/trx_translate_write_rss.png index 1a4b8bc36014683ca085af5a5fab6d7f1558143e..8da43d4039e250dde7be5dda732a822632cc6187 100644 GIT binary patch literal 66045 zcmce;1ys~)_cjb-Ac%y3(vEMLTeom6O{67};5>sZDK35vA$uiDj_LYxzB{XhKs(D<7Ef{_!uQy$ ztRuP1O1M&4S3++*^KXi}9OS>%t2D^R-6%FQGVbdZH9E);rL=Z@z!vYZ6W=K;RD0OU z)G11zi)Mv!{CVYAzH_|r&tIyz)cnu?{g-Rc&hY&G_5%@VD)!&+pQmCG{QdU!*>jeE zzx78j-^2O){Uvd1%D>+}z{TtO`z_Xe)&Je2*?!KZ_}LSvnVXweWn6yB-mdqPO}p@t z8~fgUW)6;ij_N0i{eeQ{e?16w^Tr6affcEgQ?({S?2?NhM#4;G7LUQ+AUx-O$o zC~Tbb-aCU9uC<1Kx5+r>_RikrOzL!?_%7J1U^4qX361vM?BM?#L|6plE6^)nsoxz- z5^}Td@I73voK5yBR7&Lg@#DvUk=HUlXWgr$BpMgfG4D;Kg3Y=*;nihbUEQG_KD(hk z;i&;*zdqL_QJ4$hMbdAu^%c+1RtNAy~}lZys@$A{?_6^r*%1>buaT;{f*Mn(sJJl+rf#(-OWz1 zft>?e!tGJH*~L$jUtiNlh#pu@wuDbkOn7e1t;6F<$;dn;$_H&lm-Bv6k^b3hWbCQP zy6W$Sy$O7~vfb5jV-uXp;R4>Dr52t1#Ta-k=$2OQP~|U6srrl!gNqMSZ))W z{c*6=qNsX?@|16VecfwkiKT}-l|!%Wt4VPgn4~S?0JkXicZJSP{pY0|pBY~#CyzDy z`JL|IU*%9#QQ=&lv-tk@qVV0uqm8hB_x6}uW5WZ5hKut|Vq)W7157F2H&8jqyqI+6 zp%|?DQx|w&yP>iVIPge%`pT}_&0C1`m~L$ z#10MWrR&81cmv0C8CZrCTUS%FRZ>=}AEm9as(OZspYDATD}fCfE=s!VWHM6aG_=F0 z5aaH-N&3Dl1O+Sny}ez7G^?u0{rg*ip+cw07LOsJr0;@H7H|v=b;tBNdF zh~cCs!m!!10L^ zYLZUFk-sYOf?A-v^oLhqxI^P{|1837+^3T01}ke@&uId$O<9X;?P&I9e}O(e;$)|? zaX)Cm(El=va0iS(kVsBWPIUNiXPFIU`Sbllaq*|SuDUUYTk{#q8%2KSkjZCUWG6Ky zJkI&}wmbHlH88xS7MK|Ig%J zyV{j@e@L(qJXpx?B}oLT)wKJa!zox&R)K`gE9aLHl_V+STyLMmfANM1-O{hv8cRp6 zqlltzDsAZIh_n5?h~O})yS7&+9Zt!Jy6d_WXHhzPnrTU*!J z3zyXdHBC2%|adU^N~?Z8V)1e3%{vt++A@=0spnCVDlLIp=e_$?KG@gCy0>YmEY zPTm;Q;T=!KtRGg+1oJ)ZTZAH8sxV@2KKaPZ@qLJp?=@BH)Fm>>oSO@xgQzjpXR{V3Rg1s zDx0qT`gpRNa3$-~YhNsjd~Pqqn@bZ@Wi7BHRPE z=lF2IH#jV;$Bl#$_GR^FamE^Jy_LfH&U_CO7uQCNzCE$T(r~59a8^f3{bFIQ{1WS& z!u9ufji@#;6SK>?Z-cau+qJ6=S0pY`-+f=kS2^mMNMr&LM08lei=5VHOAR6E>)SB< zJxT)CZpLA}!N;aIhnNSg8ol3lb?XVDeFGL3MZ`bqq7fpp7vb0E{nt@S4Q2aXWQls+ zIj~Rn&6cbqJKK zoSer(ZZyi%qb3x^po`FGrw2%Uu(|Pxcb82d_uVnr@Gn-vQ8yynCZ{ED=9_r20E^4|TyTfjI z&gRdP%Bq+5_xBU|>_{Z`ICa#{qtOSpET8jK0Bdj~>ze#}Ln6-fY@3l)S=IagXFr=x4 zXo97t^*dVEE4TKr_t7f;y`@KkBrKDW1jZ+&sqLRmjdke0yiU_BaQ7kgKkVY-5Ju7u&}T| zBGGn=3q)jOyq;T@h$iq4*<@k-wm+|V5{OLTT)Wo#z+vaC-|7bNHC%3Um5ORn%;Nnwb#--2P&r=v zObEXMHm4%GpQ)8WA?RW$E-uc98Yr{UKs5dO7=SY_sG)Cs*B^q@z$^kVACZK?;||`F z`g6%AO9i%*kD7F~hZu>wbs28n#6xg#bBo~Lv#Et;0w4KgLDSy8u!1M(RAhqn_9Zpq z*e)`nNQ*P8f%sSDZ88eNx(f}3jz>k0);>!FkBp4qT)2#j81p;zK{VkI3KOM%D;SUG zwvZprn(e#Vy?BF*OI`m!S3mf3$}Sgu0;~IzKR8EqTg|`l{K@gEA3nmw#KgQanTCR5 zzO>RdjQ9dM1%(Ho`&5-ky z(LKLJk>02h*34x;au?C`R^TzjRmMN)cK0z&TsDXI`h--9p>p=)<)QL1eF>lKz=N8u zg_-#Hco^8$V7brXPBfeLfo=XX?W=!G6-jq)&N@{pj9elZ{%v3TwUOt1R(g8+h6{(B z`s!G{A;$pB=Xlrp91TJejyNlyu${d*nA0vnt$pM1&L2by$(g`ukYbV`FB_YJ+M8Dk zX49k#dvDy0o<{#Q=X`j!L@?xZ(?|Q8u$R0;)WfTdr$)GUT$g_0#fKUha-`jUzV($p z0vGXCdG-Ybx3_C+$q_gMe1xFeiuZE)!1LY$t%2_y9X^l@1rl9hQtoXW@wT1Xj(P9s21za23lME}b@efLVU z_L$Ig%Vm)#F)vSmM`9QPsDPG@XF(Y;^Qk7r4x)JUgcu_Huw;wpUBCz=mr z#_oIJK^kuO{L6D5QO0ELU$d2$EDWEpB^D)o5*u zqrIfrrE+E0eZpsWaH{7Nl3Ga zZi#6tfNBx_7%JlM1F@I z4hN4o#dm;*<|!QgrdjUgrM*xIna#>Byt<&P-OH#}e9p3S0%y3wZgcR-Y?7OGtu0^T zDNehpzgBo1iq*SmD@e0ANQ-oOe>P=TtdQsi0`U+6?^yT~@>C?U&m8RSU}u@CDy#9+ zr%y1v!!O(qK45i{P>(Hxvyc8=FB>zB%+uOl9TyW79kVWXo$oe-VK+B7Cue1ag@)D< zK5Y6seb&B>*VWhZPATzVd-3X(D_5wfhKr0FVV|uxH_FavC@LzNm`uP|w2z!ZQU){K zhalC|)CALmFnhRC%L%!r#fZUJW+!p(nmn+91MP%C?j5Zed zA`_w7IW(t`u9x>LR`uF_K{!-BDq^E(BwJJ~Jd4MD?U#z!ac_1ZC5eiYE2JxC?p34q zey7K~4%O4-Va<`RUfnkR`ig*x@2opZ;MPK3&Oq~X0;;l!@>4bRuf9Ji)xBuO+_aHf z1X%vYe#hmQbTENe$oHt~c04|(nG#48aGs#;`}brR6+oqgxHwC`#6%vejjsEIaM(n- zxuX`i|2$;+%cXyFesS3UZ7T48z%mjOuV5AOaMrCoYoEq*!|*y)CVc?22Q5+vhHSet zGHK#0vzJQ_j__nfx#jYir<2b8~ZJISldKu4}); zQ#5W5S-R@a4=k=~Ih-pgHP+2wG`g)MAC2D0V{eb9zSp-k86jp=<(LM^c=9a(%iI>< zm&P0Y;7LHmtOLe95N?`x@!k6!1U9w+#|U-t3Wh5fB%`4bfujlNq$)BUxZ9jaZ!Q(h z>!(|Nj}Erx#b>J&lZC5+QxT=YVftX&h~<(F%(FkIZj3R+ahSuW(q{8g0tzk$K0epx zP%?mDk6K=M!1us$5Fr?`-_23WP=0&2*zxx#7+W|6_smM|>IwWWOdt&dfOHM`3FJaN z?C>wb4`M#sj@1Uh$`A@SPeID0U+Y@XiJJa!@%A7m)-fF2d(n?7hxzS>wm#qxx-9nF z_2#GnOTG2|D&+gEcOJF4SB}>_v9Y$chJU;Pk=Y z&JAw-kf9q~-nl^M-DO>#&@rQLUu`z^^>xoXrN@FvdI69r8$|l6U7d z>Cvo3vPVnQ7bXS<;7otPw_FzXgrycIBPRz$Q41^!WTV3hob^+3Wjs&0^HQr#y3UJ7 zeeV7Qj;9-piUV~GGV-1KUH(i;2`;PkyUD^H&J#@`umoUWr}?_2czAf^oJPVxBybqk z2*aeARFbzY`fv4dUj`)NH4|?!V(;ev1)z#z-0hF18@GtgDf_qjV$aDyg#!K7p<#a} zBSO@3I$9fCE#KprZ@aLH;Ow`SkfhBPC(m}2q*u=ay7b=L$h8INVTLq8`2!7zrR|e}m9$PIxc6NS# znP%wy!_V7`1AhC{vA_#Yeu>!Co%lTMtQ5~RjbR#r0VhgNO}&2WR)>8hr%@e-`o&-= zz!2kHctA$L>@IH2_lyJg2foXbGBZ27aR=Da`kf(|89BR-$?5Ysd*)-m{WHTPrIo0OR0ssrYOy2TRN#gjpo#CVykSS9q;Vyld+A zJ$Il5YP4P&6q_`|W^J9#XKn+lys7+lZ=mty37Ai(=;0C&&O<;7)62Z{K3e|*=%yA1 zBOO5twSix-mB^@+lyN{7aKHetW|YZTJyn7;4M*YlaCdi*>^k<%!*fI=7zag$!OyTo zZy0i+aG{Z{YzXkOD}-vyRM&TR)D461<qVh5x;HOm#lXI#o3e0&Bq5KLmnYu$T0UU9(CZFCA!8Ks2G?U_}ub|pDE3V!>y z%_bqi!Bt>6J~vHp5l}dA2il>i;lLU&FnO3++Zz@qCns=n?xv=ht)|w1E}&im^^y*t z_~$VDC<1N^Oh_TJ7gh;MCO+WIzC_SnmB>LK%p^Lzq~t|Itcqj+Bqp-?jT=(4LpaC^ zOA7!;0l_cz=0@$dfY$)zFml-W9F7Mr!n!}71@*PP9Vi4Cchc^CNCw$ZcO8HETeQQ_ zZ=rrdZQ^7Tc+s^xFvPjJIjAM5C&nIRiygDDu*?S#imojYB0>jF8s&{>Td;CH!(5s&3qH+I5S-+Uo>Xl@77k5koW|S28v(D zK(JlFeFQ5nc6zi49t+B2orPE!$LjwIih};E*@m^Qz-1>J!}hbG;NWDO(B^B|gpuD8 zZsfBc(ZPKk9UTow5gr7rycE@9^2P zXF%#k0Q>DV8B9!nq_3PJS`RTjk^$V0Jy1sAy~hxvEgKsw$>ZfdyWx?goSNxy$cBs=Z#myq+Ne4IefATU?x;IJguuD8LT!H z%j-S4^17XyQwg5k40`&RZQbSBd4TtDv~rGZx#D4c?ZH_E2jkj>#k}x{X}|8XJxh}v z0pu{IoCw}Fd)Lw9tSrX4p-@n_)f9mAY4fDdq!@;Ky7v3?fme=3_RHO(Av!F8pZeE3 z4?8ce9~zS~wzakv4=)3+x-*tT_Jxo}aBBPnljhU#S#>vM>$3>4iv2#O7z_{^Ezckt(m%I79FsJZuDxXfLG<@9&k~DYW)VXfbpsolzx~?EZ3cD*Nv>Xb`;I=a2adL!# zJ){znQc|NpsA0IB-AGs3!NI|khn)$$i;%J;@!9DCCh9QX#LyuC3kYFz=ex7OISfs> zF`hj9wQ1Y0A>sb>A1y$>E_dcBBMjZWhlFg^q1~e=UN<_1?YtckkH95rqro#fgf~CD zG3wSnCcmZU0+j(MeCCw>GwU?KO30Tn;-0nM`z4)aW)9faJ)&j3gR_^1#&&sj)0C{^ zfZu?qT$t^5>&kYr=%n9WI=%EI5j6?iH0IC_bDOSIajS?8F|!m}6cJ1W>WBlrP;>GD zY}S*l^6=qr*zVcR6fu}T3UxN6NZRM4z_?Ac_k|D**+6Lcd?njOq zJp$E9jlv=vm@%UkaF(<<)$>}m384jOfKM5zLS=VRqVtBEr}91+?pR}uK_RB*|SB&Y4Gt{H6KcF8@k~JmcW3UD6e2V8j@f1GHiao`2T19Vh{-dCU>Umes!Qqf-W zHX%U}QgLGTf5)Y}GK>s!#EU>MvBJ6*@M+gWES}YuZivxJ8ao`Sid{!Nf zKiH}_6n2wum9PT}b^#-kFg8ch0`B|p`(k4sH>>c2ZtWyMAhl3fN-}DBNn^v82qu;* z;`8*%pY!~eWw)Aof`p;md+S>~H{^hy;Djyfnim49RL?%j!m1A<6jEoG3J_{~_5aAkp4uNX~Ah%q7u8t_Zs!AuuEG1D9f;nmm0u@HZ z&=$=`hEd3@)@{Bd`cs4-ON~bgoKw1t6O`R?SJdyMF8ebcVcb@^#95{PfW*MfI6!R@ z@TiKC64dW81ma!uZ`V4?04DD4 z?glXm0J~DGWzb-hQS?9&2r}zUIKjK%^8KLPk#%djzxdIYPO(#O@PH6|Xn8DOwNHD( zx=VdXOgDJahXkt?h57`1hv&|cfWsJ|&lwCgm8r2ZR(}Wqjm~50b#HGkCj7x}P>cE2 zS*(s!KOU}h5Q8oFo+LEx^o0huEQ?NpF?(VL z3A5fiIb6k5`eE|AG1`R&^VOUBUEk1e|M)N)U?KGMhVDbjbS@);lJxexjnu`rSW(Ak zx>PnAuduacfXl%c2=9Qwm6dx~spy{om_8GV)q&R`SsgS)fbZ-FY|o3^w+?L5Pq(aJEH_k2%!9OiqBO1$j&fN%|Rowu|tkh{JP*wvZr0 zdTU%^tJ@WWkuPaplTw4}k9#iOS`CMl0y#>7dak2tz2`QhfCs;W=vd!(0#^t=N+s@g z+L^<%;MhD~HBbftC2#?xU-ts<#4CcSV6s2IlTA7#5>8X0VJ+}vyKQVmD50{hxQ-bZ zJ1`PLWP2iM@TBalEM5SQ>4KPt3B3O6VKS0cpsO-a!zhh_)Waxv06arv1T1S%VH@wt zAH9$TJUbw@(MG>h0{#)zlQHVcUjVa~I8V22xB-plk0*IxeHH@#7=~tJjbxDf13v4$ z%xE=hI~4=Hpj^(1&infHD}XdDO$XS+9Old->FbB*euXpMg}?rJJ^v4Ro8(^XulK{t z7~6(D0Lq9GG6C2i-{S+1al8I`7Y_XjTR7r@fq{UwsCcb4<>lr5F9%d``u!yngDI*$ zML>5t$EkecIxcdwKMJTtA2FeMLChLgXuYt5um-Aqr!a0byEPMeLeZ0tpV1YPw*Q2?C2pF zm8wdqBY7&L98?CQJ6pi3;2|Kb0`Wsz8%g7Up9qxhP(822d!2+1QTGc3KmiKr(A21NI?G}83{fqk_GauL(|D5ukjpXswIp!(ye{0warS{*+M@tnah@4y-EKr zWhX>;KXwpucF3e2Cxt^sVVAPHgCyPP=DN;F5Q0YASL)+}Vu6AusZ5-{xjUk+?9x|- z`&m9F@{vuc^Gxj}403h*i`j~C|7=>Q79KTqGV0%m~p%dOJY(w z_|5z=W42CTLQHH+ON+FOjP{Jv_H5Q~&U<<28sKsW#g1-^ik?7l0qGv;W5;!BntZ@HV|gq}1LiCmn#~a0F`E7>qCEGWYAaXlQE6v~