33#ifndef NANOVDB_IO_H_HAS_BEEN_INCLUDED
34#define NANOVDB_IO_H_HAS_BEEN_INCLUDED
51#ifdef NANOVDB_USE_BLOSC
52#include <nanovdb_blosc.h>
58#if defined(major) || defined(minor)
72template<
typename BufferT>
78template<
typename BufferT = HostBuffer,
template<
typename...>
class VecT = std::vector>
79void writeGrids(
const std::string& fileName,
const VecT<GridHandle<BufferT>>& handles,
Codec codec =
Codec::NONE,
int verbose = 0);
93template<
typename BufferT = HostBuffer>
94GridHandle<BufferT>
readGrid(
const std::string& fileName,
int n = 0,
int verbose = 0,
const BufferT& buffer = BufferT());
104template<
typename BufferT = HostBuffer>
105GridHandle<BufferT>
readGrid(
const std::string& fileName,
const std::string& gridName,
int verbose = 0,
const BufferT& buffer = BufferT());
116template<
typename BufferT = HostBuffer,
template<
typename...>
class VecT = std::vector>
117VecT<GridHandle<BufferT>>
readGrids(
const std::string& fileName,
int verbose = 0,
const BufferT& buffer = BufferT());
131static constexpr fileSize_t MAX_SIZE = 1UL << 30;
135template<
typename SizeT>
136inline void addGridSize(SizeT& total, SizeT gridSize)
138 static_assert(std::is_unsigned<SizeT>::value,
139 "addGridSize relies on unsigned wrap-around to detect overflow");
140 const SizeT sum = total + gridSize;
141 if (sum < total)
throw std::runtime_error(
"Grid sizes in stream overflow a byte count");
145template<
typename BufferT>
148template<
typename BufferT>
149static void read(std::istream& is, BufferT& buffer,
Codec codec);
151static inline void read(std::istream& is,
char* data,
fileSize_t size,
Codec codec);
165 return (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) |
166 (((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) |
167 (((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) |
168 (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000);
180 static_assert(
sizeof(
FileMetaData) == 176,
"Unexpected sizeof(FileMetaData)");
182 void read(std::istream& is);
183 void write(std::ostream& os)
const;
196 static_assert(
sizeof(
FileHeader) == 16u,
"Unexpected sizeof(FileHeader)");
198 std::vector<FileGridMetaData>
meta;
200#ifdef NANOVDB_USE_NEW_MAGIC_NUMBERS
208 template<
typename BufferT>
209 void add(
const GridHandle<BufferT>& h);
210 bool read(std::istream& is);
211 void write(std::ostream& os)
const;
216bool hasGrid(
const std::string& fileName,
const std::string& gridName);
219bool hasGrid(std::istream& is,
const std::string& gridName);
231template<
typename BufferT>
232fileSize_t Internal::write(std::ostream& os,
const GridHandle<BufferT>& handle,
Codec codec,
unsigned int n)
234 const char* data =
reinterpret_cast<const char*
>(handle.gridData(n));
235 fileSize_t total = 0, residual = handle.gridSize(n);
239#ifdef NANOVDB_USE_ZIP
240 uLongf size = compressBound(
static_cast<uLongf
>(residual));
241 std::unique_ptr<Bytef[]> tmp(
new Bytef[size]);
242 const int status = compress(tmp.get(), &size,
reinterpret_cast<const Bytef*
>(data),
static_cast<uLongf
>(residual));
243 if (status != Z_OK)
throw std::runtime_error(
"Internal write error in ZIP");
244 if (size > residual) std::cerr <<
"\nWarning: Unexpected ZIP compression from " << residual <<
" to " << size <<
" bytes\n";
246 os.write(
reinterpret_cast<const char*
>(&outBytes),
sizeof(
fileSize_t));
247 os.write(
reinterpret_cast<const char*
>(tmp.get()), outBytes);
250 throw std::runtime_error(
"ZIP compression codec was disabled during build");
255#ifdef NANOVDB_USE_BLOSC
257 fileSize_t chunk = residual < MAX_SIZE ? residual : MAX_SIZE, size = chunk + NANOVDB_BLOSC_MAX_OVERHEAD;
258 std::unique_ptr<char[]> tmp(
new char[size]);
259 const int count = nanovdb_blosc_compress_ctx(9, 1,
sizeof(
float), chunk, data, tmp.get(), size, NANOVDB_BLOSC_LZ4_COMPNAME, 1 << 18, 1);
260 if (count <= 0)
throw std::runtime_error(
"Internal write error in BLOSC");
262 os.write(
reinterpret_cast<const char*
>(&outBytes),
sizeof(
fileSize_t));
263 os.write(
reinterpret_cast<const char*
>(tmp.get()), outBytes);
267 }
while (residual > 0);
269 throw std::runtime_error(
"BLOSC compression codec was disabled during build");
274 os.write(data, residual);
277 if (!os)
throw std::runtime_error(
"Failed to write Tree to file");
281template<
typename BufferT>
282void Internal::read(std::istream& is, BufferT& buffer,
Codec codec)
284 Internal::read(is,
reinterpret_cast<char*
>(buffer.data()), buffer.size(), codec);
292void Internal::read(std::istream& is,
char* data,
fileSize_t residual,
Codec codec)
297#ifdef NANOVDB_USE_ZIP
299 is.read(
reinterpret_cast<char*
>(&size),
sizeof(
fileSize_t));
300 std::unique_ptr<Bytef[]> tmp(
new Bytef[size]);
301 is.read(
reinterpret_cast<char*
>(tmp.get()), size);
302 uLongf numBytes =
static_cast<uLongf
>(residual);
303 int status = uncompress(
reinterpret_cast<Bytef*
>(data), &numBytes, tmp.get(),
static_cast<uLongf
>(size));
304 if (status != Z_OK)
throw std::runtime_error(
"Internal read error in ZIP");
305 if (
fileSize_t(numBytes) != residual)
throw std::runtime_error(
"UNZIP failed on byte size");
307 throw std::runtime_error(
"ZIP compression codec was disabled during build");
312#ifdef NANOVDB_USE_BLOSC
315 is.read(
reinterpret_cast<char*
>(&size),
sizeof(
fileSize_t));
316 std::unique_ptr<char[]> tmp(
new char[size]);
317 is.read(
reinterpret_cast<char*
>(tmp.get()), size);
318 const fileSize_t chunk = residual < MAX_SIZE ? residual : MAX_SIZE;
319 const int count = nanovdb_blosc_decompress_ctx(tmp.get(), data,
size_t(chunk), 1);
320 if (count < 1)
throw std::runtime_error(
"Internal read error in BLOSC");
321 if (count !=
int(chunk))
throw std::runtime_error(
"BLOSC failed on byte size");
322 data += size_t(chunk);
324 }
while (residual > 0);
326 throw std::runtime_error(
"BLOSC compression codec was disabled during build");
331 is.read(data, residual);
333 if (!is)
throw std::runtime_error(
"Failed to read Tree from file");
353 uint16_t(gridData.mBlindMetadataCount),
355 , gridName(gridData.gridName())
357 NANOVDB_ASSERT(gridData.mBlindMetadataCount <= uint32_t(1 << 16));
358 auto &treeData = *
reinterpret_cast<const TreeData*
>(gridData.treePtr());
359 nameKey = stringHash(gridName);
360 voxelCount = treeData.mVoxelCount;
361 nameSize =
static_cast<uint32_t
>(gridName.size() + 1);
362 for (
int i = 0; i < 3; ++i) {
363 FileMetaData::nodeCount[i] = treeData.mNodeCount[i];
364 FileMetaData::tileCount[i] = treeData.mTileCount[i];
370 os.write(
reinterpret_cast<const char*
>(
this),
sizeof(
FileMetaData));
372 if (!os)
throw std::runtime_error(
"Failed writing FileGridMetaData");
377 is.read(
reinterpret_cast<char*
>(
this),
sizeof(
FileMetaData));
378 std::unique_ptr<char[]> tmp(
new char[
nameSize]);
379 is.read(
reinterpret_cast<char*
>(tmp.get()),
nameSize);
381 if (!is)
throw std::runtime_error(
"Failed reading FileGridMetaData");
389 for (
auto& m :
meta) sum += m.memUsage();
393template<
typename BufferT>
396 for (uint32_t i = 0; i < h.
gridCount(); ++i) {
398 if (!gridData)
throw std::runtime_error(
"Segment::add: GridHandle does not contain grid #" + std::to_string(i));
406 if (
header.gridCount == 0) {
407 throw std::runtime_error(
"Segment contains no grids");
408 }
else if (!os.write(
reinterpret_cast<const char*
>(&
header),
sizeof(
FileHeader))) {
409 throw std::runtime_error(
"Failed to write FileHeader of Segment");
411 for (
auto& m :
meta) m.write(os);
418 is.clear(std::ios_base::eofbit);
426 throw std::runtime_error(
"This nvdb file has reversed endianness");
429 throw std::runtime_error(
"Expected a NanoVDB file, but read an OpenVDB file!");
431 throw std::runtime_error(
"Expected a NanoVDB file, but read a raw NanoVDB grid!");
433 throw std::runtime_error(
"Expected a NanoVDB file, but read a file of unknown type!");
436 }
else if ( !
header.version.isCompatible()) {
437 std::stringstream ss;
439 is.read(
reinterpret_cast<char*
>(&v),
sizeof(
Version));
441 ss <<
"This file looks like it contains a raw grid buffer and not a standard file with meta data";
444 ss <<
"The file contains an older version of NanoVDB: " << std::string(
toStr(str,
header.version)) <<
"!\n\t"
448 <<
"Recommendation: Re-compile this tool against the newer version: " <<
header.version.getMajor() <<
".X of NanoVDB";
450 throw std::runtime_error(
"An unrecoverable error in nanovdb::Segment::read:\n\tIncompatible file format: " + ss.str());
453 for (
auto& m :
meta) {
455 m.version =
header.version;
462template<
typename BufferT>
467 const auto start = os.tellp();
469 for (uint32_t i = 0; i < handle.
gridCount(); ++i) {
470 seg.
meta[i].fileSize = Internal::write(os, handle, codec, i);
474 os.seekp(0, std::ios_base::end);
477template<
typename BufferT>
480 std::ofstream os(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
482 throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for output");
486 std::cout <<
"Wrote nanovdb::Grid to file named \"" << fileName <<
"\"" << std::endl;
492template<
typename BufferT =
HostBuffer,
template<
typename...>
class VecT = std::vector>
495 for (
auto& h : handles)
writeGrid(os, h, codec);
498template<
typename BufferT,
template<
typename...>
class VecT>
501 std::ofstream os(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
502 if (!os.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for output");
504 if (verbose) std::cout <<
"Wrote " << handles.size() <<
" nanovdb::Grid(s) to file named \"" << fileName <<
"\"" << std::endl;
509template<
typename BufferT>
515 handle.
read(is, pool);
516 }
catch(
const std::logic_error&) {
518 uint64_t bufferSize = 0u;
519 uint32_t gridCount = 0u, gridIndex = 0u;
520 const auto start = is.tellg();
521 while (seg.
read(is)) {
522 std::streamoff skipSize = 0;
523 for (
auto& m : seg.
meta) {
525 Internal::addGridSize(bufferSize, m.gridSize);
526 skipSize += m.fileSize;
528 is.seekg(skipSize, std::ios_base::cur);
530 auto buffer = BufferT::create(bufferSize, &pool);
531 char *ptr = (
char*)buffer.data();
533 while (seg.
read(is)) {
534 for (
auto& m : seg.
meta) {
535 Internal::read(is, ptr, m.gridSize, seg.
header.
codec);
544 handle.
read(is, uint32_t(n), pool);
546 }
catch(
const std::logic_error&) {
549 while (seg.
read(is)) {
550 std::streamoff seek = 0;
551 for (
auto& m : seg.
meta) {
552 if (++counter == n) {
553 auto buffer = BufferT::create(m.gridSize, &pool);
561 is.seekg(seek, std::ios_base::cur);
563 if (n != counter)
throw std::runtime_error(
"stream does not contain a #" + std::to_string(n) +
" grid");
570template<
typename BufferT>
573 std::ifstream is(fileName, std::ios::in | std::ios::binary);
574 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
578 std::cout <<
"Read all NanoGrids from the file named \"" << fileName <<
"\"" << std::endl;
580 std::cout <<
"Read NanoGrid # " << n <<
" from the file named \"" << fileName <<
"\"" << std::endl;
593template<
typename BufferT>
598 handle.
read(is, gridName, pool);
600 }
catch(
const std::logic_error&) {
603 while (seg.
read(is)) {
604 std::streamoff seek = 0;
605 for (
auto& m : seg.
meta) {
606 if ((m.nameKey == 0u || m.nameKey == key) && m.gridName == gridName) {
607 auto buffer = BufferT::create(m.gridSize, &pool);
608 is.seekg(seek, std::ios_base::cur);
616 is.seekg(seek, std::ios_base::cur);
619 throw std::runtime_error(
"Grid name '" + gridName +
"' not found in file");
623template<
typename BufferT>
626 std::ifstream is(fileName, std::ios::in | std::ios::binary);
627 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
631 std::cout <<
"Read NanoGrid named \"" << gridName <<
"\" from the file named \"" << fileName <<
"\"" << std::endl;
633 std::cout <<
"File named \"" << fileName <<
"\" does not contain a grid named \"" + gridName +
"\"" << std::endl;
641template<
typename BufferT =
HostBuffer,
template<
typename...>
class VecT = std::vector>
642VecT<GridHandle<BufferT>>
readGrids(std::istream& is,
const BufferT& pool = BufferT())
644 VecT<GridHandle<BufferT>> handles;
647 handle.
read(is, pool);
648 handles.push_back(std::move(handle));
649 }
catch(
const std::logic_error&) {
651 while (seg.
read(is)) {
652 uint64_t bufferSize = 0;
653 for (
auto& m : seg.
meta) Internal::addGridSize(bufferSize, m.gridSize);
654 auto buffer = BufferT::create(bufferSize, &pool);
655 uint64_t bufferOffset = 0;
658 Internal::read(is, (
char*)data, seg.
meta[i].gridSize, seg.
header.
codec);
660 bufferOffset += seg.
meta[i].gridSize;
662 handles.emplace_back(std::move(buffer));
669template<
typename BufferT,
template<
typename...>
class VecT>
670VecT<GridHandle<BufferT>>
readGrids(
const std::string& fileName,
int verbose,
const BufferT& buffer)
672 std::ifstream is(fileName, std::ios::in | std::ios::binary);
673 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
675 if (verbose) std::cout <<
"Read " << handles.size() <<
" NanoGrid(s) from the file named \"" << fileName <<
"\"" << std::endl;
683 std::ifstream is(fileName, std::ios::in | std::ios::binary);
684 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
691 std::vector<FileGridMetaData> meta;
696 meta = std::move(seg.
meta);
697 }
catch(
const std::logic_error&) {
698 while (seg.
read(is)) {
699 std::streamoff skip = 0;
700 for (
auto& m : seg.
meta) {
704 is.seekg(skip, std::ios_base::cur);
712inline bool hasGrid(
const std::string& fileName,
const std::string& gridName)
714 std::ifstream is(fileName, std::ios::in | std::ios::binary);
715 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
719inline bool hasGrid(std::istream& is,
const std::string& gridName)
723 while (seg.
read(is)) {
724 std::streamoff seek = 0;
725 for (
auto& m : seg.
meta) {
726 if (m.nameKey == key && m.gridName == gridName)
return true;
729 is.seekg(seek, std::ios_base::cur);
740 for (
auto* str =
reinterpret_cast<const unsigned char*
>(c_str); *str; ++str) {
741 uint64_t overflow =
hash >> (64 - 8);
743 hash += *str + overflow;
759 os <<
"(" << b[0][0] <<
"," << b[0][1] <<
"," << b[0][2] <<
") -> "
760 <<
"(" << b[1][0] <<
"," << b[1][1] <<
"," << b[1][2] <<
")";
767 os <<
"(" << b[0][0] <<
"," << b[0][1] <<
"," << b[0][2] <<
") -> "
768 <<
"(" << b[1][0] <<
"," << b[1][1] <<
"," << b[1][2] <<
")";
775 os <<
"(" << ijk[0] <<
"," << ijk[1] <<
"," << ijk[2] <<
")";
783 os <<
"(" << v[0] <<
"," << v[1] <<
"," << v[2] <<
")";
791 os <<
"(" << v[0] <<
"," << v[1] <<
"," << v[2] <<
"," << v[3] <<
")";
Computes a pair of uint32_t checksums, of a Grid, by means of 32 bit Cyclic Redundancy Check (CRC32)
Defines GridHandle, which manages a memory buffer containing one or more NanoVDB grids: host-resident...
Implements a light-weight self-contained VDB data-structure in a single file! In other words,...
#define NANOVDB_MAGIC_FILE
Definition NanoVDB.h:141
#define NANOVDB_MAJOR_VERSION_NUMBER
Definition NanoVDB.h:146
#define NANOVDB_MAGIC_NUMB
Definition NanoVDB.h:139
This class serves to manage a buffer containing one or more NanoVDB Grids.
Definition GridHandle.h:164
const GridData * gridData(uint32_t n=0) const
Access to the GridData of the n'th grid in the current handle.
Definition GridHandle.h:527
uint64_t gridSize(uint32_t n=0) const
Return the grid size of the n'th grid in this GridHandle.
Definition GridHandle.h:374
uint32_t gridCount() const
Return the total number of grids contained in this buffer.
Definition GridHandle.h:369
void * data()
Returns a pointer to the host data; not available for a single-space device buffer,...
Definition GridHandle.h:307
void read(std::istream &is, const BufferT &pool=BufferT())
Read an entire raw grid buffer from an input stream.
Definition GridHandle.h:586
This is a buffer that contains a shared or private pool to either externally or internally managed ho...
Definition HostBuffer.h:180
Bit-compacted representation of all three version numbers.
Definition NanoVDB.h:727
uint32_t getMajor() const
Definition NanoVDB.h:754
Signed (i, j, k) 32-bit integer coordinate class, similar to openvdb::math::Coord.
Definition Math.h:425
A simple vector class with three components, similar to openvdb::math::Vec3.
Definition Math.h:1865
A simple vector class with four components, similar to openvdb::math::Vec4.
Definition Math.h:2034
__hostdev__ uint32_t hash(uint32_t x)
Definition common.h:16
std::ostream & operator<<(std::ostream &os, const nanovdb::math::BBox< nanovdb::math::Vec3< T > > &b)
Definition IO.h:757
Definition NanoVDB.h:5973
VecT< GridHandle< BufferT > > readGrids(const std::string &fileName, int verbose=0, const BufferT &buffer=BufferT())
Read all the grids in the file and return them as a vector of multiple GridHandles,...
Definition IO.h:670
void writeGrids(const std::string &fileName, const VecT< GridHandle< BufferT > > &handles, Codec codec=Codec::NONE, int verbose=0)
Write multiple grids to file (over-writing existing content of the file)
Definition IO.h:499
uint64_t fileSize_t
Definition IO.h:124
std::vector< FileGridMetaData > readGridMetaData(const std::string &fileName)
Reads and returns a vector of meta data for all the grids found in the specified file.
Definition IO.h:681
uint64_t stringHash(const char *cstr)
Internal functions for compressed read/write of a NanoVDB GridHandle into a stream.
Definition IO.h:736
uint64_t reverseEndianness(uint64_t val)
Return a uint64_t with its bytes reversed so we can check for endianness.
Definition IO.h:163
void writeGrid(const std::string &fileName, const GridHandle< BufferT > &handle, io::Codec codec=io::Codec::NONE, int verbose=0)
Write a single grid to file (over-writing existing content of the file)
Definition IO.h:478
GridHandle< BufferT > readGrid(const std::string &fileName, int n=0, int verbose=0, const BufferT &buffer=BufferT())
Read and return one or all grids from a file into a single GridHandle.
Definition IO.h:571
Codec
Define compression codecs.
Definition NanoVDB.h:5981
@ ZIP
Definition NanoVDB.h:5982
@ BLOSC
Definition NanoVDB.h:5983
@ NONE
Definition NanoVDB.h:5981
bool hasGrid(const std::string &fileName, const std::string &gridName)
Return true if the file contains a grid with the specified name.
Definition IO.h:712
static DstT * PtrAdd(void *p, int64_t offset)
Adds a byte offset to a non-const pointer to produce another non-const pointer.
Definition Util.h:555
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:34
MagicType toMagic(uint64_t magic)
maps 64 bits of magic number to enum
Definition NanoVDB.h:370
MagicType
Enums used to identify magic numbers recognized by NanoVDB.
Definition NanoVDB.h:361
@ OpenVDB
Definition NanoVDB.h:362
@ NanoGrid
Definition NanoVDB.h:364
@ NanoVDB
Definition NanoVDB.h:363
@ NanoFile
Definition NanoVDB.h:365
char * toStr(char *dst, GridType gridType)
Maps a GridType to a c-string.
Definition NanoVDB.h:257
math::BBox< Coord > CoordBBox
Definition Math.h:2838
#define NANOVDB_ASSERT(x)
Definition Util.h:53
Struct with all the member data of the Grid (useful during serialization of an openvdb grid)
Definition NanoVDB.h:1952
This class defines all the data stored in segment of a file.
Definition IO.h:194
bool read(std::istream &is)
Definition IO.h:414
void add(const GridHandle< BufferT > &h)
Definition IO.h:394
void write(std::ostream &os) const
Definition IO.h:404
std::vector< FileGridMetaData > meta
Definition IO.h:198
uint64_t memUsage() const
Definition IO.h:386
Segment(Codec c=Codec::NONE)
Definition IO.h:199
FileHeader header
Definition IO.h:197