1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file points/AttributeArray.h
5 ///
6 /// @authors Dan Bailey, Mihai Alden, Nick Avramoussis, James Bird, Khang Ngo
7 ///
8 /// @brief  Attribute Array storage templated on type and compression codec.
9 
10 #ifndef OPENVDB_POINTS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED
11 #define OPENVDB_POINTS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED
12 
13 #include <openvdb/Types.h>
14 #include <openvdb/math/QuantizedUnitVec.h>
15 #include <openvdb/util/Name.h>
16 #include <openvdb/util/logging.h>
17 #include <openvdb/io/io.h> // MappedFile
18 #include <openvdb/io/Compression.h> // COMPRESS_BLOSC
19 
20 #include "IndexIterator.h"
21 #include "StreamCompression.h"
22 
23 #include <tbb/spin_mutex.h>
24 #include <atomic>
25 
26 #include <memory>
27 #include <mutex>
28 #include <string>
29 #include <type_traits>
30 
31 
32 class TestAttributeArray;
33 
34 namespace openvdb {
35 OPENVDB_USE_VERSION_NAMESPACE
36 namespace OPENVDB_VERSION_NAME {
37 
38 
39 using NamePair = std::pair<Name, Name>;
40 
41 namespace points {
42 
43 
44 ////////////////////////////////////////
45 
46 // Utility methods
47 
48 template <typename IntegerT, typename FloatT>
49 inline IntegerT
floatingPointToFixedPoint(const FloatT s)50 floatingPointToFixedPoint(const FloatT s)
51 {
52     static_assert(std::is_unsigned<IntegerT>::value, "IntegerT must be unsigned");
53     if (FloatT(0.0) > s) return std::numeric_limits<IntegerT>::min();
54     else if (FloatT(1.0) <= s) return std::numeric_limits<IntegerT>::max();
55     return IntegerT(s * FloatT(std::numeric_limits<IntegerT>::max()));
56 }
57 
58 
59 template <typename FloatT, typename IntegerT>
60 inline FloatT
fixedPointToFloatingPoint(const IntegerT s)61 fixedPointToFloatingPoint(const IntegerT s)
62 {
63     static_assert(std::is_unsigned<IntegerT>::value, "IntegerT must be unsigned");
64     return FloatT(s) / FloatT((std::numeric_limits<IntegerT>::max()));
65 }
66 
67 template <typename IntegerVectorT, typename FloatT>
68 inline IntegerVectorT
floatingPointToFixedPoint(const math::Vec3<FloatT> & v)69 floatingPointToFixedPoint(const math::Vec3<FloatT>& v)
70 {
71     return IntegerVectorT(
72         floatingPointToFixedPoint<typename IntegerVectorT::ValueType>(v.x()),
73         floatingPointToFixedPoint<typename IntegerVectorT::ValueType>(v.y()),
74         floatingPointToFixedPoint<typename IntegerVectorT::ValueType>(v.z()));
75 }
76 
77 template <typename FloatVectorT, typename IntegerT>
78 inline FloatVectorT
fixedPointToFloatingPoint(const math::Vec3<IntegerT> & v)79 fixedPointToFloatingPoint(const math::Vec3<IntegerT>& v)
80 {
81     return FloatVectorT(
82         fixedPointToFloatingPoint<typename FloatVectorT::ValueType>(v.x()),
83         fixedPointToFloatingPoint<typename FloatVectorT::ValueType>(v.y()),
84         fixedPointToFloatingPoint<typename FloatVectorT::ValueType>(v.z()));
85 }
86 
87 
88 ////////////////////////////////////////
89 
90 
91 /// Base class for storing attribute data
92 class OPENVDB_API AttributeArray
93 {
94 protected:
95     struct AccessorBase;
96     template <typename T> struct Accessor;
97 
98     using AccessorBasePtr = std::shared_ptr<AccessorBase>;
99 
100 public:
101     enum Flag {
102         TRANSIENT = 0x1,            /// by default not written to disk
103         HIDDEN = 0x2,               /// hidden from UIs or iterators
104         CONSTANTSTRIDE = 0x8,       /// stride size does not vary in the array
105         STREAMING = 0x10,           /// streaming mode collapses attributes when first accessed
106         PARTIALREAD = 0x20          /// data has been partially read (compressed bytes is used)
107     };
108 
109     enum SerializationFlag {
110         WRITESTRIDED = 0x1,         /// data is marked as strided when written
111         WRITEUNIFORM = 0x2,         /// data is marked as uniform when written
112         WRITEMEMCOMPRESS = 0x4,     /// data is marked as compressed in-memory when written
113                                     /// (deprecated flag as of ABI=6)
114         WRITEPAGED = 0x8            /// data is written out in pages
115     };
116 
117     // Scoped Lock wrapper class that locks the AttributeArray registry mutex
118     class OPENVDB_API ScopedRegistryLock
119     {
120         tbb::spin_mutex::scoped_lock lock;
121     public:
122         ScopedRegistryLock();
123     }; // class ScopedRegistryLock
124 
125     using Ptr           = std::shared_ptr<AttributeArray>;
126     using ConstPtr      = std::shared_ptr<const AttributeArray>;
127 
128     using FactoryMethod = Ptr (*)(Index, Index, bool, const Metadata*);
129 
130     template <typename ValueType, typename CodecType> friend class AttributeHandle;
131 
AttributeArray()132     AttributeArray(): mPageHandle() { mOutOfCore = 0; }
~AttributeArray()133     virtual ~AttributeArray()
134     {
135         // if this AttributeArray has been partially read, zero the compressed bytes,
136         // so the page handle won't attempt to clean up invalid memory
137         if (mFlags & PARTIALREAD)       mCompressedBytes = 0;
138     }
139     AttributeArray(const AttributeArray& rhs);
140     AttributeArray& operator=(const AttributeArray& rhs);
141     AttributeArray(AttributeArray&&) = delete;
142     AttributeArray& operator=(AttributeArray&&) = delete;
143 
144     /// Return a copy of this attribute.
145     virtual AttributeArray::Ptr copy() const = 0;
146 
147     /// Return a copy of this attribute.
148 #ifndef _MSC_VER
149     OPENVDB_DEPRECATED_MESSAGE("In-memory compression no longer supported, use AttributeArray::copy() instead")
150 #endif
151     virtual AttributeArray::Ptr copyUncompressed() const = 0;
152 
153     /// Return the number of elements in this array.
154     /// @note This does not count each data element in a strided array
155     virtual Index size() const = 0;
156 
157     /// Return the stride of this array.
158     /// @note a return value of zero means a non-constant stride
159     virtual Index stride() const = 0;
160 
161     /// Return the total number of data elements in this array.
162     /// @note This counts each data element in a strided array
163     virtual Index dataSize() const = 0;
164 
165     /// Return the name of the value type of a single element in this array (e.g., "float" or "vec3d").
166     virtual Name valueType() const = 0;
167 
168     /// Return the name of the codec used by this array (e.g., "trnc" or "fxpt").
169     virtual Name codecType() const = 0;
170 
171     /// Return the size in bytes of the value type of a single element in this array.
172     /// (e.g. "float" -> 4 bytes, "vec3d" -> 24 bytes").
173     virtual Index valueTypeSize() const = 0;
174 
175     /// Return the size in bytes of the storage type of a single element of this array.
176     /// @note If the Codec is a NullCodec, valueSize() == storageSize()
177     virtual Index storageTypeSize() const = 0;
178 
179     /// Return @c true if the value type is floating point
180     virtual bool valueTypeIsFloatingPoint() const = 0;
181 
182     /// Return @c true if the value type is a class (ie vector, matrix or quaternion return true)
183     virtual bool valueTypeIsClass() const = 0;
184 
185     /// Return @c true if the value type is a vector
186     virtual bool valueTypeIsVector() const = 0;
187 
188     /// Return @c true if the value type is a quaternion
189     virtual bool valueTypeIsQuaternion() const = 0;
190 
191     /// Return @c true if the value type is a matrix
192     virtual bool valueTypeIsMatrix() const = 0;
193 
194     /// Return the number of bytes of memory used by this attribute.
195     virtual size_t memUsage() const = 0;
196 
197     /// Create a new attribute array of the given (registered) type, length and stride.
198     /// @details If @a lock is non-null, the AttributeArray registry mutex
199     /// has already been locked
200     static Ptr create(const NamePair& type, Index length, Index stride = 1,
201         bool constantStride = true,
202         const Metadata* metadata = nullptr,
203         const ScopedRegistryLock* lock = nullptr);
204 
205     /// Return @c true if the given attribute type name is registered.
206     static bool isRegistered(const NamePair& type, const ScopedRegistryLock* lock = nullptr);
207     /// Clear the attribute type registry.
208     static void clearRegistry(const ScopedRegistryLock* lock = nullptr);
209 
210     /// Return the name of this attribute's type.
211     virtual const NamePair& type() const = 0;
212     /// Return @c true if this attribute is of the same type as the template parameter.
213     template<typename AttributeArrayType>
isType()214     bool isType() const { return this->type() == AttributeArrayType::attributeType(); }
215 
216     /// Return @c true if this attribute has a value type the same as the template parameter
217     template<typename ValueType>
hasValueType()218     bool hasValueType() const { return this->type().first == typeNameAsString<ValueType>(); }
219 
220     /// @brief Set value at given index @a n from @a sourceIndex of another @a sourceArray.
221     // Windows does not allow base classes to be easily deprecated.
222 #ifndef _MSC_VER
223     OPENVDB_DEPRECATED_MESSAGE("Use copyValues() with source-target index pairs")
224 #endif
225     virtual void set(const Index n, const AttributeArray& sourceArray, const Index sourceIndex) = 0;
226 
227     /// @brief Copy values into this array from a source array to a target array
228     /// as referenced by an iterator.
229     /// @details Iterators must adhere to the ForwardIterator interface described
230     /// in the example below:
231     /// @code
232     /// struct MyIterator
233     /// {
234     ///     // returns true if the iterator is referencing valid copying indices
235     ///     operator bool() const;
236     ///     // increments the iterator
237     ///     MyIterator& operator++();
238     ///     // returns the source index that the iterator is referencing for copying
239     ///     Index sourceIndex() const;
240     ///     // returns the target index that the iterator is referencing for copying
241     ///     Index targetIndex() const;
242     /// };
243     /// @endcode
244     /// @note It is assumed that the strided storage sizes match, the arrays are both in-core,
245     /// and both value types are floating-point or both integer.
246     /// @note It is possible to use this method to write to a uniform target array
247     /// if the iterator does not have non-zero target indices.
248     /// @note This method is not thread-safe, it must be guaranteed that this array is not
249     /// concurrently modified by another thread and that the source array is also not modified.
250     template<typename IterT>
251     void copyValuesUnsafe(const AttributeArray& sourceArray, const IterT& iter);
252     /// @brief Like copyValuesUnsafe(), but if @a compact is true, attempt to collapse this array.
253     /// @note This method is not thread-safe, it must be guaranteed that this array is not
254     /// concurrently modified by another thread and that the source array is also not modified.
255     template<typename IterT>
256     void copyValues(const AttributeArray& sourceArray, const IterT& iter, bool compact = true);
257 
258     /// Return @c true if this array is stored as a single uniform value.
259     virtual bool isUniform() const = 0;
260     /// @brief  If this array is uniform, replace it with an array of length size().
261     /// @param  fill if true, assign the uniform value to each element of the array.
262     virtual void expand(bool fill = true) = 0;
263     /// Replace the existing array with a uniform zero value.
264     virtual void collapse() = 0;
265     /// Compact the existing array to become uniform if all values are identical
266     virtual bool compact() = 0;
267 
268     // Windows does not allow base classes to be deprecated
269 #ifndef _MSC_VER
270     OPENVDB_DEPRECATED_MESSAGE("Previously this compressed the attribute array, now it does nothing")
271 #endif
272     virtual bool compress() = 0;
273     // Windows does not allow base classes to be deprecated
274 #ifndef _MSC_VER
275     OPENVDB_DEPRECATED_MESSAGE("Previously this uncompressed the attribute array, now it does nothing")
276 #endif
277     virtual bool decompress() = 0;
278 
279     /// @brief   Specify whether this attribute should be hidden (e.g., from UI or iterators).
280     /// @details This is useful if the attribute is used for blind data or as scratch space
281     ///          for a calculation.
282     /// @note    Attributes are not hidden by default.
283     void setHidden(bool state);
284     /// Return @c true if this attribute is hidden (e.g., from UI or iterators).
isHidden()285     bool isHidden() const { return bool(mFlags & HIDDEN); }
286 
287     /// @brief Specify whether this attribute should only exist in memory
288     ///        and not be serialized during stream output.
289     /// @note  Attributes are not transient by default.
290     void setTransient(bool state);
291     /// Return @c true if this attribute is not serialized during stream output.
isTransient()292     bool isTransient() const { return bool(mFlags & TRANSIENT); }
293 
294     /// @brief Specify whether this attribute is to be streamed off disk, in which
295     ///        case, the attributes are collapsed after being first loaded leaving them
296     ///        in a destroyed state.
297     ///  @note This operation is not thread-safe.
298     void setStreaming(bool state);
299     /// Return @c true if this attribute is in streaming mode.
isStreaming()300     bool isStreaming() const { return bool(mFlags & STREAMING); }
301 
302     /// Return @c true if this attribute has a constant stride
hasConstantStride()303     bool hasConstantStride() const { return bool(mFlags & CONSTANTSTRIDE); }
304 
305     /// @brief Retrieve the attribute array flags
flags()306     uint8_t flags() const { return mFlags; }
307 
308     /// Read attribute metadata and buffers from a stream.
309     virtual void read(std::istream&) = 0;
310     /// Write attribute metadata and buffers to a stream.
311     /// @param outputTransient if true, write out transient attributes
312     virtual void write(std::ostream&, bool outputTransient) const = 0;
313     /// Write attribute metadata and buffers to a stream, don't write transient attributes.
314     virtual void write(std::ostream&) const = 0;
315 
316     /// Read attribute metadata from a stream.
317     virtual void readMetadata(std::istream&) = 0;
318     /// Write attribute metadata to a stream.
319     /// @param outputTransient if true, write out transient attributes
320     /// @param paged           if true, data is written out in pages
321     virtual void writeMetadata(std::ostream&, bool outputTransient, bool paged) const = 0;
322 
323     /// Read attribute buffers from a stream.
324     virtual void readBuffers(std::istream&) = 0;
325     /// Write attribute buffers to a stream.
326     /// @param outputTransient if true, write out transient attributes
327     virtual void writeBuffers(std::ostream&, bool outputTransient) const = 0;
328 
329     /// Read attribute buffers from a paged stream.
330     virtual void readPagedBuffers(compression::PagedInputStream&) = 0;
331     /// Write attribute buffers to a paged stream.
332     /// @param outputTransient if true, write out transient attributes
333     virtual void writePagedBuffers(compression::PagedOutputStream&, bool outputTransient) const = 0;
334 
335     /// Ensures all data is in-core
336     virtual void loadData() const = 0;
337 
338     /// Return @c true if all data has been loaded
339     virtual bool isDataLoaded() const = 0;
340 
341     /// Check the compressed bytes and flags. If they are equal, perform a deeper
342     /// comparison check necessary on the inherited types (TypedAttributeArray)
343     /// Requires non operator implementation due to inheritance
344     bool operator==(const AttributeArray& other) const;
345     bool operator!=(const AttributeArray& other) const { return !this->operator==(other); }
346 
347 #if OPENVDB_ABI_VERSION_NUMBER >= 9
348     /// Indirect virtual function to retrieve the data buffer cast to a char byte array
constDataAsByteArray()349     const char* constDataAsByteArray() const { return this->dataAsByteArray(); }
350 #endif
351 
352 private:
353     friend class ::TestAttributeArray;
354 
355     /// Virtual function used by the comparison operator to perform
356     /// comparisons on inherited types
357     virtual bool isEqual(const AttributeArray& other) const = 0;
358 
359     /// Virtual function to retrieve the data buffer cast to a char byte array
360     virtual char* dataAsByteArray() = 0;
361     virtual const char* dataAsByteArray() const = 0;
362 
363     /// Private implementation for copyValues/copyValuesUnsafe
364     template <typename IterT>
365     void doCopyValues(const AttributeArray& sourceArray, const IterT& iter,
366         bool rangeChecking = true);
367 
368 protected:
369 #if OPENVDB_ABI_VERSION_NUMBER >= 7
370     AttributeArray(const AttributeArray& rhs, const tbb::spin_mutex::scoped_lock&);
371 #endif
372 
373     /// @brief Specify whether this attribute has a constant stride or not.
374     void setConstantStride(bool state);
375 
376     /// Obtain an Accessor that stores getter and setter functors.
377     virtual AccessorBasePtr getAccessor() const = 0;
378 
379     /// Register a attribute type along with a factory function.
380     static void registerType(const NamePair& type, FactoryMethod,
381         const ScopedRegistryLock* lock = nullptr);
382     /// Remove a attribute type from the registry.
383     static void unregisterType(const NamePair& type,
384         const ScopedRegistryLock* lock = nullptr);
385 
386     bool mIsUniform = true;
387     mutable tbb::spin_mutex mMutex;
388     uint8_t mFlags = 0;
389     uint8_t mUsePagedRead = 0;
390     std::atomic<Index32> mOutOfCore; // interpreted as bool
391     /// used for out-of-core, paged reading
392     union {
393         compression::PageHandle::Ptr mPageHandle;
394         size_t mCompressedBytes;
395     };
396 }; // class AttributeArray
397 
398 
399 ////////////////////////////////////////
400 
401 
402 /// Accessor base class for AttributeArray storage where type is not available
403 struct AttributeArray::AccessorBase { virtual ~AccessorBase() = default; };
404 
405 /// Templated Accessor stores typed function pointers used in binding
406 /// AttributeHandles
407 template <typename T>
408 struct AttributeArray::Accessor : public AttributeArray::AccessorBase
409 {
410     using GetterPtr = T (*)(const AttributeArray* array, const Index n);
411     using SetterPtr = void (*)(AttributeArray* array, const Index n, const T& value);
412     using ValuePtr  = void (*)(AttributeArray* array, const T& value);
413 
AccessorAccessor414     Accessor(GetterPtr getter, SetterPtr setter, ValuePtr collapser, ValuePtr filler) :
415         mGetter(getter), mSetter(setter), mCollapser(collapser), mFiller(filler) { }
416 
417     GetterPtr mGetter;
418     SetterPtr mSetter;
419     ValuePtr  mCollapser;
420     ValuePtr  mFiller;
421 }; // struct AttributeArray::Accessor
422 
423 
424 ////////////////////////////////////////
425 
426 
427 namespace attribute_traits
428 {
429     template <typename T> struct TruncateTrait { };
430     template <> struct TruncateTrait<float> { using Type = math::half; };
431     template <> struct TruncateTrait<int> { using Type = short; };
432 
433     template <typename T> struct TruncateTrait<math::Vec3<T>> {
434         using Type = math::Vec3<typename TruncateTrait<T>::Type>;
435     };
436 
437     template <bool OneByte, typename T> struct UIntTypeTrait { };
438     template<typename T> struct UIntTypeTrait</*OneByte=*/true, T> { using Type = uint8_t; };
439     template<typename T> struct UIntTypeTrait</*OneByte=*/false, T> { using Type = uint16_t; };
440     template<typename T> struct UIntTypeTrait</*OneByte=*/true, math::Vec3<T>> {
441         using Type = math::Vec3<uint8_t>;
442     };
443     template<typename T> struct UIntTypeTrait</*OneByte=*/false, math::Vec3<T>> {
444         using Type = math::Vec3<uint16_t>;
445     };
446 }
447 
448 
449 ////////////////////////////////////////
450 
451 
452 // Attribute codec schemes
453 
454 struct UnknownCodec { };
455 
456 
457 struct NullCodec
458 {
459     template <typename T>
460     struct Storage { using Type = T; };
461 
462     template<typename ValueType> static void decode(const ValueType&, ValueType&);
463     template<typename ValueType> static void encode(const ValueType&, ValueType&);
464     static const char* name() { return "null"; }
465 };
466 
467 
468 struct TruncateCodec
469 {
470     template <typename T>
471     struct Storage { using Type = typename attribute_traits::TruncateTrait<T>::Type; };
472 
473     template<typename StorageType, typename ValueType> static void decode(const StorageType&, ValueType&);
474     template<typename StorageType, typename ValueType> static void encode(const ValueType&, StorageType&);
475     static const char* name() { return "trnc"; }
476 };
477 
478 
479 // Fixed-point codec range for voxel-space positions [-0.5,0.5]
480 struct PositionRange
481 {
482     static const char* name() { return "fxpt"; }
483     template <typename ValueType> static ValueType encode(const ValueType& value) { return value + ValueType(0.5); }
484     template <typename ValueType> static ValueType decode(const ValueType& value) { return value - ValueType(0.5); }
485 };
486 
487 
488 // Fixed-point codec range for unsigned values in the unit range [0.0,1.0]
489 struct UnitRange
490 {
491     static const char* name() { return "ufxpt"; }
492     template <typename ValueType> static ValueType encode(const ValueType& value) { return value; }
493     template <typename ValueType> static ValueType decode(const ValueType& value) { return value; }
494 };
495 
496 
497 template <bool OneByte, typename Range=PositionRange>
498 struct FixedPointCodec
499 {
500     template <typename T>
501     struct Storage { using Type = typename attribute_traits::UIntTypeTrait<OneByte, T>::Type; };
502 
503     template<typename StorageType, typename ValueType> static void decode(const StorageType&, ValueType&);
504     template<typename StorageType, typename ValueType> static void encode(const ValueType&, StorageType&);
505 
506     static const char* name() {
507         static const std::string Name = std::string(Range::name()) + (OneByte ? "8" : "16");
508         return Name.c_str();
509     }
510 };
511 
512 
513 struct UnitVecCodec
514 {
515     using StorageType = uint16_t;
516 
517     template <typename T>
518     struct Storage { using Type = StorageType; };
519 
520     template<typename T> static void decode(const StorageType&, math::Vec3<T>&);
521     template<typename T> static void encode(const math::Vec3<T>&, StorageType&);
522     static const char* name() { return "uvec"; }
523 };
524 
525 
526 ////////////////////////////////////////
527 
528 
529 /// Typed class for storing attribute data
530 
531 template<typename ValueType_, typename Codec_ = NullCodec>
532 class TypedAttributeArray final: public AttributeArray
533 {
534 public:
535     using Ptr           = std::shared_ptr<TypedAttributeArray>;
536     using ConstPtr      = std::shared_ptr<const TypedAttributeArray>;
537 
538     using ValueType     = ValueType_;
539     using Codec         = Codec_;
540     using StorageType   = typename Codec::template Storage<ValueType>::Type;
541 
542     //////////
543 
544     /// Default constructor, always constructs a uniform attribute.
545     explicit TypedAttributeArray(Index n = 1, Index strideOrTotalSize = 1, bool constantStride = true,
546         const ValueType& uniformValue = zeroVal<ValueType>());
547 #if OPENVDB_ABI_VERSION_NUMBER >= 7
548     /// Deep copy constructor.
549     /// @note This method is thread-safe (as of ABI=7) for concurrently reading from the
550     /// source attribute array while being deep-copied. Specifically, this means that the
551     /// attribute array being deep-copied can be out-of-core and safely loaded in one thread
552     /// while being copied using this copy-constructor in another thread.
553     /// It is not thread-safe for write.
554     TypedAttributeArray(const TypedAttributeArray&);
555     /// Deep copy constructor.
556     OPENVDB_DEPRECATED_MESSAGE("Use copy-constructor without unused bool parameter")
557     TypedAttributeArray(const TypedAttributeArray&, bool /*unused*/);
558 #else
559     /// Deep copy constructor.
560     /// @note This method is not thread-safe for reading or writing, use
561     /// TypedAttributeArray::copy() to ensure thread-safety when reading concurrently.
562     TypedAttributeArray(const TypedAttributeArray&, bool uncompress = false);
563 #endif
564     /// Deep copy assignment operator.
565     /// @note this operator is thread-safe.
566     TypedAttributeArray& operator=(const TypedAttributeArray&);
567     /// Move constructor disabled.
568     TypedAttributeArray(TypedAttributeArray&&) = delete;
569     /// Move assignment operator disabled.
570     TypedAttributeArray& operator=(TypedAttributeArray&&) = delete;
571 
572     ~TypedAttributeArray() override { this->deallocate(); }
573 
574     /// Return a copy of this attribute.
575     /// @note This method is thread-safe.
576     AttributeArray::Ptr copy() const override;
577 
578     /// Return a copy of this attribute.
579     /// @note This method is thread-safe.
580     OPENVDB_DEPRECATED_MESSAGE("In-memory compression no longer supported, use AttributeArray::copy() instead")
581     AttributeArray::Ptr copyUncompressed() const override;
582 
583     /// Return a new attribute array of the given length @a n and @a stride with uniform value zero.
584     static Ptr create(Index n, Index strideOrTotalSize = 1, bool constantStride = true,
585         const Metadata* metadata = nullptr);
586 
587     /// Cast an AttributeArray to TypedAttributeArray<T>
588     static TypedAttributeArray& cast(AttributeArray& attributeArray);
589 
590     /// Cast an AttributeArray to TypedAttributeArray<T>
591     static const TypedAttributeArray& cast(const AttributeArray& attributeArray);
592 
593     /// Return the name of this attribute's type (includes codec)
594     static const NamePair& attributeType();
595     /// Return the name of this attribute's type.
596     const NamePair& type() const override { return attributeType(); }
597 
598     /// Return @c true if this attribute type is registered.
599     static bool isRegistered();
600     /// Register this attribute type along with a factory function.
601     static void registerType();
602     /// Remove this attribute type from the registry.
603     static void unregisterType();
604 
605     /// Return the number of elements in this array.
606     Index size() const override { return mSize; }
607 
608     /// Return the stride of this array.
609     /// @note A return value of zero means a variable stride
610     Index stride() const override { return hasConstantStride() ? mStrideOrTotalSize : 0; }
611 
612     /// Return the size of the data in this array.
613     Index dataSize() const override {
614         return hasConstantStride() ? mSize * mStrideOrTotalSize : mStrideOrTotalSize;
615     }
616 
617     /// Return the name of the value type of a single element in this array (e.g., "float" or "vec3d").
618     Name valueType() const override { return typeNameAsString<ValueType>(); }
619 
620     /// Return the name of the codec used by this array (e.g., "trnc" or "fxpt").
621     Name codecType() const override { return Codec::name(); }
622 
623     /// Return the size in bytes of the value type of a single element in this array.
624     Index valueTypeSize() const override { return sizeof(ValueType); }
625 
626     /// Return the size in bytes of the storage type of a single element of this array.
627     /// @note If the Codec is a NullCodec, valueSize() == storageSize()
628     Index storageTypeSize() const override { return sizeof(StorageType); }
629 
630     /// Return @c true if the value type is floating point
631     bool valueTypeIsFloatingPoint() const override;
632 
633     /// Return @c true if the value type is a class (ie vector, matrix or quaternion return true)
634     bool valueTypeIsClass() const override;
635 
636     /// Return @c true if the value type is a vector
637     bool valueTypeIsVector() const override;
638 
639     /// Return @c true if the value type is a quaternion
640     bool valueTypeIsQuaternion() const override;
641 
642     /// Return @c true if the value type is a matrix
643     bool valueTypeIsMatrix() const override;
644 
645     /// Return the number of bytes of memory used by this attribute.
646     size_t memUsage() const override;
647 
648     /// Return the value at index @a n (assumes in-core)
649     ValueType getUnsafe(Index n) const;
650     /// Return the value at index @a n
651     ValueType get(Index n) const;
652     /// Return the @a value at index @a n (assumes in-core)
653     template<typename T> void getUnsafe(Index n, T& value) const;
654     /// Return the @a value at index @a n
655     template<typename T> void get(Index n, T& value) const;
656 
657     /// Non-member equivalent to getUnsafe() that static_casts array to this TypedAttributeArray
658     /// (assumes in-core)
659     static ValueType getUnsafe(const AttributeArray* array, const Index n);
660 
661     /// Set @a value at the given index @a n (assumes in-core)
662     void setUnsafe(Index n, const ValueType& value);
663     /// Set @a value at the given index @a n
664     void set(Index n, const ValueType& value);
665     /// Set @a value at the given index @a n (assumes in-core)
666     template<typename T> void setUnsafe(Index n, const T& value);
667     /// Set @a value at the given index @a n
668     template<typename T> void set(Index n, const T& value);
669 
670     /// Non-member equivalent to setUnsafe() that static_casts array to this TypedAttributeArray
671     /// (assumes in-core)
672     static void setUnsafe(AttributeArray* array, const Index n, const ValueType& value);
673 
674     /// Set value at given index @a n from @a sourceIndex of another @a sourceArray
675     OPENVDB_DEPRECATED_MESSAGE("Use copyValues() with source-target index pairs")
676     void set(const Index n, const AttributeArray& sourceArray, const Index sourceIndex) override;
677 
678     /// Return @c true if this array is stored as a single uniform value.
679     bool isUniform() const override { return mIsUniform; }
680     /// @brief  Replace the single value storage with an array of length size().
681     /// @note   Non-uniform attributes are unchanged.
682     /// @param  fill toggle to initialize the array elements with the pre-expanded value.
683     void expand(bool fill = true) override;
684     /// Replace the existing array with a uniform zero value.
685     void collapse() override;
686     /// Compact the existing array to become uniform if all values are identical
687     bool compact() override;
688 
689     /// Replace the existing array with the given uniform value.
690     void collapse(const ValueType& uniformValue);
691     /// @brief Fill the existing array with the given value.
692     /// @note Identical to collapse() except a non-uniform array will not become uniform.
693     void fill(const ValueType& value);
694 
695     /// Non-member equivalent to collapse() that static_casts array to this TypedAttributeArray
696     static void collapse(AttributeArray* array, const ValueType& value);
697     /// Non-member equivalent to fill() that static_casts array to this TypedAttributeArray
698     static void fill(AttributeArray* array, const ValueType& value);
699 
700     /// Compress the attribute array.
701     OPENVDB_DEPRECATED_MESSAGE("Previously this compressed the attribute array, now it does nothing")
702     bool compress() override;
703     /// Uncompress the attribute array.
704     OPENVDB_DEPRECATED_MESSAGE("Previously this uncompressed the attribute array, now it does nothing")
705     bool decompress() override;
706 
707     /// Read attribute data from a stream.
708     void read(std::istream&) override;
709     /// Write attribute data to a stream.
710     /// @param os              the output stream
711     /// @param outputTransient if true, write out transient attributes
712     void write(std::ostream& os, bool outputTransient) const override;
713     /// Write attribute data to a stream, don't write transient attributes.
714     void write(std::ostream&) const override;
715 
716     /// Read attribute metadata from a stream.
717     void readMetadata(std::istream&) override;
718     /// Write attribute metadata to a stream.
719     /// @param os              the output stream
720     /// @param outputTransient if true, write out transient attributes
721     /// @param paged           if true, data is written out in pages
722     void writeMetadata(std::ostream& os, bool outputTransient, bool paged) const override;
723 
724     /// Read attribute buffers from a stream.
725     void readBuffers(std::istream&) override;
726     /// Write attribute buffers to a stream.
727     /// @param os              the output stream
728     /// @param outputTransient if true, write out transient attributes
729     void writeBuffers(std::ostream& os, bool outputTransient) const override;
730 
731     /// Read attribute buffers from a paged stream.
732     void readPagedBuffers(compression::PagedInputStream&) override;
733     /// Write attribute buffers to a paged stream.
734     /// @param os              the output stream
735     /// @param outputTransient if true, write out transient attributes
736     void writePagedBuffers(compression::PagedOutputStream& os, bool outputTransient) const override;
737 
738     /// Return @c true if this buffer's values have not yet been read from disk.
739     inline bool isOutOfCore() const;
740 
741     /// Ensures all data is in-core
742     void loadData() const override;
743 
744     /// Return @c true if all data has been loaded
745     bool isDataLoaded() const override;
746 
747 #if OPENVDB_ABI_VERSION_NUMBER >= 9
748     /// Return the raw data buffer
749     inline const StorageType* constData() const { return this->data(); }
750 #endif
751 
752 protected:
753     AccessorBasePtr getAccessor() const override;
754 
755     /// Return the raw data buffer
756     inline StorageType* data() { assert(validData()); return mData.get(); }
757     inline const StorageType* data() const { assert(validData()); return mData.get(); }
758 
759     /// Verify that data is not out-of-core or in a partially-read state
760     inline bool validData() const { return !(isOutOfCore() || (flags() & PARTIALREAD)); }
761 
762 private:
763     friend class ::TestAttributeArray;
764 
765 #if OPENVDB_ABI_VERSION_NUMBER >= 7
766     TypedAttributeArray(const TypedAttributeArray&, const tbb::spin_mutex::scoped_lock&);
767 #endif
768 
769     /// Load data from memory-mapped file.
770     inline void doLoad() const;
771     /// Load data from memory-mapped file (unsafe as this function is not protected by a mutex).
772     /// @param compression parameter no longer used
773     inline void doLoadUnsafe(const bool compression = true) const;
774     /// Compress in-core data assuming mutex is locked
775     inline bool compressUnsafe();
776 
777     /// Toggle out-of-core state
778     inline void setOutOfCore(const bool);
779 
780     /// Compare the this data to another attribute array. Used by the base class comparison operator
781     bool isEqual(const AttributeArray& other) const override;
782 
783     /// Virtual function to retrieve the data buffer from the derived class cast to a char byte array
784     char* dataAsByteArray() override;
785     const char* dataAsByteArray() const override;
786 
787     size_t arrayMemUsage() const;
788     void allocate();
789     void deallocate();
790 
791     /// Helper function for use with registerType()
792     static AttributeArray::Ptr factory(Index n, Index strideOrTotalSize, bool constantStride,
793         const Metadata* metadata) {
794         return TypedAttributeArray::create(n, strideOrTotalSize, constantStride, metadata);
795     }
796 
797     static std::unique_ptr<const NamePair> sTypeName;
798     std::unique_ptr<StorageType[]>      mData;
799     Index                               mSize;
800     Index                               mStrideOrTotalSize;
801 }; // class TypedAttributeArray
802 
803 
804 ////////////////////////////////////////
805 
806 
807 /// AttributeHandles provide access to specific TypedAttributeArray methods without needing
808 /// to know the compression codec, however these methods also incur the cost of a function pointer
809 template <typename ValueType, typename CodecType = UnknownCodec>
810 class AttributeHandle
811 {
812 public:
813     using Handle    = AttributeHandle<ValueType, CodecType>;
814     using Ptr       = std::shared_ptr<Handle>;
815     using UniquePtr = std::unique_ptr<Handle>;
816 
817 protected:
818     using GetterPtr = ValueType (*)(const AttributeArray* array, const Index n);
819     using SetterPtr = void (*)(AttributeArray* array, const Index n, const ValueType& value);
820     using ValuePtr  = void (*)(AttributeArray* array, const ValueType& value);
821 
822 public:
823     static Ptr create(const AttributeArray& array, const bool collapseOnDestruction = true);
824 
825     AttributeHandle(const AttributeArray& array, const bool collapseOnDestruction = true);
826 
827     AttributeHandle(const AttributeHandle&) = default;
828     AttributeHandle& operator=(const AttributeHandle&) = default;
829 
830     virtual ~AttributeHandle();
831 
832     Index stride() const { return mStrideOrTotalSize; }
833     Index size() const { return mSize; }
834 
835     bool isUniform() const;
836     bool hasConstantStride() const;
837 
838     ValueType get(Index n, Index m = 0) const;
839 
840     const AttributeArray& array() const;
841 
842 protected:
843     Index index(Index n, Index m) const;
844 
845     const AttributeArray* mArray;
846 
847     GetterPtr mGetter;
848     SetterPtr mSetter;
849     ValuePtr  mCollapser;
850     ValuePtr  mFiller;
851 
852 private:
853     friend class ::TestAttributeArray;
854 
855     template <bool IsUnknownCodec>
856     typename std::enable_if<IsUnknownCodec, bool>::type compatibleType() const;
857 
858     template <bool IsUnknownCodec>
859     typename std::enable_if<!IsUnknownCodec, bool>::type compatibleType() const;
860 
861     template <bool IsUnknownCodec>
862     typename std::enable_if<IsUnknownCodec, ValueType>::type get(Index index) const;
863 
864     template <bool IsUnknownCodec>
865     typename std::enable_if<!IsUnknownCodec, ValueType>::type get(Index index) const;
866 
867     // local copy of AttributeArray (to preserve compression)
868     AttributeArray::Ptr mLocalArray;
869 
870     Index mStrideOrTotalSize;
871     Index mSize;
872     bool mCollapseOnDestruction;
873 }; // class AttributeHandle
874 
875 
876 ////////////////////////////////////////
877 
878 
879 /// Write-able version of AttributeHandle
880 template <typename ValueType, typename CodecType = UnknownCodec>
881 class AttributeWriteHandle : public AttributeHandle<ValueType, CodecType>
882 {
883 public:
884     using Handle    = AttributeWriteHandle<ValueType, CodecType>;
885     using Ptr       = std::shared_ptr<Handle>;
886     using ScopedPtr = std::unique_ptr<Handle>;
887 
888     static Ptr create(AttributeArray& array, const bool expand = true);
889 
890     AttributeWriteHandle(AttributeArray& array, const bool expand = true);
891 
892     virtual ~AttributeWriteHandle() = default;
893 
894     /// @brief  If this array is uniform, replace it with an array of length size().
895     /// @param  fill if true, assign the uniform value to each element of the array.
896     void expand(bool fill = true);
897 
898     /// Replace the existing array with a uniform value (zero if none provided).
899     void collapse();
900     void collapse(const ValueType& uniformValue);
901 
902     /// Compact the existing array to become uniform if all values are identical
903     bool compact();
904 
905     /// @brief Fill the existing array with the given value.
906     /// @note Identical to collapse() except a non-uniform array will not become uniform.
907     void fill(const ValueType& value);
908 
909     void set(Index n, const ValueType& value);
910     void set(Index n, Index m, const ValueType& value);
911 
912     AttributeArray& array();
913 
914 private:
915     friend class ::TestAttributeArray;
916 
917     template <bool IsUnknownCodec>
918     typename std::enable_if<IsUnknownCodec, void>::type set(Index index, const ValueType& value) const;
919 
920     template <bool IsUnknownCodec>
921     typename std::enable_if<!IsUnknownCodec, void>::type set(Index index, const ValueType& value) const;
922 }; // class AttributeWriteHandle
923 
924 
925 ////////////////////////////////////////
926 
927 
928 // Attribute codec implementation
929 
930 
931 template<typename ValueType>
932 inline void
933 NullCodec::decode(const ValueType& data, ValueType& val)
934 {
935     val = data;
936 }
937 
938 
939 template<typename ValueType>
940 inline void
941 NullCodec::encode(const ValueType& val, ValueType& data)
942 {
943     data = val;
944 }
945 
946 
947 template<typename StorageType, typename ValueType>
948 inline void
949 TruncateCodec::decode(const StorageType& data, ValueType& val)
950 {
951     val = static_cast<ValueType>(data);
952 }
953 
954 
955 template<typename StorageType, typename ValueType>
956 inline void
957 TruncateCodec::encode(const ValueType& val, StorageType& data)
958 {
959     data = static_cast<StorageType>(val);
960 }
961 
962 
963 template <bool OneByte, typename Range>
964 template<typename StorageType, typename ValueType>
965 inline void
966 FixedPointCodec<OneByte, Range>::decode(const StorageType& data, ValueType& val)
967 {
968     val = fixedPointToFloatingPoint<ValueType>(data);
969 
970     // shift value range to be -0.5 => 0.5 (as this is most commonly used for position)
971 
972     val = Range::template decode<ValueType>(val);
973 }
974 
975 
976 template <bool OneByte, typename Range>
977 template<typename StorageType, typename ValueType>
978 inline void
979 FixedPointCodec<OneByte, Range>::encode(const ValueType& val, StorageType& data)
980 {
981     // shift value range to be -0.5 => 0.5 (as this is most commonly used for position)
982 
983     const ValueType newVal = Range::template encode<ValueType>(val);
984 
985     data = floatingPointToFixedPoint<StorageType>(newVal);
986 }
987 
988 
989 template<typename T>
990 inline void
991 UnitVecCodec::decode(const StorageType& data, math::Vec3<T>& val)
992 {
993     val = math::QuantizedUnitVec::unpack(data);
994 }
995 
996 
997 template<typename T>
998 inline void
999 UnitVecCodec::encode(const math::Vec3<T>& val, StorageType& data)
1000 {
1001     data = math::QuantizedUnitVec::pack(val);
1002 }
1003 
1004 
1005 ////////////////////////////////////////
1006 
1007 // AttributeArray implementation
1008 
1009 template <typename IterT>
1010 void AttributeArray::doCopyValues(const AttributeArray& sourceArray, const IterT& iter,
1011     bool rangeChecking/*=true*/)
1012 {
1013     // ensure both arrays have float-float or integer-integer value types
1014     assert(sourceArray.valueTypeIsFloatingPoint() == this->valueTypeIsFloatingPoint());
1015     // ensure both arrays have been loaded from disk (if delay-loaded)
1016     assert(sourceArray.isDataLoaded() && this->isDataLoaded());
1017     // ensure storage size * stride matches on both arrays
1018     assert(this->storageTypeSize()*this->stride() ==
1019         sourceArray.storageTypeSize()*sourceArray.stride());
1020 
1021     const size_t bytes(sourceArray.storageTypeSize()*sourceArray.stride());
1022     const char* const sourceBuffer = sourceArray.dataAsByteArray();
1023     char* const targetBuffer = this->dataAsByteArray();
1024     assert(sourceBuffer && targetBuffer);
1025 
1026     if (rangeChecking && this->isUniform()) {
1027         OPENVDB_THROW(IndexError, "Cannot copy array data as target array is uniform.");
1028     }
1029 
1030     const bool sourceIsUniform = sourceArray.isUniform();
1031 
1032     const Index sourceDataSize = rangeChecking ? sourceArray.dataSize() : 0;
1033     const Index targetDataSize = rangeChecking ? this->dataSize() : 0;
1034 
1035     for (IterT it(iter); it; ++it) {
1036         const Index sourceIndex = sourceIsUniform ? 0 : it.sourceIndex();
1037         const Index targetIndex = it.targetIndex();
1038 
1039         if (rangeChecking) {
1040             if (sourceIndex >= sourceDataSize) {
1041                 OPENVDB_THROW(IndexError,
1042                     "Cannot copy array data as source index exceeds size of source array.");
1043             }
1044             if (targetIndex >= targetDataSize) {
1045                 OPENVDB_THROW(IndexError,
1046                     "Cannot copy array data as target index exceeds size of target array.");
1047             }
1048         } else {
1049             // range-checking asserts
1050             assert(sourceIndex < sourceArray.dataSize());
1051             assert(targetIndex < this->dataSize());
1052             if (this->isUniform())  assert(targetIndex == Index(0));
1053         }
1054 
1055         const size_t targetOffset(targetIndex * bytes);
1056         const size_t sourceOffset(sourceIndex * bytes);
1057 
1058         std::memcpy(targetBuffer + targetOffset, sourceBuffer + sourceOffset, bytes);
1059     }
1060 }
1061 
1062 template <typename IterT>
1063 void AttributeArray::copyValuesUnsafe(const AttributeArray& sourceArray, const IterT& iter)
1064 {
1065     this->doCopyValues(sourceArray, iter, /*range-checking=*/false);
1066 }
1067 
1068 template <typename IterT>
1069 void AttributeArray::copyValues(const AttributeArray& sourceArray, const IterT& iter,
1070     bool compact/* = true*/)
1071 {
1072     const Index bytes = sourceArray.storageTypeSize();
1073     if (bytes != this->storageTypeSize()) {
1074         OPENVDB_THROW(TypeError, "Cannot copy array data due to mis-match in storage type sizes.");
1075     }
1076 
1077     // ensure both arrays have been loaded from disk
1078     sourceArray.loadData();
1079     this->loadData();
1080 
1081     // if the target array is uniform, expand it first
1082     this->expand();
1083 
1084     // TODO: Acquire mutex locks for source and target arrays to ensure that
1085     // value copying is always thread-safe. Note that the unsafe method will be
1086     // faster, but can only be used if neither the source or target arrays are
1087     // modified during copying. Note that this will require a new private
1088     // virtual method with ABI=7 to access the mutex from the derived class.
1089 
1090     this->doCopyValues(sourceArray, iter, true);
1091 
1092     // attempt to compact target array
1093     if (compact) {
1094         this->compact();
1095     }
1096 }
1097 
1098 
1099 ////////////////////////////////////////
1100 
1101 // TypedAttributeArray implementation
1102 
1103 template<typename ValueType_, typename Codec_>
1104 std::unique_ptr<const NamePair> TypedAttributeArray<ValueType_, Codec_>::sTypeName;
1105 
1106 
1107 template<typename ValueType_, typename Codec_>
1108 TypedAttributeArray<ValueType_, Codec_>::TypedAttributeArray(
1109     Index n, Index strideOrTotalSize, bool constantStride, const ValueType& uniformValue)
1110     : AttributeArray()
1111     , mData(new StorageType[1])
1112     , mSize(n)
1113     , mStrideOrTotalSize(strideOrTotalSize)
1114 {
1115     if (constantStride) {
1116         this->setConstantStride(true);
1117         if (strideOrTotalSize == 0) {
1118             OPENVDB_THROW(ValueError, "Creating a TypedAttributeArray with a constant stride requires that " \
1119                                         "stride to be at least one.")
1120         }
1121     }
1122     else {
1123         this->setConstantStride(false);
1124         if (mStrideOrTotalSize < n) {
1125             OPENVDB_THROW(ValueError, "Creating a TypedAttributeArray with a non-constant stride must have " \
1126                                         "a total size of at least the number of elements in the array.")
1127         }
1128     }
1129     mSize = std::max(Index(1), mSize);
1130     mStrideOrTotalSize = std::max(Index(1), mStrideOrTotalSize);
1131     Codec::encode(uniformValue, this->data()[0]);
1132 }
1133 
1134 
1135 #if OPENVDB_ABI_VERSION_NUMBER >= 7
1136 template<typename ValueType_, typename Codec_>
1137 TypedAttributeArray<ValueType_, Codec_>::TypedAttributeArray(const TypedAttributeArray& rhs)
1138     : TypedAttributeArray(rhs, tbb::spin_mutex::scoped_lock(rhs.mMutex))
1139 {
1140 }
1141 
1142 
1143 template<typename ValueType_, typename Codec_>
1144 TypedAttributeArray<ValueType_, Codec_>::TypedAttributeArray(const TypedAttributeArray& rhs,
1145     const tbb::spin_mutex::scoped_lock& lock)
1146     : AttributeArray(rhs, lock)
1147 #else
1148 template<typename ValueType_, typename Codec_>
1149 TypedAttributeArray<ValueType_, Codec_>::TypedAttributeArray(const TypedAttributeArray& rhs, bool)
1150     : AttributeArray(rhs)
1151 #endif
1152     , mSize(rhs.mSize)
1153     , mStrideOrTotalSize(rhs.mStrideOrTotalSize)
1154 {
1155     if (this->validData()) {
1156         this->allocate();
1157         std::memcpy(static_cast<void*>(this->data()), rhs.data(), this->arrayMemUsage());
1158     }
1159 }
1160 
1161 
1162 template<typename ValueType_, typename Codec_>
1163 TypedAttributeArray<ValueType_, Codec_>&
1164 TypedAttributeArray<ValueType_, Codec_>::operator=(const TypedAttributeArray& rhs)
1165 {
1166     if (&rhs != this) {
1167         // lock both the source and target arrays to ensure thread-safety
1168         tbb::spin_mutex::scoped_lock lock(mMutex);
1169         tbb::spin_mutex::scoped_lock rhsLock(rhs.mMutex);
1170 
1171         this->deallocate();
1172 
1173         mFlags = rhs.mFlags;
1174         mUsePagedRead = rhs.mUsePagedRead;
1175         mSize = rhs.mSize;
1176         mStrideOrTotalSize = rhs.mStrideOrTotalSize;
1177         mIsUniform = rhs.mIsUniform;
1178 
1179         if (this->validData()) {
1180             this->allocate();
1181             std::memcpy(static_cast<void*>(this->data()), rhs.data(), this->arrayMemUsage());
1182         }
1183     }
1184 
1185     return *this;
1186 }
1187 
1188 
1189 template<typename ValueType_, typename Codec_>
1190 inline const NamePair&
1191 TypedAttributeArray<ValueType_, Codec_>::attributeType()
1192 {
1193     static std::once_flag once;
1194     std::call_once(once, []()
1195     {
1196         sTypeName.reset(new NamePair(typeNameAsString<ValueType>(), Codec::name()));
1197     });
1198     return *sTypeName;
1199 }
1200 
1201 
1202 template<typename ValueType_, typename Codec_>
1203 inline bool
1204 TypedAttributeArray<ValueType_, Codec_>::isRegistered()
1205 {
1206     return AttributeArray::isRegistered(TypedAttributeArray::attributeType());
1207 }
1208 
1209 
1210 template<typename ValueType_, typename Codec_>
1211 inline void
1212 TypedAttributeArray<ValueType_, Codec_>::registerType()
1213 {
1214     AttributeArray::registerType(TypedAttributeArray::attributeType(), TypedAttributeArray::factory);
1215 }
1216 
1217 
1218 template<typename ValueType_, typename Codec_>
1219 inline void
1220 TypedAttributeArray<ValueType_, Codec_>::unregisterType()
1221 {
1222     AttributeArray::unregisterType(TypedAttributeArray::attributeType());
1223 }
1224 
1225 
1226 template<typename ValueType_, typename Codec_>
1227 inline typename TypedAttributeArray<ValueType_, Codec_>::Ptr
1228 TypedAttributeArray<ValueType_, Codec_>::create(Index n, Index stride, bool constantStride,
1229     const Metadata* metadata)
1230 {
1231     const TypedMetadata<ValueType>* typedMetadata = metadata ?
1232         dynamic_cast<const TypedMetadata<ValueType>*>(metadata) : nullptr;
1233 
1234     return Ptr(new TypedAttributeArray(n, stride, constantStride,
1235         typedMetadata ? typedMetadata->value() : zeroVal<ValueType>()));
1236 }
1237 
1238 template<typename ValueType_, typename Codec_>
1239 inline TypedAttributeArray<ValueType_, Codec_>&
1240 TypedAttributeArray<ValueType_, Codec_>::cast(AttributeArray& attributeArray)
1241 {
1242     if (!attributeArray.isType<TypedAttributeArray>()) {
1243         OPENVDB_THROW(TypeError, "Invalid Attribute Type");
1244     }
1245     return static_cast<TypedAttributeArray&>(attributeArray);
1246 }
1247 
1248 template<typename ValueType_, typename Codec_>
1249 inline const TypedAttributeArray<ValueType_, Codec_>&
1250 TypedAttributeArray<ValueType_, Codec_>::cast(const AttributeArray& attributeArray)
1251 {
1252     if (!attributeArray.isType<TypedAttributeArray>()) {
1253         OPENVDB_THROW(TypeError, "Invalid Attribute Type");
1254     }
1255     return static_cast<const TypedAttributeArray&>(attributeArray);
1256 }
1257 
1258 template<typename ValueType_, typename Codec_>
1259 AttributeArray::Ptr
1260 TypedAttributeArray<ValueType_, Codec_>::copy() const
1261 {
1262 #if OPENVDB_ABI_VERSION_NUMBER < 7
1263     tbb::spin_mutex::scoped_lock lock(mMutex);
1264 #endif
1265     return AttributeArray::Ptr(new TypedAttributeArray<ValueType, Codec>(*this));
1266 }
1267 
1268 
1269 template<typename ValueType_, typename Codec_>
1270 AttributeArray::Ptr
1271 TypedAttributeArray<ValueType_, Codec_>::copyUncompressed() const
1272 {
1273     return this->copy();
1274 }
1275 
1276 
1277 template<typename ValueType_, typename Codec_>
1278 size_t
1279 TypedAttributeArray<ValueType_, Codec_>::arrayMemUsage() const
1280 {
1281     if (this->isOutOfCore())        return 0;
1282 
1283     return (mIsUniform ? 1 : this->dataSize()) * sizeof(StorageType);
1284 }
1285 
1286 
1287 template<typename ValueType_, typename Codec_>
1288 void
1289 TypedAttributeArray<ValueType_, Codec_>::allocate()
1290 {
1291     assert(!mData);
1292     if (mIsUniform) {
1293         mData.reset(new StorageType[1]);
1294     }
1295     else {
1296         const size_t size(this->dataSize());
1297         assert(size > 0);
1298         mData.reset(new StorageType[size]);
1299     }
1300 }
1301 
1302 
1303 template<typename ValueType_, typename Codec_>
1304 void
1305 TypedAttributeArray<ValueType_, Codec_>::deallocate()
1306 {
1307     // detach from file if delay-loaded
1308     if (this->isOutOfCore()) {
1309         this->setOutOfCore(false);
1310         this->mPageHandle.reset();
1311     }
1312     if (mData)      mData.reset();
1313 }
1314 
1315 
1316 template<typename ValueType_, typename Codec_>
1317 bool
1318 TypedAttributeArray<ValueType_, Codec_>::valueTypeIsFloatingPoint() const
1319 {
1320     // TODO: Update to use Traits that correctly handle matrices and quaternions.
1321 
1322     if (std::is_same<ValueType, Quats>::value ||
1323         std::is_same<ValueType, Quatd>::value ||
1324         std::is_same<ValueType, Mat3s>::value ||
1325         std::is_same<ValueType, Mat3d>::value ||
1326         std::is_same<ValueType, Mat4s>::value ||
1327         std::is_same<ValueType, Mat4d>::value)      return true;
1328 
1329     using ElementT = typename VecTraits<ValueType>::ElementType;
1330 
1331     // half is not defined as float point as expected, so explicitly handle it
1332     return std::is_floating_point<ElementT>::value || std::is_same<math::half, ElementT>::value;
1333 }
1334 
1335 
1336 template<typename ValueType_, typename Codec_>
1337 bool
1338 TypedAttributeArray<ValueType_, Codec_>::valueTypeIsClass() const
1339 {
1340     // half is not defined as a non-class type as expected, so explicitly exclude it
1341     return std::is_class<ValueType>::value && !std::is_same<math::half, ValueType>::value;
1342 }
1343 
1344 
1345 template<typename ValueType_, typename Codec_>
1346 bool
1347 TypedAttributeArray<ValueType_, Codec_>::valueTypeIsVector() const
1348 {
1349     return VecTraits<ValueType>::IsVec;
1350 }
1351 
1352 
1353 template<typename ValueType_, typename Codec_>
1354 bool
1355 TypedAttributeArray<ValueType_, Codec_>::valueTypeIsQuaternion() const
1356 {
1357     // TODO: improve performance by making this a compile-time check using type traits
1358     return !this->valueType().compare(0, 4, "quat");
1359 }
1360 
1361 
1362 template<typename ValueType_, typename Codec_>
1363 bool
1364 TypedAttributeArray<ValueType_, Codec_>::valueTypeIsMatrix() const
1365 {
1366     // TODO: improve performance by making this a compile-time check using type traits
1367     return !this->valueType().compare(0, 3, "mat");
1368 }
1369 
1370 
1371 template<typename ValueType_, typename Codec_>
1372 size_t
1373 TypedAttributeArray<ValueType_, Codec_>::memUsage() const
1374 {
1375     return sizeof(*this) + (bool(mData) ? this->arrayMemUsage() : 0);
1376 }
1377 
1378 
1379 template<typename ValueType_, typename Codec_>
1380 typename TypedAttributeArray<ValueType_, Codec_>::ValueType
1381 TypedAttributeArray<ValueType_, Codec_>::getUnsafe(Index n) const
1382 {
1383     assert(n < this->dataSize());
1384 
1385     ValueType val;
1386     Codec::decode(/*in=*/this->data()[mIsUniform ? 0 : n], /*out=*/val);
1387     return val;
1388 }
1389 
1390 
1391 template<typename ValueType_, typename Codec_>
1392 typename TypedAttributeArray<ValueType_, Codec_>::ValueType
1393 TypedAttributeArray<ValueType_, Codec_>::get(Index n) const
1394 {
1395     if (n >= this->dataSize())           OPENVDB_THROW(IndexError, "Out-of-range access.");
1396     if (this->isOutOfCore())            this->doLoad();
1397 
1398     return this->getUnsafe(n);
1399 }
1400 
1401 
1402 template<typename ValueType_, typename Codec_>
1403 template<typename T>
1404 void
1405 TypedAttributeArray<ValueType_, Codec_>::getUnsafe(Index n, T& val) const
1406 {
1407     val = static_cast<T>(this->getUnsafe(n));
1408 }
1409 
1410 
1411 template<typename ValueType_, typename Codec_>
1412 template<typename T>
1413 void
1414 TypedAttributeArray<ValueType_, Codec_>::get(Index n, T& val) const
1415 {
1416     val = static_cast<T>(this->get(n));
1417 }
1418 
1419 
1420 template<typename ValueType_, typename Codec_>
1421 typename TypedAttributeArray<ValueType_, Codec_>::ValueType
1422 TypedAttributeArray<ValueType_, Codec_>::getUnsafe(const AttributeArray* array, const Index n)
1423 {
1424     return static_cast<const TypedAttributeArray<ValueType, Codec>*>(array)->getUnsafe(n);
1425 }
1426 
1427 
1428 template<typename ValueType_, typename Codec_>
1429 void
1430 TypedAttributeArray<ValueType_, Codec_>::setUnsafe(Index n, const ValueType& val)
1431 {
1432     assert(n < this->dataSize());
1433     assert(!this->isOutOfCore());
1434     assert(!this->isUniform());
1435 
1436     // this unsafe method assumes the data is not uniform, however if it is, this redirects the index
1437     // to zero, which is marginally less efficient but ensures not writing to an illegal address
1438 
1439     Codec::encode(/*in=*/val, /*out=*/this->data()[mIsUniform ? 0 : n]);
1440 }
1441 
1442 
1443 template<typename ValueType_, typename Codec_>
1444 void
1445 TypedAttributeArray<ValueType_, Codec_>::set(Index n, const ValueType& val)
1446 {
1447     if (n >= this->dataSize())           OPENVDB_THROW(IndexError, "Out-of-range access.");
1448     if (this->isOutOfCore())            this->doLoad();
1449     if (this->isUniform())              this->expand();
1450 
1451     this->setUnsafe(n, val);
1452 }
1453 
1454 
1455 template<typename ValueType_, typename Codec_>
1456 template<typename T>
1457 void
1458 TypedAttributeArray<ValueType_, Codec_>::setUnsafe(Index n, const T& val)
1459 {
1460     this->setUnsafe(n, static_cast<ValueType>(val));
1461 }
1462 
1463 
1464 template<typename ValueType_, typename Codec_>
1465 template<typename T>
1466 void
1467 TypedAttributeArray<ValueType_, Codec_>::set(Index n, const T& val)
1468 {
1469     this->set(n, static_cast<ValueType>(val));
1470 }
1471 
1472 
1473 template<typename ValueType_, typename Codec_>
1474 void
1475 TypedAttributeArray<ValueType_, Codec_>::setUnsafe(AttributeArray* array, const Index n, const ValueType& value)
1476 {
1477     static_cast<TypedAttributeArray<ValueType, Codec>*>(array)->setUnsafe(n, value);
1478 }
1479 
1480 
1481 template<typename ValueType_, typename Codec_>
1482 void
1483 TypedAttributeArray<ValueType_, Codec_>::set(Index n, const AttributeArray& sourceArray, const Index sourceIndex)
1484 {
1485     const TypedAttributeArray& sourceTypedArray = static_cast<const TypedAttributeArray&>(sourceArray);
1486 
1487     ValueType sourceValue;
1488     sourceTypedArray.get(sourceIndex, sourceValue);
1489 
1490     this->set(n, sourceValue);
1491 }
1492 
1493 
1494 template<typename ValueType_, typename Codec_>
1495 void
1496 TypedAttributeArray<ValueType_, Codec_>::expand(bool fill)
1497 {
1498     if (!mIsUniform)    return;
1499 
1500     const StorageType val = this->data()[0];
1501 
1502     {
1503         tbb::spin_mutex::scoped_lock lock(mMutex);
1504         this->deallocate();
1505         mIsUniform = false;
1506         this->allocate();
1507     }
1508 
1509     if (fill) {
1510         for (Index i = 0; i < this->dataSize(); ++i)  this->data()[i] = val;
1511     }
1512 }
1513 
1514 
1515 template<typename ValueType_, typename Codec_>
1516 bool
1517 TypedAttributeArray<ValueType_, Codec_>::compact()
1518 {
1519     if (mIsUniform)     return true;
1520 
1521     // compaction is not possible if any values are different
1522     const ValueType_ val = this->get(0);
1523     for (Index i = 1; i < this->dataSize(); i++) {
1524         if (!math::isExactlyEqual(this->get(i), val)) return false;
1525     }
1526 
1527     this->collapse(this->get(0));
1528     return true;
1529 }
1530 
1531 
1532 template<typename ValueType_, typename Codec_>
1533 void
1534 TypedAttributeArray<ValueType_, Codec_>::collapse()
1535 {
1536     this->collapse(zeroVal<ValueType>());
1537 }
1538 
1539 
1540 template<typename ValueType_, typename Codec_>
1541 void
1542 TypedAttributeArray<ValueType_, Codec_>::collapse(const ValueType& uniformValue)
1543 {
1544     if (!mIsUniform) {
1545         tbb::spin_mutex::scoped_lock lock(mMutex);
1546         this->deallocate();
1547         mIsUniform = true;
1548         this->allocate();
1549     }
1550     Codec::encode(uniformValue, this->data()[0]);
1551 }
1552 
1553 
1554 template<typename ValueType_, typename Codec_>
1555 void
1556 TypedAttributeArray<ValueType_, Codec_>::collapse(AttributeArray* array, const ValueType& value)
1557 {
1558     static_cast<TypedAttributeArray<ValueType, Codec>*>(array)->collapse(value);
1559 }
1560 
1561 
1562 template<typename ValueType_, typename Codec_>
1563 void
1564 TypedAttributeArray<ValueType_, Codec_>::fill(const ValueType& value)
1565 {
1566     if (this->isOutOfCore()) {
1567         tbb::spin_mutex::scoped_lock lock(mMutex);
1568         this->deallocate();
1569         this->allocate();
1570     }
1571 
1572     const Index size = mIsUniform ? 1 : this->dataSize();
1573     for (Index i = 0; i < size; ++i)  {
1574         Codec::encode(value, this->data()[i]);
1575     }
1576 }
1577 
1578 
1579 template<typename ValueType_, typename Codec_>
1580 void
1581 TypedAttributeArray<ValueType_, Codec_>::fill(AttributeArray* array, const ValueType& value)
1582 {
1583     static_cast<TypedAttributeArray<ValueType, Codec>*>(array)->fill(value);
1584 }
1585 
1586 
1587 template<typename ValueType_, typename Codec_>
1588 inline bool
1589 TypedAttributeArray<ValueType_, Codec_>::compress()
1590 {
1591     return false;
1592 }
1593 
1594 
1595 template<typename ValueType_, typename Codec_>
1596 inline bool
1597 TypedAttributeArray<ValueType_, Codec_>::compressUnsafe()
1598 {
1599     return false;
1600 }
1601 
1602 
1603 template<typename ValueType_, typename Codec_>
1604 inline bool
1605 TypedAttributeArray<ValueType_, Codec_>::decompress()
1606 {
1607     return false;
1608 }
1609 
1610 
1611 template<typename ValueType_, typename Codec_>
1612 bool
1613 TypedAttributeArray<ValueType_, Codec_>::isOutOfCore() const
1614 {
1615     return mOutOfCore;
1616 }
1617 
1618 
1619 template<typename ValueType_, typename Codec_>
1620 void
1621 TypedAttributeArray<ValueType_, Codec_>::setOutOfCore(const bool b)
1622 {
1623     mOutOfCore = b;
1624 }
1625 
1626 
1627 template<typename ValueType_, typename Codec_>
1628 void
1629 TypedAttributeArray<ValueType_, Codec_>::doLoad() const
1630 {
1631     if (!(this->isOutOfCore()))     return;
1632 
1633     TypedAttributeArray<ValueType_, Codec_>* self =
1634         const_cast<TypedAttributeArray<ValueType_, Codec_>*>(this);
1635 
1636     // This lock will be contended at most once, after which this buffer
1637     // will no longer be out-of-core.
1638     tbb::spin_mutex::scoped_lock lock(self->mMutex);
1639     this->doLoadUnsafe();
1640 }
1641 
1642 
1643 template<typename ValueType_, typename Codec_>
1644 void
1645 TypedAttributeArray<ValueType_, Codec_>::loadData() const
1646 {
1647     this->doLoad();
1648 }
1649 
1650 
1651 template<typename ValueType_, typename Codec_>
1652 bool
1653 TypedAttributeArray<ValueType_, Codec_>::isDataLoaded() const
1654 {
1655     return !this->isOutOfCore();
1656 }
1657 
1658 
1659 template<typename ValueType_, typename Codec_>
1660 void
1661 TypedAttributeArray<ValueType_, Codec_>::read(std::istream& is)
1662 {
1663     this->readMetadata(is);
1664     this->readBuffers(is);
1665 }
1666 
1667 
1668 template<typename ValueType_, typename Codec_>
1669 void
1670 TypedAttributeArray<ValueType_, Codec_>::readMetadata(std::istream& is)
1671 {
1672     // read data
1673 
1674     Index64 bytes = Index64(0);
1675     is.read(reinterpret_cast<char*>(&bytes), sizeof(Index64));
1676     bytes = bytes - /*flags*/sizeof(Int16) - /*size*/sizeof(Index);
1677 
1678     uint8_t flags = uint8_t(0);
1679     is.read(reinterpret_cast<char*>(&flags), sizeof(uint8_t));
1680     mFlags = flags;
1681 
1682     uint8_t serializationFlags = uint8_t(0);
1683     is.read(reinterpret_cast<char*>(&serializationFlags), sizeof(uint8_t));
1684 
1685     Index size = Index(0);
1686     is.read(reinterpret_cast<char*>(&size), sizeof(Index));
1687     mSize = size;
1688 
1689     // warn if an unknown flag has been set
1690     if (mFlags >= 0x20) {
1691         OPENVDB_LOG_WARN("Unknown attribute flags for VDB file format.");
1692     }
1693     // error if an unknown serialization flag has been set,
1694     // as this will adjust the layout of the data and corrupt the ability to read
1695     if (serializationFlags >= 0x10) {
1696         OPENVDB_THROW(IoError, "Unknown attribute serialization flags for VDB file format.");
1697     }
1698 
1699     // set uniform, compressed and page read state
1700 
1701     mIsUniform = serializationFlags & WRITEUNIFORM;
1702     mUsePagedRead = serializationFlags & WRITEPAGED;
1703     mCompressedBytes = bytes;
1704     mFlags |= PARTIALREAD; // mark data as having been partially read
1705 
1706     // read strided value (set to 1 if array is not strided)
1707 
1708     if (serializationFlags & WRITESTRIDED) {
1709         Index stride = Index(0);
1710         is.read(reinterpret_cast<char*>(&stride), sizeof(Index));
1711         mStrideOrTotalSize = stride;
1712     }
1713     else {
1714         mStrideOrTotalSize = 1;
1715     }
1716 }
1717 
1718 
1719 template<typename ValueType_, typename Codec_>
1720 void
1721 TypedAttributeArray<ValueType_, Codec_>::readBuffers(std::istream& is)
1722 {
1723     if (mUsePagedRead) {
1724         // use readBuffers(PagedInputStream&) for paged buffers
1725         OPENVDB_THROW(IoError, "Cannot read paged AttributeArray buffers.");
1726     }
1727 
1728     tbb::spin_mutex::scoped_lock lock(mMutex);
1729 
1730     this->deallocate();
1731 
1732     uint8_t bloscCompressed(0);
1733     if (!mIsUniform)    is.read(reinterpret_cast<char*>(&bloscCompressed), sizeof(uint8_t));
1734 
1735     assert(mFlags & PARTIALREAD);
1736     std::unique_ptr<char[]> buffer(new char[mCompressedBytes]);
1737     is.read(buffer.get(), mCompressedBytes);
1738     mCompressedBytes = 0;
1739     mFlags = static_cast<uint8_t>(mFlags & ~PARTIALREAD); // mark data read as having completed
1740 
1741     // compressed on-disk
1742 
1743     if (bloscCompressed == uint8_t(1)) {
1744 
1745         // decompress buffer
1746 
1747         const size_t inBytes = this->dataSize() * sizeof(StorageType);
1748         std::unique_ptr<char[]> newBuffer = compression::bloscDecompress(buffer.get(), inBytes);
1749         if (newBuffer)  buffer.reset(newBuffer.release());
1750     }
1751 
1752     // set data to buffer
1753 
1754     mData.reset(reinterpret_cast<StorageType*>(buffer.release()));
1755 }
1756 
1757 
1758 template<typename ValueType_, typename Codec_>
1759 void
1760 TypedAttributeArray<ValueType_, Codec_>::readPagedBuffers(compression::PagedInputStream& is)
1761 {
1762     if (!mUsePagedRead) {
1763         if (!is.sizeOnly()) this->readBuffers(is.getInputStream());
1764         return;
1765     }
1766 
1767     // If this array is being read from a memory-mapped file, delay loading of its data
1768     // until the data is actually accessed.
1769     io::MappedFile::Ptr mappedFile = io::getMappedFilePtr(is.getInputStream());
1770     const bool delayLoad = (mappedFile.get() != nullptr);
1771 
1772     if (is.sizeOnly())
1773     {
1774         size_t compressedBytes(mCompressedBytes);
1775         mCompressedBytes = 0; // if not set to zero, mPageHandle will attempt to destroy invalid memory
1776         mFlags = static_cast<uint8_t>(mFlags & ~PARTIALREAD); // mark data read as having completed
1777         assert(!mPageHandle);
1778         mPageHandle = is.createHandle(compressedBytes);
1779         return;
1780     }
1781 
1782     assert(mPageHandle);
1783 
1784     tbb::spin_mutex::scoped_lock lock(mMutex);
1785 
1786     this->deallocate();
1787 
1788     this->setOutOfCore(delayLoad);
1789     is.read(mPageHandle, std::streamsize(mPageHandle->size()), delayLoad);
1790 
1791     if (!delayLoad) {
1792         std::unique_ptr<char[]> buffer = mPageHandle->read();
1793         mData.reset(reinterpret_cast<StorageType*>(buffer.release()));
1794         mPageHandle.reset();
1795     }
1796 
1797     // clear page state
1798 
1799     mUsePagedRead = 0;
1800 }
1801 
1802 
1803 template<typename ValueType_, typename Codec_>
1804 void
1805 TypedAttributeArray<ValueType_, Codec_>::write(std::ostream& os) const
1806 {
1807     this->write(os, /*outputTransient=*/false);
1808 }
1809 
1810 
1811 template<typename ValueType_, typename Codec_>
1812 void
1813 TypedAttributeArray<ValueType_, Codec_>::write(std::ostream& os, bool outputTransient) const
1814 {
1815     this->writeMetadata(os, outputTransient, /*paged=*/false);
1816     this->writeBuffers(os, outputTransient);
1817 }
1818 
1819 
1820 template<typename ValueType_, typename Codec_>
1821 void
1822 TypedAttributeArray<ValueType_, Codec_>::writeMetadata(std::ostream& os, bool outputTransient, bool paged) const
1823 {
1824     if (!outputTransient && this->isTransient())    return;
1825 
1826     if (mFlags & PARTIALREAD) {
1827         OPENVDB_THROW(IoError, "Cannot write out a partially-read AttributeArray.");
1828     }
1829 
1830     uint8_t flags(mFlags);
1831     uint8_t serializationFlags(0);
1832     Index size(mSize);
1833     Index stride(mStrideOrTotalSize);
1834     bool strideOfOne(this->stride() == 1);
1835 
1836     bool bloscCompression = io::getDataCompression(os) & io::COMPRESS_BLOSC;
1837 
1838     // any compressed data needs to be loaded if out-of-core
1839     if (bloscCompression)    this->doLoad();
1840 
1841     size_t compressedBytes = 0;
1842 
1843     if (!strideOfOne)
1844     {
1845         serializationFlags |= WRITESTRIDED;
1846     }
1847 
1848     if (mIsUniform)
1849     {
1850         serializationFlags |= WRITEUNIFORM;
1851         if (bloscCompression && paged)      serializationFlags |= WRITEPAGED;
1852     }
1853     else if (bloscCompression)
1854     {
1855         if (paged)      serializationFlags |= WRITEPAGED;
1856         else {
1857             const char* charBuffer = reinterpret_cast<const char*>(this->data());
1858             const size_t inBytes = this->arrayMemUsage();
1859             compressedBytes = compression::bloscCompressedSize(charBuffer, inBytes);
1860         }
1861     }
1862 
1863     Index64 bytes = /*flags*/ sizeof(Int16) + /*size*/ sizeof(Index);
1864 
1865     bytes += (compressedBytes > 0) ? compressedBytes : this->arrayMemUsage();
1866 
1867     // write data
1868 
1869     os.write(reinterpret_cast<const char*>(&bytes), sizeof(Index64));
1870     os.write(reinterpret_cast<const char*>(&flags), sizeof(uint8_t));
1871     os.write(reinterpret_cast<const char*>(&serializationFlags), sizeof(uint8_t));
1872     os.write(reinterpret_cast<const char*>(&size), sizeof(Index));
1873 
1874     // write strided
1875     if (!strideOfOne)       os.write(reinterpret_cast<const char*>(&stride), sizeof(Index));
1876 }
1877 
1878 
1879 template<typename ValueType_, typename Codec_>
1880 void
1881 TypedAttributeArray<ValueType_, Codec_>::writeBuffers(std::ostream& os, bool outputTransient) const
1882 {
1883     if (!outputTransient && this->isTransient())    return;
1884 
1885     if (mFlags & PARTIALREAD) {
1886         OPENVDB_THROW(IoError, "Cannot write out a partially-read AttributeArray.");
1887     }
1888 
1889     this->doLoad();
1890 
1891     if (this->isUniform()) {
1892         os.write(reinterpret_cast<const char*>(this->data()), sizeof(StorageType));
1893     }
1894     else if (io::getDataCompression(os) & io::COMPRESS_BLOSC)
1895     {
1896         std::unique_ptr<char[]> compressedBuffer;
1897         size_t compressedBytes = 0;
1898         const char* charBuffer = reinterpret_cast<const char*>(this->data());
1899         const size_t inBytes = this->arrayMemUsage();
1900         compressedBuffer = compression::bloscCompress(charBuffer, inBytes, compressedBytes);
1901         if (compressedBuffer) {
1902             uint8_t bloscCompressed(1);
1903             os.write(reinterpret_cast<const char*>(&bloscCompressed), sizeof(uint8_t));
1904             os.write(reinterpret_cast<const char*>(compressedBuffer.get()), compressedBytes);
1905         }
1906         else {
1907             uint8_t bloscCompressed(0);
1908             os.write(reinterpret_cast<const char*>(&bloscCompressed), sizeof(uint8_t));
1909             os.write(reinterpret_cast<const char*>(this->data()), inBytes);
1910         }
1911     }
1912     else
1913     {
1914         uint8_t bloscCompressed(0);
1915         os.write(reinterpret_cast<const char*>(&bloscCompressed), sizeof(uint8_t));
1916         os.write(reinterpret_cast<const char*>(this->data()), this->arrayMemUsage());
1917     }
1918 }
1919 
1920 
1921 template<typename ValueType_, typename Codec_>
1922 void
1923 TypedAttributeArray<ValueType_, Codec_>::writePagedBuffers(compression::PagedOutputStream& os, bool outputTransient) const
1924 {
1925     if (!outputTransient && this->isTransient())    return;
1926 
1927     // paged compression only available when Blosc is enabled
1928     bool bloscCompression = io::getDataCompression(os.getOutputStream()) & io::COMPRESS_BLOSC;
1929     if (!bloscCompression) {
1930         if (!os.sizeOnly())   this->writeBuffers(os.getOutputStream(), outputTransient);
1931         return;
1932     }
1933 
1934     if (mFlags & PARTIALREAD) {
1935         OPENVDB_THROW(IoError, "Cannot write out a partially-read AttributeArray.");
1936     }
1937 
1938     this->doLoad();
1939 
1940     os.write(reinterpret_cast<const char*>(this->data()), this->arrayMemUsage());
1941 }
1942 
1943 
1944 template<typename ValueType_, typename Codec_>
1945 void
1946 TypedAttributeArray<ValueType_, Codec_>::doLoadUnsafe(const bool /*compression*/) const
1947 {
1948     if (!(this->isOutOfCore())) return;
1949 
1950     // this function expects the mutex to already be locked
1951 
1952     auto* self = const_cast<TypedAttributeArray<ValueType_, Codec_>*>(this);
1953 
1954     assert(self->mPageHandle);
1955     assert(!(self->mFlags & PARTIALREAD));
1956 
1957     std::unique_ptr<char[]> buffer = self->mPageHandle->read();
1958 
1959     self->mData.reset(reinterpret_cast<StorageType*>(buffer.release()));
1960 
1961     self->mPageHandle.reset();
1962 
1963     // clear all write and out-of-core flags
1964 
1965     self->mOutOfCore = false;
1966 }
1967 
1968 
1969 template<typename ValueType_, typename Codec_>
1970 AttributeArray::AccessorBasePtr
1971 TypedAttributeArray<ValueType_, Codec_>::getAccessor() const
1972 {
1973     // use the faster 'unsafe' get and set methods as attribute handles
1974     // ensure data is in-core when constructed
1975 
1976     return AccessorBasePtr(new AttributeArray::Accessor<ValueType_>(
1977         &TypedAttributeArray<ValueType_, Codec_>::getUnsafe,
1978         &TypedAttributeArray<ValueType_, Codec_>::setUnsafe,
1979         &TypedAttributeArray<ValueType_, Codec_>::collapse,
1980         &TypedAttributeArray<ValueType_, Codec_>::fill));
1981 }
1982 
1983 
1984 template<typename ValueType_, typename Codec_>
1985 bool
1986 TypedAttributeArray<ValueType_, Codec_>::isEqual(const AttributeArray& other) const
1987 {
1988     const TypedAttributeArray<ValueType_, Codec_>* const otherT = dynamic_cast<const TypedAttributeArray<ValueType_, Codec_>* >(&other);
1989     if(!otherT) return false;
1990     if(this->mSize != otherT->mSize ||
1991        this->mStrideOrTotalSize != otherT->mStrideOrTotalSize ||
1992        this->mIsUniform != otherT->mIsUniform ||
1993        this->attributeType() != this->attributeType()) return false;
1994 
1995     this->doLoad();
1996     otherT->doLoad();
1997 
1998     const StorageType *target = this->data(), *source = otherT->data();
1999     if (!target && !source) return true;
2000     if (!target || !source) return false;
2001     Index n = this->mIsUniform ? 1 : mSize;
2002     while (n && math::isExactlyEqual(*target++, *source++)) --n;
2003     return n == 0;
2004 }
2005 
2006 
2007 template<typename ValueType_, typename Codec_>
2008 char*
2009 TypedAttributeArray<ValueType_, Codec_>::dataAsByteArray()
2010 {
2011     return reinterpret_cast<char*>(this->data());
2012 }
2013 
2014 
2015 template<typename ValueType_, typename Codec_>
2016 const char*
2017 TypedAttributeArray<ValueType_, Codec_>::dataAsByteArray() const
2018 {
2019     return reinterpret_cast<const char*>(this->data());
2020 }
2021 
2022 
2023 ////////////////////////////////////////
2024 
2025 
2026 /// Accessor to call unsafe get and set methods based on templated Codec and Value
2027 template <typename CodecType, typename ValueType>
2028 struct AccessorEval
2029 {
2030     using GetterPtr = ValueType (*)(const AttributeArray* array, const Index n);
2031     using SetterPtr = void (*)(AttributeArray* array, const Index n, const ValueType& value);
2032 
2033     /// Getter that calls to TypedAttributeArray::getUnsafe()
2034     /// @note Functor argument is provided but not required for the generic case
2035     static ValueType get(GetterPtr /*functor*/, const AttributeArray* array, const Index n) {
2036         return TypedAttributeArray<ValueType, CodecType>::getUnsafe(array, n);
2037     }
2038 
2039     /// Getter that calls to TypedAttributeArray::setUnsafe()
2040     /// @note Functor argument is provided but not required for the generic case
2041     static void set(SetterPtr /*functor*/, AttributeArray* array, const Index n, const ValueType& value) {
2042         TypedAttributeArray<ValueType, CodecType>::setUnsafe(array, n, value);
2043     }
2044 };
2045 
2046 
2047 /// Partial specialization when Codec is not known at compile-time to use the supplied functor instead
2048 template <typename ValueType>
2049 struct AccessorEval<UnknownCodec, ValueType>
2050 {
2051     using GetterPtr = ValueType (*)(const AttributeArray* array, const Index n);
2052     using SetterPtr = void (*)(AttributeArray* array, const Index n, const ValueType& value);
2053 
2054     /// Getter that calls the supplied functor
2055     static ValueType get(GetterPtr functor, const AttributeArray* array, const Index n) {
2056         return (*functor)(array, n);
2057     }
2058 
2059     /// Setter that calls the supplied functor
2060     static void set(SetterPtr functor, AttributeArray* array, const Index n, const ValueType& value) {
2061         (*functor)(array, n, value);
2062     }
2063 };
2064 
2065 
2066 ////////////////////////////////////////
2067 
2068 // AttributeHandle implementation
2069 
2070 template <typename ValueType, typename CodecType>
2071 typename AttributeHandle<ValueType, CodecType>::Ptr
2072 AttributeHandle<ValueType, CodecType>::create(const AttributeArray& array, const bool collapseOnDestruction)
2073 {
2074     return  typename AttributeHandle<ValueType, CodecType>::Ptr(
2075             new AttributeHandle<ValueType, CodecType>(array, collapseOnDestruction));
2076 }
2077 
2078 template <typename ValueType, typename CodecType>
2079 AttributeHandle<ValueType, CodecType>::AttributeHandle(const AttributeArray& array, const bool collapseOnDestruction)
2080     : mArray(&array)
2081     , mStrideOrTotalSize(array.hasConstantStride() ? array.stride() : 1)
2082     , mSize(array.hasConstantStride() ? array.size() : array.dataSize())
2083     , mCollapseOnDestruction(collapseOnDestruction && array.isStreaming())
2084 {
2085     if (!this->compatibleType<std::is_same<CodecType, UnknownCodec>::value>()) {
2086         OPENVDB_THROW(TypeError, "Cannot bind handle due to incompatible type of AttributeArray.");
2087     }
2088 
2089     // load data if delay-loaded
2090 
2091     mArray->loadData();
2092 
2093     // bind getter and setter methods
2094 
2095     AttributeArray::AccessorBasePtr accessor = mArray->getAccessor();
2096     assert(accessor);
2097 
2098     AttributeArray::Accessor<ValueType>* typedAccessor = static_cast<AttributeArray::Accessor<ValueType>*>(accessor.get());
2099 
2100     mGetter = typedAccessor->mGetter;
2101     mSetter = typedAccessor->mSetter;
2102     mCollapser = typedAccessor->mCollapser;
2103     mFiller = typedAccessor->mFiller;
2104 }
2105 
2106 template <typename ValueType, typename CodecType>
2107 AttributeHandle<ValueType, CodecType>::~AttributeHandle()
2108 {
2109     // if enabled, attribute is collapsed on destruction of the handle to save memory
2110     if (mCollapseOnDestruction)  const_cast<AttributeArray*>(this->mArray)->collapse();
2111 }
2112 
2113 template <typename ValueType, typename CodecType>
2114 template <bool IsUnknownCodec>
2115 typename std::enable_if<IsUnknownCodec, bool>::type
2116 AttributeHandle<ValueType, CodecType>::compatibleType() const
2117 {
2118     // if codec is unknown, just check the value type
2119 
2120     return mArray->hasValueType<ValueType>();
2121 }
2122 
2123 template <typename ValueType, typename CodecType>
2124 template <bool IsUnknownCodec>
2125 typename std::enable_if<!IsUnknownCodec, bool>::type
2126 AttributeHandle<ValueType, CodecType>::compatibleType() const
2127 {
2128     // if the codec is known, check the value type and codec
2129 
2130     return mArray->isType<TypedAttributeArray<ValueType, CodecType>>();
2131 }
2132 
2133 template <typename ValueType, typename CodecType>
2134 const AttributeArray& AttributeHandle<ValueType, CodecType>::array() const
2135 {
2136     assert(mArray);
2137     return *mArray;
2138 }
2139 
2140 template <typename ValueType, typename CodecType>
2141 Index AttributeHandle<ValueType, CodecType>::index(Index n, Index m) const
2142 {
2143     Index index = n * mStrideOrTotalSize + m;
2144     assert(index < (mSize * mStrideOrTotalSize));
2145     return index;
2146 }
2147 
2148 template <typename ValueType, typename CodecType>
2149 ValueType AttributeHandle<ValueType, CodecType>::get(Index n, Index m) const
2150 {
2151     return this->get<std::is_same<CodecType, UnknownCodec>::value>(this->index(n, m));
2152 }
2153 
2154 template <typename ValueType, typename CodecType>
2155 template <bool IsUnknownCodec>
2156 typename std::enable_if<IsUnknownCodec, ValueType>::type
2157 AttributeHandle<ValueType, CodecType>::get(Index index) const
2158 {
2159     // if the codec is unknown, use the getter functor
2160 
2161     return (*mGetter)(mArray, index);
2162 }
2163 
2164 template <typename ValueType, typename CodecType>
2165 template <bool IsUnknownCodec>
2166 typename std::enable_if<!IsUnknownCodec, ValueType>::type
2167 AttributeHandle<ValueType, CodecType>::get(Index index) const
2168 {
2169     // if the codec is known, call the method on the attribute array directly
2170 
2171     return TypedAttributeArray<ValueType, CodecType>::getUnsafe(mArray, index);
2172 }
2173 
2174 template <typename ValueType, typename CodecType>
2175 bool AttributeHandle<ValueType, CodecType>::isUniform() const
2176 {
2177     return mArray->isUniform();
2178 }
2179 
2180 template <typename ValueType, typename CodecType>
2181 bool AttributeHandle<ValueType, CodecType>::hasConstantStride() const
2182 {
2183     return mArray->hasConstantStride();
2184 }
2185 
2186 ////////////////////////////////////////
2187 
2188 // AttributeWriteHandle implementation
2189 
2190 template <typename ValueType, typename CodecType>
2191 typename AttributeWriteHandle<ValueType, CodecType>::Ptr
2192 AttributeWriteHandle<ValueType, CodecType>::create(AttributeArray& array, const bool expand)
2193 {
2194     return  typename AttributeWriteHandle<ValueType, CodecType>::Ptr(
2195             new AttributeWriteHandle<ValueType, CodecType>(array, expand));
2196 }
2197 
2198 template <typename ValueType, typename CodecType>
2199 AttributeWriteHandle<ValueType, CodecType>::AttributeWriteHandle(AttributeArray& array, const bool expand)
2200     : AttributeHandle<ValueType, CodecType>(array, /*collapseOnDestruction=*/false)
2201 {
2202     if (expand)     array.expand();
2203 }
2204 
2205 template <typename ValueType, typename CodecType>
2206 void AttributeWriteHandle<ValueType, CodecType>::set(Index n, const ValueType& value)
2207 {
2208     this->set<std::is_same<CodecType, UnknownCodec>::value>(this->index(n, 0), value);
2209 }
2210 
2211 template <typename ValueType, typename CodecType>
2212 void AttributeWriteHandle<ValueType, CodecType>::set(Index n, Index m, const ValueType& value)
2213 {
2214     this->set<std::is_same<CodecType, UnknownCodec>::value>(this->index(n, m), value);
2215 }
2216 
2217 template <typename ValueType, typename CodecType>
2218 void AttributeWriteHandle<ValueType, CodecType>::expand(const bool fill)
2219 {
2220     const_cast<AttributeArray*>(this->mArray)->expand(fill);
2221 }
2222 
2223 template <typename ValueType, typename CodecType>
2224 void AttributeWriteHandle<ValueType, CodecType>::collapse()
2225 {
2226     const_cast<AttributeArray*>(this->mArray)->collapse();
2227 }
2228 
2229 template <typename ValueType, typename CodecType>
2230 bool AttributeWriteHandle<ValueType, CodecType>::compact()
2231 {
2232     return const_cast<AttributeArray*>(this->mArray)->compact();
2233 }
2234 
2235 template <typename ValueType, typename CodecType>
2236 void AttributeWriteHandle<ValueType, CodecType>::collapse(const ValueType& uniformValue)
2237 {
2238     this->mCollapser(const_cast<AttributeArray*>(this->mArray), uniformValue);
2239 }
2240 
2241 template <typename ValueType, typename CodecType>
2242 void AttributeWriteHandle<ValueType, CodecType>::fill(const ValueType& value)
2243 {
2244     this->mFiller(const_cast<AttributeArray*>(this->mArray), value);
2245 }
2246 
2247 template <typename ValueType, typename CodecType>
2248 template <bool IsUnknownCodec>
2249 typename std::enable_if<IsUnknownCodec, void>::type
2250 AttributeWriteHandle<ValueType, CodecType>::set(Index index, const ValueType& value) const
2251 {
2252     // if the codec is unknown, use the setter functor
2253 
2254     (*this->mSetter)(const_cast<AttributeArray*>(this->mArray), index, value);
2255 }
2256 
2257 template <typename ValueType, typename CodecType>
2258 template <bool IsUnknownCodec>
2259 typename std::enable_if<!IsUnknownCodec, void>::type
2260 AttributeWriteHandle<ValueType, CodecType>::set(Index index, const ValueType& value) const
2261 {
2262     // if the codec is known, call the method on the attribute array directly
2263 
2264     TypedAttributeArray<ValueType, CodecType>::setUnsafe(const_cast<AttributeArray*>(this->mArray), index, value);
2265 }
2266 
2267 template <typename ValueType, typename CodecType>
2268 AttributeArray& AttributeWriteHandle<ValueType, CodecType>::array()
2269 {
2270     assert(this->mArray);
2271     return *const_cast<AttributeArray*>(this->mArray);
2272 }
2273 
2274 
2275 } // namespace points
2276 } // namespace OPENVDB_VERSION_NAME
2277 } // namespace openvdb
2278 
2279 #endif // OPENVDB_POINTS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED
2280