1 //-*****************************************************************************
2 //
3 // Copyright (c) 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 Industrial Light & Magic nor the names of
19 // its contributors may be used to endorse or promote products derived
20 // from this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 //
34 //-*****************************************************************************
35 
36 #include <Alembic/Ogawa/OGroup.h>
37 #include <Alembic/Ogawa/OArchive.h>
38 #include <Alembic/Ogawa/OData.h>
39 #include <Alembic/Ogawa/OStream.h>
40 
41 namespace Alembic {
42 namespace Ogawa {
43 namespace ALEMBIC_VERSION_NS {
44 
45 typedef std::pair< OGroupPtr, Alembic::Util::uint64_t > ParentPair;
46 typedef std::vector< ParentPair > ParentPairVec;
47 
48 class OGroup::PrivateData
49 {
50 public:
PrivateData()51     PrivateData() {};
~PrivateData()52     ~PrivateData() {};
53 
54     OStreamPtr stream;
55 
56     // used before freeze
57     ParentPairVec parents;
58 
59     // used before and after freeze
60     std::vector<Alembic::Util::uint64_t> childVec;
61 
62     // set after freeze
63     Alembic::Util::uint64_t pos;
64 };
65 
OGroup(OGroupPtr iParent,Alembic::Util::uint64_t iIndex)66 OGroup::OGroup(OGroupPtr iParent, Alembic::Util::uint64_t iIndex)
67     : mData(new OGroup::PrivateData())
68 {
69     mData->stream = iParent->mData->stream;
70     mData->parents.push_back( ParentPair(iParent, iIndex) );
71     mData->pos = INVALID_GROUP;
72 }
73 
OGroup(OStreamPtr iStream)74 OGroup::OGroup(OStreamPtr iStream)
75     : mData(new OGroup::PrivateData())
76 {
77     mData->stream = iStream;
78     mData->parents.push_back(ParentPair(OGroupPtr(), 0));
79     mData->pos = INVALID_GROUP;
80 }
81 
~OGroup()82 OGroup::~OGroup()
83 {
84     freeze();
85 }
86 
addGroup()87 OGroupPtr OGroup::addGroup()
88 {
89     OGroupPtr child;
90     if (!isFrozen())
91     {
92         mData->childVec.push_back(0);
93         child.reset(new OGroup(shared_from_this(), mData->childVec.size() - 1));
94     }
95     return child;
96 }
97 
createData(Alembic::Util::uint64_t iSize,const void * iData)98 ODataPtr OGroup::createData(Alembic::Util::uint64_t iSize, const void * iData)
99 {
100     ODataPtr child;
101     if (isFrozen())
102     {
103         return child;
104     }
105 
106     if (iSize == 0)
107     {
108         mData->childVec.push_back(EMPTY_DATA);
109         child.reset(new OData());
110         return child;
111     }
112 
113     Alembic::Util::uint64_t pos = mData->stream->getAndSeekEndPos();
114 
115     Alembic::Util::uint64_t size = iSize;
116     mData->stream->write(&size, 8);
117     mData->stream->write(iData, iSize);
118 
119     child.reset(new OData(mData->stream, pos, iSize));
120 
121     return child;
122 }
123 
addData(Alembic::Util::uint64_t iSize,const void * iData)124 ODataPtr OGroup::addData(Alembic::Util::uint64_t iSize, const void * iData)
125 {
126     ODataPtr child = OGroup::createData(iSize, iData);
127     if (child)
128     {
129         // flip top bit for data so we can easily distinguish between it and
130         // a group
131         mData->childVec.push_back(child->getPos() | 0x8000000000000000ULL);
132     }
133     return child;
134 }
135 
createData(Alembic::Util::uint64_t iNumData,const Alembic::Util::uint64_t * iSizes,const void ** iDatas)136 ODataPtr OGroup::createData(Alembic::Util::uint64_t iNumData,
137                             const Alembic::Util::uint64_t * iSizes,
138                             const void ** iDatas)
139 {
140     ODataPtr child;
141     if (isFrozen())
142     {
143         return child;
144     }
145 
146     Alembic::Util::uint64_t totalSize = 0;
147     for (Alembic::Util::uint64_t i = 0; i < iNumData; ++i)
148     {
149         totalSize += iSizes[i];
150     }
151 
152     if (totalSize == 0)
153     {
154         mData->childVec.push_back(EMPTY_DATA);
155         child.reset(new OData());
156         return child;
157     }
158 
159     Alembic::Util::uint64_t pos = mData->stream->getAndSeekEndPos();
160 
161     mData->stream->write(&totalSize, 8);
162     for (Alembic::Util::uint64_t i = 0; i < iNumData; ++i)
163     {
164         Alembic::Util::uint64_t size = iSizes[i];
165         if (size != 0)
166         {
167             mData->stream->write(iDatas[i], size);
168         }
169     }
170 
171     child.reset(new OData(mData->stream, pos, totalSize));
172 
173     return child;
174 }
175 
addData(Alembic::Util::uint64_t iNumData,const Alembic::Util::uint64_t * iSizes,const void ** iDatas)176 ODataPtr OGroup::addData(Alembic::Util::uint64_t iNumData,
177                          const Alembic::Util::uint64_t * iSizes,
178                          const void ** iDatas)
179 {
180     ODataPtr child = createData(iNumData, iSizes, iDatas);
181     if (child)
182     {
183         // flip top bit for data so we can easily distinguish between it and
184         // a group
185         mData->childVec.push_back(child->getPos() | 0x8000000000000000ULL);
186     }
187     return child;
188 }
189 
addData(ODataPtr iData)190 void OGroup::addData(ODataPtr iData)
191 {
192     if (!isFrozen())
193     {
194         mData->childVec.push_back(iData->getPos() | 0x8000000000000000ULL);
195     }
196 }
197 
addGroup(OGroupPtr iGroup)198 void OGroup::addGroup(OGroupPtr iGroup)
199 {
200     if (!isFrozen())
201     {
202         if (iGroup->isFrozen())
203         {
204             mData->childVec.push_back(iGroup->mData->pos);
205         }
206         else
207         {
208             mData->childVec.push_back(EMPTY_GROUP);
209             iGroup->mData->parents.push_back(
210                 ParentPair(shared_from_this(), mData->childVec.size() - 1));
211         }
212     }
213 }
214 
addEmptyGroup()215 void OGroup::addEmptyGroup()
216 {
217     if (!isFrozen())
218     {
219         mData->childVec.push_back(EMPTY_GROUP);
220     }
221 }
222 
addEmptyData()223 void OGroup::addEmptyData()
224 {
225     if (!isFrozen())
226     {
227         mData->childVec.push_back(EMPTY_DATA);
228     }
229 }
230 
231 // no more children can be added, commit to the stream
freeze()232 void OGroup::freeze()
233 {
234     // bail if we've already done this work
235     if (isFrozen())
236     {
237         return;
238     }
239 
240     // we ended up not adding any children, so no need to commit this group
241     // to disk, use empty group instead
242     if (mData->childVec.empty())
243     {
244         mData->pos = 0;
245     }
246     else
247     {
248         mData->pos = mData->stream->getAndSeekEndPos();
249         Alembic::Util::uint64_t size = mData->childVec.size();
250         mData->stream->write(&size, 8);
251         mData->stream->write(&mData->childVec.front(), size*8);
252     }
253 
254     // go through and update each of the parents
255     ParentPairVec::iterator it;
256     for(it = mData->parents.begin(); it != mData->parents.end(); ++it)
257     {
258         // special group owned by the archive
259         if (!it->first && it->second == 0)
260         {
261             mData->stream->seek(8);
262             mData->stream->write(&mData->pos, 8);
263             continue;
264         }
265         else if (it->first->isFrozen())
266         {
267             mData->stream->seek(it->first->mData->pos + (it->second + 1) * 8);
268             mData->stream->write(&mData->pos, 8);
269         }
270         it->first->mData->childVec[it->second] = mData->pos;
271     }
272 
273     mData->parents.clear();
274 
275 }
276 
isFrozen()277 bool OGroup::isFrozen()
278 {
279     return mData->pos != INVALID_GROUP;
280 }
281 
getNumChildren() const282 Alembic::Util::uint64_t OGroup::getNumChildren() const
283 {
284     return mData->childVec.size();
285 }
286 
isChildGroup(Alembic::Util::uint64_t iIndex) const287 bool OGroup::isChildGroup(Alembic::Util::uint64_t iIndex) const
288 {
289     return (iIndex < mData->childVec.size() &&
290             (mData->childVec[iIndex] & EMPTY_DATA) == 0);
291 }
292 
isChildData(Alembic::Util::uint64_t iIndex) const293 bool OGroup::isChildData(Alembic::Util::uint64_t iIndex) const
294 {
295     return (iIndex < mData->childVec.size() &&
296             (mData->childVec[iIndex] & EMPTY_DATA) != 0);
297 }
298 
isChildEmptyGroup(Alembic::Util::uint64_t iIndex) const299 bool OGroup::isChildEmptyGroup(Alembic::Util::uint64_t iIndex) const
300 {
301     return (iIndex < mData->childVec.size() &&
302             mData->childVec[iIndex] == EMPTY_GROUP);
303 }
304 
isChildEmptyData(Alembic::Util::uint64_t iIndex) const305 bool OGroup::isChildEmptyData(Alembic::Util::uint64_t iIndex) const
306 {
307     return (iIndex < mData->childVec.size() &&
308         mData->childVec[iIndex] == EMPTY_DATA);
309 }
310 
replaceData(Alembic::Util::uint64_t iIndex,ODataPtr iData)311 void OGroup::replaceData(Alembic::Util::uint64_t iIndex, ODataPtr iData)
312 {
313     if (!isChildData(iIndex))
314     {
315         return;
316     }
317 
318     Alembic::Util::uint64_t pos = iData->getPos() | 0x8000000000000000ULL;
319     if (isFrozen())
320     {
321         mData->stream->seek(mData->pos + (iIndex + 1) * 8);
322         mData->stream->write(&pos, 8);
323     }
324     mData->childVec[iIndex] = pos;
325 }
326 
327 } // End namespace ALEMBIC_VERSION_NS
328 } // End namespace Ogawa
329 } // End namespace Alembic
330