OpenVDB 14.0.0
Loading...
Searching...
No Matches
GridChecksum.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5 \file nanovdb/tools/GridChecksum.h
6
7 \author Ken Museth
8
9 \brief Computes a pair of uint32_t checksums, of a Grid, by means of 32 bit Cyclic Redundancy Check (CRC32)
10
11 \details A CRC32 is the 32 bit remainder, or residue, of binary division of a message, by a polynomial.
12
13
14 \note before v32.6.0: checksum[0] = Grid+Tree+Root, checksum[1] = nodes
15 after v32.6.0: checksum[0] = Grid+Tree, checksum[1] = nodes + blind data in 4K blocks
16
17 When serialized:
18 [Grid,Tree][Root][ROOT TILES...][Node<5>...][Node<4>...][Leaf<3>...][BlindMeta...][BlindData...]
19 checksum[2] before v32.6.0: <------------- [0] ------------><-------------- [1] --------------->
20 checksum[2] after v32.6.0: <---[0]---><----------------------------------------[1]---------------------------------------->
21*/
22
23#ifndef NANOVDB_TOOLS_GRIDCHECKSUM_H_HAS_BEEN_INCLUDED
24#define NANOVDB_TOOLS_GRIDCHECKSUM_H_HAS_BEEN_INCLUDED
25
26#include <algorithm>// for std::generate
27#include <array>
28#include <vector>
29#include <cstdint>
30#include <cstddef>// offsetof macro
31#include <numeric>
32#include <type_traits>
33#include <memory>// for std::unique_ptr
34
35#include <nanovdb/NanoVDB.h>
37#include <nanovdb/NodeManager.h>
38
39// Define log of block size for FULL CRC32 computation.
40// A value of 12 corresponds to a block size of 4KB (2^12 = 4096).
41#define NANOVDB_CRC32_LOG2_BLOCK_SIZE 12
42
43namespace nanovdb {// ==================================================================
44
45namespace tools {// ====================================================================
46
47/// @brief Compute the (2 x CRC32) checksum of the specified @c gridData
48/// @param gridData Base pointer to the grid from which the checksum is computed.
49/// @param mode Defines the mode of computation for the checksum.
50/// @return Return the (2 x CRC32) checksum of the specified @c gridData
51Checksum evalChecksum(const GridData *gridData, CheckMode mode = CheckMode::Default);
52
53/// @brief Extract the checksum of a grid
54/// @param gridData Base pointer to grid with a checksum
55/// @return Checksum encoded in the specified grid
56inline Checksum getChecksum(const GridData *gridData)
57{
58 NANOVDB_ASSERT(gridData);
59 return gridData->mChecksum;
60}
61
62/// @brief Return true if the checksum of @c gridData matches the expected
63/// value already encoded into the grid's meta data.
64/// @param gridData Base pointer to the grid whose checksum is validated.
65/// @param mode Defines the mode of computation for the checksum.
66bool validateChecksum(const GridData *gridData, CheckMode mode = CheckMode::Default);
67
68/// @brief Updates the checksum of a grid
69/// @param gridData Base pointer to the grid whose checksum will be updated.
70/// @param mode Defines the mode of computation for the checksum.
71inline void updateChecksum(GridData *gridData, CheckMode mode)
72{
73 NANOVDB_ASSERT(gridData);
74 gridData->mChecksum = evalChecksum(gridData, mode);
75}
76
77/// @brief Updates the checksum of a grid by preserving its mode
78/// @param gridData Base pointer to grid
79inline void updateChecksum(GridData *gridData)
80{
81 updateChecksum(gridData, gridData->mChecksum.mode());
82}
83
84}// namespace tools
85
86namespace util {
87
88/// @brief Initiate single entry in look-up-table for CRC32 computations
89/// @param lut pointer of size 256 for look-up-table
90/// @param n entry in table (assumed n < 256)
91inline __hostdev__ void initCrc32Lut(uint32_t lut[256], uint32_t n)
92{
93 lut[n] = n;
94 uint32_t &cs = lut[n];
95 for (int i = 0; i < 8; ++i) cs = (cs >> 1) ^ ((cs & 1) ? 0xEDB88320 : 0);
96}
97
98/// @brief Initiate entire look-up-table for CRC32 computations
99/// @param lut pointer of size 256 for look-up-table
100inline __hostdev__ void initCrc32Lut(uint32_t lut[256]){for (uint32_t n = 0u; n < 256u; ++n) initCrc32Lut(lut, n);}
101
102/// @brief Create and initiate entire look-up-table for CRC32 computations
103/// @return returns a unique pointer to the lookup table of size 256.
104inline std::unique_ptr<uint32_t[]> createCrc32Lut()
105{
106 std::unique_ptr<uint32_t[]> lut(new uint32_t[256]);
107 initCrc32Lut(lut.get());
108 return lut;
109}
110
111/// @brief Compute crc32 checksum of @c data of @c size bytes (without a lookup table))
112/// @param data pointer to beginning of data
113/// @param size byte size of data
114/// @param crc initial value of crc32 checksum
115/// @return return crc32 checksum of @c data
116inline __hostdev__ uint32_t crc32(const void* data, size_t size, uint32_t crc = 0)
117{
118 NANOVDB_ASSERT(data);
119 crc = ~crc;
120 for (auto *p = (const uint8_t*)data, *q = p + size; p != q; ++p) {
121 crc ^= *p;
122 for (int j = 0; j < 8; ++j) crc = (crc >> 1) ^ (0xEDB88320 & (-(crc & 1)));
123 }
124 return ~crc;
125}
126
127/// @brief Compute crc32 checksum of data between @c begin and @c end
128/// @param begin points to beginning of data
129/// @param end points to end of @c data, (exclusive)
130/// @param crc initial value of crc32 checksum
131/// @return return crc32 checksum
132inline __hostdev__ uint32_t crc32(const void *begin, const void *end, uint32_t crc = 0)
133{
134 NANOVDB_ASSERT(begin && end);
135 NANOVDB_ASSERT(end >= begin);
136 return crc32(begin, (const char*)end - (const char*)begin, crc);
137}
138
139/// @brief Compute crc32 checksum of @c data with @c size bytes using a lookup table
140/// @param data pointer to begenning of data
141/// @param size byte size
142/// @param lut pointer to loopup table for accelerated crc32 computation
143/// @param crc initial value of the checksum
144/// @return crc32 checksum of @c data with @c size bytes
145inline __hostdev__ uint32_t crc32(const void *data, size_t size, const uint32_t lut[256], uint32_t crc = 0)
146{
147 NANOVDB_ASSERT(data);
148 crc = ~crc;
149 for (auto *p = (const uint8_t*)data, *q = p + size; p != q; ++p) crc = lut[(crc ^ *p) & 0xFF] ^ (crc >> 8);
150 return ~crc;
151}
152
153/// @brief Compute crc32 checksum of data between @c begin and @c end using a lookup table
154/// @param begin points to beginning of data
155/// @param end points to end of @c data, (exclusive)
156/// @param lut pointer to loopup table for accelerated crc32 computation
157/// @param crc initial value of crc32 checksum
158/// @return return crc32 checksum
159inline __hostdev__ uint32_t crc32(const void *begin, const void *end, const uint32_t lut[256], uint32_t crc = 0)
160{
161 NANOVDB_ASSERT(begin && end);
162 NANOVDB_ASSERT(end >= begin);
163 return crc32(begin, (const char*)end - (const char*)begin, lut, crc);
164}// uint32_t util::crc32(const void *begin, const void *end, const uint32_t lut[256], uint32_t crc = 0)
165
166/// @brief
167/// @param data
168/// @param size
169/// @param lut
170/// @return
171inline uint32_t blockedCrc32(const void *data, size_t size, const uint32_t *lut)
172{
173 if (size == 0 ) return ~uint32_t(0);
174 const uint64_t blockCount = size >> NANOVDB_CRC32_LOG2_BLOCK_SIZE;// number of 4 KB (4096 byte) blocks
175 std::unique_ptr<uint32_t[]> checksums(new uint32_t[blockCount]);
176 forEach(0, blockCount, 64, [&](const Range1D &r) {
177 uint32_t blockSize = 1 << NANOVDB_CRC32_LOG2_BLOCK_SIZE, *p = checksums.get() + r.begin();
178 for (auto i = r.begin(); i != r.end(); ++i) {
179 if (i+1 == blockCount) blockSize += static_cast<uint32_t>(size - (blockCount<<NANOVDB_CRC32_LOG2_BLOCK_SIZE));
180 *p++ = crc32((const uint8_t*)data + (i<<NANOVDB_CRC32_LOG2_BLOCK_SIZE), blockSize, lut);
181 }
182 });
183 return crc32(checksums.get(), sizeof(uint32_t)*blockCount, lut);
184}// uint32_t util::blockedCrc32(const void *data, size_t size, const uint32_t *lut)
185
186/// @brief
187/// @param begin
188/// @param end
189/// @param lut
190/// @return
191inline uint32_t blockedCrc32(const void *begin, const void *end, const uint32_t *lut)
192{
193 return blockedCrc32(begin, PtrDiff(end, begin), lut);
194}
195
196}// namespace util =======================================================================================
197
198namespace tools {// ======================================================================================
199
200// When serialized:
201// [Grid,Tree][Root][ROOT TILES...][Node<5>...][Node<4>...][Leaf<3>...][BlindMeta...][BlindData...]
202// checksum[2] before v32.6.0: <------------- [0] ------------><-------------- [1] --------------->
203// checksum[]2 after v32.6.0: <---[0]---><----------------------------------------[1]---------------------------------------->
204
205// ----------------------------> crc32Head <--------------------------------------
206
207/// @brief Compute the crc32 checksum of a grid's head, i.e. its GridData and
208/// TreeData, using a lookup table. GridData::mMagic and
209/// GridData::mChecksum are excluded.
210/// @param gridData Base pointer to the grid.
211/// @param lut Pointer to the lookup table for accelerated crc32 computation.
212/// @return crc32 checksum of the grid's head.
213inline __hostdev__ uint32_t crc32Head(const GridData *gridData, const uint32_t *lut)
214{
215 NANOVDB_ASSERT(gridData);
216 const uint8_t *begin = (const uint8_t*)(gridData), *mid = begin + sizeof(GridData) + sizeof(TreeData);
217 if (gridData->mVersion <= Version(32,6,0)) mid = (const uint8_t*)(gridData->template nodePtr<2>());
218 return util::crc32(begin + 16u, mid, lut);// exclude GridData::mMagic and GridData::mChecksum
219}// uint32_t crc32Head(const GridData *gridData, const uint32_t *lut)
220
221/// @brief
222/// @param gridData
223/// @return
224inline __hostdev__ uint32_t crc32Head(const GridData *gridData)
225{
226 NANOVDB_ASSERT(gridData);
227 const uint8_t *begin = (const uint8_t*)(gridData), *mid = begin + sizeof(GridData) + sizeof(TreeData);
228 if (gridData->mVersion <= Version(32,6,0)) mid = (const uint8_t*)(gridData->template nodePtr<2>());
229 return util::crc32(begin + 16, mid);// exclude GridData::mMagic and GridData::mChecksum
230}// uint32_t crc32Head(const GridData *gridData)
231
232// ----------------------------> crc32TailOld <--------------------------------------
233
234// Old checksum
235template <typename ValueT>
236uint32_t crc32TailOld(const NanoGrid<ValueT> *grid, const uint32_t *lut)
237{
238 NANOVDB_ASSERT(grid->mVersion <= Version(32,6,0));
239 const auto &tree = grid->tree();
240 auto nodeMgrHandle = createNodeManager(*grid);
241 auto *nodeMgr = nodeMgrHandle.template mgr<ValueT>();
242 assert(nodeMgr && isAligned(nodeMgr));
243 const auto nodeCount = tree.nodeCount(0) + tree.nodeCount(1) + tree.nodeCount(2);
244 std::vector<uint32_t> checksums(nodeCount, 0);
245 util::forEach(0, tree.nodeCount(2), 1,[&](const util::Range1D &r) {// process upper internal nodes
246 uint32_t *p = checksums.data() + r.begin();
247 for (auto i = r.begin(); i != r.end(); ++i) {
248 const auto &node = nodeMgr->upper(static_cast<uint32_t>(i));
249 *p++ = util::crc32(&node, node.memUsage(), lut);
250 }
251 });
252 util::forEach(0, tree.nodeCount(1), 1, [&](const util::Range1D &r) { // process lower internal nodes
253 uint32_t *p = checksums.data() + r.begin() + tree.nodeCount(2);
254 for (auto i = r.begin(); i != r.end(); ++i) {
255 const auto &node = nodeMgr->lower(static_cast<uint32_t>(i));
256 *p++ = util::crc32(&node, node.memUsage(), lut);
257 }
258 });
259 util::forEach(0, tree.nodeCount(0), 8, [&](const util::Range1D &r) { // process leaf nodes
260 uint32_t *p = checksums.data() + r.begin() + tree.nodeCount(1) + tree.nodeCount(2);
261 for (auto i = r.begin(); i != r.end(); ++i) {
262 const auto &leaf = nodeMgr->leaf(static_cast<uint32_t>(i));
263 *p++ = util::crc32(&leaf, leaf.memUsage(), lut);
264 }
265 });
266 return util::crc32(checksums.data(), sizeof(uint32_t)*checksums.size(), lut);
267}// uint32_t crc32TailOld(const NanoGrid<ValueT> *grid, const uint32_t *lut)
268
270 template <typename BuildT>
271 static uint32_t known(const GridData *gridData, const uint32_t *lut)
272 {
273 return crc32TailOld((const NanoGrid<BuildT>*)gridData, lut);
274 }
275 static uint32_t unknown(const GridData*, const uint32_t*)
276 {
277 throw std::runtime_error("Cannot call Crc32TailOld with grid of unknown type");
278 return 0u;//dummy
279 }
280};// struct Crc32TailOld
281
282inline uint32_t crc32Tail(const GridData *gridData, const uint32_t *lut)
283{
284 NANOVDB_ASSERT(gridData);
285 if (gridData->mVersion > Version(32,6,0)) {
286 const uint8_t *begin = (const uint8_t*)(gridData);
287 return util::blockedCrc32(begin + sizeof(GridData) + sizeof(TreeData), begin + gridData->mGridSize, lut);
288 } else {
289 return callNanoGrid<Crc32TailOld>(gridData, lut);
290 }
291}// uint32_t crc32Tail(const GridData *gridData, const uint32_t *lut)
292
293template <typename ValueT>
294uint32_t crc32Tail(const NanoGrid<ValueT> *grid, const uint32_t *lut)
295{
296 NANOVDB_ASSERT(grid);
297 if (grid->mVersion > Version(32,6,0)) {
298 const uint8_t *begin = (const uint8_t*)(grid);
299 return util::blockedCrc32(begin + sizeof(GridData) + sizeof(TreeData), begin + grid->mGridSize, lut);
300 } else {
301 return crc32TailOld(grid, lut);
302 }
303}// uint32_t crc32Tail(const NanoGrid<ValueT> *gridData, const uint32_t *lut)
304
305// ----------------------------> evalChecksum <--------------------------------------
306
307/// @brief
308/// @tparam ValueT
309/// @param grid
310/// @param mode
311/// @return
312template <typename ValueT>
314{
315 NANOVDB_ASSERT(grid);
316 Checksum cs;
317 if (mode != CheckMode::Empty) {
318 auto lut = util::createCrc32Lut();
319 cs.head() = crc32Head(grid, lut.get());
320 if (mode == CheckMode::Full) cs.tail() = crc32Tail(grid, lut.get());
321 }
322 return cs;
323}// evalChecksum(const NanoGrid*, CheckMode)
324
325inline Checksum evalChecksum(const GridData *gridData, CheckMode mode)
326{
327 NANOVDB_ASSERT(gridData);
328 Checksum cs;
329 if (mode != CheckMode::Disable) {
330 auto lut = util::createCrc32Lut();
331 cs.head() = crc32Head(gridData, lut.get());
332 if (mode == CheckMode::Full) cs.tail() = crc32Tail(gridData, lut.get());
333 }
334 return cs;
335}// evalChecksum(GridData *data, CheckMode mode)
336
337// ----------------------------> validateChecksum <--------------------------------------
338
339/// @brief
340/// @tparam ValueT
341/// @param grid
342/// @param mode
343/// @return
344template <typename ValueT>
346{
347 if (grid->mChecksum.isEmpty() || mode == CheckMode::Empty) return true;
348 auto lut = util::createCrc32Lut();
349 bool checkHead = grid->mChecksum.head() == crc32Head(grid->data(), lut.get());
350 if (grid->mChecksum.isHalf() || mode == CheckMode::Half || !checkHead) {
351 return checkHead;
352 } else {
353 return grid->mChecksum.tail() == crc32Tail(grid, lut.get());
354 }
355}
356
357inline bool validateChecksum(const GridData *gridData, CheckMode mode)
358{
359 if (gridData->mChecksum.isEmpty()|| mode == CheckMode::Empty) return true;
360 auto lut = util::createCrc32Lut();
361 bool checkHead = gridData->mChecksum.head() == crc32Head(gridData, lut.get());
362 if (gridData->mChecksum.isHalf() || mode == CheckMode::Half || !checkHead) {
363 return checkHead;
364 } else {
365 return gridData->mChecksum.tail() == crc32Tail(gridData, lut.get());
366 }
367}// bool validateChecksum(const GridData *gridData, CheckMode mode)
368
369// ----------------------------> updateChecksum <--------------------------------------
370
371/// @brief
372/// @tparam ValueT
373/// @param grid
374/// @param mode
375template <typename ValueT>
376void updateChecksum(NanoGrid<ValueT> *grid, CheckMode mode){grid->mChecksum = evalChecksum(grid, mode);}
377
378template <typename ValueT>
380
381// ----------------------------> updateGridCount <--------------------------------------
382
383/// @brief Updates the ground index and count, as well as the head checksum if needed
384/// @param data Pointer to grid data
385/// @param gridIndex New value of the index
386/// @param gridCount New value of the grid count
387inline __hostdev__ void updateGridCount(GridData *data, uint32_t gridIndex, uint32_t gridCount)
388{
389 NANOVDB_ASSERT(data && gridIndex < gridCount);
390 if (data->mGridIndex != gridIndex || data->mGridCount != gridCount) {
391 data->mGridIndex = gridIndex;
392 data->mGridCount = gridCount;
393 if (!data->mChecksum.isEmpty()) data->mChecksum.head() = crc32Head(data);
394 }
395}
396
397} // namespace tools ======================================================================
398
399
400} // namespace nanovdb ====================================================================
401
402#endif // NANOVDB_TOOLS_GRIDCHECKSUM_H_HAS_BEEN_INCLUDED
A unified wrapper for tbb::parallel_for and a naive std::thread fallback.
#define NANOVDB_CRC32_LOG2_BLOCK_SIZE
Definition GridChecksum.h:41
Implements a light-weight self-contained VDB data-structure in a single file! In other words,...
#define __hostdev__
Definition SampleFromVoxels.h:29
Class that encapsulates two CRC32 checksums, one for the Grid, Tree and Root node meta data and one f...
Definition NanoVDB.h:1858
uint32_t tail() const
Definition NanoVDB.h:1890
bool isHalf() const
return true if the 64 bit checksum is half, i.e. of head only
Definition NanoVDB.h:1894
CheckMode mode() const
return the mode of the 64 bit checksum
Definition NanoVDB.h:1905
bool isEmpty() const
return true if the 64 bit checksum is disables (unset)
Definition NanoVDB.h:1900
uint32_t head() const
Definition NanoVDB.h:1888
DataType * data()
Definition NanoVDB.h:2180
const TreeT & tree() const
Return a const reference to the tree.
Definition NanoVDB.h:2211
Bit-compacted representation of all three version numbers.
Definition NanoVDB.h:727
bool validateChecksum(const GridData *gridData, CheckMode mode=CheckMode::Default)
Return true if the checksum of gridData matches the expected value already encoded into the grid's me...
Definition GridChecksum.h:357
__hostdev__ uint32_t crc32Head(const GridData *gridData, const uint32_t *lut)
Compute the crc32 checksum of a grid's head, i.e. its GridData and TreeData, using a lookup table....
Definition GridChecksum.h:213
Checksum evalChecksum(const GridData *gridData, CheckMode mode=CheckMode::Default)
Compute the (2 x CRC32) checksum of the specified gridData.
Definition GridChecksum.h:325
Checksum getChecksum(const GridData *gridData)
Extract the checksum of a grid.
Definition GridChecksum.h:56
uint32_t crc32Tail(const GridData *gridData, const uint32_t *lut)
Definition GridChecksum.h:282
__hostdev__ void updateGridCount(GridData *data, uint32_t gridIndex, uint32_t gridCount)
Updates the ground index and count, as well as the head checksum if needed.
Definition GridChecksum.h:387
void updateChecksum(GridData *gridData, CheckMode mode)
Updates the checksum of a grid.
Definition GridChecksum.h:71
uint32_t crc32TailOld(const NanoGrid< ValueT > *grid, const uint32_t *lut)
Definition GridChecksum.h:236
Definition GridChecksum.h:86
uint32_t blockedCrc32(const void *data, size_t size, const uint32_t *lut)
Definition GridChecksum.h:171
__hostdev__ uint32_t crc32(const void *data, size_t size, uint32_t crc=0)
Compute crc32 checksum of data of size bytes (without a lookup table))
Definition GridChecksum.h:116
static int64_t PtrDiff(const void *p, const void *q)
Compute the distance, in bytes, between two pointers, dist = p - q.
Definition Util.h:541
__hostdev__ void initCrc32Lut(uint32_t lut[256], uint32_t n)
Initiate single entry in look-up-table for CRC32 computations.
Definition GridChecksum.h:91
std::unique_ptr< uint32_t[]> createCrc32Lut()
Create and initiate entire look-up-table for CRC32 computations.
Definition GridChecksum.h:104
void forEach(RangeT range, const FuncT &func)
simple wrapper for tbb::parallel_for with a naive std fallback
Definition ForEach.h:42
Range< 1, size_t > Range1D
Definition Range.h:33
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:34
auto callNanoGrid(GridDataT *gridData, ArgsT &&... args)
Below is an example of the struct used for generic programming with callNanoGrid.
Definition NanoVDB.h:4805
Grid< NanoTree< BuildT > > NanoGrid
Definition NanoVDB.h:4699
CheckMode
List of different modes for computing for a checksum.
Definition NanoVDB.h:1831
@ Default
Definition NanoVDB.h:1835
@ Full
Definition NanoVDB.h:1836
@ Disable
Definition NanoVDB.h:1831
@ Half
Definition NanoVDB.h:1833
@ Empty
Definition NanoVDB.h:1832
static bool isAligned(const void *p)
return true if the specified pointer is 32 byte aligned
Definition NanoVDB.h:597
NodeManagerHandle< BufferT > createNodeManager(const NanoGrid< BuildT > &grid, const BufferT &buffer=BufferT())
brief Construct a NodeManager and return its handle
Definition NodeManager.h:259
This class allows for sequential access to nodes in a NanoVDB tree on both the host and device.
#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
Version mVersion
Definition NanoVDB.h:1956
uint64_t mGridSize
Definition NanoVDB.h:1960
Checksum mChecksum
Definition NanoVDB.h:1955
uint32_t mGridCount
Definition NanoVDB.h:1959
uint32_t mGridIndex
Definition NanoVDB.h:1958
Definition NanoVDB.h:2389
Definition GridChecksum.h:269
static uint32_t unknown(const GridData *, const uint32_t *)
Definition GridChecksum.h:275
static uint32_t known(const GridData *gridData, const uint32_t *lut)
Definition GridChecksum.h:271