Field3D
SparseFieldIO.cpp
Go to the documentation of this file.
1 //----------------------------------------------------------------------------//
2 
3 /*
4  * Copyright (c) 2009 Sony Pictures Imageworks Inc
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the
17  * distribution. Neither the name of Sony Pictures Imageworks nor the
18  * names of its contributors may be used to endorse or promote
19  * products derived from this software without specific prior written
20  * permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33  * OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 //----------------------------------------------------------------------------//
37 
42 //----------------------------------------------------------------------------//
43 
44 #include <boost/intrusive_ptr.hpp>
45 
46 #include <boost/thread/thread.hpp>
47 #include <boost/thread/mutex.hpp>
48 
49 #include "InitIO.h"
50 #include "SparseFieldIO.h"
51 #include "Types.h"
52 
53 //----------------------------------------------------------------------------//
54 
55 using namespace boost;
56 using namespace std;
57 
58 //----------------------------------------------------------------------------//
59 
61 
62 //----------------------------------------------------------------------------//
63 // Field3D namespaces
64 //----------------------------------------------------------------------------//
65 
66 using namespace Exc;
67 using namespace Hdf5Util;
68 
69 //----------------------------------------------------------------------------//
70 // Anonymous namespace
71 //----------------------------------------------------------------------------//
72 
73 namespace {
74 
75 //----------------------------------------------------------------------------//
76 
77 template <typename Data_T>
78 struct ReadThreadingState
79 {
80  ReadThreadingState(const OgIGroup &i_location,
81  Sparse::SparseBlock<Data_T> *i_blocks,
82  const size_t i_numVoxels,
83  const size_t i_numBlocks,
84  const size_t i_numOccupiedBlocks,
85  const bool i_isCompressed,
86  const std::vector<size_t> &i_blockIdxToDatasetIdx)
87  : location(i_location),
88  blocks(i_blocks),
89  numVoxels(i_numVoxels),
90  numBlocks(i_numBlocks),
91  numOccupiedBlocks(i_numOccupiedBlocks),
92  isCompressed(i_isCompressed),
93  blockIdxToDatasetIdx(i_blockIdxToDatasetIdx),
94  nextBlockToRead(0)
95  { }
96  // Data members
97  const OgIGroup &location;
99  const size_t numVoxels;
100  const size_t numBlocks;
101  const size_t numOccupiedBlocks;
102  const bool isCompressed;
103  const std::vector<size_t> &blockIdxToDatasetIdx;
104  size_t nextBlockToRead;
105  // Mutexes
106  boost::mutex readMutex;
107 };
108 
109 //----------------------------------------------------------------------------//
110 
111 template <typename Data_T>
112 class ReadBlockOp
113 {
114 public:
115  ReadBlockOp(ReadThreadingState<Data_T> &state, const size_t threadId)
116  : m_state(state)
117  {
118  // Set up the compression cache
119  const uLong srcLen = m_state.numVoxels * sizeof(Data_T);
120  const uLong cmpLenBound = compressBound(srcLen);
121  m_cache.resize(cmpLenBound);
122  // Initialize the reader
123  m_readerPtr.reset(
124  new OgSparseDataReader<Data_T>(m_state.location, m_state.numVoxels,
125  m_state.numOccupiedBlocks,
126  m_state.isCompressed));
127  m_reader = m_readerPtr.get();
128  // Set the thread id
129  m_reader->setThreadId(threadId);
130  }
131  void operator() ()
132  {
133  // Get next block to read
134  size_t blockIdx;
135  {
136  boost::mutex::scoped_lock lock(m_state.readMutex);
137  blockIdx = m_state.nextBlockToRead;
138  m_state.nextBlockToRead++;
139  }
140  // Loop over blocks until we run out
141  while (blockIdx < m_state.numBlocks) {
142  if (m_state.blocks[blockIdx].isAllocated) {
143  const size_t datasetIdx = m_state.blockIdxToDatasetIdx[blockIdx];
144  m_reader->readBlock(datasetIdx, m_state.blocks[blockIdx].data);
145  }
146  // Get next block idx
147  {
148  boost::mutex::scoped_lock lock(m_state.readMutex);
149  blockIdx = m_state.nextBlockToRead;
150  m_state.nextBlockToRead++;
151  }
152  }
153  }
154 private:
155  // Data members ---
156  ReadThreadingState<Data_T> &m_state;
157  std::vector<uint8_t> m_cache;
158  boost::shared_ptr<OgSparseDataReader<Data_T> > m_readerPtr;
159  OgSparseDataReader<Data_T> *m_reader;
160 };
161 
162 //----------------------------------------------------------------------------//
163 
164 template <typename Data_T>
165 struct ThreadingState
166 {
167  ThreadingState(OgOCDataset<Data_T> &i_data,
168  Sparse::SparseBlock<Data_T> *i_blocks,
169  const size_t i_numVoxels,
170  const size_t i_numBlocks,
171  const std::vector<uint8_t> &i_isAllocated)
172  : data(i_data),
173  blocks(i_blocks),
174  numVoxels(i_numVoxels),
175  numBlocks(i_numBlocks),
176  isAllocated(i_isAllocated),
177  nextBlockToCompress(0),
178  nextBlockToWrite(0)
179  {
180  // Find first in-use block
181  for (size_t i = 0; i < numBlocks; ++i) {
182  if (blocks[i].isAllocated) {
183  nextBlockToCompress = i;
184  nextBlockToWrite = i;
185  return;
186  }
187  }
188  // If we get here, there are no active blocks. Set to numBlocks
189  nextBlockToCompress = numBlocks;
190  nextBlockToWrite = numBlocks;
191  }
192  // Data members
193  OgOCDataset<Data_T> &data;
195  const size_t numVoxels;
196  const size_t numBlocks;
197  const std::vector<uint8_t> isAllocated;
198  size_t nextBlockToCompress;
199  size_t nextBlockToWrite;
200  // Mutexes
201  boost::mutex compressMutex;
202 };
203 
204 //----------------------------------------------------------------------------//
205 
206 template <typename Data_T>
207 class WriteBlockOp
208 {
209 public:
210  WriteBlockOp(ThreadingState<Data_T> &state, const size_t threadId)
211  : m_state(state), m_threadId(threadId)
212  {
213  const uLong srcLen = m_state.numVoxels * sizeof(Data_T);
214  const uLong cmpLenBound = compressBound(srcLen);
215  m_cache.resize(cmpLenBound);
216  }
217  void operator() ()
218  {
219  const int level = 1;
220  // Get next block to compress
221  size_t blockIdx;
222  {
223  boost::mutex::scoped_lock lock(m_state.compressMutex);
224  blockIdx = m_state.nextBlockToCompress;
225  // Step counter to next
226  while (m_state.nextBlockToCompress < m_state.numBlocks) {
227  m_state.nextBlockToCompress++;
228  if (m_state.blocks[m_state.nextBlockToCompress].isAllocated) {
229  break;
230  }
231  }
232  }
233  // Loop over blocks until we run out
234  while (blockIdx < m_state.numBlocks) {
235  if (m_state.blocks[blockIdx].isAllocated) {
236  // Block data as bytes
237  const uint8_t *srcData =
238  reinterpret_cast<const uint8_t *>(m_state.blocks[blockIdx].data);
239  // Length of compressed data is stored here
240  const uLong srcLen = m_state.numVoxels * sizeof(Data_T);
241  const uLong cmpLenBound = compressBound(srcLen);
242  uLong cmpLen = cmpLenBound;
243  // Perform compression
244  const int status = compress2(&m_cache[0], &cmpLen,
245  srcData, srcLen, level);
246  // Error check
247  if (status != Z_OK) {
248  std::cout << "ERROR: Couldn't compress in SparseFieldIO." << std::endl
249  << " Level: " << level << std::endl
250  << " Status: " << status << std::endl
251  << " srcLen: " << srcLen << std::endl
252  << " cmpLenBound: " << cmpLenBound << std::endl
253  << " cmpLen: " << cmpLen << std::endl;
254  return;
255  }
256  // Wait to write data
257  while (m_state.nextBlockToWrite != blockIdx) {
258  // Spin
259  boost::this_thread::sleep(boost::posix_time::microseconds(1));
260  }
261  // Do the writing
262  m_state.data.addData(cmpLen, &m_cache[0]);
263  // Let next block write
264  while (m_state.nextBlockToWrite < m_state.numBlocks){
265  // Increment to next
266  m_state.nextBlockToWrite++;
267  if (m_state.blocks[m_state.nextBlockToWrite].isAllocated) {
268  break;
269  }
270  }
271  }
272  // Get next block idx
273  {
274  boost::mutex::scoped_lock lock(m_state.compressMutex);
275  blockIdx = m_state.nextBlockToCompress;
276  // Step counter to next
277  while (m_state.nextBlockToCompress < m_state.numBlocks) {
278  m_state.nextBlockToCompress++;
279  if (m_state.blocks[m_state.nextBlockToCompress].isAllocated) {
280  break;
281  }
282  }
283  }
284  }
285  }
286 private:
287  // Data members ---
288  ThreadingState<Data_T> &m_state;
289  std::vector<uint8_t> m_cache;
290  const size_t m_threadId;
291 };
292 
293 //----------------------------------------------------------------------------//
294 
295 } // Anonymous namespace
296 
297 //----------------------------------------------------------------------------//
298 // Static members
299 //----------------------------------------------------------------------------//
300 
301 const int SparseFieldIO::k_versionNumber(1);
302 const std::string SparseFieldIO::k_versionAttrName("version");
303 const std::string SparseFieldIO::k_extentsStr("extents");
304 const std::string SparseFieldIO::k_extentsMinStr("extents_min");
305 const std::string SparseFieldIO::k_extentsMaxStr("extents_max");
306 const std::string SparseFieldIO::k_dataWindowStr("data_window");
307 const std::string SparseFieldIO::k_dataWindowMinStr("data_window_min");
308 const std::string SparseFieldIO::k_dataWindowMaxStr("data_window_max");
309 const std::string SparseFieldIO::k_componentsStr("components");
310 const std::string SparseFieldIO::k_dataStr("data");
311 const std::string SparseFieldIO::k_blockOrderStr("block_order");
312 const std::string SparseFieldIO::k_numBlocksStr("num_blocks");
313 const std::string SparseFieldIO::k_blockResStr("block_res");
314 const std::string SparseFieldIO::k_bitsPerComponentStr("bits_per_component");
315 const std::string SparseFieldIO::k_numOccupiedBlocksStr("num_occupied_blocks");
316 const std::string SparseFieldIO::k_isCompressed("data_is_compressed");
317 
318 //----------------------------------------------------------------------------//
319 
321 SparseFieldIO::read(hid_t layerGroup, const std::string &filename,
322  const std::string &layerPath,
323  DataTypeEnum typeEnum)
324 {
325  Box3i extents, dataW;
326  int components;
327  int blockOrder;
328  int numBlocks;
329  V3i blockRes;
330 
331  if (layerGroup == -1) {
332  Msg::print(Msg::SevWarning, "Bad layerGroup.");
333  return FieldBase::Ptr();
334  }
335 
336  int version;
337  if (!readAttribute(layerGroup, k_versionAttrName, 1, version))
338  throw MissingAttributeException("Couldn't find attribute: " +
339  k_versionAttrName);
340 
341  if (version != k_versionNumber)
342  throw UnsupportedVersionException("SparseField version not supported: " +
343  lexical_cast<std::string>(version));
344 
345  if (!readAttribute(layerGroup, k_extentsStr, 6, extents.min.x))
346  throw MissingAttributeException("Couldn't find attribute: " +
347  k_extentsStr);
348 
349  if (!readAttribute(layerGroup, k_dataWindowStr, 6, dataW.min.x))
350  throw MissingAttributeException("Couldn't find attribute: " +
351  k_dataWindowStr);
352 
353  if (!readAttribute(layerGroup, k_componentsStr, 1, components))
354  throw MissingAttributeException("Couldn't find attribute: " +
355  k_componentsStr);
356 
357  // Read block order
358  if (!readAttribute(layerGroup, k_blockOrderStr, 1, blockOrder))
359  throw MissingAttributeException("Couldn't find attribute: " +
360  k_blockOrderStr);
361 
362  // Read number of blocks total
363  if (!readAttribute(layerGroup, k_numBlocksStr, 1, numBlocks))
364  throw MissingAttributeException("Couldn't find attribute: " +
365  k_numBlocksStr);
366 
367  // Read block resolution in each dimension
368  if (!readAttribute(layerGroup, k_blockResStr, 3, blockRes.x))
369  throw MissingAttributeException("Couldn't find attribute: " +
370  k_blockResStr);
371 
372  // ... Check that it matches the # reported by summing the active blocks
373 
374  int numCalculatedBlocks = blockRes.x * blockRes.y * blockRes.z;
375  if (numCalculatedBlocks != numBlocks)
376  throw FileIntegrityException("Incorrect block count in SparseFieldIO::read");
377 
378  // Call the appropriate read function based on the data type ---
379 
380  FieldBase::Ptr result;
381 
382  int occupiedBlocks;
383  if (!readAttribute(layerGroup, k_numOccupiedBlocksStr, 1, occupiedBlocks))
384  throw MissingAttributeException("Couldn't find attribute: " +
385  k_numOccupiedBlocksStr);
386 
387  // Check the data type ---
388 
389  int bits;
390  if (!readAttribute(layerGroup, k_bitsPerComponentStr, 1, bits))
391  throw MissingAttributeException("Couldn't find attribute: " +
392  k_bitsPerComponentStr);
393 
394  bool isHalf = false;
395  bool isFloat = false;
396  bool isDouble = false;
397 
398  switch (bits) {
399  case 16:
400  isHalf = true;
401  break;
402  case 64:
403  isDouble = true;
404  break;
405  case 32:
406  default:
407  isFloat = true;
408  }
409 
410  // Finally, read the data ---
411 
412  if (components == 1) {
413  if (isHalf && typeEnum == DataTypeHalf) {
415  field->setSize(extents, dataW);
416  field->setBlockOrder(blockOrder);
417  readData<half>(layerGroup, numBlocks, filename, layerPath, field);
418  result = field;
419  } else if (isFloat && typeEnum == DataTypeFloat) {
421  field->setSize(extents, dataW);
422  field->setBlockOrder(blockOrder);
423  readData<float>(layerGroup, numBlocks, filename, layerPath, field);
424  result = field;
425  } else if (isDouble && typeEnum == DataTypeDouble) {
427  field->setSize(extents, dataW);
428  field->setBlockOrder(blockOrder);
429  readData<double>(layerGroup, numBlocks, filename, layerPath, field);
430  result = field;
431  }
432  } else if (components == 3) {
433  if (isHalf && typeEnum == DataTypeVecHalf) {
435  field->setSize(extents, dataW);
436  field->setBlockOrder(blockOrder);
437  readData<V3h>(layerGroup, numBlocks, filename, layerPath, field);
438  result = field;
439  } else if (isFloat && typeEnum == DataTypeVecFloat) {
441  field->setSize(extents, dataW);
442  field->setBlockOrder(blockOrder);
443  readData<V3f>(layerGroup, numBlocks, filename, layerPath, field);
444  result = field;
445  } else if (isDouble && typeEnum == DataTypeVecDouble) {
447  field->setSize(extents, dataW);
448  field->setBlockOrder(blockOrder);
449  readData<V3d>(layerGroup, numBlocks, filename, layerPath, field);
450  result = field;
451  }
452  }
453 
454  return result;
455 }
456 
457 //----------------------------------------------------------------------------//
458 
460 SparseFieldIO::read(const OgIGroup &layerGroup, const std::string &filename,
461  const std::string &layerPath, OgDataType typeEnum)
462 {
463  Box3i extents, dataW;
464  int blockOrder;
465  int numBlocks;
466  V3i blockRes;
467 
468  if (!layerGroup.isValid()) {
469  throw MissingGroupException("Invalid group in SparseFieldIO::read()");
470  }
471 
472  // Check version ---
473 
474  OgIAttribute<int> versionAttr =
475  layerGroup.findAttribute<int>(k_versionAttrName);
476  if (!versionAttr.isValid()) {
477  throw MissingAttributeException("Couldn't find attribute: " +
478  k_versionAttrName);
479  }
480  const int version = versionAttr.value();
481 
482  if (version != k_versionNumber) {
483  throw UnsupportedVersionException("SparseField version not supported: " +
484  lexical_cast<std::string>(version));
485  }
486 
487  // Get extents ---
488 
489  OgIAttribute<veci32_t> extMinAttr =
490  layerGroup.findAttribute<veci32_t>(k_extentsMinStr);
491  OgIAttribute<veci32_t> extMaxAttr =
492  layerGroup.findAttribute<veci32_t>(k_extentsMaxStr);
493  if (!extMinAttr.isValid()) {
494  throw MissingAttributeException("Couldn't find attribute " +
495  k_extentsMinStr);
496  }
497  if (!extMaxAttr.isValid()) {
498  throw MissingAttributeException("Couldn't find attribute " +
499  k_extentsMaxStr);
500  }
501 
502  extents.min = extMinAttr.value();
503  extents.max = extMaxAttr.value();
504 
505  // Get data window ---
506 
507  OgIAttribute<veci32_t> dwMinAttr =
508  layerGroup.findAttribute<veci32_t>(k_dataWindowMinStr);
509  OgIAttribute<veci32_t> dwMaxAttr =
510  layerGroup.findAttribute<veci32_t>(k_dataWindowMaxStr);
511  if (!dwMinAttr.isValid()) {
512  throw MissingAttributeException("Couldn't find attribute " +
513  k_dataWindowMinStr);
514  }
515  if (!dwMaxAttr.isValid()) {
516  throw MissingAttributeException("Couldn't find attribute " +
517  k_dataWindowMaxStr);
518  }
519 
520  dataW.min = dwMinAttr.value();
521  dataW.max = dwMaxAttr.value();
522 
523  // Get num components ---
524 
525  OgIAttribute<uint8_t> numComponentsAttr =
526  layerGroup.findAttribute<uint8_t>(k_componentsStr);
527  if (!numComponentsAttr.isValid()) {
528  throw MissingAttributeException("Couldn't find attribute " +
529  k_componentsStr);
530  }
531 
532  // Read block order ---
533 
534  OgIAttribute<uint8_t> blockOrderAttr =
535  layerGroup.findAttribute<uint8_t>(k_blockOrderStr);
536  if (!blockOrderAttr.isValid()) {
537  throw MissingAttributeException("Couldn't find attribute: " +
538  k_blockOrderStr);
539  }
540  blockOrder = blockOrderAttr.value();
541 
542  // Read number of blocks total ---
543 
544  OgIAttribute<uint32_t> numBlocksAttr =
545  layerGroup.findAttribute<uint32_t>(k_numBlocksStr);
546  if (!numBlocksAttr.isValid()) {
547  throw MissingAttributeException("Couldn't find attribute: " +
548  k_numBlocksStr);
549  }
550  numBlocks = numBlocksAttr.value();
551 
552  // Read block resolution in each dimension ---
553 
554  OgIAttribute<veci32_t> blockResAttr =
555  layerGroup.findAttribute<veci32_t>(k_blockResStr);
556  if (!blockResAttr.isValid()) {
557  throw MissingAttributeException("Couldn't find attribute: " +
558  k_blockResStr);
559  }
560  blockRes = blockResAttr.value();
561 
562  // ... Check that it matches the # reported by summing the active blocks
563 
564  int numCalculatedBlocks = blockRes.x * blockRes.y * blockRes.z;
565  if (numCalculatedBlocks != numBlocks) {
566  throw FileIntegrityException("Incorrect block count in "
567  "SparseFieldIO::read()");
568  }
569 
570  // Call the appropriate read function based on the data type ---
571 
572  OgIAttribute<uint32_t> occupiedBlocksAttr =
573  layerGroup.findAttribute<uint32_t>(k_numOccupiedBlocksStr);
574  if (!occupiedBlocksAttr.isValid()) {
575  throw MissingAttributeException("Couldn't find attribute: " +
576  k_numOccupiedBlocksStr);
577  }
578 
579  // Check if the data is compressed ---
580 
581  OgIAttribute<uint8_t> isCompressedAttr =
582  layerGroup.findAttribute<uint8_t>(k_isCompressed);
583  if (!isCompressedAttr.isValid()) {
584  throw MissingAttributeException("Couldn't find attribute: " +
585  k_isCompressed);
586  }
587 
588  // Finally, read the data ---
589 
590  FieldBase::Ptr result;
591 
592  OgDataType typeOnDisk;
593 
594  if (isCompressedAttr.value() == 0) {
595  typeOnDisk = layerGroup.datasetType(k_dataStr);
596  } else {
597  typeOnDisk = layerGroup.compressedDatasetType(k_dataStr);
598  }
599 
600  if (typeEnum == typeOnDisk) {
601  if (typeEnum == F3DFloat16) {
602  result = readData<float16_t>(layerGroup, extents, dataW, blockOrder,
603  numBlocks, filename, layerPath);
604  } else if (typeEnum == F3DFloat32) {
605  result = readData<float32_t>(layerGroup, extents, dataW, blockOrder,
606  numBlocks, filename, layerPath);
607  } else if (typeEnum == F3DFloat64) {
608  result = readData<float64_t>(layerGroup, extents, dataW, blockOrder,
609  numBlocks, filename, layerPath);
610  } else if (typeEnum == F3DVec16) {
611  result = readData<vec16_t>(layerGroup, extents, dataW, blockOrder,
612  numBlocks, filename, layerPath);
613  } else if (typeEnum == F3DVec32) {
614  result = readData<vec32_t>(layerGroup, extents, dataW, blockOrder,
615  numBlocks, filename, layerPath);
616  } else if (typeEnum == F3DVec64) {
617  result = readData<vec64_t>(layerGroup, extents, dataW, blockOrder,
618  numBlocks, filename, layerPath);
619  }
620  }
621 
622  return result;
623 }
624 
625 //----------------------------------------------------------------------------//
626 
627 bool
628 SparseFieldIO::write(hid_t layerGroup, FieldBase::Ptr field)
629 {
630  if (layerGroup == -1) {
631  Msg::print(Msg::SevWarning, "Bad layerGroup.");
632  return false;
633  }
634 
635  // Add version attribute
636  if (!writeAttribute(layerGroup, k_versionAttrName,
637  1, k_versionNumber)) {
638  Msg::print(Msg::SevWarning, "Error adding version attribute.");
639  return false;
640  }
641 
642  SparseField<half>::Ptr halfField =
644  SparseField<float>::Ptr floatField =
646  SparseField<double>::Ptr doubleField =
648  SparseField<V3h>::Ptr vecHalfField =
650  SparseField<V3f>::Ptr vecFloatField =
652  SparseField<V3d>::Ptr vecDoubleField =
654 
655  bool success = true;
656  if (halfField) {
657  success = writeInternal<half>(layerGroup, halfField);
658  } else if (floatField) {
659  success = writeInternal<float>(layerGroup, floatField);
660  } else if (doubleField) {
661  success = writeInternal<double>(layerGroup, doubleField);
662  } else if (vecHalfField) {
663  success = writeInternal<V3h>(layerGroup, vecHalfField);
664  } else if (vecFloatField) {
665  success = writeInternal<V3f>(layerGroup, vecFloatField);
666  } else if (vecDoubleField) {
667  success = writeInternal<V3d>(layerGroup, vecDoubleField);
668  } else {
669  throw WriteLayerException("SparseFieldIO::write does not support the given "
670  "SparseField template parameter");
671  }
672 
673  return success;
674 }
675 
676 //----------------------------------------------------------------------------//
677 
678 bool
679 SparseFieldIO::write(OgOGroup &layerGroup, FieldBase::Ptr field)
680 {
681  using namespace Exc;
682 
683  // Add version attribute
684  OgOAttribute<int> version(layerGroup, k_versionAttrName, k_versionNumber);
685 
686  SparseField<half>::Ptr halfField =
688  SparseField<float>::Ptr floatField =
690  SparseField<double>::Ptr doubleField =
692  SparseField<V3h>::Ptr vecHalfField =
694  SparseField<V3f>::Ptr vecFloatField =
696  SparseField<V3d>::Ptr vecDoubleField =
698 
699  bool success = true;
700 
701  if (floatField) {
702  success = writeInternal<float>(layerGroup, floatField);
703  }
704  else if (halfField) {
705  success = writeInternal<half>(layerGroup, halfField);
706  }
707  else if (doubleField) {
708  success = writeInternal<double>(layerGroup, doubleField);
709  }
710  else if (vecFloatField) {
711  success = writeInternal<V3f>(layerGroup, vecFloatField);
712  }
713  else if (vecHalfField) {
714  success = writeInternal<V3h>(layerGroup, vecHalfField);
715  }
716  else if (vecDoubleField) {
717  success = writeInternal<V3d>(layerGroup, vecDoubleField);
718  }
719  else {
720  throw WriteLayerException("SparseFieldIO does not support the given "
721  "SparseField template parameter");
722  }
723 
724  return success;
725 }
726 
727 //----------------------------------------------------------------------------//
728 
729 template <class Data_T>
731 SparseFieldIO::readData(const OgIGroup &location, const Box3i &extents,
732  const Box3i &dataW, const size_t blockOrder,
733  const size_t numBlocks, const std::string &filename,
734  const std::string &layerPath)
735 {
736  using namespace std;
737  using namespace Exc;
738  using namespace Sparse;
739 
740  typename SparseField<Data_T>::Ptr result(new SparseField<Data_T>);
741  result->setSize(extents, dataW);
742  result->setBlockOrder(blockOrder);
743 
744  const bool dynamicLoading = SparseFileManager::singleton().doLimitMemUse();
745  const int components = FieldTraits<Data_T>::dataDims();
746  const size_t numVoxels = (1 << (result->m_blockOrder * 3));
747  const int valuesPerBlock = (1 << (result->m_blockOrder * 3)) * components;
748 
749  // Read the number of occupied blocks ---
750 
751  OgIAttribute<uint32_t> occupiedBlocksAttr =
752  location.findAttribute<uint32_t>(k_numOccupiedBlocksStr);
753  if (!occupiedBlocksAttr.isValid()) {
754  throw MissingAttributeException("Couldn't find attribute: " +
755  k_numOccupiedBlocksStr);
756  }
757  const size_t occupiedBlocks = occupiedBlocksAttr.value();
758 
759  // Set up the dynamic read info ---
760 
761  if (dynamicLoading) {
762  // Set up the field reference
764  result->addReference(filename, layerPath, valuesPerBlock, numVoxels,
765  occupiedBlocks);
766  }
767 
768  // Read the block info data sets ---
769 
770  SparseBlock<Data_T> *blocks = result->m_blocks;
771 
772  // ... Read the isAllocated array and set up the block mapping array
773  std::vector<size_t> blockIdxToDatasetIdx(numBlocks);
774 
775  {
776  // Grab the data
777  vector<uint8_t> isAllocated(numBlocks);
778  OgIDataset<uint8_t> isAllocatedData =
779  location.findDataset<uint8_t>("block_is_allocated_data");
780  if (!isAllocatedData.isValid()) {
781  throw MissingGroupException("Couldn't find block_is_allocated_data: ");
782  }
783  isAllocatedData.getData(0, &isAllocated[0], OGAWA_THREAD);
784  // Allocate the blocks and set up the block mapping array
785  for (size_t i = 0, nextBlockOnDisk = 0; i < numBlocks; ++i) {
786  blocks[i].isAllocated = isAllocated[i];
787  if (!dynamicLoading && isAllocated[i]) {
788  blocks[i].resize(numVoxels);
789  // Update the block mapping array
790  blockIdxToDatasetIdx[i] = nextBlockOnDisk;
791  nextBlockOnDisk++;
792  }
793  }
794  }
795 
796  // ... Read the emptyValue array ---
797 
798  {
799  // Grab the data
800  vector<Data_T> emptyValue(numBlocks);
801  OgIDataset<Data_T> emptyValueData =
802  location.findDataset<Data_T>("block_empty_value_data");
803  if (!emptyValueData.isValid()) {
804  throw MissingGroupException("Couldn't find block_empty_value_data: ");
805  }
806  emptyValueData.getData(0, &emptyValue[0], OGAWA_THREAD);
807  // Fill in the field
808  for (size_t i = 0; i < numBlocks; ++i) {
809  blocks[i].emptyValue = emptyValue[i];
810  }
811  }
812 
813  // Read the data ---
814 
815  // Check whether data is compressed
816  OgIAttribute<uint8_t> isCompressedAttr =
817  location.findAttribute<uint8_t>(k_isCompressed);
818  const bool isCompressed = isCompressedAttr.value() != 0;
819 
820  if (occupiedBlocks > 0) {
821  if (dynamicLoading) {
822  // Defer loading to the sparse cache
823  result->setupReferenceBlocks();
824  } else {
825  // Threading state
826  ReadThreadingState<Data_T> state(location, blocks, numVoxels, numBlocks,
827  occupiedBlocks, isCompressed,
828  blockIdxToDatasetIdx);
829  // Number of threads
830  const size_t numThreads = numIOThreads();
831  // Launch threads
832  boost::thread_group threads;
833  for (size_t i = 0; i < numThreads; ++i) {
834  threads.create_thread(ReadBlockOp<Data_T>(state, i));
835  }
836  threads.join_all();
837  }
838  }
839 
840  return result;
841 }
842 
843 //----------------------------------------------------------------------------//
844 // Template implementations
845 //----------------------------------------------------------------------------//
846 
848 template <class Data_T>
849 bool SparseFieldIO::writeInternal(hid_t layerGroup,
850  typename SparseField<Data_T>::Ptr field)
851 {
852  using namespace std;
853  using namespace Exc;
854  using namespace Hdf5Util;
855  using namespace Sparse;
856 
857  Box3i ext(field->extents()), dw(field->dataWindow());
858 
859  int components = FieldTraits<Data_T>::dataDims();
860 
861  int valuesPerBlock = (1 << (field->m_blockOrder * 3)) * components;
862 
863  // Add extents attribute ---
864 
865  int extents[6] =
866  { ext.min.x, ext.min.y, ext.min.z, ext.max.x, ext.max.y, ext.max.z };
867 
868  if (!writeAttribute(layerGroup, k_extentsStr, 6, extents[0])) {
869  Msg::print(Msg::SevWarning, "Error adding size attribute.");
870  return false;
871  }
872 
873  // Add data window attribute ---
874 
875  int dataWindow[6] =
876  { dw.min.x, dw.min.y, dw.min.z, dw.max.x, dw.max.y, dw.max.z };
877 
878  if (!writeAttribute(layerGroup, k_dataWindowStr, 6, dataWindow[0])) {
879  Msg::print(Msg::SevWarning, "Error adding size attribute.");
880  return false;
881  }
882 
883  // Add components attribute ---
884 
885  if (!writeAttribute(layerGroup, k_componentsStr, 1, components)) {
886  Msg::print(Msg::SevWarning, "Error adding components attribute.");
887  return false;
888  }
889 
890  // Add block order attribute ---
891 
892  int blockOrder = field->m_blockOrder;
893 
894  if (!writeAttribute(layerGroup, k_blockOrderStr, 1, blockOrder)) {
895  Msg::print(Msg::SevWarning, "Error adding block order attribute.");
896  return false;
897  }
898 
899  // Add number of blocks attribute ---
900 
901  V3i &blockRes = field->m_blockRes;
902  int numBlocks = blockRes.x * blockRes.y * blockRes.z;
903 
904  if (!writeAttribute(layerGroup, k_numBlocksStr, 1, numBlocks)) {
905  Msg::print(Msg::SevWarning, "Error adding number of blocks attribute.");
906  return false;
907  }
908 
909  // Add block resolution in each dimension ---
910 
911  if (!writeAttribute(layerGroup, k_blockResStr, 3, blockRes.x)) {
912  Msg::print(Msg::SevWarning, "Error adding block res attribute.");
913  return false;
914  }
915 
916  // Add the bits per component attribute ---
917 
918  int bits = DataTypeTraits<Data_T>::h5bits();
919  if (!writeAttribute(layerGroup, k_bitsPerComponentStr, 1, bits)) {
920  Msg::print(Msg::SevWarning, "Error adding bits per component attribute.");
921  return false;
922  }
923 
924  // Write the block info data sets ---
925 
926  SparseBlock<Data_T> *blocks = field->m_blocks;
927 
928  // ... Write the isAllocated array
929  {
930  vector<char> isAllocated(numBlocks);
931  for (int i = 0; i < numBlocks; ++i) {
932  isAllocated[i] = static_cast<char>(blocks[i].isAllocated);
933  }
934  writeSimpleData<char>(layerGroup, "block_is_allocated_data", isAllocated);
935  }
936 
937  // ... Write the emptyValue array
938  {
939  vector<Data_T> emptyValue(numBlocks);
940  for (int i = 0; i < numBlocks; ++i) {
941  emptyValue[i] = static_cast<Data_T>(blocks[i].emptyValue);
942  }
943  writeSimpleData<Data_T>(layerGroup, "block_empty_value_data", emptyValue);
944  }
945 
946  // Count the number of occupied blocks ---
947  int occupiedBlocks = 0;
948  for (int i = 0; i < numBlocks; ++i) {
949  if (blocks[i].isAllocated) {
950  occupiedBlocks++;
951  }
952  }
953 
954  if (!writeAttribute(layerGroup, k_numOccupiedBlocksStr, 1, occupiedBlocks)) {
955  throw WriteAttributeException("Couldn't add attribute " +
956  k_numOccupiedBlocksStr);
957  }
958 
959  if (occupiedBlocks > 0) {
960 
961  // Make the memory data space
962  hsize_t memDims[1];
963  memDims[0] = valuesPerBlock;
964  H5ScopedScreate memDataSpace(H5S_SIMPLE);
965  H5Sset_extent_simple(memDataSpace.id(), 1, memDims, NULL);
966 
967  // Make the file data space
968  hsize_t fileDims[2];
969  fileDims[0] = occupiedBlocks;
970  fileDims[1] = valuesPerBlock;
971  H5ScopedScreate fileDataSpace(H5S_SIMPLE);
972  H5Sset_extent_simple(fileDataSpace.id(), 2, fileDims, NULL);
973 
974  // Set up gzip property list
975  bool gzipAvailable = checkHdf5Gzip();
976  hid_t dcpl = H5Pcreate(H5P_DATASET_CREATE);
977  hsize_t chunkSize[2];
978  chunkSize[0] = 1;
979  chunkSize[1] = valuesPerBlock;
980  if (gzipAvailable) {
981  herr_t status = H5Pset_deflate(dcpl, 9);
982  if (status < 0) {
983  return false;
984  }
985  status = H5Pset_chunk(dcpl, 2, chunkSize);
986  if (status < 0) {
987  return false;
988  }
989  }
990 
991  // Add the data set
992  H5ScopedDcreate dataSet(layerGroup, k_dataStr,
994  fileDataSpace.id(),
995  H5P_DEFAULT, dcpl, H5P_DEFAULT);
996  if (dataSet.id() < 0)
997  throw CreateDataSetException("Couldn't create data set in "
998  "SparseFieldIO::writeInternal");
999 
1000  // For each allocated block ---
1001 
1002  int nextBlockIdx = 0;
1003  hsize_t offset[2];
1004  hsize_t count[2];
1005  herr_t status;
1006 
1007  for (int i = 0; i < numBlocks; ++i) {
1008  if (blocks[i].isAllocated) {
1009  offset[0] = nextBlockIdx; // Index of next block
1010  offset[1] = 0; // Index of first data in block. Always 0
1011  count[0] = 1; // Number of columns to read. Always 1
1012  count[1] = valuesPerBlock; // Number of values in one column
1013  status = H5Sselect_hyperslab(fileDataSpace.id(), H5S_SELECT_SET,
1014  offset, NULL, count, NULL);
1015  if (status < 0) {
1016  throw WriteHyperSlabException(
1017  "Couldn't select slab " +
1018  boost::lexical_cast<std::string>(nextBlockIdx));
1019  }
1020  Data_T *data = field->m_blocks[i].data;
1021  status = H5Dwrite(dataSet.id(), DataTypeTraits<Data_T>::h5type(),
1022  memDataSpace.id(),
1023  fileDataSpace.id(), H5P_DEFAULT, data);
1024  if (status < 0) {
1025  throw WriteHyperSlabException(
1026  "Couldn't write slab " +
1027  boost::lexical_cast<std::string>(nextBlockIdx));
1028  }
1029  // Increment nextBlockIdx
1030  nextBlockIdx++;
1031  }
1032  }
1033 
1034  } // if occupiedBlocks > 0
1035 
1036  return true;
1037 
1038 }
1039 
1040 //----------------------------------------------------------------------------//
1041 
1042 template <class Data_T>
1043 bool SparseFieldIO::writeInternal(OgOGroup &layerGroup,
1044  typename SparseField<Data_T>::Ptr field)
1045 {
1046  using namespace Exc;
1047  using namespace Sparse;
1048 
1049  SparseBlock<Data_T> *blocks = field->m_blocks;
1050 
1051  const int components = FieldTraits<Data_T>::dataDims();
1052  const int bits = DataTypeTraits<Data_T>::h5bits();
1053  const V3i &blockRes = field->m_blockRes;
1054  const size_t numBlocks = blockRes.x * blockRes.y * blockRes.z;
1055  const size_t numVoxels = (1 << (field->m_blockOrder * 3));
1056 
1057  const Box3i ext(field->extents()), dw(field->dataWindow());
1058 
1059  // Add attributes ---
1060 
1061  OgOAttribute<veci32_t> extMinAttr(layerGroup, k_extentsMinStr, ext.min);
1062  OgOAttribute<veci32_t> extMaxAttr(layerGroup, k_extentsMaxStr, ext.max);
1063 
1064  OgOAttribute<veci32_t> dwMinAttr(layerGroup, k_dataWindowMinStr, dw.min);
1065  OgOAttribute<veci32_t> dwMaxAttr(layerGroup, k_dataWindowMaxStr, dw.max);
1066 
1067  OgOAttribute<uint8_t> componentsAttr(layerGroup, k_componentsStr, components);
1068 
1069  OgOAttribute<uint8_t> bitsAttr(layerGroup, k_bitsPerComponentStr, bits);
1070 
1071  OgOAttribute<uint8_t> blockOrderAttr(layerGroup, k_blockOrderStr,
1072  field->m_blockOrder);
1073 
1074  OgOAttribute<uint32_t> numBlocksAttr(layerGroup, k_numBlocksStr, numBlocks);
1075 
1076  OgOAttribute<veci32_t> blockResAttr(layerGroup, k_blockResStr, blockRes);
1077 
1078  OgOAttribute<uint8_t> isCompressedAttr(layerGroup, k_isCompressed, 1);
1079 
1080  // Write the isAllocated array
1081  std::vector<uint8_t> isAllocated(numBlocks);
1082  for (size_t i = 0; i < numBlocks; ++i) {
1083  isAllocated[i] = static_cast<uint8_t>(blocks[i].isAllocated);
1084  }
1085  OgODataset<uint8_t> isAllocatedData(layerGroup, "block_is_allocated_data");
1086  isAllocatedData.addData(numBlocks, &isAllocated[0]);
1087 
1088  // Write the emptyValue array
1089  std::vector<Data_T> emptyValue(numBlocks);
1090  for (size_t i = 0; i < numBlocks; ++i) {
1091  emptyValue[i] = static_cast<Data_T>(blocks[i].emptyValue);
1092  }
1093  OgODataset<Data_T> emptyValueData(layerGroup, "block_empty_value_data");
1094  emptyValueData.addData(numBlocks, &emptyValue[0]);
1095 
1096  // Count the number of occupied blocks
1097  int occupiedBlocks = 0;
1098  for (size_t i = 0; i < numBlocks; ++i) {
1099  if (blocks[i].isAllocated) {
1100  occupiedBlocks++;
1101  }
1102  }
1103  OgOAttribute<uint32_t> numOccupiedBlockAttr(layerGroup,
1104  k_numOccupiedBlocksStr,
1105  occupiedBlocks);
1106 
1107  // Add data to file ---
1108 
1109  // Create the compressed dataset regardless of whether there are blocks
1110  // to write.
1111  OgOCDataset<Data_T> data(layerGroup, k_dataStr);
1112  // Write data if there is any
1113  if (occupiedBlocks > 0) {
1114  // Threading state
1115  ThreadingState<Data_T> state(data, blocks, numVoxels, numBlocks,
1116  isAllocated);
1117  // Number of threads
1118  const size_t numThreads = numIOThreads();
1119  // Launch threads
1120  boost::thread_group threads;
1121  for (size_t i = 0; i < numThreads; ++i) {
1122  threads.create_thread(WriteBlockOp<Data_T>(state, i));
1123  }
1124  threads.join_all();
1125  }
1126 
1127  return true;
1128 }
1129 
1130 //----------------------------------------------------------------------------//
1131 
1132 template <class Data_T>
1133 bool SparseFieldIO::readData(hid_t location,
1134  int numBlocks,
1135  const std::string &filename,
1136  const std::string &layerPath,
1137  typename SparseField<Data_T>::Ptr result)
1138 {
1139  using namespace std;
1140  using namespace Exc;
1141  using namespace Hdf5Util;
1142  using namespace Sparse;
1143 
1144  int occupiedBlocks;
1145 
1146  bool dynamicLoading = SparseFileManager::singleton().doLimitMemUse();
1147 
1148  int components = FieldTraits<Data_T>::dataDims();
1149  int numVoxels = (1 << (result->m_blockOrder * 3));
1150  int valuesPerBlock = numVoxels * components;
1151 
1152  // Read the number of occupied blocks ---
1153 
1154  if (!readAttribute(location, k_numOccupiedBlocksStr, 1, occupiedBlocks))
1155  throw MissingAttributeException("Couldn't find attribute: " +
1156  k_numOccupiedBlocksStr);
1157 
1158  // Set up the dynamic read info ---
1159 
1160  if (dynamicLoading) {
1161  // Set up the field reference
1162  result->addReference(filename, layerPath,
1163  valuesPerBlock, numVoxels,
1164  occupiedBlocks);
1165  }
1166 
1167  // Read the block info data sets ---
1168 
1169  SparseBlock<Data_T> *blocks = result->m_blocks;
1170 
1171  // ... Read the isAllocated array
1172 
1173  {
1174  vector<char> isAllocated(numBlocks);
1175  readSimpleData<char>(location, "block_is_allocated_data", isAllocated);
1176  for (int i = 0; i < numBlocks; ++i) {
1177  blocks[i].isAllocated = isAllocated[i];
1178  if (!dynamicLoading && isAllocated[i]) {
1179  blocks[i].resize(numVoxels);
1180  }
1181  }
1182  }
1183 
1184  // ... Read the emptyValue array ---
1185 
1186  {
1187  vector<Data_T> emptyValue(numBlocks);
1188  readSimpleData<Data_T>(location, "block_empty_value_data", emptyValue);
1189  for (int i = 0; i < numBlocks; ++i) {
1190  blocks[i].emptyValue = emptyValue[i];
1191  }
1192  }
1193 
1194  // Read the data ---
1195 
1196  if (occupiedBlocks > 0) {
1197 
1198  if (dynamicLoading) {
1199 
1200  result->setupReferenceBlocks();
1201 
1202  } else {
1203 
1204  size_t b = 0, bend = b + numBlocks;
1205 
1206  SparseDataReader<Data_T> reader(location, valuesPerBlock, occupiedBlocks);
1207 
1208  // We'll read at most 50meg at a time
1209  static const long maxMemPerPass = 50*1024*1024;
1210 
1211  for (int nextBlockIdx = 0;;) {
1212 
1213  long mem = 0;
1214  std::vector<Data_T*> memoryList;
1215 
1216  for (; b != bend && mem < maxMemPerPass; ++b) {
1217  if (blocks[b].isAllocated) {
1218  mem += sizeof(Data_T)*numVoxels;
1219  memoryList.push_back(blocks[b].data);
1220  }
1221  }
1222 
1223  // all done.
1224  if (!memoryList.size()) {
1225  break;
1226  }
1227 
1228  reader.readBlockList(nextBlockIdx, memoryList);
1229  nextBlockIdx += memoryList.size();
1230  }
1231 
1232  }
1233 
1234  } // if occupiedBlocks > 0
1235 
1236  return true;
1237 
1238 }
1239 
1240 //----------------------------------------------------------------------------//
1241 
1243 
1244 //----------------------------------------------------------------------------//
Field_T::Ptr field_dynamic_cast(RefBase::Ptr field)
Dynamic cast that uses string-comparison in order to be safe even after an object crosses a shared li...
Definition: RefCount.h:256
Imath::Box3i Box3i
Definition: SpiMathLib.h:77
V3i m_blockRes
Block array resolution.
Definition: SparseField.h:612
bool doLimitMemUse() const
Returns whether to limit memory usage and do dynamic loading for sparse fields.
Definition: SparseFile.cpp:83
Contains utility functions and classes for Hdf5 files.
Definition: Hdf5Util.h:86
Contains typedefs for the commonly used types in Field3D.
FIELD3D_API bool writeAttribute(hid_t location, const std::string &attrName, const std::string &value)
Writes a string attribute.
#define FIELD3D_NAMESPACE_SOURCE_CLOSE
Definition: ns.h:60
static int dataDims()
Definition: Traits.h:178
Namespace for Exception objects.
Definition: Exception.h:57
const Box3i & extents() const
Returns the extents of the data. This signifies the relevant area that the data exists over...
Definition: Field.h:249
FIELD3D_API bool readAttribute(hid_t location, const std::string &attrName, std::string &value)
Reads a string attribute.
void setupReferenceBlocks()
Internal function to setup the Reference&#39;s block pointers, for use with dynamic reading.
Definition: SparseField.h:1413
boost::intrusive_ptr< FieldBase > Ptr
Definition: Field.h:97
Data_T * data
Pointer to data. Null if block is unallocated.
Definition: SparseField.h:311
FIELD3D_API size_t numIOThreads()
Returns the number of I/O threads to use.
Definition: InitIO.cpp:92
FIELD3D_API void print(Severity severity, const std::string &message)
Sends the string to the assigned output, prefixing the message with the severity. ...
Definition: Log.cpp:70
int m_blockOrder
Block order (size = 2^blockOrder)
Definition: SparseField.h:610
boost::intrusive_ptr< SparseField > Ptr
Definition: SparseField.h:357
OgDataType
Enumerates the various uses for Ogawa-level groups.
Definition: Traits.h:125
Data_T emptyValue
The value to use if the block isn&#39;t allocated. We allow setting this per block so that we for example...
Definition: SparseField.h:308
Imath::V3i V3i
Definition: SpiMathLib.h:71
const Box3i & dataWindow() const
Returns the data window. Any coordinate inside this window is safe to pass to value() in the Field su...
Definition: Field.h:253
This class gets used by SparseFieldIO and SparseFileManager to read the block data. On creation it will open the data set and not close it until the object is destroyed.
void addReference(const std::string &filename, const std::string &layerPath, int valuesPerBlock, int numVoxels, int occupiedBlocks)
Internal function to create a Reference for the current field, for use in dynamic reading...
Definition: SparseField.h:1379
Field3D::V3i veci32_t
Definition: Traits.h:94
FIELD3D_API bool checkHdf5Gzip()
Checks whether gzip is available in the current hdf5 library.
Definition: Hdf5Util.cpp:722
static hid_t h5type()
Namespace for sparse field specifics.
Definition: SparseField.h:221
void resize(int n)
Alloc data.
Definition: SparseField.h:259
static int h5bits()
bool isAllocated
Whether the block is allocated or not.
Definition: SparseField.h:303
Storage for one individual block of a SparseField.
Definition: SparseField.h:227
Scoped object - creates a dataspace on creation and closes it on destruction.
Definition: Hdf5Util.h:234
Contains the initIO function.
Scoped object - creates a dataset on creation and closes it on destruction.
Definition: Hdf5Util.h:263
This Field subclass stores voxel data in block-allocated arrays.
Definition: SparseField.h:350
DataTypeEnum
Definition: Traits.h:108
static SparseFileManager & singleton()
Returns a reference to the singleton instance.
Definition: SparseFile.cpp:66
Block * m_blocks
Array of blocks. Not using std::vector since SparseBlock is noncopyable.
Definition: SparseField.h:616