GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/points/AttributeSet.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 16 17 94.1%
Functions: 3 3 100.0%
Branches: 38 78 48.7%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @file points/AttributeSet.h
5 ///
6 /// @authors Dan Bailey, Mihai Alden
7 ///
8 /// @brief Set of Attribute Arrays which tracks metadata about each array.
9
10 #ifndef OPENVDB_POINTS_ATTRIBUTE_SET_HAS_BEEN_INCLUDED
11 #define OPENVDB_POINTS_ATTRIBUTE_SET_HAS_BEEN_INCLUDED
12
13 #include "AttributeArray.h"
14 #include <openvdb/version.h>
15 #include <openvdb/MetaMap.h>
16
17 #include <limits>
18 #include <memory>
19 #include <vector>
20
21
22 class TestAttributeSet;
23
24
25 namespace openvdb {
26 OPENVDB_USE_VERSION_NAMESPACE
27 namespace OPENVDB_VERSION_NAME {
28 namespace points {
29
30
31 using GroupType = uint8_t;
32
33
34 ////////////////////////////////////////
35
36
37 /// Ordered collection of uniquely-named attribute arrays
38 class OPENVDB_API AttributeSet
39 {
40 public:
41 enum { INVALID_POS = std::numeric_limits<size_t>::max() };
42
43 using Ptr = std::shared_ptr<AttributeSet>;
44 using ConstPtr = std::shared_ptr<const AttributeSet>;
45 using UniquePtr = std::unique_ptr<AttributeSet>;
46
47 class Descriptor;
48
49 using DescriptorPtr = std::shared_ptr<Descriptor>;
50 using DescriptorConstPtr = std::shared_ptr<const Descriptor>;
51
52 //////////
53
54 struct Util
55 {
56 /// Attribute and type name pair.
57 67248 struct NameAndType {
58 54992 NameAndType(const std::string& n, const NamePair& t, const Index s = 1)
59
1/2
✓ Branch 2 taken 54992 times.
✗ Branch 3 not taken.
54992 : name(n), type(t), stride(s) {}
60 Name name;
61 NamePair type;
62 Index stride;
63 };
64
65 using NameAndTypeVec = std::vector<NameAndType>;
66 using NameToPosMap = std::map<std::string, size_t>;
67 using GroupIndex = std::pair<size_t, uint8_t>;
68 };
69
70 //////////
71
72 AttributeSet();
73
74 /// Construct a new AttributeSet from the given AttributeSet.
75 /// @param attributeSet the old attribute set
76 /// @param arrayLength the desired length of the arrays in the new AttributeSet
77 /// @param lock an optional scoped registry lock to avoid contention
78 /// @note This constructor is typically used to resize an existing AttributeSet as
79 /// it transfers attribute metadata such as hidden and transient flags
80 AttributeSet(const AttributeSet& attributeSet, Index arrayLength,
81 const AttributeArray::ScopedRegistryLock* lock = nullptr);
82
83 /// Construct a new AttributeSet from the given Descriptor.
84 /// @param descriptor stored in the new AttributeSet and used in construction
85 /// @param arrayLength the desired length of the arrays in the new AttributeSet
86 /// @param lock an optional scoped registry lock to avoid contention
87 /// @note Descriptors do not store attribute metadata such as hidden and transient flags
88 /// which live on the AttributeArrays, so for constructing from an existing AttributeSet
89 /// use the AttributeSet(const AttributeSet&, Index) constructor instead
90 AttributeSet(const DescriptorPtr& descriptor, Index arrayLength = 1,
91 const AttributeArray::ScopedRegistryLock* lock = nullptr);
92
93 /// Shallow copy constructor, the descriptor and attribute arrays will be shared.
94 AttributeSet(const AttributeSet&);
95
96 /// Disallow copy assignment, since it wouldn't be obvious whether the copy is deep or shallow.
97 AttributeSet& operator=(const AttributeSet&) = delete;
98
99 //@{
100 /// @brief Return a reference to this attribute set's descriptor, which might
101 /// be shared with other sets.
102 Descriptor& descriptor() { return *mDescr; }
103 const Descriptor& descriptor() const { return *mDescr; }
104 //@}
105
106 /// @brief Return a pointer to this attribute set's descriptor, which might be
107 /// shared with other sets
108 DescriptorPtr descriptorPtr() const { return mDescr; }
109
110 /// Return the number of attributes in this set.
111 size_t size() const { return mAttrs.size(); }
112
113 /// Return the number of bytes of memory used by this attribute set.
114 size_t memUsage() const;
115
116 #if OPENVDB_ABI_VERSION_NUMBER >= 10
117 /// Return the number of bytes of memory used by this attribute set once it
118 /// has been deserialized (this may be different to memUsage() if delay-loading
119 /// is in use).
120 size_t memUsageIfLoaded() const;
121 #endif
122
123 /// @brief Return the position of the attribute array whose name is @a name,
124 /// or @c INVALID_POS if no match is found.
125 size_t find(const std::string& name) const;
126
127 /// @brief Replace the attribute array whose name is @a name.
128 /// @return The position of the updated attribute array or @c INVALID_POS
129 /// if the given name does not exist or if the replacement failed because
130 /// the new array type does not comply with the descriptor.
131 size_t replace(const std::string& name, const AttributeArray::Ptr&);
132
133 /// @brief Replace the attribute array stored at position @a pos in this container.
134 /// @return The position of the updated attribute array or @c INVALID_POS
135 /// if replacement failed because the new array type does not comply with
136 /// the descriptor.
137 size_t replace(size_t pos, const AttributeArray::Ptr&);
138
139 //@{
140 /// @brief Return a pointer to the attribute array whose name is @a name or
141 /// a null pointer if no match is found.
142 const AttributeArray* getConst(const std::string& name) const;
143 const AttributeArray* get(const std::string& name) const;
144 AttributeArray* get(const std::string& name);
145 //@}
146
147 //@{
148 /// @brief Return a pointer to the attribute array stored at position @a pos
149 /// in this set.
150 const AttributeArray* getConst(size_t pos) const;
151 const AttributeArray* get(size_t pos) const;
152 AttributeArray* get(size_t pos);
153 //@}
154
155 //@{
156 /// @brief Return the group offset from the name or index of the group
157 /// A group attribute array is a single byte (8-bit), each bit of which
158 /// can denote a group. The group offset is the position of the bit that
159 /// denotes the requested group if all group attribute arrays in the set
160 /// (and only attribute arrays marked as group) were to be laid out linearly
161 /// according to their order in the set.
162 size_t groupOffset(const Name& groupName) const;
163 size_t groupOffset(const Util::GroupIndex& index) const;
164 //@}
165
166 /// Return the group index from the name of the group
167 Util::GroupIndex groupIndex(const Name& groupName) const;
168 /// Return the group index from the offset of the group
169 /// @note see offset description for groupOffset()
170 Util::GroupIndex groupIndex(const size_t offset) const;
171
172 /// Return the indices of the attribute arrays which are group attribute arrays
173 std::vector<size_t> groupAttributeIndices() const;
174
175 /// Return true if the attribute array stored at position @a pos is shared.
176 bool isShared(size_t pos) const;
177 /// @brief If the attribute array stored at position @a pos is shared,
178 /// replace the array with a deep copy of itself that is not
179 /// shared with anyone else.
180 void makeUnique(size_t pos);
181
182 /// Append attribute @a attribute (simple method)
183 AttributeArray::Ptr appendAttribute(const Name& name,
184 const NamePair& type,
185 const Index strideOrTotalSize = 1,
186 const bool constantStride = true,
187 const Metadata* defaultValue = nullptr);
188
189 /// Append attribute @a attribute (descriptor-sharing)
190 /// Requires current descriptor to match @a expected
191 /// On append, current descriptor is replaced with @a replacement
192 /// Provide a @a lock object to avoid contention from appending in parallel
193 AttributeArray::Ptr appendAttribute(const Descriptor& expected, DescriptorPtr& replacement,
194 const size_t pos, const Index strideOrTotalSize = 1,
195 const bool constantStride = true,
196 const Metadata* defaultValue = nullptr,
197 const AttributeArray::ScopedRegistryLock* lock = nullptr);
198
199 /// @brief Remove and return an attribute array by name
200 /// @param name the name of the attribute array to release
201 /// @details Detaches the attribute array from this attribute set and returns it, if
202 /// @a name is invalid, returns an empty shared pointer. This also updates the descriptor
203 /// to remove the reference to the attribute array.
204 /// @note AttributeArrays are stored as shared pointers, so they are not guaranteed
205 /// to be unique. Check the reference count before blindly re-using in a new AttributeSet.
206 AttributeArray::Ptr removeAttribute(const Name& name);
207
208 /// @brief Remove and return an attribute array by index
209 /// @param pos the position index of the attribute to release
210 /// @details Detaches the attribute array from this attribute set and returns it, if
211 /// @a pos is invalid, returns an empty shared pointer. This also updates the descriptor
212 /// to remove the reference to the attribute array.
213 /// @note AttributeArrays are stored as shared pointers, so they are not guaranteed
214 /// to be unique. Check the reference count before blindly re-using in a new AttributeSet.
215 AttributeArray::Ptr removeAttribute(const size_t pos);
216
217 /// @brief Remove and return an attribute array by index (unsafe method)
218 /// @param pos the position index of the attribute to release
219 /// @details Detaches the attribute array from this attribute set and returns it, if
220 /// @a pos is invalid, returns an empty shared pointer.
221 /// In cases where the AttributeSet is due to be destroyed, a small performance
222 /// advantage can be gained by leaving the attribute array as a nullptr and not
223 /// updating the descriptor. However, this leaves the AttributeSet in an invalid
224 /// state making it unsafe to call any methods that implicitly derefence the attribute array.
225 /// @note AttributeArrays are stored as shared pointers, so they are not guaranteed
226 /// to be unique. Check the reference count before blindly re-using in a new AttributeSet.
227 /// @warning Only use this method if you're an expert and know the risks of not
228 /// updating the array of attributes or the descriptor.
229 AttributeArray::Ptr removeAttributeUnsafe(const size_t pos);
230
231 /// Drop attributes with @a pos indices (simple method)
232 /// Creates a new descriptor for this attribute set
233 void dropAttributes(const std::vector<size_t>& pos);
234
235 /// Drop attributes with @a pos indices (descriptor-sharing method)
236 /// Requires current descriptor to match @a expected
237 /// On drop, current descriptor is replaced with @a replacement
238 void dropAttributes(const std::vector<size_t>& pos,
239 const Descriptor& expected, DescriptorPtr& replacement);
240
241 /// Re-name attributes in set to match a provided descriptor
242 /// Replaces own descriptor with @a replacement
243 void renameAttributes(const Descriptor& expected, const DescriptorPtr& replacement);
244
245 /// Re order attribute set to match a provided descriptor
246 /// Replaces own descriptor with @a replacement
247 void reorderAttributes(const DescriptorPtr& replacement);
248
249 /// Replace the current descriptor with a @a replacement
250 /// Note the provided Descriptor must be identical to the replacement
251 /// unless @a allowMismatchingDescriptors is true (default is false)
252 void resetDescriptor(const DescriptorPtr& replacement, const bool allowMismatchingDescriptors = false);
253
254 /// Read the entire set from a stream.
255 void read(std::istream&);
256 /// Write the entire set to a stream.
257 /// @param outputTransient if true, write out transient attributes
258 void write(std::ostream&, bool outputTransient = false) const;
259
260 /// This will read the attribute descriptor from a stream.
261 void readDescriptor(std::istream&);
262 /// This will write the attribute descriptor to a stream.
263 /// @param outputTransient if true, write out transient attributes
264 void writeDescriptor(std::ostream&, bool outputTransient = false) const;
265
266 /// This will read the attribute metadata from a stream.
267 void readMetadata(std::istream&);
268 /// This will write the attribute metadata to a stream.
269 /// @param outputTransient if true, write out transient attributes
270 /// @param paged if true, data is written out in pages
271 void writeMetadata(std::ostream&, bool outputTransient = false, bool paged = false) const;
272
273 /// This will read the attribute data from a stream.
274 void readAttributes(std::istream&);
275 /// This will write the attribute data to a stream.
276 /// @param outputTransient if true, write out transient attributes
277 void writeAttributes(std::ostream&, bool outputTransient = false) const;
278
279 /// Compare the descriptors and attribute arrays on the attribute sets
280 /// Exit early if the descriptors do not match
281 bool operator==(const AttributeSet& other) const;
282
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
2 bool operator!=(const AttributeSet& other) const { return !this->operator==(other); }
283
284 private:
285 using AttrArrayVec = std::vector<AttributeArray::Ptr>;
286
287 DescriptorPtr mDescr;
288 AttrArrayVec mAttrs;
289 }; // class AttributeSet
290
291 ////////////////////////////////////////
292
293
294 /// A container for ABI=5 to help ease introduction of upcoming features
295 namespace future {
296 class Container
297 {
298 class Element { };
299 std::vector<std::shared_ptr<Element>> mElements;
300 };
301 }
302
303
304 ////////////////////////////////////////
305
306
307 /// @brief An immutable object that stores name, type and AttributeSet position
308 /// for a constant collection of attribute arrays.
309 /// @note The attribute name is actually mutable, but the attribute type
310 /// and position can not be changed after creation.
311 class OPENVDB_API AttributeSet::Descriptor
312 {
313 public:
314 using Ptr = std::shared_ptr<Descriptor>;
315
316 using NameAndType = Util::NameAndType;
317 using NameAndTypeVec = Util::NameAndTypeVec;
318 using GroupIndex = Util::GroupIndex;
319 using NameToPosMap = Util::NameToPosMap;
320 using ConstIterator = NameToPosMap::const_iterator;
321
322 /// Utility method to construct a NameAndType sequence.
323 7266 struct Inserter {
324 NameAndTypeVec vec;
325 Inserter& add(const NameAndType& nameAndType) {
326
1/2
✓ Branch 1 taken 7257 times.
✗ Branch 2 not taken.
7257 vec.push_back(nameAndType); return *this;
327 }
328 Inserter& add(const Name& name, const NamePair& type) {
329
21/42
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 1 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 1 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 1 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 1 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 1 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 1 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 1 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 1 times.
✗ Branch 59 not taken.
✓ Branch 61 taken 1 times.
✗ Branch 62 not taken.
22 vec.emplace_back(name, type); return *this;
330 }
331 Inserter& add(const NameAndTypeVec& other) {
332 for (NameAndTypeVec::const_iterator it = other.begin(), itEnd = other.end(); it != itEnd; ++it) {
333 vec.emplace_back(it->name, it->type);
334 }
335 return *this;
336 }
337 };
338
339 //////////
340
341 Descriptor();
342
343 /// Copy constructor
344 Descriptor(const Descriptor&);
345
346 /// Create a new descriptor from a position attribute type and assumes "P" (for convenience).
347 static Ptr create(const NamePair&);
348
349 /// Create a new descriptor as a duplicate with a new attribute appended
350 Ptr duplicateAppend(const Name& name, const NamePair& type) const;
351
352 /// Create a new descriptor as a duplicate with existing attributes dropped
353 Ptr duplicateDrop(const std::vector<size_t>& pos) const;
354
355 /// Return the number of attributes in this descriptor.
356 size_t size() const { return mTypes.size(); }
357
358 /// Return the number of attributes with this attribute type
359 size_t count(const NamePair& type) const;
360
361 /// Return the number of bytes of memory used by this attribute set.
362 size_t memUsage() const;
363
364 /// @brief Return the position of the attribute array whose name is @a name,
365 /// or @c INVALID_POS if no match is found.
366 size_t find(const std::string& name) const;
367
368 /// Rename an attribute array
369 size_t rename(const std::string& fromName, const std::string& toName);
370
371 /// Return the name of the attribute array's type.
372 const Name& valueType(size_t pos) const;
373 /// Return the name of the attribute array's type.
374 const NamePair& type(size_t pos) const;
375
376 /// Retrieve metadata map
377 MetaMap& getMetadata();
378 const MetaMap& getMetadata() const;
379
380 /// Return true if the attribute has a default value
381 bool hasDefaultValue(const Name& name) const;
382 /// Get a default value for an existing attribute
383 template<typename ValueType>
384 6 ValueType getDefaultValue(const Name& name) const
385 {
386 6 const size_t pos = find(name);
387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
6 if (pos == INVALID_POS) {
388 OPENVDB_THROW(LookupError, "Cannot find attribute name to set default value.")
389 }
390
391 12 std::stringstream ss;
392 ss << "default:" << name;
393
394
4/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1 times.
12 auto metadata = mMetadata.getMetadata<TypedMetadata<ValueType>>(ss.str());
395
396
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
6 if (metadata) return metadata->value();
397
398 return zeroVal<ValueType>();
399 }
400 /// Set a default value for an existing attribute
401 void setDefaultValue(const Name& name, const Metadata& defaultValue);
402 // Remove the default value if it exists
403 void removeDefaultValue(const Name& name);
404 // Prune any default values for which the key is no longer present
405 void pruneUnusedDefaultValues();
406
407 /// Return true if this descriptor is equal to the given one.
408 bool operator==(const Descriptor&) const;
409 /// Return true if this descriptor is not equal to the given one.
410
2/4
✓ Branch 1 taken 3950 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
48368 bool operator!=(const Descriptor& rhs) const { return !this->operator==(rhs); }
411 /// Return true if this descriptor contains the same attributes
412 /// as the given descriptor, ignoring attribute order
413 bool hasSameAttributes(const Descriptor& rhs) const;
414
415 /// Return a reference to the name-to-position map.
416
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 const NameToPosMap& map() const { return mNameMap; }
417 /// Return a reference to the name-to-position group map.
418
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 const NameToPosMap& groupMap() const { return mGroupMap; }
419
420 /// Return @c true if group exists
421 bool hasGroup(const Name& group) const;
422 /// @brief Define a group name to offset mapping
423 /// @param group group name
424 /// @param offset group offset
425 /// @param checkValidOffset throws if offset out-of-range or in-use
426 void setGroup(const Name& group, const size_t offset,
427 const bool checkValidOffset = false);
428 /// Drop any mapping keyed by group name
429 void dropGroup(const Name& group);
430 /// Clear all groups
431 void clearGroups();
432 /// Rename a group
433 size_t renameGroup(const std::string& fromName, const std::string& toName);
434 /// Return a unique name for a group based on given name
435 const Name uniqueGroupName(const Name& name) const;
436
437 //@{
438 /// @brief Return the group offset from the name or index of the group
439 /// A group attribute array is a single byte (8-bit), each bit of which
440 /// can denote a group. The group offset is the position of the bit that
441 /// denotes the requested group if all group attribute arrays in the set
442 /// (and only attribute arrays marked as group) were to be laid out linearly
443 /// according to their order in the set.
444 size_t groupOffset(const Name& groupName) const;
445 size_t groupOffset(const GroupIndex& index) const;
446 //@}
447
448 /// Return the group index from the name of the group
449 GroupIndex groupIndex(const Name& groupName) const;
450 /// Return the group index from the offset of the group
451 /// @note see offset description for groupOffset()
452 GroupIndex groupIndex(const size_t offset) const;
453
454 /// Return number of bits occupied by a group attribute array
455 static size_t groupBits() { return sizeof(GroupType) * CHAR_BIT; }
456
457 /// Return the total number of available groups
458 /// (group bits * number of group attributes)
459 size_t availableGroups() const;
460
461 /// Return the number of empty group slots which correlates to the number of groups
462 /// that can be stored without increasing the number of group attribute arrays
463 size_t unusedGroups() const;
464
465 /// Return @c true if there are sufficient empty slots to allow compacting
466 bool canCompactGroups() const;
467
468 /// @brief Return a group offset that is not in use
469 /// @param hint if provided, request a specific offset as a hint
470 /// @return index of an offset or size_t max if no available group offsets
471 size_t unusedGroupOffset(size_t hint = std::numeric_limits<size_t>::max()) const;
472
473 /// @brief Determine if a move is required to efficiently compact the data and store the
474 /// source name, offset and the target offset in the input parameters
475 /// @param sourceName source name
476 /// @param sourceOffset source offset
477 /// @param targetOffset target offset
478 /// @return @c true if move is required to compact the data
479 bool requiresGroupMove(Name& sourceName, size_t& sourceOffset, size_t& targetOffset) const;
480
481 /// @brief Test if there are any group names shared by both descriptors which
482 /// have a different index
483 /// @param rhs the descriptor to compare with
484 /// @return @c true if an index collision exists
485 bool groupIndexCollision(const Descriptor& rhs) const;
486
487 /// Return a unique name for an attribute array based on given name
488 const Name uniqueName(const Name& name) const;
489
490 /// Return true if the name is valid
491 static bool validName(const Name& name);
492
493 /// @brief Extract each name from @a nameStr into @a includeNames, or into @a excludeNames
494 /// if the name is prefixed with a caret.
495 /// @param nameStr the input string of names
496 /// @param includeNames on exit, the list of names that are not prefixed with a caret
497 /// @param excludeNames on exit, the list of names that are prefixed with a caret
498 /// @param includeAll on exit, @c true if a "*" wildcard is present in the @a includeNames
499 static void parseNames( std::vector<std::string>& includeNames,
500 std::vector<std::string>& excludeNames,
501 bool& includeAll,
502 const std::string& nameStr);
503
504 /// @brief Extract each name from @a nameStr into @a includeNames, or into @a excludeNames
505 /// if the name is prefixed with a caret.
506 static void parseNames( std::vector<std::string>& includeNames,
507 std::vector<std::string>& excludeNames,
508 const std::string& nameStr);
509
510 /// Serialize this descriptor to the given stream.
511 void write(std::ostream&) const;
512 /// Unserialize this transform from the given stream.
513 void read(std::istream&);
514
515 protected:
516 /// Append to a vector of names and types from this Descriptor in position order
517 void appendTo(NameAndTypeVec& attrs) const;
518
519 /// Create a new descriptor from the given attribute and type name pairs
520 /// and copy the group maps and metamap.
521 static Ptr create(const NameAndTypeVec&, const NameToPosMap&, const MetaMap&);
522
523 size_t insert(const std::string& name, const NamePair& typeName);
524
525 private:
526 friend class ::TestAttributeSet;
527
528 NameToPosMap mNameMap;
529 std::vector<NamePair> mTypes;
530 NameToPosMap mGroupMap;
531 MetaMap mMetadata;
532 // as this change is part of an ABI change, there's no good reason to reduce the reserved
533 // space aside from keeping the memory size of an AttributeSet the same for convenience
534 // (note that this assumes a typical three-pointer implementation for std::vector)
535 future::Container mFutureContainer; // occupies 3 reserved slots
536 int64_t mReserved[5]; // for future use
537 }; // class Descriptor
538
539 } // namespace points
540 } // namespace OPENVDB_VERSION_NAME
541 } // namespace openvdb
542
543 #endif // OPENVDB_POINTS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED
544