Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | #include "Archive.h" | ||
5 | |||
6 | #include "GridDescriptor.h" | ||
7 | #include "DelayedLoadMetadata.h" | ||
8 | #include "io.h" | ||
9 | |||
10 | #include <openvdb/Exceptions.h> | ||
11 | #include <openvdb/Metadata.h> | ||
12 | #include <openvdb/tree/LeafManager.h> | ||
13 | #include <openvdb/util/logging.h> | ||
14 | #include <openvdb/openvdb.h> | ||
15 | |||
16 | // Boost.Interprocess uses a header-only portion of Boost.DateTime | ||
17 | #ifdef __clang__ | ||
18 | #pragma clang diagnostic push | ||
19 | #pragma clang diagnostic ignored "-Wunused-macros" | ||
20 | #endif | ||
21 | #define BOOST_DATE_TIME_NO_LIB | ||
22 | #ifdef __clang__ | ||
23 | #pragma clang diagnostic pop | ||
24 | #endif | ||
25 | #include <boost/interprocess/file_mapping.hpp> | ||
26 | #include <boost/interprocess/mapped_region.hpp> | ||
27 | #include <boost/iostreams/device/array.hpp> | ||
28 | #include <boost/iostreams/stream.hpp> | ||
29 | #include <boost/uuid/uuid_generators.hpp> | ||
30 | #include <boost/uuid/uuid_io.hpp> | ||
31 | |||
32 | #include <atomic> | ||
33 | |||
34 | #ifdef _WIN32 | ||
35 | #include <boost/interprocess/detail/os_file_functions.hpp> // open_existing_file(), close_file() | ||
36 | extern "C" __declspec(dllimport) bool __stdcall GetFileTime( | ||
37 | void* fh, void* ctime, void* atime, void* mtime); | ||
38 | // boost::interprocess::detail was renamed to boost::interprocess::ipcdetail in Boost 1.48. | ||
39 | // Ensure that both namespaces exist. | ||
40 | namespace boost { namespace interprocess { namespace detail {} namespace ipcdetail {} } } | ||
41 | #else | ||
42 | #include <sys/types.h> // for struct stat | ||
43 | #include <sys/stat.h> // for stat() | ||
44 | #include <unistd.h> // for unlink() | ||
45 | #endif | ||
46 | #include <algorithm> // for std::find_if() | ||
47 | #include <cerrno> // for errno | ||
48 | #include <cstdlib> // for getenv() | ||
49 | #include <cstring> // for std::memcpy() | ||
50 | #include <ctime> // for std::time() | ||
51 | #include <iostream> | ||
52 | #include <map> | ||
53 | #include <random> | ||
54 | #include <set> | ||
55 | #include <sstream> | ||
56 | #include <system_error> // for std::error_code() | ||
57 | |||
58 | |||
59 | namespace openvdb { | ||
60 | OPENVDB_USE_VERSION_NAMESPACE | ||
61 | namespace OPENVDB_VERSION_NAME { | ||
62 | namespace io { | ||
63 | |||
64 | #ifdef OPENVDB_USE_BLOSC | ||
65 | const uint32_t Archive::DEFAULT_COMPRESSION_FLAGS = (COMPRESS_BLOSC | COMPRESS_ACTIVE_MASK); | ||
66 | #else | ||
67 | #ifdef OPENVDB_USE_ZLIB | ||
68 | const uint32_t Archive::DEFAULT_COMPRESSION_FLAGS = (COMPRESS_ZIP | COMPRESS_ACTIVE_MASK); | ||
69 | #else | ||
70 | const uint32_t Archive::DEFAULT_COMPRESSION_FLAGS = (COMPRESS_ACTIVE_MASK); | ||
71 | #endif | ||
72 | #endif | ||
73 | |||
74 | |||
75 | namespace { | ||
76 | |||
77 | // Indices into a stream's internal extensible array of values used by readers and writers | ||
78 | struct StreamState | ||
79 | { | ||
80 | static const long MAGIC_NUMBER; | ||
81 | |||
82 | StreamState(); | ||
83 | ~StreamState(); | ||
84 | |||
85 | // Important: The size and order of these member variables must *only* change when | ||
86 | // OpenVDB ABI changes to avoid potential segfaults when performing I/O | ||
87 | // across two different versions of the library. Adding new member | ||
88 | // variables to the end of the struct is allowed provided that they | ||
89 | // are only accessed from within an appropriate ABI guard. | ||
90 | int magicNumber; | ||
91 | int fileVersion; | ||
92 | int libraryMajorVersion; | ||
93 | int libraryMinorVersion; | ||
94 | int dataCompression; | ||
95 | int writeGridStatsMetadata; | ||
96 | int gridBackground; | ||
97 | int gridClass; | ||
98 | int halfFloat; | ||
99 | int mappedFile; | ||
100 | int metadata; | ||
101 | } | ||
102 | sStreamState; | ||
103 | |||
104 | const long StreamState::MAGIC_NUMBER = | ||
105 | long((uint64_t(OPENVDB_MAGIC) << 32) | (uint64_t(OPENVDB_MAGIC))); | ||
106 | |||
107 | |||
108 | //////////////////////////////////////// | ||
109 | |||
110 | |||
111 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | StreamState::StreamState(): magicNumber(std::ios_base::xalloc()) |
112 | { | ||
113 | // Having reserved an entry (the one at index magicNumber) in the extensible array | ||
114 | // associated with every stream, store a magic number at that location in the | ||
115 | // array belonging to the cout stream. | ||
116 | 2 | std::cout.iword(magicNumber) = MAGIC_NUMBER; | |
117 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
4 | std::cout.pword(magicNumber) = this; |
118 | |||
119 | // Search for a lower-numbered entry in cout's array that already contains the magic number. | ||
120 | /// @todo This assumes that the indices returned by xalloc() increase monotonically. | ||
121 | int existingArray = -1; | ||
122 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
|
10 | for (int i = 0; i < magicNumber; ++i) { |
123 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (std::cout.iword(i) == MAGIC_NUMBER) { |
124 | existingArray = i; | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | |||
129 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
2 | if (existingArray >= 0 && std::cout.pword(existingArray) != nullptr) { |
130 | // If a lower-numbered entry was found to contain the magic number, | ||
131 | // a coexisting version of this library must have registered it. | ||
132 | // In that case, the corresponding pointer should point to an existing | ||
133 | // StreamState struct. Copy the other array indices from that StreamState | ||
134 | // into this one, so as to share state with the other library. | ||
135 | const StreamState& other = | ||
136 | ✗ | *static_cast<const StreamState*>(std::cout.pword(existingArray)); | |
137 | ✗ | fileVersion = other.fileVersion; | |
138 | ✗ | libraryMajorVersion = other.libraryMajorVersion; | |
139 | ✗ | libraryMinorVersion = other.libraryMinorVersion; | |
140 | ✗ | dataCompression = other.dataCompression; | |
141 | ✗ | writeGridStatsMetadata = other.writeGridStatsMetadata; | |
142 | ✗ | gridBackground = other.gridBackground; | |
143 | ✗ | gridClass = other.gridClass; | |
144 | ✗ | if (other.mappedFile != 0) { // memory-mapped file support was added in OpenVDB 3.0.0 | |
145 | ✗ | mappedFile = other.mappedFile; | |
146 | ✗ | metadata = other.metadata; | |
147 | ✗ | halfFloat = other.halfFloat; | |
148 | } else { | ||
149 | ✗ | mappedFile = std::ios_base::xalloc(); | |
150 | ✗ | metadata = std::ios_base::xalloc(); | |
151 | ✗ | halfFloat = std::ios_base::xalloc(); | |
152 | } | ||
153 | } else { | ||
154 | // Reserve storage for per-stream file format and library version numbers | ||
155 | // and other values of use to readers and writers. Each of the following | ||
156 | // values is an index into the extensible arrays associated with all streams. | ||
157 | // The indices are common to all streams, but the values stored at those indices | ||
158 | // are unique to each stream. | ||
159 | 2 | fileVersion = std::ios_base::xalloc(); | |
160 | 2 | libraryMajorVersion = std::ios_base::xalloc(); | |
161 | 2 | libraryMinorVersion = std::ios_base::xalloc(); | |
162 | 2 | dataCompression = std::ios_base::xalloc(); | |
163 | 2 | writeGridStatsMetadata = std::ios_base::xalloc(); | |
164 | 2 | gridBackground = std::ios_base::xalloc(); | |
165 | 2 | gridClass = std::ios_base::xalloc(); | |
166 | 2 | mappedFile = std::ios_base::xalloc(); | |
167 | 2 | metadata = std::ios_base::xalloc(); | |
168 | 2 | halfFloat = std::ios_base::xalloc(); | |
169 | } | ||
170 | 2 | } | |
171 | |||
172 | |||
173 | 4 | StreamState::~StreamState() | |
174 | { | ||
175 | // Ensure that this StreamState struct can no longer be accessed. | ||
176 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | std::cout.iword(magicNumber) = 0; |
177 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | std::cout.pword(magicNumber) = nullptr; |
178 | 2 | } | |
179 | |||
180 | } // unnamed namespace | ||
181 | |||
182 | |||
183 | //////////////////////////////////////// | ||
184 | |||
185 | |||
186 | struct StreamMetadata::Impl | ||
187 | { | ||
188 | // Important: The size and order of these member variables must *only* change when | ||
189 | // OpenVDB ABI changes to avoid potential segfaults when performing I/O | ||
190 | // across two different versions of the library. Adding new member | ||
191 | // variables to the end of the struct is allowed provided that they | ||
192 | // are only accessed from within an appropriate ABI guard. | ||
193 | |||
194 | uint32_t mFileVersion = OPENVDB_FILE_VERSION; | ||
195 | VersionId mLibraryVersion = { OPENVDB_LIBRARY_MAJOR_VERSION, OPENVDB_LIBRARY_MINOR_VERSION }; | ||
196 | uint32_t mCompression = COMPRESS_NONE; | ||
197 | uint32_t mGridClass = GRID_UNKNOWN; | ||
198 | const void* mBackgroundPtr = nullptr; ///< @todo use Metadata::Ptr? | ||
199 | bool mHalfFloat = false; | ||
200 | bool mWriteGridStats = false; | ||
201 | bool mSeekable = false; | ||
202 | bool mCountingPasses = false; | ||
203 | uint32_t mPass = 0; | ||
204 | MetaMap mGridMetadata; | ||
205 | AuxDataMap mAuxData; | ||
206 | bool mDelayedLoadMeta = DelayedLoadMetadata::isRegisteredType(); | ||
207 | uint64_t mLeaf = 0; | ||
208 | uint32_t mTest = 0; // for testing only | ||
209 | }; // struct StreamMetadata | ||
210 | |||
211 | |||
212 |
1/2✓ Branch 2 taken 137 times.
✗ Branch 3 not taken.
|
137 | StreamMetadata::StreamMetadata(): mImpl(new Impl) |
213 | { | ||
214 | 137 | } | |
215 | |||
216 | |||
217 |
1/2✓ Branch 2 taken 264 times.
✗ Branch 3 not taken.
|
264 | StreamMetadata::StreamMetadata(const StreamMetadata& other): mImpl(new Impl(*other.mImpl)) |
218 | { | ||
219 | 264 | } | |
220 | |||
221 | |||
222 | ✗ | StreamMetadata::StreamMetadata(std::ios_base& strm): mImpl(new Impl) | |
223 | { | ||
224 | ✗ | mImpl->mFileVersion = getFormatVersion(strm); | |
225 | ✗ | mImpl->mLibraryVersion = getLibraryVersion(strm); | |
226 | ✗ | mImpl->mCompression = getDataCompression(strm); | |
227 | ✗ | mImpl->mGridClass = getGridClass(strm); | |
228 | ✗ | mImpl->mHalfFloat = getHalfFloat(strm); | |
229 | ✗ | mImpl->mWriteGridStats = getWriteGridStatsMetadata(strm); | |
230 | } | ||
231 | |||
232 | |||
233 | 401 | StreamMetadata::~StreamMetadata() | |
234 | { | ||
235 | 401 | } | |
236 | |||
237 | |||
238 | StreamMetadata& | ||
239 | ✗ | StreamMetadata::operator=(const StreamMetadata& other) | |
240 | { | ||
241 | ✗ | if (&other != this) { | |
242 | ✗ | mImpl.reset(new Impl(*other.mImpl)); | |
243 | } | ||
244 | ✗ | return *this; | |
245 | } | ||
246 | |||
247 | |||
248 | void | ||
249 | 19334 | StreamMetadata::transferTo(std::ios_base& strm) const | |
250 | { | ||
251 | 19334 | io::setVersion(strm, mImpl->mLibraryVersion, mImpl->mFileVersion); | |
252 | 19334 | io::setDataCompression(strm, mImpl->mCompression); | |
253 | 19334 | io::setGridBackgroundValuePtr(strm, mImpl->mBackgroundPtr); | |
254 | 19334 | io::setGridClass(strm, mImpl->mGridClass); | |
255 | 19334 | io::setHalfFloat(strm, mImpl->mHalfFloat); | |
256 | 19334 | io::setWriteGridStatsMetadata(strm, mImpl->mWriteGridStats); | |
257 | 19334 | } | |
258 | |||
259 | |||
260 | ✗ | uint32_t StreamMetadata::fileVersion() const { return mImpl->mFileVersion; } | |
261 | ✗ | VersionId StreamMetadata::libraryVersion() const { return mImpl->mLibraryVersion; } | |
262 | ✗ | uint32_t StreamMetadata::compression() const { return mImpl->mCompression; } | |
263 | ✗ | uint32_t StreamMetadata::gridClass() const { return mImpl->mGridClass; } | |
264 | ✗ | const void* StreamMetadata::backgroundPtr() const { return mImpl->mBackgroundPtr; } | |
265 | ✗ | bool StreamMetadata::halfFloat() const { return mImpl->mHalfFloat; } | |
266 | ✗ | bool StreamMetadata::writeGridStats() const { return mImpl->mWriteGridStats; } | |
267 | 48365 | bool StreamMetadata::seekable() const { return mImpl->mSeekable; } | |
268 | 12654 | bool StreamMetadata::delayedLoadMeta() const { return mImpl->mDelayedLoadMeta; } | |
269 | 55125 | bool StreamMetadata::countingPasses() const { return mImpl->mCountingPasses; } | |
270 | 354253 | uint32_t StreamMetadata::pass() const { return mImpl->mPass; } | |
271 | 35711 | uint64_t StreamMetadata::leaf() const { return mImpl->mLeaf; } | |
272 | 12929 | MetaMap& StreamMetadata::gridMetadata() { return mImpl->mGridMetadata; } | |
273 | ✗ | const MetaMap& StreamMetadata::gridMetadata() const { return mImpl->mGridMetadata; } | |
274 | 125 | uint32_t StreamMetadata::__test() const { return mImpl->mTest; } | |
275 | |||
276 | 230382 | StreamMetadata::AuxDataMap& StreamMetadata::auxData() { return mImpl->mAuxData; } | |
277 | ✗ | const StreamMetadata::AuxDataMap& StreamMetadata::auxData() const { return mImpl->mAuxData; } | |
278 | |||
279 | 19391 | void StreamMetadata::setFileVersion(uint32_t v) { mImpl->mFileVersion = v; } | |
280 | 19391 | void StreamMetadata::setLibraryVersion(VersionId v) { mImpl->mLibraryVersion = v; } | |
281 | 19965 | void StreamMetadata::setCompression(uint32_t c) { mImpl->mCompression = c; } | |
282 | 19584 | void StreamMetadata::setGridClass(uint32_t c) { mImpl->mGridClass = c; } | |
283 | 19717 | void StreamMetadata::setBackgroundPtr(const void* ptr) { mImpl->mBackgroundPtr = ptr; } | |
284 | 19609 | void StreamMetadata::setHalfFloat(bool b) { mImpl->mHalfFloat = b; } | |
285 | 19399 | void StreamMetadata::setWriteGridStats(bool b) { mImpl->mWriteGridStats = b; } | |
286 | 56 | void StreamMetadata::setSeekable(bool b) { mImpl->mSeekable = b; } | |
287 | 22 | void StreamMetadata::setCountingPasses(bool b) { mImpl->mCountingPasses = b; } | |
288 | 20731 | void StreamMetadata::setPass(uint32_t i) { mImpl->mPass = i; } | |
289 | 23182 | void StreamMetadata::setLeaf(uint64_t i) { mImpl->mLeaf = i; } | |
290 | 1 | void StreamMetadata::__setTest(uint32_t t) { mImpl->mTest = t; } | |
291 | |||
292 | std::string | ||
293 | ✗ | StreamMetadata::str() const | |
294 | { | ||
295 | ✗ | std::ostringstream ostr; | |
296 | ostr << std::boolalpha; | ||
297 | ✗ | ostr << "version: " << libraryVersion().first << "." << libraryVersion().second | |
298 | ✗ | << "/" << fileVersion() << "\n"; | |
299 | ✗ | ostr << "class: " << GridBase::gridClassToString(static_cast<GridClass>(gridClass())) << "\n"; | |
300 | ✗ | ostr << "compression: " << compressionToString(compression()) << "\n"; | |
301 | ✗ | ostr << "half_float: " << halfFloat() << "\n"; | |
302 | ✗ | ostr << "seekable: " << seekable() << "\n"; | |
303 | ✗ | ostr << "delayed_load_meta: " << delayedLoadMeta() << "\n"; | |
304 | ✗ | ostr << "pass: " << pass() << "\n"; | |
305 | ✗ | ostr << "counting_passes: " << countingPasses() << "\n"; | |
306 | ✗ | ostr << "write_grid_stats_metadata: " << writeGridStats() << "\n"; | |
307 | ✗ | if (!auxData().empty()) ostr << auxData(); | |
308 | ✗ | if (gridMetadata().metaCount() != 0) { | |
309 | ✗ | ostr << "grid_metadata:\n" << gridMetadata().str(/*indent=*/" "); | |
310 | } | ||
311 | ✗ | return ostr.str(); | |
312 | } | ||
313 | |||
314 | |||
315 | std::ostream& | ||
316 | ✗ | operator<<(std::ostream& os, const StreamMetadata& meta) | |
317 | { | ||
318 | ✗ | os << meta.str(); | |
319 | ✗ | return os; | |
320 | } | ||
321 | |||
322 | |||
323 | namespace { | ||
324 | |||
325 | template<typename T> | ||
326 | inline bool | ||
327 | ✗ | writeAsType(std::ostream& os, const boost::any& val) | |
328 | { | ||
329 | ✗ | if (val.type() == typeid(T)) { | |
330 | ✗ | os << boost::any_cast<T>(val); | |
331 | ✗ | return true; | |
332 | } | ||
333 | return false; | ||
334 | } | ||
335 | |||
336 | struct PopulateDelayedLoadMetadataOp | ||
337 | { | ||
338 | DelayedLoadMetadata& metadata; | ||
339 | uint32_t compression; | ||
340 | |||
341 | PopulateDelayedLoadMetadataOp(DelayedLoadMetadata& _metadata, uint32_t _compression) | ||
342 | 150 | : metadata(_metadata) | |
343 | 150 | , compression(_compression) { } | |
344 | |||
345 | template<typename GridT> | ||
346 | 266 | void operator()(const GridT& grid) const | |
347 | { | ||
348 | using TreeT = typename GridT::TreeType; | ||
349 | using ValueT = typename TreeT::ValueType; | ||
350 | using LeafT = typename TreeT::LeafNodeType; | ||
351 | using MaskT = typename LeafT::NodeMaskType; | ||
352 | |||
353 | const TreeT& tree = grid.constTree(); | ||
354 | 266 | const Index32 leafCount = tree.leafCount(); | |
355 | |||
356 | // early exit if not leaf nodes | ||
357 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 130 times.
|
266 | if (leafCount == Index32(0)) return; |
358 | |||
359 | 260 | metadata.resizeMask(leafCount); | |
360 | |||
361 |
2/2✓ Branch 0 taken 124 times.
✓ Branch 1 taken 6 times.
|
260 | if (compression & (COMPRESS_BLOSC | COMPRESS_ZIP)) { |
362 | 248 | metadata.resizeCompressedSize(leafCount); | |
363 | } | ||
364 | |||
365 | 260 | const auto background = tree.background(); | |
366 | 260 | const bool saveFloatAsHalf = grid.saveFloatAsHalf(); | |
367 | |||
368 | 260 | tree::LeafManager<const TreeT> leafManager(tree); | |
369 | |||
370 |
1/2✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
|
260 | leafManager.foreach( |
371 | 23803 | [&](const LeafT& leaf, size_t idx) { | |
372 | // set mask value | ||
373 | 47606 | MaskCompress<ValueT, MaskT> maskCompressData( | |
374 | leaf.valueMask(), /*childMask=*/MaskT(), leaf.buffer().data(), background); | ||
375 | 23803 | metadata.setMask(idx, maskCompressData.metadata); | |
376 | |||
377 |
5/14✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 366 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 21063 times.
✓ Branch 9 taken 606 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1194 times.
✓ Branch 13 taken 574 times.
|
23803 | if (compression & (COMPRESS_BLOSC | COMPRESS_ZIP)) { |
378 | // set compressed size value | ||
379 | size_t sizeBytes(8); | ||
380 | 22623 | size_t compressedSize = io::writeCompressedValuesSize( | |
381 | leaf.buffer().data(), LeafT::SIZE, | ||
382 | 22623 | leaf.valueMask(), maskCompressData.metadata, saveFloatAsHalf, compression); | |
383 | 22623 | metadata.setCompressedSize(idx, compressedSize+sizeBytes); | |
384 | } | ||
385 | } | ||
386 | ); | ||
387 | } | ||
388 | }; | ||
389 | |||
390 | 150 | bool populateDelayedLoadMetadata(DelayedLoadMetadata& metadata, | |
391 | const GridBase& gridBase, uint32_t compression) | ||
392 | { | ||
393 | PopulateDelayedLoadMetadataOp op(metadata, compression); | ||
394 | |||
395 | using AllowedTypes = TypeList< | ||
396 | Int32Grid, Int64Grid, | ||
397 | FloatGrid, DoubleGrid, | ||
398 | Vec3IGrid, Vec3SGrid, Vec3DGrid>; | ||
399 | |||
400 | 150 | return gridBase.apply<AllowedTypes>(op); | |
401 | } | ||
402 | |||
403 | } // unnamed namespace | ||
404 | |||
405 | std::ostream& | ||
406 | ✗ | operator<<(std::ostream& os, const StreamMetadata::AuxDataMap& auxData) | |
407 | { | ||
408 | ✗ | for (StreamMetadata::AuxDataMap::const_iterator it = auxData.begin(), end = auxData.end(); | |
409 | ✗ | it != end; ++it) | |
410 | { | ||
411 | ✗ | os << it->first << ": "; | |
412 | // Note: boost::any doesn't support serialization. | ||
413 | ✗ | const boost::any& val = it->second; | |
414 | ✗ | if (!writeAsType<int32_t>(os, val) | |
415 | ✗ | && !writeAsType<int64_t>(os, val) | |
416 | ✗ | && !writeAsType<int16_t>(os, val) | |
417 | ✗ | && !writeAsType<int8_t>(os, val) | |
418 | ✗ | && !writeAsType<uint32_t>(os, val) | |
419 | ✗ | && !writeAsType<uint64_t>(os, val) | |
420 | ✗ | && !writeAsType<uint16_t>(os, val) | |
421 | ✗ | && !writeAsType<uint8_t>(os, val) | |
422 | ✗ | && !writeAsType<float>(os, val) | |
423 | ✗ | && !writeAsType<double>(os, val) | |
424 | ✗ | && !writeAsType<long double>(os, val) | |
425 | ✗ | && !writeAsType<bool>(os, val) | |
426 | ✗ | && !writeAsType<std::string>(os, val) | |
427 | ✗ | && !writeAsType<const char*>(os, val)) | |
428 | { | ||
429 | ✗ | os << val.type().name() << "(...)"; | |
430 | } | ||
431 | ✗ | os << "\n"; | |
432 | } | ||
433 | ✗ | return os; | |
434 | } | ||
435 | |||
436 | |||
437 | //////////////////////////////////////// | ||
438 | |||
439 | |||
440 | // Memory-mapping a VDB file permits threaded input (and output, potentially, | ||
441 | // though that might not be practical for compressed files or files containing | ||
442 | // multiple grids). In particular, a memory-mapped file can be loaded lazily, | ||
443 | // meaning that the voxel buffers of the leaf nodes of a grid's tree are not allocated | ||
444 | // until they are actually accessed. When access to its buffer is requested, | ||
445 | // a leaf node allocates memory for the buffer and then streams in (and decompresses) | ||
446 | // its contents from the memory map, starting from a stream offset that was recorded | ||
447 | // at the time the node was constructed. The memory map must persist as long as | ||
448 | // there are unloaded leaf nodes; this is ensured by storing a shared pointer | ||
449 | // to the map in each unloaded node. | ||
450 | |||
451 | class MappedFile::Impl | ||
452 | { | ||
453 | public: | ||
454 | 62 | Impl(const std::string& filename, bool autoDelete) | |
455 | 62 | : mMap(filename.c_str(), boost::interprocess::read_only) | |
456 | , mRegion(mMap, boost::interprocess::read_only) | ||
457 |
1/2✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
|
62 | , mAutoDelete(autoDelete) |
458 | { | ||
459 | mLastWriteTime = this->getLastWriteTime(); | ||
460 | |||
461 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 8 times.
|
62 | if (mAutoDelete) { |
462 | #ifndef _WIN32 | ||
463 | // On Unix systems, unlink the file so that it gets deleted once it is closed. | ||
464 | 54 | ::unlink(mMap.get_name()); | |
465 | #endif | ||
466 | } | ||
467 | 62 | } | |
468 | |||
469 |
1/2✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
|
63 | ~Impl() |
470 | 63 | { | |
471 | std::string filename; | ||
472 |
1/2✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
|
63 | if (const char* s = mMap.get_name()) filename = s; |
473 | OPENVDB_LOG_DEBUG_RUNTIME("closing memory-mapped file " << filename); | ||
474 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 60 times.
|
66 | if (mNotifier) mNotifier(filename); |
475 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 9 times.
|
63 | if (mAutoDelete) { |
476 |
1/2✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
|
54 | if (!boost::interprocess::file_mapping::remove(filename.c_str())) { |
477 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
|
54 | if (errno != ENOENT) { |
478 | // Warn if the file exists but couldn't be removed. | ||
479 | ✗ | std::string mesg = getErrorString(); | |
480 | ✗ | if (!mesg.empty()) mesg = " (" + mesg + ")"; | |
481 | OPENVDB_LOG_WARN("failed to remove temporary file " << filename << mesg); | ||
482 | } | ||
483 | } | ||
484 | } | ||
485 | 63 | } | |
486 | |||
487 | Index64 getLastWriteTime() const | ||
488 | { | ||
489 | Index64 result = 0; | ||
490 | const char* filename = mMap.get_name(); | ||
491 | |||
492 | #ifdef _WIN32 | ||
493 | // boost::interprocess::detail was renamed to boost::interprocess::ipcdetail in Boost 1.48. | ||
494 | using namespace boost::interprocess::detail; | ||
495 | using namespace boost::interprocess::ipcdetail; | ||
496 | |||
497 | if (void* fh = open_existing_file(filename, boost::interprocess::read_only)) { | ||
498 | struct { unsigned long lo, hi; } mtime; // Windows FILETIME struct | ||
499 | if (GetFileTime(fh, nullptr, nullptr, &mtime)) { | ||
500 | result = (Index64(mtime.hi) << 32) | mtime.lo; | ||
501 | } | ||
502 | close_file(fh); | ||
503 | } | ||
504 | #else | ||
505 | struct stat info; | ||
506 |
2/4✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
|
88 | if (0 == ::stat(filename, &info)) { |
507 | 88 | result = Index64(info.st_mtime); | |
508 | } | ||
509 | #endif | ||
510 | return result; | ||
511 | } | ||
512 | |||
513 | boost::interprocess::file_mapping mMap; | ||
514 | boost::interprocess::mapped_region mRegion; | ||
515 | bool mAutoDelete; | ||
516 | Notifier mNotifier; | ||
517 | mutable std::atomic<Index64> mLastWriteTime; | ||
518 | |||
519 | private: | ||
520 | Impl(const Impl&); // not copyable | ||
521 | Impl& operator=(const Impl&); // not copyable | ||
522 | }; | ||
523 | |||
524 | |||
525 | 62 | MappedFile::MappedFile(const std::string& filename, bool autoDelete): | |
526 |
1/2✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
|
62 | mImpl(new Impl(filename, autoDelete)) |
527 | { | ||
528 | 62 | } | |
529 | |||
530 | |||
531 | 63 | MappedFile::~MappedFile() | |
532 | { | ||
533 | 63 | } | |
534 | |||
535 | |||
536 | std::string | ||
537 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | MappedFile::filename() const |
538 | { | ||
539 | std::string result; | ||
540 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
|
5 | if (const char* s = mImpl->mMap.get_name()) result = s; |
541 | 5 | return result; | |
542 | } | ||
543 | |||
544 | |||
545 | SharedPtr<std::streambuf> | ||
546 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 19333 times.
|
19359 | MappedFile::createBuffer() const |
547 | { | ||
548 |
3/4✓ Branch 0 taken 26 times.
✓ Branch 1 taken 19333 times.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
|
19359 | if (!mImpl->mAutoDelete && mImpl->mLastWriteTime > 0) { |
549 | // Warn if the file has been modified since it was opened | ||
550 | // (but don't bother checking if it is a private, temporary file). | ||
551 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (mImpl->getLastWriteTime() > mImpl->mLastWriteTime) { |
552 | ✗ | OPENVDB_LOG_WARN("file " << this->filename() << " might have changed on disk" | |
553 | << " since it was opened"); | ||
554 | mImpl->mLastWriteTime = 0; // suppress further warnings | ||
555 | } | ||
556 | } | ||
557 | |||
558 | return SharedPtr<std::streambuf>{ | ||
559 | new boost::iostreams::stream_buffer<boost::iostreams::array_source>{ | ||
560 |
1/2✓ Branch 2 taken 19359 times.
✗ Branch 3 not taken.
|
38718 | static_cast<const char*>(mImpl->mRegion.get_address()), mImpl->mRegion.get_size()}}; |
561 | } | ||
562 | |||
563 | |||
564 | void | ||
565 | 56 | MappedFile::setNotifier(const Notifier& notifier) | |
566 | { | ||
567 | 56 | mImpl->mNotifier = notifier; | |
568 | 56 | } | |
569 | |||
570 | |||
571 | void | ||
572 | ✗ | MappedFile::clearNotifier() | |
573 | { | ||
574 | mImpl->mNotifier = nullptr; | ||
575 | } | ||
576 | |||
577 | |||
578 | //////////////////////////////////////// | ||
579 | |||
580 | |||
581 | std::string | ||
582 | 1 | getErrorString(int errorNum) | |
583 | { | ||
584 | 1 | return std::error_code(errorNum, std::generic_category()).message(); | |
585 | } | ||
586 | |||
587 | |||
588 | std::string | ||
589 | 1 | getErrorString() | |
590 | { | ||
591 | 1 | return getErrorString(errno); | |
592 | } | ||
593 | |||
594 | |||
595 | //////////////////////////////////////// | ||
596 | |||
597 | |||
598 | 129 | Archive::Archive() | |
599 | : mFileVersion(OPENVDB_FILE_VERSION) | ||
600 | , mLibraryVersion(OPENVDB_LIBRARY_MAJOR_VERSION, OPENVDB_LIBRARY_MINOR_VERSION) | ||
601 | 129 | , mUuid(boost::uuids::nil_uuid()) | |
602 | , mInputHasGridOffsets(false) | ||
603 | , mEnableInstancing(true) | ||
604 | , mCompression(DEFAULT_COMPRESSION_FLAGS) | ||
605 | 129 | , mEnableGridStats(true) | |
606 | { | ||
607 | 129 | } | |
608 | |||
609 | |||
610 | 298 | Archive::~Archive() | |
611 | { | ||
612 | } | ||
613 | |||
614 | |||
615 | Archive::Ptr | ||
616 | ✗ | Archive::copy() const | |
617 | { | ||
618 | ✗ | return Archive::Ptr(new Archive(*this)); | |
619 | } | ||
620 | |||
621 | |||
622 | //////////////////////////////////////// | ||
623 | |||
624 | |||
625 | std::string | ||
626 | 5 | Archive::getUniqueTag() const | |
627 | { | ||
628 | 5 | return boost::uuids::to_string(mUuid); | |
629 | } | ||
630 | |||
631 | |||
632 | bool | ||
633 | 2 | Archive::isIdentical(const std::string& uuidStr) const | |
634 | { | ||
635 | 2 | return uuidStr == getUniqueTag(); | |
636 | } | ||
637 | |||
638 | |||
639 | //////////////////////////////////////// | ||
640 | |||
641 | |||
642 | uint32_t | ||
643 | 63455 | getFormatVersion(std::ios_base& is) | |
644 | { | ||
645 | /// @todo get from StreamMetadata | ||
646 |
1/2✓ Branch 0 taken 63455 times.
✗ Branch 1 not taken.
|
63455 | return static_cast<uint32_t>(is.iword(sStreamState.fileVersion)); |
647 | } | ||
648 | |||
649 | |||
650 | void | ||
651 | 56 | Archive::setFormatVersion(std::istream& is) | |
652 | { | ||
653 |
1/2✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
|
56 | is.iword(sStreamState.fileVersion) = mFileVersion; ///< @todo remove |
654 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(is)) { |
655 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | meta->setFileVersion(mFileVersion); |
656 | } | ||
657 | 56 | } | |
658 | |||
659 | |||
660 | VersionId | ||
661 |
1/2✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
|
127 | getLibraryVersion(std::ios_base& is) |
662 | { | ||
663 | /// @todo get from StreamMetadata | ||
664 | VersionId version; | ||
665 |
1/2✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
|
127 | version.first = static_cast<uint32_t>(is.iword(sStreamState.libraryMajorVersion)); |
666 |
1/2✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
|
127 | version.second = static_cast<uint32_t>(is.iword(sStreamState.libraryMinorVersion)); |
667 | 127 | return version; | |
668 | } | ||
669 | |||
670 | |||
671 | void | ||
672 | 56 | Archive::setLibraryVersion(std::istream& is) | |
673 | { | ||
674 |
1/2✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
|
56 | is.iword(sStreamState.libraryMajorVersion) = mLibraryVersion.first; ///< @todo remove |
675 |
1/2✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
|
56 | is.iword(sStreamState.libraryMinorVersion) = mLibraryVersion.second; ///< @todo remove |
676 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(is)) { |
677 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | meta->setLibraryVersion(mLibraryVersion); |
678 | } | ||
679 | 56 | } | |
680 | |||
681 | |||
682 | std::string | ||
683 | ✗ | getVersion(std::ios_base& is) | |
684 | { | ||
685 | ✗ | VersionId version = getLibraryVersion(is); | |
686 | ✗ | std::ostringstream ostr; | |
687 | ✗ | ostr << version.first << "." << version.second << "/" << getFormatVersion(is); | |
688 | ✗ | return ostr.str(); | |
689 | } | ||
690 | |||
691 | |||
692 | void | ||
693 | 28 | setCurrentVersion(std::istream& is) | |
694 | { | ||
695 |
1/2✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
|
28 | is.iword(sStreamState.fileVersion) = OPENVDB_FILE_VERSION; ///< @todo remove |
696 |
1/2✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
|
28 | is.iword(sStreamState.libraryMajorVersion) = OPENVDB_LIBRARY_MAJOR_VERSION; ///< @todo remove |
697 |
1/2✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
|
28 | is.iword(sStreamState.libraryMinorVersion) = OPENVDB_LIBRARY_MINOR_VERSION; ///< @todo remove |
698 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 27 times.
|
28 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(is)) { |
699 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | meta->setFileVersion(OPENVDB_FILE_VERSION); |
700 |
1/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
1 | meta->setLibraryVersion(VersionId( |
701 | OPENVDB_LIBRARY_MAJOR_VERSION, OPENVDB_LIBRARY_MINOR_VERSION)); | ||
702 | } | ||
703 | 28 | } | |
704 | |||
705 | |||
706 | void | ||
707 | 19341 | setVersion(std::ios_base& strm, const VersionId& libraryVersion, uint32_t fileVersion) | |
708 | { | ||
709 |
1/2✓ Branch 0 taken 19341 times.
✗ Branch 1 not taken.
|
19341 | strm.iword(sStreamState.fileVersion) = fileVersion; ///< @todo remove |
710 |
1/2✓ Branch 0 taken 19341 times.
✗ Branch 1 not taken.
|
19341 | strm.iword(sStreamState.libraryMajorVersion) = libraryVersion.first; ///< @todo remove |
711 |
1/2✓ Branch 0 taken 19341 times.
✗ Branch 1 not taken.
|
19341 | strm.iword(sStreamState.libraryMinorVersion) = libraryVersion.second; ///< @todo remove |
712 |
2/2✓ Branch 1 taken 19334 times.
✓ Branch 2 taken 7 times.
|
19341 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { |
713 |
1/2✓ Branch 1 taken 19334 times.
✗ Branch 2 not taken.
|
19334 | meta->setFileVersion(fileVersion); |
714 |
1/2✓ Branch 1 taken 19334 times.
✗ Branch 2 not taken.
|
19334 | meta->setLibraryVersion(libraryVersion); |
715 | } | ||
716 | 19341 | } | |
717 | |||
718 | |||
719 | std::string | ||
720 | ✗ | Archive::version() const | |
721 | { | ||
722 | ✗ | std::ostringstream ostr; | |
723 | ✗ | ostr << mLibraryVersion.first << "." << mLibraryVersion.second << "/" << mFileVersion; | |
724 | ✗ | return ostr.str(); | |
725 | } | ||
726 | |||
727 | |||
728 | //////////////////////////////////////// | ||
729 | |||
730 | |||
731 | uint32_t | ||
732 | 110748 | getDataCompression(std::ios_base& strm) | |
733 | { | ||
734 | /// @todo get from StreamMetadata | ||
735 |
2/2✓ Branch 0 taken 110712 times.
✓ Branch 1 taken 36 times.
|
110748 | return uint32_t(strm.iword(sStreamState.dataCompression)); |
736 | } | ||
737 | |||
738 | |||
739 | void | ||
740 | 19918 | setDataCompression(std::ios_base& strm, uint32_t c) | |
741 | { | ||
742 |
2/2✓ Branch 0 taken 19916 times.
✓ Branch 1 taken 2 times.
|
19918 | strm.iword(sStreamState.dataCompression) = c; ///< @todo remove |
743 |
2/2✓ Branch 1 taken 19909 times.
✓ Branch 2 taken 9 times.
|
19918 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { |
744 |
1/2✓ Branch 1 taken 19909 times.
✗ Branch 2 not taken.
|
19909 | meta->setCompression(c); |
745 | } | ||
746 | 19918 | } | |
747 | |||
748 | |||
749 | void | ||
750 | 56 | Archive::setDataCompression(std::istream& is) | |
751 | { | ||
752 | 56 | io::setDataCompression(is, mCompression); ///< @todo remove | |
753 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(is)) { |
754 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | meta->setCompression(mCompression); |
755 | } | ||
756 | 56 | } | |
757 | |||
758 | |||
759 | //static | ||
760 | bool | ||
761 | 1 | Archive::hasBloscCompression() | |
762 | { | ||
763 | #ifdef OPENVDB_USE_BLOSC | ||
764 | 1 | return true; | |
765 | #else | ||
766 | return false; | ||
767 | #endif | ||
768 | } | ||
769 | |||
770 | |||
771 | //static | ||
772 | bool | ||
773 | ✗ | Archive::hasZLibCompression() | |
774 | { | ||
775 | #ifdef OPENVDB_USE_ZLIB | ||
776 | ✗ | return true; | |
777 | #else | ||
778 | return false; | ||
779 | #endif | ||
780 | } | ||
781 | |||
782 | |||
783 | void | ||
784 | 163 | Archive::setGridCompression(std::ostream& os, const GridBase& grid) const | |
785 | { | ||
786 | // Start with the options that are enabled globally for this archive. | ||
787 | 163 | uint32_t c = compression(); | |
788 | |||
789 | // Disable options that are inappropriate for the given grid. | ||
790 |
2/2✓ Branch 1 taken 79 times.
✓ Branch 2 taken 84 times.
|
163 | switch (grid.getGridClass()) { |
791 | 79 | case GRID_LEVEL_SET: | |
792 | case GRID_FOG_VOLUME: | ||
793 | // ZLIB compression is not used on level sets or fog volumes. | ||
794 | 79 | c = c & ~COMPRESS_ZIP; | |
795 | 79 | break; | |
796 | case GRID_STAGGERED: | ||
797 | case GRID_UNKNOWN: | ||
798 | break; | ||
799 | } | ||
800 | 163 | io::setDataCompression(os, c); | |
801 | |||
802 | 163 | os.write(reinterpret_cast<const char*>(&c), sizeof(uint32_t)); | |
803 | 163 | } | |
804 | |||
805 | |||
806 | void | ||
807 | 134 | Archive::readGridCompression(std::istream& is) | |
808 | { | ||
809 |
1/2✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
|
134 | if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) { |
810 | 134 | uint32_t c = COMPRESS_NONE; | |
811 | 134 | is.read(reinterpret_cast<char*>(&c), sizeof(uint32_t)); | |
812 | 134 | io::setDataCompression(is, c); | |
813 | } | ||
814 | 134 | } | |
815 | |||
816 | |||
817 | //////////////////////////////////////// | ||
818 | |||
819 | |||
820 | bool | ||
821 | 150 | getWriteGridStatsMetadata(std::ios_base& strm) | |
822 | { | ||
823 | /// @todo get from StreamMetadata | ||
824 |
1/2✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
|
150 | return strm.iword(sStreamState.writeGridStatsMetadata) != 0; |
825 | } | ||
826 | |||
827 | |||
828 | void | ||
829 | 19399 | setWriteGridStatsMetadata(std::ios_base& strm, bool writeGridStats) | |
830 | { | ||
831 |
1/2✓ Branch 0 taken 19399 times.
✗ Branch 1 not taken.
|
19399 | strm.iword(sStreamState.writeGridStatsMetadata) = writeGridStats; ///< @todo remove |
832 |
1/2✓ Branch 1 taken 19399 times.
✗ Branch 2 not taken.
|
19399 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { |
833 |
1/2✓ Branch 1 taken 19399 times.
✗ Branch 2 not taken.
|
19399 | meta->setWriteGridStats(writeGridStats); |
834 | } | ||
835 | 19399 | } | |
836 | |||
837 | |||
838 | //////////////////////////////////////// | ||
839 | |||
840 | |||
841 | uint32_t | ||
842 | ✗ | getGridClass(std::ios_base& strm) | |
843 | { | ||
844 | /// @todo get from StreamMetadata | ||
845 | ✗ | const uint32_t val = static_cast<uint32_t>(strm.iword(sStreamState.gridClass)); | |
846 | ✗ | if (val >= NUM_GRID_CLASSES) return GRID_UNKNOWN; | |
847 | return val; | ||
848 | } | ||
849 | |||
850 | |||
851 | void | ||
852 | 19584 | setGridClass(std::ios_base& strm, uint32_t cls) | |
853 | { | ||
854 |
1/2✓ Branch 0 taken 19584 times.
✗ Branch 1 not taken.
|
19584 | strm.iword(sStreamState.gridClass) = long(cls); ///< @todo remove |
855 |
1/2✓ Branch 1 taken 19584 times.
✗ Branch 2 not taken.
|
19584 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { |
856 |
1/2✓ Branch 1 taken 19584 times.
✗ Branch 2 not taken.
|
19584 | meta->setGridClass(cls); |
857 | } | ||
858 | 19584 | } | |
859 | |||
860 | |||
861 | bool | ||
862 | 18978 | getHalfFloat(std::ios_base& strm) | |
863 | { | ||
864 | /// @todo get from StreamMetadata | ||
865 |
1/2✓ Branch 0 taken 18978 times.
✗ Branch 1 not taken.
|
18978 | return strm.iword(sStreamState.halfFloat) != 0; |
866 | } | ||
867 | |||
868 | |||
869 | void | ||
870 | 19334 | setHalfFloat(std::ios_base& strm, bool halfFloat) | |
871 | { | ||
872 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 19333 times.
|
19334 | strm.iword(sStreamState.halfFloat) = halfFloat; ///< @todo remove |
873 |
1/2✓ Branch 1 taken 19334 times.
✗ Branch 2 not taken.
|
19334 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { |
874 |
1/2✓ Branch 1 taken 19334 times.
✗ Branch 2 not taken.
|
19334 | meta->setHalfFloat(halfFloat); |
875 | } | ||
876 | 19334 | } | |
877 | |||
878 | |||
879 | const void* | ||
880 | 48744 | getGridBackgroundValuePtr(std::ios_base& strm) | |
881 | { | ||
882 | /// @todo get from StreamMetadata | ||
883 |
1/2✓ Branch 0 taken 48744 times.
✗ Branch 1 not taken.
|
48744 | return strm.pword(sStreamState.gridBackground); |
884 | } | ||
885 | |||
886 | |||
887 | void | ||
888 | 19724 | setGridBackgroundValuePtr(std::ios_base& strm, const void* background) | |
889 | { | ||
890 |
2/2✓ Branch 0 taken 19723 times.
✓ Branch 1 taken 1 times.
|
19724 | strm.pword(sStreamState.gridBackground) = const_cast<void*>(background); ///< @todo remove |
891 |
2/2✓ Branch 1 taken 19717 times.
✓ Branch 2 taken 7 times.
|
19724 | if (StreamMetadata::Ptr meta = getStreamMetadataPtr(strm)) { |
892 |
1/2✓ Branch 1 taken 19717 times.
✗ Branch 2 not taken.
|
19717 | meta->setBackgroundPtr(background); |
893 | } | ||
894 | 19724 | } | |
895 | |||
896 | |||
897 | MappedFile::Ptr | ||
898 | 100148 | getMappedFilePtr(std::ios_base& strm) | |
899 | { | ||
900 |
4/4✓ Branch 0 taken 100147 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 96030 times.
✓ Branch 3 taken 4118 times.
|
200296 | if (const void* ptr = strm.pword(sStreamState.mappedFile)) { |
901 | return *static_cast<const MappedFile::Ptr*>(ptr); | ||
902 | } | ||
903 | return MappedFile::Ptr(); | ||
904 | } | ||
905 | |||
906 | |||
907 | void | ||
908 | 76 | setMappedFilePtr(std::ios_base& strm, io::MappedFile::Ptr& mappedFile) | |
909 | { | ||
910 |
1/2✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
|
76 | strm.pword(sStreamState.mappedFile) = &mappedFile; |
911 | 76 | } | |
912 | |||
913 | |||
914 | StreamMetadata::Ptr | ||
915 | 386261 | getStreamMetadataPtr(std::ios_base& strm) | |
916 | { | ||
917 |
4/4✓ Branch 0 taken 386157 times.
✓ Branch 1 taken 104 times.
✓ Branch 2 taken 377902 times.
✓ Branch 3 taken 8359 times.
|
772522 | if (const void* ptr = strm.pword(sStreamState.metadata)) { |
918 | return *static_cast<const StreamMetadata::Ptr*>(ptr); | ||
919 | } | ||
920 | return StreamMetadata::Ptr(); | ||
921 | } | ||
922 | |||
923 | |||
924 | void | ||
925 | 19738 | setStreamMetadataPtr(std::ios_base& strm, StreamMetadata::Ptr& meta, bool transfer) | |
926 | { | ||
927 |
2/2✓ Branch 0 taken 350 times.
✓ Branch 1 taken 19388 times.
|
19738 | strm.pword(sStreamState.metadata) = &meta; |
928 |
4/4✓ Branch 0 taken 19337 times.
✓ Branch 1 taken 401 times.
✓ Branch 2 taken 19334 times.
✓ Branch 3 taken 3 times.
|
19738 | if (transfer && meta) meta->transferTo(strm); |
929 | 19738 | } | |
930 | |||
931 | |||
932 | StreamMetadata::Ptr | ||
933 | ✗ | clearStreamMetadataPtr(std::ios_base& strm) | |
934 | { | ||
935 | ✗ | StreamMetadata::Ptr result = getStreamMetadataPtr(strm); | |
936 | ✗ | strm.pword(sStreamState.metadata) = nullptr; | |
937 | ✗ | return result; | |
938 | } | ||
939 | |||
940 | |||
941 | //////////////////////////////////////// | ||
942 | |||
943 | |||
944 | bool | ||
945 | 58 | Archive::readHeader(std::istream& is) | |
946 | { | ||
947 | // 1) Read the magic number for VDB. | ||
948 | int64_t magic; | ||
949 | 58 | is.read(reinterpret_cast<char*>(&magic), sizeof(int64_t)); | |
950 | |||
951 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 57 times.
|
58 | if (magic != OPENVDB_MAGIC) { |
952 |
2/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
4 | OPENVDB_THROW(IoError, "not a VDB file"); |
953 | } | ||
954 | |||
955 | // 2) Read the file format version number. | ||
956 | 57 | is.read(reinterpret_cast<char*>(&mFileVersion), sizeof(uint32_t)); | |
957 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
|
57 | if (mFileVersion > OPENVDB_FILE_VERSION) { |
958 | ✗ | OPENVDB_LOG_WARN("unsupported VDB file format (expected version " | |
959 | << OPENVDB_FILE_VERSION << " or earlier, got version " << mFileVersion << ")"); | ||
960 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
|
57 | } else if (mFileVersion < 211) { |
961 | // Versions prior to 211 stored separate major, minor and patch numbers. | ||
962 | uint32_t version; | ||
963 | ✗ | is.read(reinterpret_cast<char*>(&version), sizeof(uint32_t)); | |
964 | ✗ | mFileVersion = 100 * mFileVersion + 10 * version; | |
965 | ✗ | is.read(reinterpret_cast<char*>(&version), sizeof(uint32_t)); | |
966 | ✗ | mFileVersion += version; | |
967 | } | ||
968 | |||
969 | // 3) Read the library version numbers (not stored prior to file format version 211). | ||
970 | 57 | mLibraryVersion.first = mLibraryVersion.second = 0; | |
971 |
1/2✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
|
57 | if (mFileVersion >= 211) { |
972 | uint32_t version; | ||
973 | 57 | is.read(reinterpret_cast<char*>(&version), sizeof(uint32_t)); | |
974 | 57 | mLibraryVersion.first = version; // major version | |
975 | 57 | is.read(reinterpret_cast<char*>(&version), sizeof(uint32_t)); | |
976 | 57 | mLibraryVersion.second = version; // minor version | |
977 | } | ||
978 | |||
979 | // 4) Read the flag indicating whether the stream supports partial reading. | ||
980 | // (Versions prior to 212 have no flag because they always supported partial reading.) | ||
981 | 57 | mInputHasGridOffsets = true; | |
982 |
1/2✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
|
57 | if (mFileVersion >= 212) { |
983 | char hasGridOffsets; | ||
984 | 57 | is.read(&hasGridOffsets, sizeof(char)); | |
985 | 57 | mInputHasGridOffsets = hasGridOffsets; | |
986 | } | ||
987 | |||
988 | // 5) Read the flag that indicates whether data is compressed. | ||
989 | // (From version 222 on, compression information is stored per grid.) | ||
990 | 57 | mCompression = DEFAULT_COMPRESSION_FLAGS; | |
991 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
|
57 | if (mFileVersion < OPENVDB_FILE_VERSION_BLOSC_COMPRESSION) { |
992 | // Prior to the introduction of Blosc, ZLIB was the default compression scheme. | ||
993 | ✗ | mCompression = (COMPRESS_ZIP | COMPRESS_ACTIVE_MASK); | |
994 | } | ||
995 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
|
57 | if (mFileVersion >= OPENVDB_FILE_VERSION_SELECTIVE_COMPRESSION && |
996 | mFileVersion < OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) | ||
997 | { | ||
998 | char isCompressed; | ||
999 | ✗ | is.read(&isCompressed, sizeof(char)); | |
1000 | ✗ | mCompression = (isCompressed != 0 ? COMPRESS_ZIP : COMPRESS_NONE); | |
1001 | } | ||
1002 | |||
1003 | // 6) Read the 16-byte (128-bit) uuid. | ||
1004 | 57 | boost::uuids::uuid oldUuid = mUuid; | |
1005 |
1/2✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
|
57 | if (mFileVersion >= OPENVDB_FILE_VERSION_BOOST_UUID) { |
1006 | // UUID is stored as an ASCII string. | ||
1007 | 57 | is >> mUuid; | |
1008 | } else { | ||
1009 | // Older versions stored the UUID as a byte string. | ||
1010 | char uuidBytes[16]; | ||
1011 | ✗ | is.read(uuidBytes, 16); | |
1012 | ✗ | std::memcpy(&mUuid.data[0], uuidBytes, std::min<size_t>(16, mUuid.size())); | |
1013 | } | ||
1014 | 57 | return oldUuid != mUuid; // true if UUID in input stream differs from old UUID | |
1015 | } | ||
1016 | |||
1017 | |||
1018 | void | ||
1019 | 67 | Archive::writeHeader(std::ostream& os, bool seekable) const | |
1020 | { | ||
1021 | // 1) Write the magic number for VDB. | ||
1022 | 67 | int64_t magic = OPENVDB_MAGIC; | |
1023 | 67 | os.write(reinterpret_cast<char*>(&magic), sizeof(int64_t)); | |
1024 | |||
1025 | // 2) Write the file format version number. | ||
1026 | 67 | uint32_t version = OPENVDB_FILE_VERSION; | |
1027 | 67 | os.write(reinterpret_cast<char*>(&version), sizeof(uint32_t)); | |
1028 | |||
1029 | // 3) Write the library version numbers. | ||
1030 | 67 | version = OPENVDB_LIBRARY_MAJOR_VERSION; | |
1031 | 67 | os.write(reinterpret_cast<char*>(&version), sizeof(uint32_t)); | |
1032 | 67 | version = OPENVDB_LIBRARY_MINOR_VERSION; | |
1033 | 67 | os.write(reinterpret_cast<char*>(&version), sizeof(uint32_t)); | |
1034 | |||
1035 | // 4) Write a flag indicating that this stream contains no grid offsets. | ||
1036 | 67 | char hasGridOffsets = seekable; | |
1037 | 67 | os.write(&hasGridOffsets, sizeof(char)); | |
1038 | |||
1039 | // 5) Write a flag indicating that this stream contains compressed leaf data. | ||
1040 | // (Omitted as of version 222) | ||
1041 | |||
1042 | // 6) Generate a new random 16-byte (128-bit) uuid and write it to the stream. | ||
1043 | std::mt19937 ran; | ||
1044 | 134 | ran.seed(std::mt19937::result_type(std::random_device()() + std::time(nullptr))); | |
1045 | 67 | boost::uuids::basic_random_generator<std::mt19937> gen(&ran); | |
1046 | 67 | mUuid = gen(); // mUuid is mutable | |
1047 |
1/2✓ Branch 1 taken 67 times.
✗ Branch 2 not taken.
|
67 | os << mUuid; |
1048 | 67 | } | |
1049 | |||
1050 | |||
1051 | //////////////////////////////////////// | ||
1052 | |||
1053 | |||
1054 | int32_t | ||
1055 | 58 | Archive::readGridCount(std::istream& is) | |
1056 | { | ||
1057 | 58 | int32_t gridCount = 0; | |
1058 | 58 | is.read(reinterpret_cast<char*>(&gridCount), sizeof(int32_t)); | |
1059 | 58 | return gridCount; | |
1060 | } | ||
1061 | |||
1062 | |||
1063 | //////////////////////////////////////// | ||
1064 | |||
1065 | |||
1066 | void | ||
1067 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 61 times.
|
70 | Archive::connectInstance(const GridDescriptor& gd, const NamedGridMap& grids) const |
1068 | { | ||
1069 |
3/4✓ Branch 0 taken 9 times.
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
|
70 | if (!gd.isInstance() || grids.empty()) return; |
1070 | |||
1071 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | NamedGridMap::const_iterator it = grids.find(gd.uniqueName()); |
1072 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (it == grids.end()) return; |
1073 | GridBase::Ptr grid = it->second; | ||
1074 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (!grid) return; |
1075 | |||
1076 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | it = grids.find(gd.instanceParentName()); |
1077 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (it != grids.end()) { |
1078 | GridBase::Ptr parent = it->second; | ||
1079 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
|
9 | if (mEnableInstancing) { |
1080 | // Share the instance parent's tree. | ||
1081 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
|
14 | grid->setTree(parent->baseTreePtr()); |
1082 | } else { | ||
1083 | // Copy the instance parent's tree. | ||
1084 |
3/8✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
4 | grid->setTree(parent->baseTree().copy()); |
1085 | } | ||
1086 | } else { | ||
1087 | ✗ | OPENVDB_THROW(KeyError, "missing instance parent \"" | |
1088 | << GridDescriptor::nameAsString(gd.instanceParentName()) | ||
1089 | << "\" for grid " << GridDescriptor::nameAsString(gd.uniqueName())); | ||
1090 | } | ||
1091 | } | ||
1092 | |||
1093 | |||
1094 | //////////////////////////////////////// | ||
1095 | |||
1096 | |||
1097 | //static | ||
1098 | bool | ||
1099 | 62 | Archive::isDelayedLoadingEnabled() | |
1100 | { | ||
1101 | 62 | return (nullptr == std::getenv("OPENVDB_DISABLE_DELAYED_LOAD")); | |
1102 | } | ||
1103 | |||
1104 | |||
1105 | namespace { | ||
1106 | |||
1107 | struct NoBBox {}; | ||
1108 | |||
1109 | template<typename BoxType> | ||
1110 | void | ||
1111 | 250 | doReadGrid(GridBase::Ptr grid, const GridDescriptor& gd, std::istream& is, const BoxType& bbox) | |
1112 | { | ||
1113 | struct Local { | ||
1114 |
1/4✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
206 | static void readBuffers(GridBase& g, std::istream& istrm, NoBBox) { g.readBuffers(istrm); } |
1115 | static void readBuffers(GridBase& g, std::istream& istrm, const CoordBBox& indexBBox) { | ||
1116 | ✗ | g.readBuffers(istrm, indexBBox); | |
1117 | } | ||
1118 | 5 | static void readBuffers(GridBase& g, std::istream& istrm, const BBoxd& worldBBox) { | |
1119 | 5 | g.readBuffers(istrm, g.constTransform().worldToIndexNodeCentered(worldBBox)); | |
1120 | } | ||
1121 | }; | ||
1122 | |||
1123 | // Restore the file-level stream metadata on exit. | ||
1124 | struct OnExit { | ||
1125 | 125 | OnExit(std::ios_base& strm_): strm(&strm_), ptr(strm_.pword(sStreamState.metadata)) {} | |
1126 | 125 | ~OnExit() { strm->pword(sStreamState.metadata) = ptr; } | |
1127 | std::ios_base* strm; | ||
1128 | void* ptr; | ||
1129 | }; | ||
1130 | 500 | OnExit restore(is); | |
1131 | |||
1132 | // Stream metadata varies per grid, and it needs to persist | ||
1133 | // in case delayed load is in effect. | ||
1134 | 250 | io::StreamMetadata::Ptr streamMetadata; | |
1135 |
3/4✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 123 times.
✓ Branch 4 taken 2 times.
|
250 | if (io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is)) { |
1136 | // Make a grid-level copy of the file-level stream metadata. | ||
1137 |
3/6✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 123 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 123 times.
✗ Branch 8 not taken.
|
246 | streamMetadata.reset(new StreamMetadata(*meta)); |
1138 | } else { | ||
1139 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
4 | streamMetadata.reset(new StreamMetadata); |
1140 | } | ||
1141 |
2/4✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 125 times.
✗ Branch 5 not taken.
|
250 | streamMetadata->setHalfFloat(grid->saveFloatAsHalf()); |
1142 |
1/2✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
|
250 | io::setStreamMetadataPtr(is, streamMetadata, /*transfer=*/false); |
1143 | |||
1144 |
1/2✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
|
250 | io::setGridClass(is, GRID_UNKNOWN); |
1145 |
1/2✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
|
250 | io::setGridBackgroundValuePtr(is, nullptr); |
1146 | |||
1147 |
1/2✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
|
250 | grid->readMeta(is); |
1148 | |||
1149 | // Add a description of the compression settings to the grid as metadata. | ||
1150 | /// @todo Would this be useful? | ||
1151 | //const uint32_t c = getDataCompression(is); | ||
1152 | //grid->insertMeta(GridBase::META_FILE_COMPRESSION, | ||
1153 | // StringMetadata(compressionToString(c))); | ||
1154 | |||
1155 |
1/2✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
|
250 | const VersionId version = getLibraryVersion(is); |
1156 |
6/6✓ Branch 0 taken 123 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 121 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
|
250 | if (version.first < 6 || (version.first == 6 && version.second <= 1)) { |
1157 | // If delay load metadata exists, but the file format version does not support | ||
1158 | // delay load metadata, this likely means the original grid was read and then | ||
1159 | // written using a prior version of OpenVDB and ABI>=5 where unknown metadata | ||
1160 | // can be blindly copied. This means that it is possible for the metadata to | ||
1161 | // no longer be in sync with the grid, so we remove it to ensure correctness. | ||
1162 | |||
1163 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
12 | if ((*grid)[GridBase::META_FILE_DELAYED_LOAD]) { |
1164 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
12 | grid->removeMeta(GridBase::META_FILE_DELAYED_LOAD); |
1165 | } | ||
1166 | } | ||
1167 | |||
1168 |
2/4✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 125 times.
✗ Branch 5 not taken.
|
250 | streamMetadata->gridMetadata() = static_cast<MetaMap&>(*grid); |
1169 |
1/2✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
|
250 | const GridClass gridClass = grid->getGridClass(); |
1170 |
1/2✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
|
250 | io::setGridClass(is, gridClass); |
1171 | |||
1172 | // reset leaf value to zero | ||
1173 |
1/2✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
|
250 | streamMetadata->setLeaf(0); |
1174 | |||
1175 | // drop DelayedLoadMetadata from the grid as it is only useful for IO | ||
1176 | // a stream metadata non-zero value disables this behaviour for testing | ||
1177 | |||
1178 |
3/4✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 120 times.
✓ Branch 4 taken 5 times.
|
250 | if (streamMetadata->__test() == uint32_t(0)) { |
1179 |
3/6✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 76 times.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
480 | if ((*grid)[GridBase::META_FILE_DELAYED_LOAD]) { |
1180 |
2/4✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
|
304 | grid->removeMeta(GridBase::META_FILE_DELAYED_LOAD); |
1181 | } | ||
1182 | } | ||
1183 | |||
1184 |
2/4✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 125 times.
✗ Branch 4 not taken.
|
250 | if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) { |
1185 | grid->readTransform(is); | ||
1186 |
2/2✓ Branch 0 taken 108 times.
✓ Branch 1 taken 17 times.
|
250 | if (!gd.isInstance()) { |
1187 |
1/2✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
|
216 | grid->readTopology(is); |
1188 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | Local::readBuffers(*grid, is, bbox); |
1189 | } | ||
1190 | } else { | ||
1191 | // Older versions of the library stored the transform after the topology. | ||
1192 | ✗ | grid->readTopology(is); | |
1193 | grid->readTransform(is); | ||
1194 | ✗ | Local::readBuffers(*grid, is, bbox); | |
1195 | } | ||
1196 |
2/4✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 125 times.
|
250 | if (getFormatVersion(is) < OPENVDB_FILE_VERSION_NO_GRIDMAP) { |
1197 | // Older versions of the library didn't store grid names as metadata, | ||
1198 | // so when reading older files, copy the grid name from the descriptor | ||
1199 | // to the grid's metadata. | ||
1200 | ✗ | if (grid->getName().empty()) { | |
1201 | ✗ | grid->setName(gd.gridName()); | |
1202 | } | ||
1203 | } | ||
1204 | } | ||
1205 | |||
1206 | } // unnamed namespace | ||
1207 | |||
1208 | |||
1209 | void | ||
1210 | 120 | Archive::readGrid(GridBase::Ptr grid, const GridDescriptor& gd, std::istream& is) | |
1211 | { | ||
1212 | // Read the compression settings for this grid and tag the stream with them | ||
1213 | // so that downstream functions can reference them. | ||
1214 | 120 | readGridCompression(is); | |
1215 | |||
1216 |
1/2✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
|
120 | doReadGrid(grid, gd, is, NoBBox()); |
1217 | 120 | } | |
1218 | |||
1219 | void | ||
1220 | 5 | Archive::readGrid(GridBase::Ptr grid, const GridDescriptor& gd, | |
1221 | std::istream& is, const BBoxd& worldBBox) | ||
1222 | { | ||
1223 | 5 | readGridCompression(is); | |
1224 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | doReadGrid(grid, gd, is, worldBBox); |
1225 | 5 | } | |
1226 | |||
1227 | void | ||
1228 | ✗ | Archive::readGrid(GridBase::Ptr grid, const GridDescriptor& gd, | |
1229 | std::istream& is, const CoordBBox& indexBBox) | ||
1230 | { | ||
1231 | ✗ | readGridCompression(is); | |
1232 | ✗ | doReadGrid(grid, gd, is, indexBBox); | |
1233 | } | ||
1234 | |||
1235 | |||
1236 | //////////////////////////////////////// | ||
1237 | |||
1238 | |||
1239 | void | ||
1240 | ✗ | Archive::write(std::ostream& os, const GridPtrVec& grids, bool seekable, | |
1241 | const MetaMap& metadata) const | ||
1242 | { | ||
1243 | ✗ | this->write(os, GridCPtrVec(grids.begin(), grids.end()), seekable, metadata); | |
1244 | } | ||
1245 | |||
1246 | |||
1247 | void | ||
1248 | 65 | Archive::write(std::ostream& os, const GridCPtrVec& grids, bool seekable, | |
1249 | const MetaMap& metadata) const | ||
1250 | { | ||
1251 | // Set stream flags so that downstream functions can reference them. | ||
1252 | 65 | io::StreamMetadata::Ptr streamMetadata = io::getStreamMetadataPtr(os); | |
1253 |
1/2✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
|
65 | if (!streamMetadata) { |
1254 |
3/6✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 65 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 65 times.
✗ Branch 8 not taken.
|
65 | streamMetadata.reset(new StreamMetadata); |
1255 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | io::setStreamMetadataPtr(os, streamMetadata, /*transfer=*/false); |
1256 | } | ||
1257 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | io::setDataCompression(os, compression()); |
1258 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | io::setWriteGridStatsMetadata(os, isGridStatsMetadataEnabled()); |
1259 | |||
1260 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | this->writeHeader(os, seekable); |
1261 | |||
1262 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | metadata.writeMeta(os); |
1263 | |||
1264 | // Write the number of non-null grids. | ||
1265 | 65 | int32_t gridCount = 0; | |
1266 |
2/2✓ Branch 0 taken 154 times.
✓ Branch 1 taken 65 times.
|
219 | for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) { |
1267 |
1/2✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
|
154 | if (*i) ++gridCount; |
1268 | } | ||
1269 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | os.write(reinterpret_cast<char*>(&gridCount), sizeof(int32_t)); |
1270 | |||
1271 | using TreeMap = std::map<const TreeBase*, GridDescriptor>; | ||
1272 | using TreeMapIter = TreeMap::iterator; | ||
1273 | TreeMap treeMap; | ||
1274 | |||
1275 | // Determine which grid names are unique and which are not. | ||
1276 | using NameHistogram = std::map<std::string, int /*count*/>; | ||
1277 | NameHistogram nameCount; | ||
1278 |
2/2✓ Branch 0 taken 154 times.
✓ Branch 1 taken 65 times.
|
219 | for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) { |
1279 |
1/2✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
|
154 | if (const GridBase::ConstPtr& grid = *i) { |
1280 |
1/2✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
|
154 | const std::string name = grid->getName(); |
1281 |
2/2✓ Branch 0 taken 61 times.
✓ Branch 1 taken 93 times.
|
154 | NameHistogram::iterator it = nameCount.find(name); |
1282 |
2/2✓ Branch 0 taken 61 times.
✓ Branch 1 taken 93 times.
|
154 | if (it != nameCount.end()) it->second++; |
1283 |
1/2✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
|
93 | else nameCount[name] = 1; |
1284 | } | ||
1285 | } | ||
1286 | |||
1287 | std::set<std::string> uniqueNames; | ||
1288 | |||
1289 | // Write out the non-null grids. | ||
1290 |
2/2✓ Branch 0 taken 154 times.
✓ Branch 1 taken 65 times.
|
219 | for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) { |
1291 |
1/2✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
|
154 | if (const GridBase::ConstPtr& grid = *i) { |
1292 | |||
1293 | // Ensure that the grid's descriptor has a unique grid name, by appending | ||
1294 | // a number to it if a grid with the same name was already written. | ||
1295 | // Always add a number if the grid name is empty, so that the grid can be | ||
1296 | // properly identified as an instance parent, if necessary. | ||
1297 |
1/2✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
|
154 | std::string name = grid->getName(); |
1298 |
5/6✓ Branch 0 taken 65 times.
✓ Branch 1 taken 89 times.
✓ Branch 3 taken 65 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 55 times.
|
154 | if (name.empty() || nameCount[name] > 1) { |
1299 |
1/2✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
|
198 | name = GridDescriptor::addSuffix(name, 0); |
1300 | } | ||
1301 |
2/2✓ Branch 0 taken 89 times.
✓ Branch 1 taken 154 times.
|
243 | for (int n = 1; uniqueNames.find(name) != uniqueNames.end(); ++n) { |
1302 |
2/4✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 89 times.
✗ Branch 5 not taken.
|
178 | name = GridDescriptor::addSuffix(grid->getName(), n); |
1303 | } | ||
1304 | uniqueNames.insert(name); | ||
1305 | |||
1306 | // Create a grid descriptor. | ||
1307 |
4/8✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 154 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 154 times.
✗ Branch 11 not taken.
|
462 | GridDescriptor gd(name, grid->type(), grid->saveFloatAsHalf()); |
1308 | |||
1309 | // Check if this grid's tree is shared with a grid that has already been written. | ||
1310 | 154 | const TreeBase* treePtr = &(grid->baseTree()); | |
1311 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 139 times.
|
154 | TreeMapIter mapIter = treeMap.find(treePtr); |
1312 | |||
1313 | bool isInstance = ((mapIter != treeMap.end()) | ||
1314 |
3/4✓ Branch 0 taken 15 times.
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
|
154 | && (mapIter->second.saveFloatAsHalf() == gd.saveFloatAsHalf())); |
1315 | |||
1316 |
4/4✓ Branch 0 taken 147 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 134 times.
|
154 | if (mEnableInstancing && isInstance) { |
1317 | // This grid's tree is shared with another grid that has already been written. | ||
1318 | // Get the name of the other grid. | ||
1319 | gd.setInstanceParentName(mapIter->second.uniqueName()); | ||
1320 | // Write out this grid's descriptor and metadata, but not its tree. | ||
1321 |
1/2✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
|
26 | writeGridInstance(gd, grid, os, seekable); |
1322 | |||
1323 | OPENVDB_LOG_DEBUG_RUNTIME("io::Archive::write(): " | ||
1324 | << GridDescriptor::nameAsString(gd.uniqueName()) | ||
1325 | << " (" << std::hex << treePtr << std::dec << ")" | ||
1326 | << " is an instance of " | ||
1327 | << GridDescriptor::nameAsString(gd.instanceParentName())); | ||
1328 | } else { | ||
1329 | // Write out the grid descriptor and its associated grid. | ||
1330 |
1/2✓ Branch 1 taken 141 times.
✗ Branch 2 not taken.
|
141 | writeGrid(gd, grid, os, seekable); |
1331 | // Record the grid's tree pointer so that the tree doesn't get written | ||
1332 | // more than once. | ||
1333 |
2/4✓ Branch 1 taken 141 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 141 times.
✗ Branch 5 not taken.
|
141 | treeMap[treePtr] = gd; |
1334 | } | ||
1335 | } | ||
1336 | |||
1337 | // Some compression options (e.g., mask compression) are set per grid. | ||
1338 | // Restore the original settings before writing the next grid. | ||
1339 |
1/2✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
|
154 | io::setDataCompression(os, compression()); |
1340 | } | ||
1341 | 65 | } | |
1342 | |||
1343 | |||
1344 | void | ||
1345 | 150 | Archive::writeGrid(GridDescriptor& gd, GridBase::ConstPtr grid, | |
1346 | std::ostream& os, bool seekable) const | ||
1347 | { | ||
1348 | // Restore file-level stream metadata on exit. | ||
1349 | struct OnExit { | ||
1350 |
2/2✓ Branch 0 taken 144 times.
✓ Branch 1 taken 6 times.
|
150 | OnExit(std::ios_base& strm_): strm(&strm_), ptr(strm_.pword(sStreamState.metadata)) {} |
1351 |
1/2✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
|
150 | ~OnExit() { strm->pword(sStreamState.metadata) = ptr; } |
1352 | std::ios_base* strm; | ||
1353 | void* ptr; | ||
1354 | }; | ||
1355 | 300 | OnExit restore(os); | |
1356 | |||
1357 | // Stream metadata varies per grid, so make a copy of the file-level stream metadata. | ||
1358 | 150 | io::StreamMetadata::Ptr streamMetadata; | |
1359 |
3/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 141 times.
✓ Branch 4 taken 9 times.
|
150 | if (io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(os)) { |
1360 |
3/6✓ Branch 1 taken 141 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 141 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 141 times.
✗ Branch 8 not taken.
|
141 | streamMetadata.reset(new StreamMetadata(*meta)); |
1361 | } else { | ||
1362 |
3/6✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
|
9 | streamMetadata.reset(new StreamMetadata); |
1363 | } | ||
1364 |
2/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
|
150 | streamMetadata->setHalfFloat(grid->saveFloatAsHalf()); |
1365 |
2/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
|
150 | streamMetadata->gridMetadata() = static_cast<const MetaMap&>(*grid); |
1366 |
1/2✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
|
150 | io::setStreamMetadataPtr(os, streamMetadata, /*transfer=*/false); |
1367 | |||
1368 | // Write out the Descriptor's header information (grid name and type) | ||
1369 |
1/2✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
|
150 | gd.writeHeader(os); |
1370 | |||
1371 | // Save the curent stream position as postion to where the offsets for | ||
1372 | // this GridDescriptor will be written to. | ||
1373 |
3/6✓ Branch 0 taken 137 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 137 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
150 | int64_t offsetPos = (seekable ? int64_t(os.tellp()) : 0); |
1374 | |||
1375 | // Write out the offset information. At this point it will be incorrect. | ||
1376 | // But we need to write it out to move the stream head forward. | ||
1377 |
1/2✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
|
150 | gd.writeStreamPos(os); |
1378 | |||
1379 | // Now we know the starting grid storage position. | ||
1380 |
3/4✓ Branch 0 taken 137 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 137 times.
✗ Branch 4 not taken.
|
150 | if (seekable) gd.setGridPos(os.tellp()); |
1381 | |||
1382 | // Save the compression settings for this grid. | ||
1383 |
1/2✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
|
150 | setGridCompression(os, *grid); |
1384 | |||
1385 | // copy grid and add delay load metadata | ||
1386 |
1/2✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
|
150 | const auto copyOfGrid = grid->copyGrid(); // shallow copy |
1387 | const auto nonConstCopyOfGrid = ConstPtrCast<GridBase>(copyOfGrid); | ||
1388 |
2/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
|
150 | nonConstCopyOfGrid->insertMeta(GridBase::META_FILE_DELAYED_LOAD, |
1389 |
1/4✓ Branch 2 taken 150 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
150 | DelayedLoadMetadata()); |
1390 | DelayedLoadMetadata::Ptr delayLoadMeta = | ||
1391 |
3/6✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 150 times.
✗ Branch 8 not taken.
|
300 | nonConstCopyOfGrid->getMetadata<DelayedLoadMetadata>(GridBase::META_FILE_DELAYED_LOAD); |
1392 |
3/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✓ Branch 4 taken 133 times.
|
150 | if (!populateDelayedLoadMetadata(*delayLoadMeta, *grid, compression())) { |
1393 |
2/4✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
|
34 | nonConstCopyOfGrid->removeMeta(GridBase::META_FILE_DELAYED_LOAD); |
1394 | } | ||
1395 | |||
1396 | // Save the grid's metadata and transform. | ||
1397 |
3/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 141 times.
✓ Branch 4 taken 9 times.
|
150 | if (getWriteGridStatsMetadata(os)) { |
1398 | // Compute and add grid statistics metadata. | ||
1399 |
1/2✓ Branch 1 taken 141 times.
✗ Branch 2 not taken.
|
141 | nonConstCopyOfGrid->addStatsMetadata(); |
1400 |
2/4✓ Branch 1 taken 141 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 141 times.
✗ Branch 5 not taken.
|
282 | nonConstCopyOfGrid->insertMeta(GridBase::META_FILE_COMPRESSION, |
1401 |
2/6✓ Branch 1 taken 141 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 141 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
282 | StringMetadata(compressionToString(getDataCompression(os)))); |
1402 | } | ||
1403 |
1/2✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
|
150 | copyOfGrid->writeMeta(os); |
1404 | grid->writeTransform(os); | ||
1405 | |||
1406 | // Save the grid's structure. | ||
1407 |
1/2✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
|
150 | grid->writeTopology(os); |
1408 | |||
1409 | // Now we know the grid block storage position. | ||
1410 |
3/4✓ Branch 0 taken 137 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 137 times.
✗ Branch 4 not taken.
|
150 | if (seekable) gd.setBlockPos(os.tellp()); |
1411 | |||
1412 | // Save out the data blocks of the grid. | ||
1413 |
1/2✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
|
150 | grid->writeBuffers(os); |
1414 | |||
1415 | // Now we know the end position of this grid. | ||
1416 |
3/4✓ Branch 0 taken 137 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 137 times.
✗ Branch 4 not taken.
|
150 | if (seekable) gd.setEndPos(os.tellp()); |
1417 | |||
1418 |
2/2✓ Branch 0 taken 137 times.
✓ Branch 1 taken 13 times.
|
150 | if (seekable) { |
1419 | // Now, go back to where the Descriptor's offset information is written | ||
1420 | // and write the offsets again. | ||
1421 |
1/2✓ Branch 1 taken 137 times.
✗ Branch 2 not taken.
|
137 | os.seekp(offsetPos, std::ios_base::beg); |
1422 |
1/2✓ Branch 1 taken 137 times.
✗ Branch 2 not taken.
|
137 | gd.writeStreamPos(os); |
1423 | |||
1424 | // Now seek back to the end. | ||
1425 |
1/2✓ Branch 1 taken 137 times.
✗ Branch 2 not taken.
|
137 | gd.seekToEnd(os); |
1426 | } | ||
1427 | 150 | } | |
1428 | |||
1429 | |||
1430 | void | ||
1431 | 13 | Archive::writeGridInstance(GridDescriptor& gd, GridBase::ConstPtr grid, | |
1432 | std::ostream& os, bool seekable) const | ||
1433 | { | ||
1434 | // Write out the Descriptor's header information (grid name, type | ||
1435 | // and instance parent name). | ||
1436 | 13 | gd.writeHeader(os); | |
1437 | |||
1438 | // Save the curent stream position as postion to where the offsets for | ||
1439 | // this GridDescriptor will be written to. | ||
1440 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
|
13 | int64_t offsetPos = (seekable ? int64_t(os.tellp()) : 0); |
1441 | |||
1442 | // Write out the offset information. At this point it will be incorrect. | ||
1443 | // But we need to write it out to move the stream head forward. | ||
1444 | 13 | gd.writeStreamPos(os); | |
1445 | |||
1446 | // Now we know the starting grid storage position. | ||
1447 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
|
13 | if (seekable) gd.setGridPos(os.tellp()); |
1448 | |||
1449 | // Save the compression settings for this grid. | ||
1450 | 13 | setGridCompression(os, *grid); | |
1451 | |||
1452 | // Save the grid's metadata and transform. | ||
1453 | 13 | grid->writeMeta(os); | |
1454 | grid->writeTransform(os); | ||
1455 | |||
1456 | // Now we know the end position of this grid. | ||
1457 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
|
13 | if (seekable) gd.setEndPos(os.tellp()); |
1458 | |||
1459 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
|
13 | if (seekable) { |
1460 | // Now, go back to where the Descriptor's offset information is written | ||
1461 | // and write the offsets again. | ||
1462 | 11 | os.seekp(offsetPos, std::ios_base::beg); | |
1463 | 11 | gd.writeStreamPos(os); | |
1464 | |||
1465 | // Now seek back to the end. | ||
1466 | 11 | gd.seekToEnd(os); | |
1467 | } | ||
1468 | 13 | } | |
1469 | |||
1470 | } // namespace io | ||
1471 | } // namespace OPENVDB_VERSION_NAME | ||
1472 | } // namespace openvdb | ||
1473 |