1 //-*****************************************************************************
2 //
3 // Copyright (c) 2009-2012,
4 // Sony Pictures Imageworks, Inc. and
5 // Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
6 //
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are
11 // met:
12 // * Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // * Redistributions in binary form must reproduce the above
15 // copyright notice, this list of conditions and the following disclaimer
16 // in the documentation and/or other materials provided with the
17 // distribution.
18 // * Neither the name of Sony Pictures Imageworks, nor
19 // Industrial Light & Magic nor the names of their contributors may be used
20 // to endorse or promote products derived from this software without specific
21 // prior written permission.
22 //
23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 //
35 //-*****************************************************************************
36
37 #ifndef Alembic_AbcCoreHDF5_SimplePrImpl_h
38 #define Alembic_AbcCoreHDF5_SimplePrImpl_h
39
40 #include <Alembic/AbcCoreHDF5/Foundation.h>
41
42 #include <Alembic/AbcCoreHDF5/OrImpl.h>
43 #include <Alembic/AbcCoreHDF5/ArImpl.h>
44 #include <Alembic/AbcCoreHDF5/ReadUtil.h>
45 #include <Alembic/AbcCoreHDF5/DataTypeRegistry.h>
46 #include <Alembic/AbcCoreHDF5/HDF5Util.h>
47
48 namespace Alembic {
49 namespace AbcCoreHDF5 {
50 namespace ALEMBIC_VERSION_NS {
51
52 //-*****************************************************************************
53 // This templated base class implements the common logic behind both the
54 // scalar and array property readers. The only way these two readers differ
55 // is the type of sample they read and the way those samples are returned
56 // and managed. Scalar samples are simply copied by value, compared by value,
57 // and stored by value. Array samples are returned from an optional sample
58 // cache.
59 //
60 // There is a bit of template hoosafudgery going on here, which is always
61 // troublesome in foundation libraries, because it is hard to read and
62 // hard to decipher errors. I have kept it to a reasonable minimum, but the
63 // value of a single code instance is high enough that it's worth a bit of
64 // obfuscation.
65 //
66 // The IMPL class is assumed to have the following functions:
67 // void readSample( H5G &iGroup,
68 // const std::string &iSampleName,
69 // index_t iSampleIndex,
70 // SAMPLE oSample );
71 //
72 //-*****************************************************************************
73 template <class ABSTRACT, class IMPL, class SAMPLE>
74 class SimplePrImpl : public ABSTRACT
75 {
76 protected:
77 SimplePrImpl( AbcA::CompoundPropertyReaderPtr iParent,
78 H5Node & iParentGroup,
79 PropertyHeaderPtr iHeader,
80 uint32_t iNumSamples,
81 uint32_t iFirstChangedIndex,
82 uint32_t iLastChangedIndex );
83
84 public:
85 //-*************************************************************************
86 // ABSTRACT API
87 //-*************************************************************************
88 virtual ~SimplePrImpl();
89
90 virtual const AbcA::PropertyHeader &getHeader() const;
91
92 virtual AbcA::ObjectReaderPtr getObject();
93
94 virtual AbcA::CompoundPropertyReaderPtr getParent();
95
96 virtual size_t getNumSamples();
97
98 virtual bool isConstant();
99
100 virtual void getSample( index_t iSampleIndex,
101 SAMPLE oSample );
102
103 virtual std::pair<index_t, chrono_t> getFloorIndex( chrono_t iTime );
104
105 virtual std::pair<index_t, chrono_t> getCeilIndex( chrono_t iTime );
106
107 virtual std::pair<index_t, chrono_t> getNearIndex( chrono_t iTime );
108
109 virtual bool getKey( index_t iSampleIndex, AbcA::ArraySampleKey & oKey );
110
111 protected:
112
113 index_t verifySampleIndex( index_t iSampleIndex );
114
115 void checkSamplesIGroup();
116
117 // Parent compound property writer. It must exist.
118 AbcA::CompoundPropertyReaderPtr m_parent;
119
120 // The HDF5 Group associated with the parent property reader.
121 H5Node m_parentGroup;
122
123 // We don't hold a pointer to the object, but instead
124 // get it from the compound property reader.
125
126 // The Header
127 PropertyHeaderPtr m_header;
128
129 // Data Types.
130 hid_t m_fileDataType;
131 bool m_cleanFileDataType;
132 hid_t m_nativeDataType;
133 bool m_cleanNativeDataType;
134
135 // The number of samples that were written. This may be greater
136 // than the number of samples that were stored, because we don't
137 // repeat head or tail samples that repeat
138 uint32_t m_numSamples;
139
140 // The first sample index that is different from sample 0
141 uint32_t m_firstChangedIndex;
142
143 // The last sample index that needed to be written out, if the last sample
144 // repeats m_numSamples will be greater than this.
145 uint32_t m_lastChangedIndex;
146
147 // The simple properties only store samples after the first
148 // sample in a sub group. Therefore, there may not actually be
149 // a group associated with this property.
150 H5Node m_samplesIGroup;
151
152 // used to prevent race condition when setting m_samplesIGroup
153 Alembic::Util::mutex m_samplesIGroupMutex;
154 };
155
156 //-*****************************************************************************
157 //-*****************************************************************************
158 //-*****************************************************************************
159 // IMPLEMENTATION
160 //-*****************************************************************************
161 //-*****************************************************************************
162 //-*****************************************************************************
163
164 //-*****************************************************************************
165 template <class ABSTRACT, class IMPL, class SAMPLE>
SimplePrImpl(AbcA::CompoundPropertyReaderPtr iParent,H5Node & iParentGroup,PropertyHeaderPtr iHeader,uint32_t iNumSamples,uint32_t iFirstChangedIndex,uint32_t iLastChangedIndex)166 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::SimplePrImpl
167 (
168 AbcA::CompoundPropertyReaderPtr iParent,
169 H5Node & iParentGroup,
170 PropertyHeaderPtr iHeader,
171 uint32_t iNumSamples,
172 uint32_t iFirstChangedIndex,
173 uint32_t iLastChangedIndex
174 )
175 : m_parent( iParent )
176 , m_parentGroup( iParentGroup )
177 , m_header( iHeader )
178 , m_fileDataType( -1 )
179 , m_cleanFileDataType( false )
180 , m_nativeDataType( -1 )
181 , m_cleanNativeDataType( false )
182 , m_numSamples( iNumSamples )
183 , m_firstChangedIndex( iFirstChangedIndex )
184 , m_lastChangedIndex( iLastChangedIndex )
185 {
186 // Validate all inputs.
187 ABCA_ASSERT( m_parent, "Invalid parent" );
188 ABCA_ASSERT( m_parentGroup.isValidObject(), "Invalid parent group" );
189 ABCA_ASSERT( m_header, "Invalid header" );
190 ABCA_ASSERT( m_header->getPropertyType() != AbcA::kCompoundProperty,
191 "Tried to create a simple property with a compound header" );
192
193 // Get data types
194 PlainOldDataType POD = m_header->getDataType().getPod();
195 if ( POD != kStringPOD && POD != kWstringPOD )
196 {
197 m_fileDataType = GetFileH5T( m_header->getDataType(),
198 m_cleanFileDataType );
199 m_nativeDataType = GetNativeH5T( m_header->getDataType(),
200 m_cleanNativeDataType );
201 }
202
203 // Get our name.
204 const std::string &myName = m_header->getName();
205
206 // Validate the first and last changed index
207 ABCA_ASSERT( m_firstChangedIndex <= m_numSamples &&
208 m_lastChangedIndex <= m_numSamples &&
209 m_firstChangedIndex <= m_lastChangedIndex,
210 "Corrupt sampling information for property: " << myName
211 << " first change index: " << m_firstChangedIndex
212 << " last change index: " << m_lastChangedIndex
213 << " total number of samples: " << m_numSamples );
214 }
215
216 //-*****************************************************************************
217 // Destructor is at the end, so that this file has a logical ordering that
218 // matches the order of operations (create, get samples, destroy)
219 //-*****************************************************************************
220
221 //-*****************************************************************************
222 template <class ABSTRACT, class IMPL, class SAMPLE>
223 const AbcA::PropertyHeader &
getHeader()224 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getHeader() const
225 {
226 ABCA_ASSERT( m_header, "Invalid header" );
227 return *m_header;
228 }
229
230 //-*****************************************************************************
231 template <class ABSTRACT, class IMPL, class SAMPLE>
232 AbcA::ObjectReaderPtr
getObject()233 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getObject()
234 {
235 ABCA_ASSERT( m_parent, "Invalid parent" );
236 return m_parent->getObject();
237 }
238
239 //-*****************************************************************************
240 template <class ABSTRACT, class IMPL, class SAMPLE>
241 AbcA::CompoundPropertyReaderPtr
getParent()242 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getParent()
243 {
244 ABCA_ASSERT( m_parent, "Invalid parent" );
245 return m_parent;
246 }
247
248 //-*****************************************************************************
249 template <class ABSTRACT, class IMPL, class SAMPLE>
getNumSamples()250 size_t SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getNumSamples()
251 {
252 return ( size_t )m_numSamples;
253 }
254
255 //-*****************************************************************************
256 template <class ABSTRACT, class IMPL, class SAMPLE>
isConstant()257 bool SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::isConstant()
258 {
259 // No first change means no changes at all
260 return ( m_firstChangedIndex == 0 );
261 }
262
263 //-*****************************************************************************
264 template <class ABSTRACT, class IMPL, class SAMPLE>
verifySampleIndex(index_t iIndex)265 index_t SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::verifySampleIndex( index_t iIndex )
266 {
267 // Verify sample index
268 ABCA_ASSERT( iIndex >= 0 &&
269 iIndex < m_numSamples,
270 "Invalid sample index: " << iIndex
271 << ", should be between 0 and " << m_numSamples-1 );
272
273 // greater than the last index that had a change? read it from there
274 if ( iIndex > m_lastChangedIndex )
275 {
276 iIndex = m_lastChangedIndex;
277 }
278 // less than the first change? map to 0
279 else if ( iIndex < m_firstChangedIndex )
280 {
281 iIndex = 0;
282 }
283
284 return iIndex;
285 }
286
287 //-*****************************************************************************
288 template <class ABSTRACT, class IMPL, class SAMPLE>
checkSamplesIGroup()289 void SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::checkSamplesIGroup()
290 {
291 // Create the subsequent samples group.
292 if ( !m_samplesIGroup.isValidObject() )
293 {
294 Alembic::Util::scoped_lock l( m_samplesIGroupMutex );
295
296 if ( m_samplesIGroup.isValidObject() )
297 return;
298
299 std::string samplesIName = m_header->getName() + ".smpi";
300 ABCA_ASSERT( GroupExists( m_parentGroup, samplesIName ),
301 "Invalid property: " << m_header->getName()
302 << ", missing smpi" );
303
304 m_samplesIGroup = OpenGroup( m_parentGroup,
305 samplesIName.c_str() );
306 ABCA_ASSERT( m_samplesIGroup.isValidObject(),
307 "Invalid property: " << m_header->getName()
308 << ", invalid smpi group" );
309 }
310 }
311
312 //-*****************************************************************************
313 template <class ABSTRACT, class IMPL, class SAMPLE>
314 std::pair<index_t, chrono_t>
getFloorIndex(chrono_t iTime)315 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getFloorIndex( chrono_t iTime )
316 {
317 return m_header->getTimeSampling()->getFloorIndex( iTime, m_numSamples );
318 }
319
320 //-*****************************************************************************
321 template <class ABSTRACT, class IMPL, class SAMPLE>
322 std::pair<index_t, chrono_t>
getCeilIndex(chrono_t iTime)323 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getCeilIndex( chrono_t iTime )
324 {
325 return m_header->getTimeSampling()->getCeilIndex( iTime, m_numSamples );
326 }
327
328 //-*****************************************************************************
329 template <class ABSTRACT, class IMPL, class SAMPLE>
330 std::pair<index_t, chrono_t>
getNearIndex(chrono_t iTime)331 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getNearIndex( chrono_t iTime )
332 {
333 return m_header->getTimeSampling()->getNearIndex( iTime, m_numSamples );
334 }
335
336 //-*****************************************************************************
337 template <class ABSTRACT, class IMPL, class SAMPLE>
338 void
getSample(index_t iSampleIndex,SAMPLE oSample)339 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getSample( index_t iSampleIndex,
340 SAMPLE oSample )
341 {
342 iSampleIndex = verifySampleIndex( iSampleIndex );
343
344 // Get our name.
345 const std::string &myName = m_header->getName();
346
347 if ( iSampleIndex == 0 )
348 {
349 // Read the sample from the parent group.
350 // Sample 0 is always on the parent group, with
351 // our name + ".smp0" as the name of it.
352 std::string sample0Name = getSampleName( myName, 0 );
353 if ( m_header->getPropertyType() == AbcA::kScalarProperty )
354 {
355 ABCA_ASSERT( AttrExists( m_parentGroup, sample0Name.c_str() ),
356 "Invalid property in SimplePrImpl getSample: "
357 << myName << ", missing smp0" );
358 }
359 else
360 {
361 ABCA_ASSERT( DatasetExists( m_parentGroup, sample0Name ),
362 "Invalid propertyin SimplePrImpl getSample: "
363 << myName << ", missing smp1" );
364 }
365
366 static_cast<IMPL *>( this )->readSample( m_parentGroup.getObject(),
367 sample0Name,
368 iSampleIndex,
369 oSample );
370 }
371 else
372 {
373 checkSamplesIGroup();
374
375 // Read the sample.
376 std::string sampleName = getSampleName( myName, iSampleIndex );
377 static_cast<IMPL *>( this )->readSample( m_samplesIGroup.getObject(),
378 sampleName,
379 iSampleIndex,
380 oSample );
381 }
382 }
383
384 //-*****************************************************************************
385 template <class ABSTRACT, class IMPL, class SAMPLE>
386 bool
getKey(index_t iSampleIndex,AbcA::ArraySampleKey & oKey)387 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::getKey( index_t iSampleIndex,
388 AbcA::ArraySampleKey & oKey )
389 {
390 iSampleIndex = verifySampleIndex( iSampleIndex );
391
392 // Get our name.
393 const std::string &myName = m_header->getName();
394
395 if ( iSampleIndex == 0 )
396 {
397 // Read the sample from the parent group.
398 // Sample 0 is always on the parent group, with
399 // our name + ".smp0" as the name of it.
400 std::string sample0Name = getSampleName( myName, 0 );
401 if ( m_header->getPropertyType() == AbcA::kScalarProperty )
402 {
403 ABCA_ASSERT( AttrExists( m_parentGroup, sample0Name.c_str() ),
404 "Invalid property in SimplePrImpl getKey: "
405 << myName << ", missing smp0" );
406 }
407 else
408 {
409 ABCA_ASSERT( DatasetExists( m_parentGroup, sample0Name ),
410 "Invalid property in SimplePrImpl getKey: "
411 << myName << ", missing smp1" );
412 }
413
414 return static_cast<IMPL *>( this )->readKey( m_parentGroup.getObject(),
415 sample0Name,
416 oKey );
417 }
418 else
419 {
420 checkSamplesIGroup();
421
422 // Read the sample.
423 std::string sampleName = getSampleName( myName, iSampleIndex );
424 return static_cast<IMPL*>( this )->readKey( m_samplesIGroup.getObject(),
425 sampleName,
426 oKey );
427 }
428 }
429
430 //-*****************************************************************************
431 template <class ABSTRACT, class IMPL, class SAMPLE>
~SimplePrImpl()432 SimplePrImpl<ABSTRACT,IMPL,SAMPLE>::~SimplePrImpl()
433 {
434 // Clean up our samples group, if necessary.
435 CloseObject( m_samplesIGroup );
436
437 if ( m_fileDataType >= 0 && m_cleanFileDataType )
438 {
439 H5Tclose( m_fileDataType );
440 m_fileDataType = -1;
441 }
442
443 if ( m_nativeDataType >= 0 && m_cleanNativeDataType )
444 {
445 H5Tclose( m_nativeDataType );
446 m_nativeDataType = -1;
447 }
448 }
449
450 } // End namespace ALEMBIC_VERSION_NS
451
452 using namespace ALEMBIC_VERSION_NS;
453
454 } // End namespace AbcCoreHDF5
455 } // End namespace Alembic
456
457 #endif
458