HDTree  0.5.2
HDTree C++ API
AtomicBranch.h
1 #pragma once
2 
3 namespace hdtree {
4 
13 template <typename AtomicType>
14 class Branch<AtomicType, std::enable_if_t<is_atomic_v<AtomicType>>>
15  : public AbstractBranch<AtomicType> {
16  class ReadBuffer {
17  std::size_t max_len_;
18  HighFive::DataSet set_;
19  std::vector<AtomicType> buffer_;
20  std::size_t i_file_;
21  std::size_t i_memory_;
22  std::size_t entries_;
47  // determine the length we want to request depending
48  // on the number of entries left in the file
49  std::size_t request_len = this->max_len_;
50  if (request_len + i_file_ > entries_) {
51  request_len = entries_ - i_file_;
52  assert(request_len >= 0);
53  }
54  // load the next chunk into memory
55  if constexpr (std::is_same_v<AtomicType, bool>) {
61  std::vector<Bool> buff;
62  buff.resize(request_len);
63  this->set_.select({i_file_}, {request_len})
64  .read(buff.data(), create_enum_bool());
65  buffer_.reserve(buff.size());
66  for (const auto& v : buff) buffer_.push_back(v == Bool::TRUE);
67  } else {
68  this->set_.select({i_file_}, {request_len}).read(buffer_);
69  }
70  // update indices
71  i_file_ += buffer_.size();
72  i_memory_ = 0;
73  }
74 
75  public:
76  explicit ReadBuffer(std::size_t max, HighFive::DataSet s)
77  : max_len_{max}, set_{s}, buffer_{}, i_file_{0}, i_memory_{0} {
78  entries_ = this->set_.getDimensions().at(0);
79  this->read_chunk_from_disk();
80  // TODO deduce max_len from chunk size of dataset
81  }
82  void read(AtomicType& v) {
83  if (i_memory_ == buffer_.size()) this->read_chunk_from_disk();
84  v = buffer_[i_memory_];
85  ++i_memory_;
86  }
87  };
88  std::unique_ptr<ReadBuffer> read_buffer_;
89 
90  class WriteBuffer {
91  std::size_t max_len_;
92  HighFive::DataSet set_;
93  std::vector<AtomicType> buffer_;
94  std::size_t i_file_;
124  void flush() {
125  if (buffer_.size() == 0) return;
126  std::size_t new_extent = i_file_ + buffer_.size();
127  // throws if not created yet
128  if (this->set_.getDimensions().at(0) < new_extent) {
129  this->set_.resize({new_extent});
130  }
131  if constexpr (std::is_same_v<AtomicType, bool>) {
132  // handle bool specialization
133  std::vector<Bool> buff;
134  buff.reserve(buffer_.size());
135  for (const auto& v : buffer_)
136  buff.push_back(v ? Bool::TRUE : Bool::FALSE);
137  this->set_.select({i_file_}, {buffer_.size()}).write(buff);
138  } else {
139  this->set_.select({i_file_}, {buffer_.size()}).write(buffer_);
140  }
141  i_file_ += buffer_.size();
142  buffer_.clear();
143  buffer_.reserve(this->max_len_);
144  }
145 
146  public:
159  explicit WriteBuffer(std::size_t max, HighFive::DataSet s)
160  : max_len_{max}, set_{s}, buffer_{}, i_file_{0} {
161  buffer_.reserve(this->max_len_);
162  }
163 
164  // flush before deleting
165  ~WriteBuffer() { flush(); }
166 
175  void save(const AtomicType& val) {
176  buffer_.push_back(val);
177  if (buffer_.size() > this->max_len_) flush();
178  }
179  };
180 
181  std::unique_ptr<WriteBuffer> write_buffer_;
182 
183  public:
191  explicit Branch(const std::string& branch_name, AtomicType* handle = nullptr)
192  : AbstractBranch<AtomicType>(branch_name, handle) {}
193 
194  void attach(Reader& f) final override try {
195  // deletes old read_buffer_ if there was one already constructed
196  read_buffer_ =
197  std::make_unique<ReadBuffer>(10000, f.getDataSet(this->name_));
198  } catch (const HighFive::DataSetException& e) {
199  // an exception was thrown when we tried to `get` the dataset by name
200  std::stringstream msg, help;
201  msg << "HDTreeBadType: Branch at " << this->name_
202  << " could not be accessed.";
203  help << "Check that this branch exists in your HDTree.\n"
204  " H5 Error: " << e.what();
205  throw HDTreeException(msg.str(), help.str());
206  }
207 
216  void load() final override {
217  if (read_buffer_) read_buffer_->read(*(this->handle_));
218  }
219 
228  void save() final override {
229  if (write_buffer_) write_buffer_->save(*(this->handle_));
230  }
231 
238  void attach(Writer& f) final override try {
239  HighFive::DataType t;
240  if constexpr (std::is_same_v<AtomicType, bool>) {
241  t = create_enum_bool();
242  } else {
243  t = HighFive::AtomicType<AtomicType>();
244  }
245  auto ds = f.createDataSet(this->name_, t);
246  ds.createAttribute(constants::TYPE_ATTR_NAME,
247  boost::core::demangle(typeid(AtomicType).name()));
248  ds.createAttribute(constants::VERS_ATTR_NAME, 0);
249  // flush and deletes old buffer if it exists
250  write_buffer_ = std::make_unique<WriteBuffer>(f.getRowsPerChunk(), ds);
251  } catch (const HighFive::DataSetException& e) {
252  // an exception was thrown when we tried to create the dataset by name
253  std::stringstream msg, help;
254  msg << "HDTreeBadType: Branch at " << this->name_
255  << " could not be created.";
256  help << "Check that this branch does not already exist in your HDTree.\n"
257  " H5 Error: " << e.what();
258  throw HDTreeException(msg.str(), help.str());
259  }
260 }; // Branch<AtomicType>
261 
262 } // namespace hdtree
Type-specific base class to hold common data methods.
Definition: AbstractBranch.h:87
DataType * handle_
handle on current object in memory
Definition: AbstractBranch.h:247
std::string name_
name of branch
Definition: AbstractBranch.h:74
void read_chunk_from_disk()
Load the next chunk of data into memory.
Definition: AtomicBranch.h:46
void save(const AtomicType &val)
Put the new value into the buffer.
Definition: AtomicBranch.h:175
WriteBuffer(std::size_t max, HighFive::DataSet s)
Define the buffer size and the set we will write to.
Definition: AtomicBranch.h:159
void flush()
Flush our in-memory buffer onto disk.
Definition: AtomicBranch.h:124
void attach(Writer &f) final override
do NOT persist any structure for atomic types
Definition: AtomicBranch.h:238
void load() final override
Down to a type that Reader can handle.
Definition: AtomicBranch.h:216
Branch(const std::string &branch_name, AtomicType *handle=nullptr)
We don't do any more initialization except which is handled by the AbstractBranch.
Definition: AtomicBranch.h:191
void attach(Reader &f) final override
pure virtual method for loading data from the input file
Definition: AtomicBranch.h:194
void save() final override
Down to a type that io::Writer can handle.
Definition: AtomicBranch.h:228
General data set.
Definition: GeneralBranch.h:46
user-facing exception class for hdtree
Definition: Exception.h:15
Reading a file generated by fire.
Definition: Reader.h:24
Write the fire DataSets into a deterministic structure in the output HDF5 data file.
Definition: Writer.h:21
Geant4 does a GLOBAL definition of the keyword TRUE.
Definition: Atomic.cxx:3
HighFive::EnumType< Bool > create_enum_bool()
HighFive method for creating the enum data type.
Definition: Atomic.cxx:5
static const std::string TYPE_ATTR_NAME
the name of the HDF5 object attribute that holds the event object type
Definition: Constants.h:17
static const std::string VERS_ATTR_NAME
the name of the hdtree version attribute
Definition: Constants.h:19