openPMD-api
JSONIOHandlerImpl.hpp
1 /* Copyright 2017-2021 Franz Poeschel
2  *
3  * This file is part of openPMD-api.
4  *
5  * openPMD-api is free software: you can redistribute it and/or modify
6  * it under the terms of of either the GNU General Public License or
7  * the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * openPMD-api is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License and the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * and the GNU Lesser General Public License along with openPMD-api.
19  * If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #pragma once
23 
24 #include "openPMD/config.hpp"
25 #include "openPMD/auxiliary/Filesystem.hpp"
26 #include "openPMD/IO/AbstractIOHandler.hpp"
27 #include "openPMD/IO/AbstractIOHandlerImpl.hpp"
28 #include "openPMD/IO/Access.hpp"
29 #include "openPMD/IO/JSON/JSONFilePosition.hpp"
30 
31 #include <nlohmann/json.hpp>
32 
33 #include <complex>
34 #include <fstream>
35 #include <memory>
36 #include <tuple>
37 #include <unordered_map>
38 #include <unordered_set>
39 #include <vector>
40 #include <stdexcept>
41 
42 
43 namespace openPMD
44 {
45  // Wrapper around a shared pointer to:
46  // * a filename
47  // * and a boolean indicating whether the file still exists
48  // The wrapper adds no extra information, but some commodity functions.
49  // Invariant for JSONIOHandlerImpl:
50  // For any valid filename, there is at any time at most one
51  // such shared pointer (wrapper) in the HandlerImpl's data structures
52  // (counting by pointer equality)
53  // This means, that a file can be invalidated (i.e. deleted or overwritten)
54  // by simply searching for one instance of the file e.g. in m_files and
55  // invalidating this instance
56  // A new instance may hence only be created after making sure that there are
57  // no valid instances in the data structures.
58  struct File
59  {
60  explicit File( std::string s ) :
61  fileState { std::make_shared< FileState >( s ) }
62  {}
63 
64 
65  File( ) = default;
66 
67 
68  struct FileState
69  {
70  explicit FileState( std::string s ) :
71  name { std::move( s ) }
72  {}
73 
74 
75  std::string name;
76  bool valid = true;
77  };
78 
79  std::shared_ptr< FileState > fileState;
80 
81 
82  void invalidate( )
83  {
84  fileState->valid = false;
85  }
86 
87 
88  bool valid( ) const
89  {
90  return fileState->valid;
91  }
92 
93 
94  File & operator=( std::string s )
95  {
96  if( fileState )
97  {
98  fileState->name = s;
99  }
100  else
101  {
102  fileState = std::make_shared< FileState >( s );
103  }
104  return *this;
105  }
106 
107 
108  bool operator==(
109  File const & f
110  ) const
111  {
112  return this->fileState == f.fileState;
113  }
114 
115 
116  std::string & operator*( ) const
117  {
118  return fileState->name;
119  }
120 
121 
122  std::string * operator->( ) const
123  {
124  return &fileState->name;
125  }
126 
127 
128  explicit operator bool( ) const
129  {
130  return fileState.operator bool( );
131  }
132  };
133 }
134 
135 namespace std
136 {
137  template< >
138  struct hash< openPMD::File >
139  {
141  typedef std::size_t result_type;
142 
143 
144  result_type operator()( argument_type const & s ) const noexcept
145  {
146  return std::hash< shared_ptr< openPMD::File::FileState>> {}( s.fileState );
147  }
148  };
149 
150  // std::complex handling
151  template< class T > void to_json(nlohmann::json &j, const std::complex< T > &p) {
152  j = nlohmann::json {p.real(), p.imag()};
153  }
154 
155  template< class T > void from_json(const nlohmann::json &j, std::complex< T > &p) {
156  p.real(j.at(0));
157  p.imag(j.at(1));
158  }
159 }
160 
161 namespace openPMD
162 {
164  public AbstractIOHandlerImpl
165  {
166  using json = nlohmann::json;
167 
168  public:
169  explicit JSONIOHandlerImpl( AbstractIOHandler * );
170 
171  ~JSONIOHandlerImpl( ) override;
172 
173  void createFile(
174  Writable *,
176  ) override;
177 
178  void createPath(
179  Writable *,
181  ) override;
182 
183  void createDataset(
184  Writable *,
186  ) override;
187 
188  void extendDataset(
189  Writable *,
191  ) override;
192 
193  void
195  Writable *,
197  ) override;
198 
199  void openFile(
200  Writable *,
202  ) override;
203 
204  void closeFile(
205  Writable *,
207  ) override;
208 
209  void openPath(
210  Writable *,
212  ) override;
213 
214  void openDataset(
215  Writable *,
217  ) override;
218 
219  void deleteFile(
220  Writable *,
222  ) override;
223 
224  void deletePath(
225  Writable *,
227  ) override;
228 
229  void deleteDataset(
230  Writable *,
232  ) override;
233 
234  void deleteAttribute(
235  Writable *,
237  ) override;
238 
239  void writeDataset(
240  Writable *,
242  ) override;
243 
244  void writeAttribute(
245  Writable *,
247  ) override;
248 
249  void readDataset(
250  Writable *,
252  ) override;
253 
254  void readAttribute(
255  Writable *,
257  ) override;
258 
259  void listPaths(
260  Writable *,
262  ) override;
263 
264  void listDatasets(
265  Writable *,
267  ) override;
268 
269  void listAttributes(
270  Writable *,
272  ) override;
273 
274  std::future< void > flush( ) override;
275 
276 
277  private:
278 
279  using FILEHANDLE = std::fstream;
280 
281  // map each Writable to its associated file
282  // contains only the filename, without the OS path
283  std::unordered_map<
284  Writable *,
285  File
286  > m_files;
287 
288  std::unordered_map<
289  File,
290  std::shared_ptr< nlohmann::json >> m_jsonVals;
291 
292  // files that have logically, but not physically been written to
293  std::unordered_set< File > m_dirty;
294 
295 
296  // HELPER FUNCTIONS
297 
298 
299  // will use the IOHandler to retrieve the correct directory
300  // shared pointer to circumvent the fact that c++ pre 17 does
301  // not enforce (only allow) copy elision in return statements
302  std::shared_ptr< FILEHANDLE > getFilehandle(
303  File,
304  Access access
305  ); //, Access m_frontendAccess=this->m_handler->m_frontendAccess);
306 
307  // full operating system path of the given file
308  std::string fullPath( File );
309 
310  std::string fullPath( std::string const & );
311 
312  // from a path specification /a/b/c, remove the last
313  // "folder" (i.e. modify the string to equal /a/b)
314  static void parentDir( std::string & );
315 
316  // Fileposition is assumed to have already been set,
317  // get it in string form
318  static std::string filepositionOf( Writable * w );
319 
320  // Execute visitor on each pair of positions in the json value
321  // and the flattened multidimensional array.
322  // Used for writing from the data to JSON and for reading back into
323  // the array from JSON
324  template<
325  typename T,
326  typename Visitor
327  >
328  static void syncMultidimensionalJson(
329  nlohmann::json & j,
330  Offset const & offset,
331  Extent const & extent,
332  Extent const & multiplicator,
333  Visitor visitor,
334  T * data,
335  size_t currentdim = 0
336  );
337 
338  // multiplicators: an array [m_0,...,m_n] s.t.
339  // data[i_0]...[i_n] = data[m_0*i_0+...+m_n*i_n]
340  // (m_n = 1)
341  // essentially: m_i = \prod_{j=0}^{i-1} extent_j
342  static Extent getMultiplicators( Extent const & extent );
343 
344  static nlohmann::json initializeNDArray( Extent const & extent );
345 
346  static Extent getExtent( nlohmann::json & j );
347 
348 
349  // remove single '/' in the beginning and end of a string
350  static std::string removeSlashes( std::string );
351 
352  template< typename KeyT >
353  static bool hasKey(
354  nlohmann::json &,
355  KeyT && key
356  );
357 
358  // make sure that the given path exists in proper form in
359  // the passed json value
360  static void ensurePath(
361  nlohmann::json * json,
362  std::string path
363  );
364 
365  // In order not to insert the same file name into the data structures
366  // with a new pointer (e.g. when reopening), search for a possibly
367  // existing old pointer. Construct a new pointer only upon failure.
368  // The bool is true iff the pointer has been newly-created.
369  // The iterator is an iterator for m_files
370  std::tuple<
371  File,
372  std::unordered_map<
373  Writable *,
374  File
375  >::iterator,
376  bool
377  > getPossiblyExisting(
378  std::string file
379  );
380 
381  // get the json value representing the whole file, possibly reading
382  // from disk
383  std::shared_ptr< nlohmann::json > obtainJsonContents( File );
384 
385  // get the json value at the writable's fileposition
386  nlohmann::json & obtainJsonContents( Writable * writable );
387 
388  // write to disk the json contents associated with the file
389  // remove from m_dirty if unsetDirty == true
390  void putJsonContents(
391  File,
392  bool unsetDirty = true
393  );
394 
395  // figure out the file position of the writable
396  // (preferring the parent's file position) and extend it
397  // by extend. return the modified file position.
398  std::shared_ptr< JSONFilePosition > setAndGetFilePosition(
399  Writable *,
400  std::string extend
401  );
402 
403  // figure out the file position of the writable
404  // (preferring the parent's file position)
405  // only modify the writable's fileposition when specified
406  std::shared_ptr< JSONFilePosition > setAndGetFilePosition(
407  Writable *,
408  bool write = true
409  );
410 
411  // get the writable's containing file
412  // if the parent is associated with another file,
413  // associate the writable with that file and return it
414  File refreshFileFromParent( Writable * writable );
415 
416  void associateWithFile(
417  Writable * writable,
418  File
419  );
420 
421  // need to check the name too in order to exclude "attributes" key
422  static bool isGroup( nlohmann::json::const_iterator it );
423 
424  static bool isDataset( nlohmann::json const & j );
425 
426 
427  // check whether the json reference contains a valid dataset
428  template< typename Param >
429  void verifyDataset(
430  Param const & parameters,
431  nlohmann::json &
432  );
433 
434  static nlohmann::json platformSpecifics( );
435 
436  struct DatasetWriter
437  {
438  template< typename T >
439  void operator()(
440  nlohmann::json & json,
441  const Parameter< Operation::WRITE_DATASET > & parameters
442  );
443 
444  std::string errorMsg = "JSON: writeDataset";
445  };
446 
447  struct DatasetReader
448  {
449  template< typename T >
450  void operator()(
451  nlohmann::json & json,
453  );
454 
455  std::string errorMsg = "JSON: readDataset";
456  };
457 
458  struct AttributeWriter
459  {
460  template< typename T >
461  void operator()(
462  nlohmann::json &,
463  Attribute::resource const &
464  );
465 
466  std::string errorMsg = "JSON: writeAttribute";
467  };
468 
469  struct AttributeReader
470  {
471  template< typename T >
472  void operator()(
473  nlohmann::json &,
475  );
476 
477  std::string errorMsg = "JSON: writeAttribute";
478  };
479 
480  template< typename T >
481  struct CppToJSON
482  {
483  nlohmann::json operator()( T const & );
484  };
485 
486  template< typename T >
487  struct CppToJSON< std::vector< T>>
488  {
489  nlohmann::json operator()( std::vector< T > const & );
490  };
491 
492  template< typename T, int n >
493  struct CppToJSON<
494  std::array<
495  T,
496  n>>
497  {
498  nlohmann::json operator()(
499  std::array<
500  T,
501  n
502  > const &
503  );
504  };
505 
506  template<
507  typename T,
508  typename Enable = T
509  >
510  struct JsonToCpp
511  {
512  T operator()( nlohmann::json const & );
513  };
514 
515  template< typename T >
516  struct JsonToCpp< std::vector< T > >
517  {
518  std::vector< T > operator()( nlohmann::json const & );
519  };
520 
521  template< typename T, int n >
522  struct JsonToCpp<
523  std::array<
524  T,
525  n
526  >
527  >
528  {
529  std::array<
530  T,
531  n
532  > operator()( nlohmann::json const & );
533  };
534 
535  template< typename T >
536  struct JsonToCpp<
537  T,
538  typename std::enable_if<
539  std::is_floating_point<
540  T
541  >::value
542  >::type
543  >
544  {
545  T operator()( nlohmann::json const & );
546  };
547  };
548 
549 } // openPMD
openPMD::Parameter< Operation::CLOSE_FILE >
Definition: IOTask.hpp:149
openPMD::Parameter< Operation::LIST_ATTS >
Definition: IOTask.hpp:514
openPMD::Writable
Layer to mirror structure of logical data and persistent data in file.
Definition: Writable.hpp:64
openPMD::Parameter< Operation::READ_DATASET >
Definition: IOTask.hpp:373
openPMD::Parameter< Operation::DELETE_FILE >
Definition: IOTask.hpp:163
openPMD::Parameter< Operation::WRITE_ATT >
Definition: IOTask.hpp:467
openPMD::Parameter< Operation::AVAILABLE_CHUNKS >
Definition: IOTask.hpp:555
openPMD::Parameter< Operation::CREATE_FILE >
Definition: IOTask.hpp:108
openPMD::JSONIOHandlerImpl::listAttributes
void listAttributes(Writable *, Parameter< Operation::LIST_ATTS > &) override
List all attributes associated with an object.
Definition: JSONIOHandlerImpl.cpp:1070
openPMD::JSONIOHandlerImpl::deletePath
void deletePath(Writable *, Parameter< Operation::DELETE_PATH > const &) override
Delete all objects within an existing path.
Definition: JSONIOHandlerImpl.cpp:701
openPMD::JSONIOHandlerImpl::deleteDataset
void deleteDataset(Writable *, Parameter< Operation::DELETE_DATASET > const &) override
Delete an existing dataset.
Definition: JSONIOHandlerImpl.cpp:807
openPMD::JSONIOHandlerImpl::openPath
void openPath(Writable *, Parameter< Operation::OPEN_PATH > const &) override
Open all contained groups in a path, possibly recursively.
Definition: JSONIOHandlerImpl.cpp:604
openPMD::Parameter< Operation::DELETE_PATH >
Definition: IOTask.hpp:233
openPMD::JSONIOHandlerImpl::listPaths
void listPaths(Writable *, Parameter< Operation::LIST_PATHS > &) override
List all paths/sub-groups inside a group, non-recursively.
Definition: JSONIOHandlerImpl.cpp:1024
openPMD::Parameter< Operation::WRITE_DATASET >
Definition: IOTask.hpp:344
openPMD::JSONIOHandlerImpl::createFile
void createFile(Writable *, Parameter< Operation::CREATE_FILE > const &) override
Create a new file in physical storage, possibly overriding an existing file.
Definition: JSONIOHandlerImpl.cpp:84
openPMD::UnitDimension::T
@ T
time
openPMD::JSONIOHandlerImpl::listDatasets
void listDatasets(Writable *, Parameter< Operation::LIST_DATASETS > &) override
List all datasets inside a group, non-recursively.
Definition: JSONIOHandlerImpl.cpp:1047
openPMD::JSONIOHandlerImpl
Definition: JSONIOHandlerImpl.hpp:163
openPMD::Parameter< Operation::LIST_PATHS >
Definition: IOTask.hpp:249
openPMD::AbstractIOHandler
Interface for communicating between logical and physically persistent data.
Definition: AbstractIOHandler.hpp:98
openPMD::File
Definition: JSONIOHandlerImpl.hpp:58
openPMD::JSONIOHandlerImpl::createDataset
void createDataset(Writable *, Parameter< Operation::CREATE_DATASET > const &) override
Create a new dataset of given type, extent and storage properties.
Definition: JSONIOHandlerImpl.cpp:203
openPMD::Parameter< Operation::LIST_DATASETS >
Definition: IOTask.hpp:402
openPMD::JSONIOHandlerImpl::openDataset
void openDataset(Writable *, Parameter< Operation::OPEN_DATASET > &) override
Open an existing dataset and determine its datatype and extent.
Definition: JSONIOHandlerImpl.cpp:640
openPMD::AbstractIOHandlerImpl
Definition: AbstractIOHandlerImpl.hpp:35
openPMD
Public definitions of openPMD-api.
Definition: Date.cpp:29
openPMD::JSONIOHandlerImpl::createPath
void createPath(Writable *, Parameter< Operation::CREATE_PATH > const &) override
Create all necessary groups for a path, possibly recursively.
Definition: JSONIOHandlerImpl.cpp:144
openPMD::Parameter< Operation::EXTEND_DATASET >
Definition: IOTask.hpp:291
openPMD::Parameter< Operation::DELETE_ATT >
Definition: IOTask.hpp:451
openPMD::Parameter< Operation::OPEN_DATASET >
Definition: IOTask.hpp:307
openPMD::JSONIOHandlerImpl::deleteFile
void deleteFile(Writable *, Parameter< Operation::DELETE_FILE > const &) override
Delete an existing file from physical storage.
Definition: JSONIOHandlerImpl.cpp:667
openPMD::Parameter< Operation::DELETE_DATASET >
Definition: IOTask.hpp:328
openPMD::Parameter< Operation::CREATE_DATASET >
Definition: IOTask.hpp:266
openPMD::Parameter< Operation::CREATE_PATH >
Definition: IOTask.hpp:179
openPMD::JSONIOHandlerImpl::extendDataset
void extendDataset(Writable *, Parameter< Operation::EXTEND_DATASET > const &) override
Increase the extent of an existing dataset.
Definition: JSONIOHandlerImpl.cpp:277
openPMD::JSONIOHandlerImpl::readAttribute
void readAttribute(Writable *, Parameter< Operation::READ_ATT > &) override
Read the value of an existing attribute.
Definition: JSONIOHandlerImpl.cpp:985
openPMD::File::FileState
Definition: JSONIOHandlerImpl.hpp:68
openPMD::JSONIOHandlerImpl::closeFile
void closeFile(Writable *, Parameter< Operation::CLOSE_FILE > const &) override
Close the file corresponding with the writable and release file handles.
Definition: JSONIOHandlerImpl.cpp:588
openPMD::Access
Access
File access mode to use during IO.
Definition: Access.hpp:28
openPMD::JSONIOHandlerImpl::availableChunks
void availableChunks(Writable *, Parameter< Operation::AVAILABLE_CHUNKS > &) override
Report chunks that are available for loading from the dataset represented by this writable.
Definition: JSONIOHandlerImpl.cpp:543
openPMD::Parameter< Operation::OPEN_PATH >
Definition: IOTask.hpp:217
openPMD::JSONIOHandlerImpl::readDataset
void readDataset(Writable *, Parameter< Operation::READ_DATASET > &) override
Read a chunk of data from an existing dataset.
Definition: JSONIOHandlerImpl.cpp:956
openPMD::JSONIOHandlerImpl::deleteAttribute
void deleteAttribute(Writable *, Parameter< Operation::DELETE_ATT > const &) override
Delete an existing attribute.
Definition: JSONIOHandlerImpl.cpp:862
openPMD::Parameter< Operation::OPEN_FILE >
Definition: IOTask.hpp:126
openPMD::JSONIOHandlerImpl::openFile
void openFile(Writable *, Parameter< Operation::OPEN_FILE > const &) override
Open an existing file assuming it conforms to openPMD.
Definition: JSONIOHandlerImpl.cpp:554
openPMD::JSONIOHandlerImpl::writeDataset
void writeDataset(Writable *, Parameter< Operation::WRITE_DATASET > const &) override
Write a chunk of data into an existing dataset.
Definition: JSONIOHandlerImpl.cpp:881
openPMD::Parameter< Operation::READ_ATT >
Definition: IOTask.hpp:486
openPMD::JSONIOHandlerImpl::writeAttribute
void writeAttribute(Writable *, Parameter< Operation::WRITE_ATT > const &) override
Create a single attribute and fill the value, possibly overwriting an existing attribute.
Definition: JSONIOHandlerImpl.cpp:912