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