| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | #include "Stream.h" | ||
| 5 | |||
| 6 | #include "File.h" ///< @todo refactor | ||
| 7 | #include "GridDescriptor.h" | ||
| 8 | #include "TempFile.h" | ||
| 9 | #include <openvdb/Exceptions.h> | ||
| 10 | #include <cstdint> | ||
| 11 | #include <boost/iostreams/copy.hpp> | ||
| 12 | #include <cstdio> // for remove() | ||
| 13 | #include <functional> // for std::bind() | ||
| 14 | #include <iostream> | ||
| 15 | #include <vector> | ||
| 16 | |||
| 17 | |||
| 18 | namespace openvdb { | ||
| 19 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 20 | namespace OPENVDB_VERSION_NAME { | ||
| 21 | namespace io { | ||
| 22 | |||
| 23 | struct Stream::Impl | ||
| 24 | { | ||
| 25 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
10 | Impl(): mOutputStream{nullptr} {} |
| 26 | 2 | Impl(const Impl& other) { *this = other; } | |
| 27 | 2 | Impl& operator=(const Impl& other) | |
| 28 | { | ||
| 29 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (&other != this) { |
| 30 | mMeta = other.mMeta; ///< @todo deep copy? | ||
| 31 | mGrids = other.mGrids; ///< @todo deep copy? | ||
| 32 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | mOutputStream = other.mOutputStream; |
| 33 | mFile.reset(); | ||
| 34 | } | ||
| 35 | 2 | return *this; | |
| 36 | } | ||
| 37 | |||
| 38 | MetaMap::Ptr mMeta; | ||
| 39 | GridPtrVecPtr mGrids; | ||
| 40 | std::ostream* mOutputStream; | ||
| 41 | std::unique_ptr<File> mFile; | ||
| 42 | }; | ||
| 43 | |||
| 44 | |||
| 45 | //////////////////////////////////////// | ||
| 46 | |||
| 47 | |||
| 48 | namespace { | ||
| 49 | |||
| 50 | /// @todo Use MappedFile auto-deletion instead. | ||
| 51 | void | ||
| 52 | 3 | removeTempFile(const std::string expectedFilename, const std::string& filename) | |
| 53 | { | ||
| 54 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (filename == expectedFilename) { |
| 55 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if (0 != std::remove(filename.c_str())) { |
| 56 | ✗ | std::string mesg = getErrorString(); | |
| 57 | ✗ | if (!mesg.empty()) mesg = " (" + mesg + ")"; | |
| 58 | OPENVDB_LOG_WARN("failed to remove temporary file " << filename << mesg); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | 3 | } | |
| 62 | |||
| 63 | } | ||
| 64 | |||
| 65 | |||
| 66 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | Stream::Stream(std::istream& is, bool delayLoad): mImpl(new Impl) |
| 67 | { | ||
| 68 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (!is) return; |
| 69 | |||
| 70 |
3/6✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
3 | if (delayLoad && Archive::isDelayedLoadingEnabled()) { |
| 71 | // Copy the contents of the stream to a temporary private file | ||
| 72 | // and open the file instead. | ||
| 73 | 3 | std::unique_ptr<TempFile> tempFile; | |
| 74 | try { | ||
| 75 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | tempFile.reset(new TempFile); |
| 76 | ✗ | } catch (std::exception& e) { | |
| 77 | std::string mesg; | ||
| 78 | ✗ | if (e.what()) mesg = std::string(" (") + e.what() + ")"; | |
| 79 | OPENVDB_LOG_WARN("failed to create a temporary file for delayed loading" << mesg | ||
| 80 | << "; will read directly from the input stream instead"); | ||
| 81 | } | ||
| 82 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (tempFile) { |
| 83 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | boost::iostreams::copy(is, *tempFile); |
| 84 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | const std::string& filename = tempFile->filename(); |
| 85 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | mImpl->mFile.reset(new File(filename)); |
| 86 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | mImpl->mFile->setCopyMaxBytes(0); // don't make a copy of the temporary file |
| 87 | /// @todo Need to pass auto-deletion flag to MappedFile. | ||
| 88 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
6 | mImpl->mFile->open(delayLoad, |
| 89 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
3 | std::bind(&removeTempFile, filename, std::placeholders::_1)); |
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (!mImpl->mFile) { |
| 94 | ✗ | readHeader(is); | |
| 95 | |||
| 96 | // Tag the input stream with the library and file format version numbers | ||
| 97 | // and the compression options specified in the header. | ||
| 98 | ✗ | StreamMetadata::Ptr streamMetadata(new StreamMetadata); | |
| 99 | ✗ | io::setStreamMetadataPtr(is, streamMetadata, /*transfer=*/false); | |
| 100 | ✗ | io::setVersion(is, libraryVersion(), fileVersion()); | |
| 101 | ✗ | io::setDataCompression(is, compression()); | |
| 102 | |||
| 103 | // Read in the VDB metadata. | ||
| 104 | ✗ | mImpl->mMeta.reset(new MetaMap); | |
| 105 | ✗ | mImpl->mMeta->readMeta(is); | |
| 106 | |||
| 107 | // Read in the number of grids. | ||
| 108 | ✗ | const int32_t gridCount = readGridCount(is); | |
| 109 | |||
| 110 | // Read in all grids and insert them into mGrids. | ||
| 111 | ✗ | mImpl->mGrids.reset(new GridPtrVec); | |
| 112 | ✗ | std::vector<GridDescriptor> descriptors; | |
| 113 | ✗ | descriptors.reserve(gridCount); | |
| 114 | Archive::NamedGridMap namedGrids; | ||
| 115 | ✗ | for (int32_t i = 0; i < gridCount; ++i) { | |
| 116 | ✗ | GridDescriptor gd; | |
| 117 | ✗ | gd.read(is); | |
| 118 | ✗ | descriptors.push_back(gd); | |
| 119 | ✗ | GridBase::Ptr grid = readGrid(gd, is); | |
| 120 | ✗ | mImpl->mGrids->push_back(grid); | |
| 121 | ✗ | namedGrids[gd.uniqueName()] = grid; | |
| 122 | } | ||
| 123 | |||
| 124 | // Connect instances (grids that share trees with other grids). | ||
| 125 | ✗ | for (size_t i = 0, N = descriptors.size(); i < N; ++i) { | |
| 126 | ✗ | Archive::connectInstance(descriptors[i], namedGrids); | |
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | |||
| 132 | ✗ | Stream::Stream(): mImpl(new Impl) | |
| 133 | { | ||
| 134 | } | ||
| 135 | |||
| 136 | |||
| 137 |
1/2✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | Stream::Stream(std::ostream& os): mImpl(new Impl) |
| 138 | { | ||
| 139 | 7 | mImpl->mOutputStream = &os; | |
| 140 | 7 | } | |
| 141 | |||
| 142 | |||
| 143 | 28 | Stream::~Stream() | |
| 144 | { | ||
| 145 | 28 | } | |
| 146 | |||
| 147 | |||
| 148 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | Stream::Stream(const Stream& other): Archive(other), mImpl(new Impl(*other.mImpl)) |
| 149 | { | ||
| 150 | 2 | } | |
| 151 | |||
| 152 | |||
| 153 | Stream& | ||
| 154 | ✗ | Stream::operator=(const Stream& other) | |
| 155 | { | ||
| 156 | ✗ | if (&other != this) { | |
| 157 | ✗ | mImpl.reset(new Impl(*other.mImpl)); | |
| 158 | } | ||
| 159 | ✗ | return *this; | |
| 160 | } | ||
| 161 | |||
| 162 | |||
| 163 | SharedPtr<Archive> | ||
| 164 | 2 | Stream::copy() const | |
| 165 | { | ||
| 166 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
4 | return SharedPtr<Archive>(new Stream(*this)); |
| 167 | } | ||
| 168 | |||
| 169 | |||
| 170 | //////////////////////////////////////// | ||
| 171 | |||
| 172 | |||
| 173 | GridBase::Ptr | ||
| 174 | ✗ | Stream::readGrid(const GridDescriptor& gd, std::istream& is) const | |
| 175 | { | ||
| 176 | ✗ | GridBase::Ptr grid; | |
| 177 | |||
| 178 | ✗ | if (!GridBase::isRegistered(gd.gridType())) { | |
| 179 | ✗ | OPENVDB_THROW(TypeError, "can't read grid \"" | |
| 180 | << GridDescriptor::nameAsString(gd.uniqueName()) << | ||
| 181 | "\" from input stream because grid type " << gd.gridType() << " is unknown"); | ||
| 182 | } else { | ||
| 183 | ✗ | grid = GridBase::createGrid(gd.gridType()); | |
| 184 | ✗ | if (grid) grid->setSaveFloatAsHalf(gd.saveFloatAsHalf()); | |
| 185 | |||
| 186 | ✗ | Archive::readGrid(grid, gd, is); | |
| 187 | } | ||
| 188 | ✗ | return grid; | |
| 189 | } | ||
| 190 | |||
| 191 | |||
| 192 | void | ||
| 193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | Stream::write(const GridCPtrVec& grids, const MetaMap& metadata) const |
| 194 | { | ||
| 195 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (mImpl->mOutputStream == nullptr) { |
| 196 | ✗ | OPENVDB_THROW(ValueError, "no output stream was specified"); | |
| 197 | } | ||
| 198 | 6 | this->writeGrids(*mImpl->mOutputStream, grids, metadata); | |
| 199 | 6 | } | |
| 200 | |||
| 201 | |||
| 202 | void | ||
| 203 | 6 | Stream::writeGrids(std::ostream& os, const GridCPtrVec& grids, const MetaMap& metadata) const | |
| 204 | { | ||
| 205 | 6 | Archive::write(os, grids, /*seekable=*/false, metadata); | |
| 206 | 6 | } | |
| 207 | |||
| 208 | |||
| 209 | //////////////////////////////////////// | ||
| 210 | |||
| 211 | |||
| 212 | MetaMap::Ptr | ||
| 213 | 2 | Stream::getMetadata() const | |
| 214 | { | ||
| 215 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | MetaMap::Ptr result; |
| 216 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (mImpl->mFile) { |
| 217 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
4 | result = mImpl->mFile->getMetadata(); |
| 218 | ✗ | } else if (mImpl->mMeta) { | |
| 219 | // Return a deep copy of the file-level metadata | ||
| 220 | // that was read when this object was constructed. | ||
| 221 | ✗ | result.reset(new MetaMap(*mImpl->mMeta)); | |
| 222 | } | ||
| 223 | 2 | return result; | |
| 224 | } | ||
| 225 | |||
| 226 | |||
| 227 | GridPtrVecPtr | ||
| 228 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | Stream::getGrids() |
| 229 | { | ||
| 230 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (mImpl->mFile) { |
| 231 | 3 | return mImpl->mFile->getGrids(); | |
| 232 | } | ||
| 233 | return mImpl->mGrids; | ||
| 234 | } | ||
| 235 | |||
| 236 | } // namespace io | ||
| 237 | } // namespace OPENVDB_VERSION_NAME | ||
| 238 | } // namespace openvdb | ||
| 239 |