77#include " H5capi.hpp"
88#include " H5Tmeta.hpp"
99#include " H5cout.hpp"
10- #include < cstring>
10+ #include " H5Zpipeline_threaded.hpp"
11+ #include < memory>
1112#include < string>
13+ #include < variant>
1214#include < vector>
1315#include < stdexcept>
1416#include < type_traits>
@@ -19,18 +21,35 @@ namespace h5 {
1921}
2022std::ostream& operator <<(std::ostream& os, const h5::pt_t & pt);
2123
24+ namespace h5 ::impl {
25+ // pt_t::pipeline selects between the synchronous basic pipeline (default,
26+ // bytewise-identical to pre-241 behavior) and the parallel threaded pipeline.
27+ // Both alternatives are indirect-owned through unique_ptr so that the
28+ // variant remains move-assignable regardless of the underlying pipeline's
29+ // move semantics (threaded_pipeline_t deletes moves because it owns
30+ // std::jthread workers and atomics; basic_pipeline_t inherits a manually-
31+ // written move-assign from pipeline_t<Derived> that suppresses the
32+ // implicit move-ctor needed by variant assignment).
33+ using pt_pipeline_t = std::variant<
34+ std::unique_ptr<impl::basic_pipeline_t >,
35+ std::unique_ptr<impl::threaded_pipeline_t >
36+ >;
37+ }
38+
2239// packet table template specialization with inheritance
2340namespace h5 {
2441 struct pt_t {
2542 pt_t ();
26- pt_t ( const h5::ds_t & handle ); // conversion ctor
27- // deep copy with own cache memory
43+ pt_t ( const h5::ds_t & handle ); // conversion ctor — synchronous pipeline
44+ pt_t ( const h5::ds_t & handle, h5::filter::threads workers ); // threaded pipeline
45+ // deep copy with own cache memory — always uses the synchronous pipeline,
46+ // since the threaded pipeline owns workers that cannot be duplicated.
2847 pt_t ( const h5::pt_t & pt ) : h5::pt_t (pt.ds) {
2948 };
3049 ~pt_t ();
3150
3251 pt_t & operator =( h5::pt_t && pt ){
33- // prevent self assign
52+ // prevent self assign
3453 if (this == &pt) return *this ;
3554 if (H5Iis_valid (this ->ds )){ // flush and close dataset
3655 this ->flush ();
@@ -75,7 +94,19 @@ namespace h5 {
7594 void > append ( const T& ref );
7695 void append ( const std::string& ref );
7796
78- impl::pipeline_t <impl::basic_pipeline_t > pipeline;
97+ // Resolve the variant to a reference to the live pipeline_t<Derived> CRTP
98+ // instance, regardless of which alternative is active. Both alternatives
99+ // expose the same set of fields (chunk0, block_size, ds, dxpl, ...) inherited
100+ // from pipeline_t<Derived>, so the visiting lambda can use the result
101+ // duck-typed across alternatives.
102+ template <typename F>
103+ decltype (auto ) visit_pipeline(F&& f) {
104+ return std::visit ([&](auto & p) -> decltype (auto ) {
105+ return std::forward<F>(f)(*p); // both alternatives are unique_ptr
106+ }, pipeline);
107+ }
108+
109+ impl::pt_pipeline_t pipeline;
79110 h5::dxpl_t dxpl;
80111 h5::ds_t ds;
81112 h5::dt_t <void > dt;
@@ -91,19 +122,30 @@ namespace h5 {
91122/* initialized to invalid state
92123 * */
93124inline h5::pt_t::pt_t () :
125+ pipeline{std::make_unique<impl::basic_pipeline_t >()},
94126 dxpl{H5Pcreate (H5P_DATASET_XFER )},ds{H5I_UNINIT },n{0 },fill_value{nullptr }{
95127 for (hsize_t i=0 ; i<H5CPP_MAX_RANK ; i++ )
96128 count[i] = 1 , offset[i] = 0 ;
97129 }
98130
99- // conversion ctor
131+ // conversion ctor — synchronous pipeline (default)
100132inline
101133h5::pt_t::pt_t ( const h5::ds_t & handle ) : pt_t() {
102134 /* default ctor has an invalid state -- skip initialization */
103135 if ( !is_valid (handle) ) return ;
104136 init (handle);
105137}
106138
139+ // conversion ctor — threaded pipeline with N compression workers
140+ inline
141+ h5::pt_t::pt_t ( const h5::ds_t & handle, h5::filter::threads workers ) : pt_t() {
142+ if ( !is_valid (handle) ) return ;
143+ auto threaded = std::make_unique<impl::threaded_pipeline_t >();
144+ threaded->set_worker_count (workers.n );
145+ pipeline.emplace <std::unique_ptr<impl::threaded_pipeline_t >>(std::move (threaded));
146+ init (handle);
147+ }
148+
107149inline
108150h5::pt_t::~pt_t (){
109151 /* default ctor has an invalid state -- skip flushing cache */
@@ -137,12 +179,14 @@ void h5::pt_t::init( const h5::ds_t& handle ){
137179 h5::dt_t <void *> type = h5::get_type<void *>( ds );
138180 hsize_t size = h5::get_size ( type );
139181 this ->fill_value = h5::get_fill_value (dcpl, type, size);
140- pipeline.set_cache (dcpl, size);
141- this ->ptr = pipeline.chunk0 ;
142- this ->block_size = pipeline.block_size ;
143- this ->element_size = pipeline.element_size ;
144- this ->N = pipeline.n ;
145- pipeline.ds = ds; pipeline.dxpl = dxpl;
182+ visit_pipeline ([&](auto & p) {
183+ p.set_cache (dcpl, size);
184+ this ->ptr = p.chunk0 ;
185+ this ->block_size = p.block_size ;
186+ this ->element_size = p.element_size ;
187+ this ->N = p.n ;
188+ p.ds = ds; p.dxpl = dxpl;
189+ });
146190 h5::get_chunk_dims ( dcpl, chunk_dims );
147191 for (hsize_t i=1 ; i<rank; i++)
148192 current_dims[i] = chunk_dims[i];
@@ -156,7 +200,7 @@ void> h5::pt_t::append( const T* ptr ) try {
156200 *offset = *current_dims;
157201 *current_dims += *chunk_dims;
158202 h5::set_extent (ds, current_dims);
159- pipeline .write_chunk (offset,block_size,ptr);
203+ visit_pipeline ([&]( auto & p){ p .write_chunk (offset, block_size, ptr); } );
160204} catch ( const std::runtime_error& err ){
161205 throw h5::error::io::dataset::append ( err.what () );
162206}
@@ -209,7 +253,7 @@ void> h5::pt_t::append( const T& ref ) try {
209253 *offset = *current_dims;
210254 *current_dims += *chunk_dims;
211255 h5::set_extent (ds, current_dims);
212- pipeline .write_chunk (offset,block_size,ptr);
256+ visit_pipeline ([&]( auto & p){ p .write_chunk (offset, block_size, ptr); } );
213257} catch ( const std::runtime_error& err ){
214258 throw h5::error::io::dataset::append ( err.what () );
215259}
@@ -235,21 +279,21 @@ void> h5::pt_t::append( const T& ref ) try {
235279 switch ( dims_.size () ){
236280 case 1 : // vector
237281 if ( dims[0 ] * element_size == block_size )
238- pipeline .write_chunk (offset, block_size, (void *) ptr_ );
282+ visit_pipeline ([&]( auto & p){ p .write_chunk (offset, block_size, (void *) ptr_ ); } );
239283 else throw h5::error::io::packet_table::write (
240284 H5CPP_ERROR_MSG (" dimension mismatch: "
241285 + std::to_string ( dims[0 ] * element_size) + " != " + std::to_string (block_size) ));
242286 break ;
243287 case 2 : // matrix
244288 if ( dims[0 ] * dims[1 ] * element_size == block_size )
245- pipeline .write_chunk (offset, block_size, (void *) ptr_ );
289+ visit_pipeline ([&]( auto & p){ p .write_chunk (offset, block_size, (void *) ptr_ ); } );
246290 else throw h5::error::io::packet_table::write (
247291 H5CPP_ERROR_MSG (" dimension mismatch: "
248292 + std::to_string ( dims[0 ] * dims[1 ] * element_size) + " != " + std::to_string (block_size) ));
249293 break ;
250294 case 3 : // cube
251295 if ( dims[0 ] * dims[1 ] * dims[2 ] * element_size == block_size )
252- pipeline .write_chunk (offset, block_size, (void *) ptr_ );
296+ visit_pipeline ([&]( auto & p){ p .write_chunk (offset, block_size, (void *) ptr_ ); } );
253297 else throw h5::error::io::packet_table::write (
254298 H5CPP_ERROR_MSG (" dimension mismatch: "
255299 + std::to_string ( dims[0 ] * dims[1 ] * dims[2 ] * element_size) + " != " + std::to_string (block_size) ));
@@ -283,7 +327,7 @@ void h5::pt_t::flush(){
283327 for (hsize_t i=0 ; i<(N-n); i++)
284328 for (size_t j=0 ; j < element_size; j++)
285329 static_cast <char *>( ptr )[(n + i) * element_size + j] = static_cast <char *>( fill_value )[ j ];
286- pipeline .write_chunk ( offset, block_size, ptr );
330+ visit_pipeline ([&]( auto & p){ p .write_chunk (offset, block_size, ptr); } );
287331 }
288332 n = 0 ;
289333}
0 commit comments