Alembic 1.8.11
Loading...
Searching...
No Matches
SimplePwImpl.h
1//-*****************************************************************************
2//
3// Copyright (c) 2009-2013,
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_SimplePwImpl_h
38#define Alembic_AbcCoreHDF5_SimplePwImpl_h
39
40#include <Alembic/AbcCoreHDF5/Foundation.h>
41#include <Alembic/AbcCoreHDF5/WriteUtil.h>
42#include <Alembic/AbcCoreHDF5/DataTypeRegistry.h>
43#include <Alembic/AbcCoreHDF5/HDF5Util.h>
44
45namespace Alembic {
46namespace AbcCoreHDF5 {
47namespace ALEMBIC_VERSION_NS {
48
49//-*****************************************************************************
50// This templated base class implements the common logic behind both the
51// scalar and array property writers. The only way these two writers differ
52// is the type of sample they write and the way those samples are stored
53// and copied. Scalar samples are simply copied by value, compared by value,
54// and stored by value. Array samples are assumed to be "big", and are
55// first condensed into a "key" (using a checksum), which is compared
56// to other "keys" of previously written samples. However, the primary logic
57// of when to compare, when to copy, when to write, and how to manage time
58// values is common across both classes, and represents the primary complexity.
59// To minimize redundancy, errors, and code size, we create a common base class.
60//
61// It is assumed that the IMPL class will provide a mechanism for constructing
62// a KEY from a SAMPLE.
63//
64// The IMPL class is assumed to have the following functions:
65// KEY computeSampleKey( SAMPLE iSamp ) const;
66// bool sameAsPreviousSample( SAMPLE iSamp, const KEY &iKey ) const;
67// void copyPreviousSample( index_t iSampleIndex );
68// void writeSample( index_t iSampleIndex, SAMPLE iSamp, const KEY &iKey );
69//
70//-*****************************************************************************
71template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
72class SimplePwImpl : public ABSTRACT
73{
74protected:
75 SimplePwImpl( AbcA::CompoundPropertyWriterPtr iParent,
76 hid_t iParentGroup,
77 const std::string & iName,
78 const AbcA::MetaData & iMetaData,
79 const AbcA::DataType & iDataType,
80 uint32_t iTimeSamplingIndex,
81 AbcA::PropertyType iPropType );
82
83public:
84 virtual ~SimplePwImpl();
85
86 //-*************************************************************************
87 // FROM ABSTRACT
88 //-*************************************************************************
89
90 virtual const AbcA::PropertyHeader & getHeader() const;
91
92 virtual AbcA::ObjectWriterPtr getObject();
93
94 virtual AbcA::CompoundPropertyWriterPtr getParent();
95
96private:
97 hid_t getSampleIGroup();
98
99public:
100 // Scalar/Array API
101 virtual void setSample( SAMPLE iSamp );
102
103 virtual void setFromPreviousSample( );
104
105 virtual size_t getNumSamples();
106
107 virtual void setTimeSamplingIndex( uint32_t iIndex );
108
109protected:
110 // The parent compound property writer.
111 AbcA::CompoundPropertyWriterPtr m_parent;
112
113 // The parent group. We need to keep this around because we
114 // don't create our group until we need to. This is guaranteed to
115 // exist because our parent (or object) is guaranteed to exist.
116 hid_t m_parentGroup;
117
118 // The header which defines this property.
119 PropertyHeaderPtr m_header;
120
121 // The DataTypes for this property.
122 hid_t m_fileDataType;
123 bool m_cleanFileDataType;
124 hid_t m_nativeDataType;
125 bool m_cleanNativeDataType;
126
127 // The group corresponding to this property.
128 // It may never be created or written.
129 hid_t m_sampleIGroup;
130
131 // Index of the next sample to write
132 uint32_t m_nextSampleIndex;
133
134 // Index representing the first sample that is different from sample 0
135 uint32_t m_firstChangedIndex;
136
137 // Index representing the last sample in which a change has occured
138 // There is no need to repeat samples if they are the same between this
139 // index and m_nextSampleIndex
140 uint32_t m_lastChangedIndex;
141
142 // Index representing which TimeSampling from the ArchiveWriter to use.
143 uint32_t m_timeSamplingIndex;
144};
145
146//-*****************************************************************************
147//-*****************************************************************************
148//-*****************************************************************************
149// IMPLEMENTATION
150//-*****************************************************************************
151//-*****************************************************************************
152//-*****************************************************************************
153
154//-*****************************************************************************
155template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
156SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::SimplePwImpl
157(
158 AbcA::CompoundPropertyWriterPtr iParent,
159 hid_t iParentGroup,
160 const std::string & iName,
161 const AbcA::MetaData & iMetaData,
162 const AbcA::DataType & iDataType,
163 uint32_t iTimeSamplingIndex,
164 AbcA::PropertyType iPropType
165)
166 : m_parent( iParent )
167 , m_parentGroup( iParentGroup )
168 , m_fileDataType( -1 )
169 , m_cleanFileDataType( false )
170 , m_nativeDataType( -1 )
171 , m_cleanNativeDataType( false )
172 , m_sampleIGroup( -1 )
173 , m_nextSampleIndex( 0 )
174 , m_firstChangedIndex( 0 )
175 , m_lastChangedIndex( 0 )
176 , m_timeSamplingIndex(iTimeSamplingIndex)
177{
178 // Check the validity of all inputs.
179 ABCA_ASSERT( m_parent, "Invalid parent" );
180
181 ABCA_ASSERT( iName != "" && iName.find('/') == std::string::npos,
182 "Invalid name" );
183
184 // will assert if TimeSamplingPtr not found
185 AbcA::TimeSamplingPtr ts =
186 m_parent->getObject()->getArchive()->getTimeSampling(
187 m_timeSamplingIndex );
188
189 m_header = PropertyHeaderPtr( new AbcA::PropertyHeader( iName, iPropType,
190 iMetaData, iDataType, ts ) );
191
192 ABCA_ASSERT( m_header, "Invalid property header" );
193 ABCA_ASSERT( m_parentGroup >= 0, "Invalid parent group" );
194 ABCA_ASSERT( m_header->getDataType().getExtent() > 0,
195 "Invalid DatatType extent");
196
197 // Get data types
198 PlainOldDataType POD = m_header->getDataType().getPod();
199 if ( POD != kStringPOD && POD != kWstringPOD )
200 {
201 m_fileDataType = GetFileH5T( m_header->getDataType(),
202 m_cleanFileDataType );
203 m_nativeDataType = GetNativeH5T( m_header->getDataType(),
204 m_cleanNativeDataType );
205
206 ABCA_ASSERT( m_fileDataType >= 0, "Couldn't get file datatype" );
207 ABCA_ASSERT( m_nativeDataType >= 0, "Couldn't get native datatype" );
208 }
209}
210
211//-*****************************************************************************
212// Destructor is at the end, so that this file has a logical ordering that
213// matches the order of operations (create, set samples, destroy)
214//-*****************************************************************************
215
216//-*****************************************************************************
217template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
218const AbcA::PropertyHeader &
219SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::getHeader() const
220{
221 ABCA_ASSERT( m_header, "Invalid header" );
222 return *m_header;
223}
224
225//-*****************************************************************************
226template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
227AbcA::ObjectWriterPtr
228SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::getObject()
229{
230 ABCA_ASSERT( m_parent, "Invalid parent" );
231 return m_parent->getObject();
232}
233
234//-*****************************************************************************
235template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
236AbcA::CompoundPropertyWriterPtr
237SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::getParent()
238{
239 ABCA_ASSERT( m_parent, "Invalid parent" );
240 return m_parent;
241}
242
243//-*****************************************************************************
244template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
245hid_t SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::getSampleIGroup()
246{
247 if ( m_sampleIGroup >= 0 )
248 {
249 return m_sampleIGroup;
250 }
251
252 ABCA_ASSERT( m_parentGroup >= 0, "invalid parent group" );
253 ABCA_ASSERT( m_nextSampleIndex > 0,
254 "can't create sampleI group before numSamples > 1" );
255
256 const std::string groupName = m_header->getName() + ".smpi";
257
258 hid_t copl = CreationOrderPlist();
259 PlistCloser plistCloser( copl );
260
261 m_sampleIGroup = H5Gcreate2( m_parentGroup,
262 groupName.c_str(),
263 H5P_DEFAULT,
264 copl,
265 H5P_DEFAULT );
266 ABCA_ASSERT( m_sampleIGroup >= 0,
267 "Could not create simple samples group named: "
268 << groupName );
269
270 return m_sampleIGroup;
271}
272
273//-*****************************************************************************
274template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
275void SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::setSample
276( SAMPLE iSamp )
277{
278 // Make sure we aren't writing more samples than we have times for
279 // This applies to acyclic sampling only
280 ABCA_ASSERT(
281 !m_header->getTimeSampling()->getTimeSamplingType().isAcyclic() ||
282 m_header->getTimeSampling()->getNumStoredTimes() >
283 m_nextSampleIndex,
284 "Can not write more samples than we have times for when using "
285 "Acyclic sampling." );
286
287 // The Key helps us analyze the sample.
288 KEY key = static_cast<IMPL*>(this)->computeSampleKey( iSamp );
289
290 // We need to write the sample
291 if ( m_nextSampleIndex == 0 ||
292 !(static_cast<IMPL*>(this)->sameAsPreviousSample( iSamp, key )) )
293 {
294 const std::string &myName = m_header->getName();
295
296 // we only need to repeat samples if this is not the first change
297 if (m_firstChangedIndex != 0)
298 {
299 // copy the samples from after the last change to the latest index
300 for ( index_t smpI = m_lastChangedIndex + 1;
301 smpI < m_nextSampleIndex; ++smpI )
302 {
303 assert( smpI > 0 );
304 static_cast<IMPL*>(this)->copyPreviousSample(
305 getSampleIGroup(),
306 getSampleName( myName, smpI ),
307 smpI );
308 }
309 }
310 else
311 {
312 m_firstChangedIndex = m_nextSampleIndex;
313 }
314
315 // Write this sample, which will update its internal
316 // cache of what the previously written sample was.
317 static_cast<IMPL*>(this)->writeSample(
318 m_nextSampleIndex == 0 ? m_parentGroup : getSampleIGroup(),
319 getSampleName( myName, m_nextSampleIndex ),
320 m_nextSampleIndex, iSamp, key );
321
322 // this index is now the last change
323 m_lastChangedIndex = m_nextSampleIndex;
324 }
325
326
327 m_nextSampleIndex ++;
328}
329
330//-*****************************************************************************
331template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
332void SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::setFromPreviousSample
333()
334{
335
336 // Make sure we aren't writing more samples than we have times for
337 // This applies to acyclic sampling only
338 ABCA_ASSERT(
339 !m_header->getTimeSampling()->getTimeSamplingType().isAcyclic() ||
340 m_header->getTimeSampling()->getNumStoredTimes() >
341 m_nextSampleIndex,
342 "Can not set more samples than we have times for when using "
343 "Acyclic sampling." );
344
345 ABCA_ASSERT( m_nextSampleIndex > 0,
346 "Can't set from previous sample before any samples have been written" );
347
348 m_nextSampleIndex ++;
349}
350
351//-*****************************************************************************
352template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
353size_t SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::getNumSamples()
354{
355 return ( size_t )m_nextSampleIndex;
356}
357
358template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
359void SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::setTimeSamplingIndex
360( uint32_t iIndex )
361{
362 // will assert if TimeSamplingPtr not found
363 AbcA::TimeSamplingPtr ts =
364 m_parent->getObject()->getArchive()->getTimeSampling(
365 iIndex );
366
367 ABCA_ASSERT( !ts->getTimeSamplingType().isAcyclic() ||
368 ts->getNumStoredTimes() >= m_nextSampleIndex,
369 "Already have written more samples than we have times for when using "
370 "Acyclic sampling." );
371
372 m_header->setTimeSampling(ts);
373 m_timeSamplingIndex = iIndex;
374}
375
376//-*****************************************************************************
377template <class ABSTRACT, class IMPL, class SAMPLE, class KEY>
378SimplePwImpl<ABSTRACT,IMPL,SAMPLE,KEY>::~SimplePwImpl()
379{
380 // Wrap the whole thing in a try block, so as to prevent
381 // exceptions from being thrown out of a destructor.
382 try
383 {
384 if ( m_fileDataType >= 0 && m_cleanFileDataType )
385 { H5Tclose( m_fileDataType ); }
386 if ( m_nativeDataType >= 0 && m_cleanNativeDataType )
387 { H5Tclose( m_nativeDataType ); }
388
389 // Check validity of the group.
390 ABCA_ASSERT( m_parentGroup >= 0, "Invalid parent group" );
391
392 // Close the sampleIGroup if it was open
393 if ( m_sampleIGroup >= 0 )
394 {
395 // this should never have been openened, if a change was never
396 // detected.
397 ABCA_ASSERT( m_firstChangedIndex > 0, "Corrupt SimplePwImpl" );
398 H5Gclose( m_sampleIGroup );
399 m_sampleIGroup = -1;
400 }
401
402
403 AbcA::ArchiveWriterPtr archive = m_parent->getObject()->getArchive();
404
405 index_t maxSamples = archive->getMaxNumSamplesForTimeSamplingIndex(
406 m_timeSamplingIndex );
407
408 uint32_t numSamples = m_nextSampleIndex;
409
410 // a constant property, we wrote the same sample over and over
411 if ( m_lastChangedIndex == 0 && m_nextSampleIndex > 0 )
412 {
413 numSamples = 1;
414 }
415
416 if ( maxSamples < numSamples )
417 {
418 archive->setMaxNumSamplesForTimeSamplingIndex( m_timeSamplingIndex,
419 numSamples );
420 }
421
422 }
423 catch ( std::exception & exc )
424 {
425 std::cerr << "AbcCoreHDF5::SimplePwImpl<A,I,K>::"
426 << "~SimplePwImpl(): EXCEPTION: "
427 << exc.what() << std::endl;
428 }
429 catch ( ... )
430 {
431 std::cerr << "AbcCoreHDF5::SimplePwImpl<A,I,K>::~SimplePwImpl(): "
432 << "UNKNOWN EXCEPTION: " << std::endl;
433 }
434
435 m_parentGroup = -1;
436 m_sampleIGroup = -1;
437 m_fileDataType = -1;
438 m_nativeDataType = -1;
439}
440
441} // End namespace ALEMBIC_VERSION_NS
442
443using namespace ALEMBIC_VERSION_NS;
444
445} // End namespace AbcCoreHDF5
446} // End namespace Alembic
447
448#endif
Alembic namespace ...
Definition ArchiveInfo.cpp:39