openPMD-api
JSONIOHandlerImpl.hpp
1 /* Copyright 2017-2020 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
194  availableChunks(
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  template< int n >
445  void operator()(
446  nlohmann::json & json,
447  const Parameter< Operation::WRITE_DATASET > & parameters
448  );
449 
450  };
451 
452  struct DatasetReader
453  {
454  template< typename T >
455  void operator()(
456  nlohmann::json & json,
458  );
459 
460  template< int n >
461  void operator()(
462  nlohmann::json & json,
464  );
465  };
466 
467  struct AttributeWriter
468  {
469  template< typename T >
470  void operator()(
471  nlohmann::json &,
472  Attribute::resource const &
473  );
474 
475  template< int n >
476  void operator()(
477  nlohmann::json &,
478  Attribute::resource const &
479  );
480 
481  };
482 
483  struct AttributeReader
484  {
485  template< typename T >
486  void operator()(
487  nlohmann::json &,
489  );
490 
491  template< int n >
492  void operator()(
493  nlohmann::json &,
495  );
496 
497  };
498 
499  template< typename T >
500  struct CppToJSON
501  {
502  nlohmann::json operator()( T const & );
503  };
504 
505  template< typename T >
506  struct CppToJSON< std::vector< T>>
507  {
508  nlohmann::json operator()( std::vector< T > const & );
509  };
510 
511  template< typename T, int n >
512  struct CppToJSON<
513  std::array<
514  T,
515  n>>
516  {
517  nlohmann::json operator()(
518  std::array<
519  T,
520  n
521  > const &
522  );
523  };
524 
525  template<
526  typename T,
527  typename Enable = T
528  >
529  struct JsonToCpp
530  {
531  T operator()( nlohmann::json const & );
532  };
533 
534  template< typename T >
535  struct JsonToCpp< std::vector< T > >
536  {
537  std::vector< T > operator()( nlohmann::json const & );
538  };
539 
540  template< typename T, int n >
541  struct JsonToCpp<
542  std::array<
543  T,
544  n
545  >
546  >
547  {
548  std::array<
549  T,
550  n
551  > operator()( nlohmann::json const & );
552  };
553 
554  template< typename T >
555  struct JsonToCpp<
556  T,
557  typename std::enable_if<
558  std::is_floating_point<
559  T
560  >::value
561  >::type
562  >
563  {
564  T operator()( nlohmann::json const & );
565  };
566  };
567 
568 } // openPMD
Definition: JSONIOHandlerImpl.hpp:68
Access
File access mode to use during IO.
Definition: Access.hpp:28
STL namespace.
Definition: JSONIOHandlerImpl.hpp:58
Definition: JSONIOHandlerImpl.hpp:163
Interface for communicating between logical and physically persistent data.
Definition: AbstractIOHandler.hpp:68
Public definitions of openPMD-api.
Definition: Date.cpp:29
Layer to mirror structure of logical data and persistent data in file.
Definition: Writable.hpp:55
Definition: AbstractIOHandlerImpl.hpp:35