GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/io/File.cc
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 253 283 89.4%
Functions: 40 44 90.9%
Branches: 292 622 46.9%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @file io/File.cc
5
6 #include "File.h"
7
8 #include "TempFile.h"
9 #include <openvdb/Exceptions.h>
10 #include <openvdb/util/logging.h>
11 #include <cstdint>
12 #include <boost/iostreams/copy.hpp>
13 #ifndef _WIN32
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #endif
18 #include <cassert>
19 #include <cstdlib> // for getenv(), strtoul()
20 #include <cstring> // for strerror_r()
21 #include <fstream>
22 #include <iostream>
23 #include <limits>
24 #include <sstream>
25
26
27 namespace openvdb {
28 OPENVDB_USE_VERSION_NAMESPACE
29 namespace OPENVDB_VERSION_NAME {
30 namespace io {
31
32 // Implementation details of the File class
33 struct File::Impl
34 {
35 enum { DEFAULT_COPY_MAX_BYTES = 500000000 }; // 500 MB
36
37 struct NoBBox {};
38
39 // Common implementation of the various File::readGrid() overloads,
40 // with and without bounding box clipping
41 template<typename BoxType>
42
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
212 static GridBase::Ptr readGrid(const File& file, const GridDescriptor& gd, const BoxType& bbox)
43 {
44 // This method should not be called for files that don't contain grid offsets.
45
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
212 assert(file.inputHasGridOffsets());
46
47 212 GridBase::Ptr grid = file.createGrid(gd);
48
2/4
✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
212 gd.seekToGrid(file.inputStream());
49
1/2
✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
212 unarchive(file, grid, gd, bbox);
50 212 return grid;
51 }
52
53 102 static void unarchive(const File& file, GridBase::Ptr& grid,
54 const GridDescriptor& gd, NoBBox)
55 {
56
1/2
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
204 file.Archive::readGrid(grid, gd, file.inputStream());
57 102 }
58
59 static void unarchive(const File& file, GridBase::Ptr& grid,
60 const GridDescriptor& gd, const CoordBBox& indexBBox)
61 {
62 file.Archive::readGrid(grid, gd, file.inputStream(), indexBBox);
63 }
64
65 4 static void unarchive(const File& file, GridBase::Ptr& grid,
66 const GridDescriptor& gd, const BBoxd& worldBBox)
67 {
68
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 file.Archive::readGrid(grid, gd, file.inputStream(), worldBBox);
69 4 }
70
71 119 static Index64 getDefaultCopyMaxBytes()
72 {
73 Index64 result = DEFAULT_COPY_MAX_BYTES;
74
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 119 times.
119 if (const char* s = std::getenv("OPENVDB_DELAYED_LOAD_COPY_MAX_BYTES")) {
75 char* endptr = nullptr;
76 result = std::strtoul(s, &endptr, /*base=*/10);
77 }
78 119 return result;
79 }
80
81 std::string mFilename;
82 // The file-level metadata
83 MetaMap::Ptr mMeta;
84 // The memory-mapped file
85 MappedFile::Ptr mFileMapping;
86 // The buffer for the input stream, if it is a memory-mapped file
87 SharedPtr<std::streambuf> mStreamBuf;
88 // The file stream that is open for reading
89 std::unique_ptr<std::istream> mInStream;
90 // File-level stream metadata (file format, compression, etc.)
91 StreamMetadata::Ptr mStreamMetadata;
92 // Flag indicating if we have read in the global information (header,
93 // metadata, and grid descriptors) for this VDB file
94 bool mIsOpen;
95 // File size limit for copying during delayed loading
96 Index64 mCopyMaxBytes;
97 // Grid descriptors for all grids stored in the file, indexed by grid name
98 NameMap mGridDescriptors;
99 // All grids, indexed by unique name (used only when mHasGridOffsets is false)
100 Archive::NamedGridMap mNamedGrids;
101 // All grids stored in the file (used only when mHasGridOffsets is false)
102 GridPtrVecPtr mGrids;
103 }; // class File::Impl
104
105
106 ////////////////////////////////////////
107
108
109
1/2
✓ Branch 2 taken 119 times.
✗ Branch 3 not taken.
119 File::File(const std::string& filename): mImpl(new Impl)
110 {
111
1/2
✓ Branch 1 taken 119 times.
✗ Branch 2 not taken.
119 mImpl->mFilename = filename;
112 119 mImpl->mIsOpen = false;
113 119 mImpl->mCopyMaxBytes = Impl::getDefaultCopyMaxBytes();
114 setInputHasGridOffsets(true);
115 119 }
116
117
118 316 File::~File()
119 {
120 316 }
121
122
123 18 File::File(const File& other)
124 : Archive(other)
125
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 , mImpl(new Impl)
126 {
127
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 *this = other;
128 18 }
129
130
131 File&
132 18 File::operator=(const File& other)
133 {
134
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (&other != this) {
135 Archive::operator=(other);
136 const Impl& otherImpl = *other.mImpl;
137 18 mImpl->mFilename = otherImpl.mFilename;
138 mImpl->mMeta = otherImpl.mMeta;
139 18 mImpl->mIsOpen = false; // don't want two file objects reading from the same stream
140 18 mImpl->mCopyMaxBytes = otherImpl.mCopyMaxBytes;
141 mImpl->mGridDescriptors = otherImpl.mGridDescriptors;
142 mImpl->mNamedGrids = otherImpl.mNamedGrids;
143 mImpl->mGrids = otherImpl.mGrids;
144 }
145 18 return *this;
146 }
147
148
149 SharedPtr<Archive>
150 18 File::copy() const
151 {
152
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
36 return SharedPtr<Archive>{new File{*this}};
153 }
154
155
156 ////////////////////////////////////////
157
158
159 const std::string&
160 315 File::filename() const
161 {
162 315 return mImpl->mFilename;
163 }
164
165
166 MetaMap::Ptr
167 1 File::fileMetadata()
168 {
169 1 return mImpl->mMeta;
170 }
171
172 MetaMap::ConstPtr
173 File::fileMetadata() const
174 {
175 return mImpl->mMeta;
176 }
177
178
179 const File::NameMap&
180 312 File::gridDescriptors() const
181 {
182 312 return mImpl->mGridDescriptors;
183 }
184
185 File::NameMap&
186 252 File::gridDescriptors()
187 {
188 252 return mImpl->mGridDescriptors;
189 }
190
191
192 std::istream&
193
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 639 times.
642 File::inputStream() const
194 {
195
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 639 times.
642 if (!mImpl->mInStream) {
196
3/8
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
15 OPENVDB_THROW(IoError, filename() << " is not open for reading");
197 }
198 639 return *mImpl->mInStream;
199 }
200
201
202 ////////////////////////////////////////
203
204
205 Index64
206 58 File::getSize() const
207 {
208 /// @internal boost::filesystem::file_size() would be a more portable alternative,
209 /// but as of 9/2014, Houdini ships without the Boost.Filesystem library,
210 /// which makes it much less convenient to use that library.
211
212 Index64 result = std::numeric_limits<Index64>::max();
213
214 58 std::string mesg = "could not get size of file " + filename();
215
216 #ifdef _WIN32
217 // Get the file size by seeking to the end of the file.
218 std::ifstream fstrm(filename());
219 if (fstrm) {
220 fstrm.seekg(0, fstrm.end);
221 result = static_cast<Index64>(fstrm.tellg());
222 } else {
223 OPENVDB_THROW(IoError, mesg);
224 }
225 #else
226 // Get the file size using the stat() system call.
227 struct stat info;
228
3/4
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 57 times.
116 if (0 != ::stat(filename().c_str(), &info)) {
229
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::string s = getErrorString();
230
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
3 if (!s.empty()) mesg += " (" + s + ")";
231
1/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
4 OPENVDB_THROW(IoError, mesg);
232 }
233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if (!S_ISREG(info.st_mode)) {
234 mesg += " (not a regular file)";
235 OPENVDB_THROW(IoError, mesg);
236 }
237 57 result = static_cast<Index64>(info.st_size);
238 #endif
239
240 57 return result;
241 }
242
243
244 Index64
245 57 File::copyMaxBytes() const
246 {
247 57 return mImpl->mCopyMaxBytes;
248 }
249
250
251 void
252 3 File::setCopyMaxBytes(Index64 bytes)
253 {
254 3 mImpl->mCopyMaxBytes = bytes;
255 3 }
256
257
258 ////////////////////////////////////////
259
260
261 bool
262 265 File::isOpen() const
263 {
264 265 return mImpl->mIsOpen;
265 }
266
267
268 bool
269 59 File::open(bool delayLoad, const MappedFile::Notifier& notifier)
270 {
271
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 58 times.
59 if (isOpen()) {
272
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
5 OPENVDB_THROW(IoError, filename() << " is already open");
273 }
274 mImpl->mInStream.reset();
275
276 // Open the file.
277 58 std::unique_ptr<std::istream> newStream;
278 58 SharedPtr<std::streambuf> newStreamBuf;
279 58 MappedFile::Ptr newFileMapping;
280
3/6
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 58 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 58 times.
58 if (!delayLoad || !Archive::isDelayedLoadingEnabled()) {
281 newStream.reset(new std::ifstream(
282 filename().c_str(), std::ios_base::in | std::ios_base::binary));
283 } else {
284 bool isTempFile = false;
285
2/4
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 58 times.
✗ Branch 5 not taken.
58 std::string fname = filename();
286
5/6
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 54 times.
✓ Branch 7 taken 3 times.
58 if (getSize() < copyMaxBytes()) {
287 // If the file is not too large, make a temporary private copy of it
288 // and open the copy instead. The original file can then be modified
289 // or removed without affecting delayed load.
290 try {
291
1/2
✓ Branch 1 taken 54 times.
✗ Branch 2 not taken.
108 TempFile tempFile;
292
1/2
✓ Branch 1 taken 54 times.
✗ Branch 2 not taken.
54 std::ifstream fstrm(filename().c_str(),
293
1/2
✓ Branch 1 taken 54 times.
✗ Branch 2 not taken.
108 std::ios_base::in | std::ios_base::binary);
294
1/2
✓ Branch 1 taken 54 times.
✗ Branch 2 not taken.
54 boost::iostreams::copy(fstrm, tempFile);
295
1/2
✓ Branch 1 taken 54 times.
✗ Branch 2 not taken.
54 fname = tempFile.filename();
296 isTempFile = true;
297 } catch (std::exception& e) {
298 std::string mesg;
299 if (e.what()) mesg = std::string(" (") + e.what() + ")";
300 OPENVDB_LOG_WARN("failed to create a temporary copy of " << filename()
301 << " for delayed loading" << mesg
302 << "; will read directly from " << filename() << " instead");
303 }
304 }
305
306 // While the file is open, its mapping, stream buffer and stream
307 // must all be maintained. Once the file is closed, the buffer and
308 // the stream can be discarded, but the mapping needs to persist
309 // if any grids were lazily loaded.
310 try {
311
3/6
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 57 times.
✗ Branch 8 not taken.
57 newFileMapping.reset(new MappedFile(fname, /*autoDelete=*/isTempFile));
312
2/4
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
114 newStreamBuf = newFileMapping->createBuffer();
313
2/4
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
57 newStream.reset(new std::istream(newStreamBuf.get()));
314 } catch (std::exception& e) {
315 std::ostringstream ostr;
316 ostr << "could not open file " << filename();
317 if (e.what() != nullptr) ostr << " (" << e.what() << ")";
318 OPENVDB_THROW(IoError, ostr.str());
319 }
320 }
321
322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if (newStream->fail()) {
323 OPENVDB_THROW(IoError, "could not open file " << filename());
324 }
325
326 // Read in the file header.
327 bool newFile = false;
328 try {
329
2/2
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 1 times.
57 newFile = Archive::readHeader(*newStream);
330 1 } catch (IoError& e) {
331
5/10
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
5 if (e.what() && std::string("not a VDB file") == e.what()) {
332 // Rethrow, adding the filename.
333 OPENVDB_THROW(IoError, filename() << " is not a VDB file");
334 }
335 1 throw;
336 }
337
338 mImpl->mFileMapping = newFileMapping;
339
2/4
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 56 times.
✗ Branch 4 not taken.
56 if (mImpl->mFileMapping) mImpl->mFileMapping->setNotifier(notifier);
340 mImpl->mStreamBuf = newStreamBuf;
341 mImpl->mInStream.swap(newStream);
342
343 // Tag the input stream with the file format and library version numbers
344 // and other metadata.
345
3/6
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 56 times.
✗ Branch 8 not taken.
56 mImpl->mStreamMetadata.reset(new StreamMetadata);
346
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 mImpl->mStreamMetadata->setSeekable(true);
347
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
56 io::setStreamMetadataPtr(inputStream(), mImpl->mStreamMetadata, /*transfer=*/false);
348
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
56 Archive::setFormatVersion(inputStream());
349
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
56 Archive::setLibraryVersion(inputStream());
350
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
56 Archive::setDataCompression(inputStream());
351
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
56 io::setMappedFilePtr(inputStream(), mImpl->mFileMapping);
352
353 // Read in the VDB metadata.
354
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
112 mImpl->mMeta = MetaMap::Ptr(new MetaMap);
355
2/4
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
56 mImpl->mMeta->readMeta(inputStream());
356
357
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 51 times.
56 if (!inputHasGridOffsets()) {
358 OPENVDB_LOG_DEBUG_RUNTIME("file " << filename() << " does not support partial reading");
359
360
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 mImpl->mGrids.reset(new GridPtrVec);
361 mImpl->mNamedGrids.clear();
362
363 // Stream in the entire contents of the file and append all grids to mGrids.
364
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 const int32_t gridCount = readGridCount(inputStream());
365
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 5 times.
17 for (int32_t i = 0; i < gridCount; ++i) {
366
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
24 GridDescriptor gd;
367
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 gd.read(inputStream());
368
369
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 GridBase::Ptr grid = createGrid(gd);
370
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
24 Archive::readGrid(grid, gd, inputStream());
371
372
2/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
24 gridDescriptors().insert(std::make_pair(gd.gridName(), gd));
373
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 mImpl->mGrids->push_back(grid);
374
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 mImpl->mNamedGrids[gd.uniqueName()] = grid;
375 }
376 // Connect instances (grids that share trees with other grids).
377
4/6
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 5 times.
22 for (NameMapCIter it = gridDescriptors().begin(); it != gridDescriptors().end(); ++it) {
378
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 Archive::connectInstance(it->second, mImpl->mNamedGrids);
379 }
380 } else {
381 // Read in just the grid descriptors.
382
2/4
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 51 times.
✗ Branch 5 not taken.
51 readGridDescriptors(inputStream());
383 }
384
385
1/2
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
56 mImpl->mIsOpen = true;
386 56 return newFile; // true if file is not identical to opened file
387 }
388
389
390 void
391
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 11 times.
45 File::close()
392 {
393 // Reset all data.
394 mImpl->mMeta.reset();
395 mImpl->mGridDescriptors.clear();
396 mImpl->mGrids.reset();
397 mImpl->mNamedGrids.clear();
398 mImpl->mInStream.reset();
399 mImpl->mStreamBuf.reset();
400 mImpl->mStreamMetadata.reset();
401 mImpl->mFileMapping.reset();
402
403 45 mImpl->mIsOpen = false;
404 setInputHasGridOffsets(true);
405 45 }
406
407
408 ////////////////////////////////////////
409
410
411 bool
412 19 File::hasGrid(const Name& name) const
413 {
414
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 18 times.
19 if (!isOpen()) {
415
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
5 OPENVDB_THROW(IoError, filename() << " is not open for reading");
416 }
417 18 return (findDescriptor(name) != gridDescriptors().end());
418 }
419
420
421 MetaMap::Ptr
422 16 File::getMetadata() const
423 {
424
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 15 times.
16 if (!isOpen()) {
425
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
5 OPENVDB_THROW(IoError, filename() << " is not open for reading");
426 }
427 // Return a deep copy of the file-level metadata, which was read
428 // when the file was opened.
429
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
30 return MetaMap::Ptr(new MetaMap(*mImpl->mMeta));
430 }
431
432
433 GridPtrVecPtr
434 35 File::getGrids() const
435 {
436
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 28 times.
35 if (!isOpen()) {
437
3/8
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
35 OPENVDB_THROW(IoError, filename() << " is not open for reading");
438 }
439
440
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 25 times.
28 GridPtrVecPtr ret;
441
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 25 times.
28 if (!inputHasGridOffsets()) {
442 // If the input file doesn't have grid offsets, then all of the grids
443 // have already been streamed in and stored in mGrids.
444 ret = mImpl->mGrids;
445 } else {
446
2/4
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
25 ret.reset(new GridPtrVec);
447
448 Archive::NamedGridMap namedGrids;
449
450 // Read all grids represented by the GridDescriptors.
451
4/6
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 58 times.
✓ Branch 7 taken 25 times.
108 for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) {
452 58 const GridDescriptor& gd = i->second;
453
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
58 GridBase::Ptr grid = readGrid(gd);
454
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
58 ret->push_back(grid);
455
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
58 namedGrids[gd.uniqueName()] = grid;
456 }
457
458 // Connect instances (grids that share trees with other grids).
459
4/6
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 58 times.
✓ Branch 7 taken 25 times.
108 for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) {
460
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
58 Archive::connectInstance(i->second, namedGrids);
461 }
462 }
463 28 return ret;
464 }
465
466
467 GridBase::Ptr
468
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 6 times.
47 File::retrieveCachedGrid(const Name& name) const
469 {
470 // If the file has grid offsets, grids are read on demand
471 // and not cached in mNamedGrids.
472
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 6 times.
47 if (inputHasGridOffsets()) return GridBase::Ptr();
473
474 // If the file does not have grid offsets, mNamedGrids should already
475 // contain the entire contents of the file.
476
477 // Search by unique name.
478 Archive::NamedGridMap::const_iterator it =
479
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
12 mImpl->mNamedGrids.find(GridDescriptor::stringAsUniqueName(name));
480 // If not found, search by grid name.
481
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
7 if (it == mImpl->mNamedGrids.end()) it = mImpl->mNamedGrids.find(name);
482
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (it == mImpl->mNamedGrids.end()) {
483
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
5 OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\"");
484 }
485 return it->second;
486 }
487
488
489 ////////////////////////////////////////
490
491
492 GridPtrVecPtr
493 4 File::readAllGridMetadata()
494 {
495
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (!isOpen()) {
496
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 15 not taken.
✗ Branch 16 not taken.
10 OPENVDB_THROW(IoError, filename() << " is not open for reading");
497 }
498
499 2 GridPtrVecPtr ret(new GridPtrVec);
500
501
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!inputHasGridOffsets()) {
502 // If the input file doesn't have grid offsets, then all of the grids
503 // have already been streamed in and stored in mGrids.
504
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (size_t i = 0, N = mImpl->mGrids->size(); i < N; ++i) {
505 // Return copies of the grids, but with empty trees.
506
1/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
4 ret->push_back((*mImpl->mGrids)[i]->copyGridWithNewTree());
507 }
508 } else {
509 // Read just the metadata and transforms for all grids.
510
4/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1 times.
4 for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) {
511 2 const GridDescriptor& gd = i->second;
512
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 GridBase::ConstPtr grid = readGridPartial(gd, /*readTopology=*/false);
513 // Return copies of the grids, but with empty trees.
514 // (As of 0.98.0, at least, it would suffice to just const cast
515 // the grid pointers returned by readGridPartial(), but shallow
516 // copying the grids helps to ensure future compatibility.)
517
2/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
4 ret->push_back(grid->copyGridWithNewTree());
518 }
519 }
520 2 return ret;
521 }
522
523
524 GridBase::Ptr
525 10 File::readGridMetadata(const Name& name)
526 {
527
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
10 if (!isOpen()) {
528
3/8
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
20 OPENVDB_THROW(IoError, filename() << " is not open for reading.");
529 }
530
531
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 GridBase::ConstPtr ret;
532
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (!inputHasGridOffsets()) {
533 // Retrieve the grid from mGrids, which should already contain
534 // the entire contents of the file.
535
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 ret = readGrid(name);
536 } else {
537
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 NameMapCIter it = findDescriptor(name);
538
3/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
3 if (it == gridDescriptors().end()) {
539
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
5 OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\"");
540 }
541
542 // Seek to and read in the grid from the file.
543 2 const GridDescriptor& gd = it->second;
544
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ret = readGridPartial(gd, /*readTopology=*/false);
545 }
546
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 return ret->copyGridWithNewTree();
547 }
548
549
550 ////////////////////////////////////////
551
552
553 GridBase::Ptr
554 40 File::readGrid(const Name& name)
555 {
556 40 return readGridByName(name, BBoxd());
557 }
558
559
560 GridBase::Ptr
561 7 File::readGrid(const Name& name, const BBoxd& bbox)
562 {
563 7 return readGridByName(name, bbox);
564 }
565
566
567 GridBase::Ptr
568 47 File::readGridByName(const Name& name, const BBoxd& bbox)
569 {
570
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 47 times.
47 if (!isOpen()) {
571 OPENVDB_THROW(IoError, filename() << " is not open for reading.");
572 }
573
574 const bool clip = bbox.isSorted();
575
576 // If a grid with the given name was already read and cached
577 // (along with the entire contents of the file, because the file
578 // doesn't support random access), retrieve and return it.
579 47 GridBase::Ptr grid = retrieveCachedGrid(name);
580
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 41 times.
46 if (grid) {
581
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (clip) {
582
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 grid = grid->deepCopyGrid();
583
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 grid->clipGrid(bbox);
584 }
585 return grid;
586 }
587
588
1/2
✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
41 NameMapCIter it = findDescriptor(name);
589
3/4
✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 40 times.
41 if (it == gridDescriptors().end()) {
590
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
5 OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\"");
591 }
592
593 // Seek to and read in the grid from the file.
594 40 const GridDescriptor& gd = it->second;
595
6/8
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 36 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 36 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✓ Branch 9 taken 32 times.
80 grid = (clip ? readGrid(gd, bbox) : readGrid(gd));
596
597
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 32 times.
40 if (gd.isInstance()) {
598 /// @todo Refactor to share code with Archive::connectInstance()?
599 NameMapCIter parentIt =
600
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
9 findDescriptor(GridDescriptor::nameAsString(gd.instanceParentName()));
601
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if (parentIt == gridDescriptors().end()) {
602 OPENVDB_THROW(KeyError, "missing instance parent \""
603 << GridDescriptor::nameAsString(gd.instanceParentName())
604 << "\" for grid " << GridDescriptor::nameAsString(gd.uniqueName())
605 << " in file " << filename());
606 }
607
608 8 GridBase::Ptr parent;
609
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (clip) {
610 const CoordBBox indexBBox = grid->constTransform().worldToIndexNodeCentered(bbox);
611 parent = readGrid(parentIt->second, indexBBox);
612 } else {
613
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 parent = readGrid(parentIt->second);
614 }
615
3/8
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
16 if (parent) grid->setTree(parent->baseTreePtr());
616 }
617 return grid;
618 }
619
620
621 ////////////////////////////////////////
622
623
624 void
625 60 File::writeGrids(const GridCPtrVec& grids, const MetaMap& meta) const
626 {
627
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 59 times.
60 if (isOpen()) {
628
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
5 OPENVDB_THROW(IoError,
629 filename() << " cannot be written because it is open for reading");
630 }
631
632 // Create a file stream and write it out.
633 118 std::ofstream file;
634
2/4
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 59 times.
✗ Branch 5 not taken.
59 file.open(filename().c_str(),
635 std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
636
637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
59 if (file.fail()) {
638 OPENVDB_THROW(IoError, "could not open " << filename() << " for writing");
639 }
640
641 // Write out the vdb.
642
1/2
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
59 Archive::write(file, grids, /*seekable=*/true, meta);
643
644
1/2
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
59 file.close();
645 59 }
646
647
648 ////////////////////////////////////////
649
650
651 void
652
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
53 File::readGridDescriptors(std::istream& is)
653 {
654 // This method should not be called for files that don't contain grid offsets.
655
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
53 assert(inputHasGridOffsets());
656
657 53 gridDescriptors().clear();
658
659
2/2
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 53 times.
155 for (int32_t i = 0, N = readGridCount(is); i < N; ++i) {
660 // Read the grid descriptor.
661 204 GridDescriptor gd;
662
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 gd.read(is);
663
664 // Add the descriptor to the dictionary.
665
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
204 gridDescriptors().insert(std::make_pair(gd.gridName(), gd));
666
667 // Skip forward to the next descriptor.
668
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 gd.seekToEnd(is);
669 }
670 53 }
671
672
673 ////////////////////////////////////////
674
675
676 File::NameMapCIter
677 76 File::findDescriptor(const Name& name) const
678 {
679 76 const Name uniqueName = GridDescriptor::stringAsUniqueName(name);
680
681 // Find all descriptors with the given grid name.
682
3/4
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 30 times.
✓ Branch 4 taken 46 times.
152 std::pair<NameMapCIter, NameMapCIter> range = gridDescriptors().equal_range(name);
683
684
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 46 times.
76 if (range.first == range.second) {
685 // If no descriptors were found with the given grid name, the name might have
686 // a suffix ("name[N]"). In that case, remove the "[N]" suffix and search again.
687
2/4
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
60 range = gridDescriptors().equal_range(GridDescriptor::stripSuffix(uniqueName));
688 }
689
690 76 const size_t count = size_t(std::distance(range.first, range.second));
691
4/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 27 times.
76 if (count > 1 && name == uniqueName) {
692
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 OPENVDB_LOG_WARN(filename() << " has more than one grid named \"" << name << "\"");
693 }
694
695
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 NameMapCIter ret = gridDescriptors().end();
696
697
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 3 times.
76 if (count > 0) {
698
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 30 times.
73 if (name == uniqueName) {
699 // If the given grid name is unique or if no "[N]" index was given,
700 // use the first matching descriptor.
701 43 ret = range.first;
702 } else {
703 // If the given grid name has a "[N]" index, find the descriptor
704 // with a matching unique name.
705
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 1 times.
53 for (NameMapCIter it = range.first; it != range.second; ++it) {
706
1/2
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
52 const Name candidateName = it->second.uniqueName();
707
4/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 3 times.
52 if (candidateName == uniqueName || candidateName == name) {
708 29 ret = it;
709 break;
710 }
711 }
712 }
713 }
714 76 return ret;
715 }
716
717
718 ////////////////////////////////////////
719
720
721 GridBase::Ptr
722 122 File::createGrid(const GridDescriptor& gd) const
723 {
724 // Create the grid.
725
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 122 times.
122 if (!GridBase::isRegistered(gd.gridType())) {
726 OPENVDB_THROW(KeyError, "Cannot read grid "
727 << GridDescriptor::nameAsString(gd.uniqueName())
728 << " from " << filename() << ": grid type "
729 << gd.gridType() << " is not registered");
730 }
731
732 122 GridBase::Ptr grid = GridBase::createGrid(gd.gridType());
733
2/4
✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 122 times.
✗ Branch 4 not taken.
122 if (grid) grid->setSaveFloatAsHalf(gd.saveFloatAsHalf());
734
735 122 return grid;
736 }
737
738
739 GridBase::ConstPtr
740
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 File::readGridPartial(const GridDescriptor& gd, bool readTopology) const
741 {
742 // This method should not be called for files that don't contain grid offsets.
743
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 assert(inputHasGridOffsets());
744
745 4 GridBase::Ptr grid = createGrid(gd);
746
747 // Seek to grid.
748
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 gd.seekToGrid(inputStream());
749
750 // Read the grid partially.
751
2/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
12 readGridPartial(grid, inputStream(), gd.isInstance(), readTopology);
752
753 // Promote to a const grid.
754 GridBase::ConstPtr constGrid = grid;
755
756 4 return constGrid;
757 }
758
759
760 GridBase::Ptr
761 102 File::readGrid(const GridDescriptor& gd) const
762 {
763 102 return Impl::readGrid(*this, gd, Impl::NoBBox());
764 }
765
766
767 GridBase::Ptr
768 4 File::readGrid(const GridDescriptor& gd, const BBoxd& bbox) const
769 {
770 4 return Impl::readGrid(*this, gd, bbox);
771 }
772
773
774 GridBase::Ptr
775 File::readGrid(const GridDescriptor& gd, const CoordBBox& bbox) const
776 {
777 return Impl::readGrid(*this, gd, bbox);
778 }
779
780
781 void
782
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 File::readGridPartial(GridBase::Ptr grid, std::istream& is,
783 bool isInstance, bool readTopology) const
784 {
785 // This method should not be called for files that don't contain grid offsets.
786
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 assert(inputHasGridOffsets());
787
788 // This code needs to stay in sync with io::Archive::readGrid(), in terms of
789 // the order of operations.
790 4 readGridCompression(is);
791 4 grid->readMeta(is);
792
793 // drop DelayedLoadMetadata from the grid as it is only useful for IO
794
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
8 if ((*grid)[GridBase::META_FILE_DELAYED_LOAD]) {
795
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
8 grid->removeMeta(GridBase::META_FILE_DELAYED_LOAD);
796 }
797
798
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) {
799 grid->readTransform(is);
800
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!isInstance && readTopology) {
801 grid->readTopology(is);
802 }
803 } else {
804 if (readTopology) {
805 grid->readTopology(is);
806 grid->readTransform(is);
807 }
808 }
809 4 }
810
811
812 ////////////////////////////////////////
813
814
815 File::NameIterator
816 4 File::beginName() const
817 {
818
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
4 if (!isOpen()) {
819
3/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
5 OPENVDB_THROW(IoError, filename() << " is not open for reading");
820 }
821 3 return File::NameIterator(gridDescriptors().begin());
822 }
823
824
825 File::NameIterator
826 9 File::endName() const
827 {
828 9 return File::NameIterator(gridDescriptors().end());
829 }
830
831 } // namespace io
832 } // namespace OPENVDB_VERSION_NAME
833 } // namespace openvdb
834