Field3D
IStreams.cpp
Go to the documentation of this file.
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 "IStreams.h"
37 #include <fstream>
38 #include <stdexcept>
39 
40 namespace Alembic {
41 namespace Ogawa {
42 namespace ALEMBIC_VERSION_NS {
43 
44 class IStreams::PrivateData
45 {
46 public:
47  PrivateData()
48  {
49  locks = NULL;
50  valid = false;
51  frozen = false;
52  version = 0;
53  }
54 
55  ~PrivateData()
56  {
57  if (locks)
58  {
59  delete [] locks;
60  }
61 
62  // only cleanup if we were the ones who opened it
63  if (!fileName.empty())
64  {
65  std::vector<std::istream *>::iterator it;
66  for (it = streams.begin(); it != streams.end(); ++it)
67  {
68  std::ifstream * filestream = dynamic_cast<std::ifstream *>(*it);
69  if (filestream)
70  {
71  filestream->close();
72  delete filestream;
73  }
74  }
75  }
76  }
77 
78  std::vector<std::istream *> streams;
79  std::vector<Alembic::Util::uint64_t> offsets;
80  Alembic::Util::mutex * locks;
81  std::string fileName;
82  bool valid;
83  bool frozen;
84  Alembic::Util::uint16_t version;
85 };
86 
87 IStreams::IStreams(const std::string & iFileName, std::size_t iNumStreams) :
88  mData(new IStreams::PrivateData())
89 {
90 
91  std::ifstream * filestream = new std::ifstream;
92  filestream->open(iFileName.c_str(), std::ios::binary);
93 
94  if (filestream->is_open())
95  {
96  mData->fileName = iFileName;
97  }
98  else
99  {
100  delete filestream;
101  return;
102  }
103 
104  mData->streams.push_back(filestream);
105  init();
106  if (!mData->valid || mData->version != 1)
107  {
108  mData->streams.clear();
109  filestream->close();
110  delete filestream;
111  }
112  else
113  {
114  // we are valid, so we'll allocate (but not open) the others
115  mData->streams.resize(iNumStreams, NULL);
116  mData->offsets.resize(iNumStreams, 0);
117  }
118  mData->locks = new Alembic::Util::mutex[mData->streams.size()];
119 }
120 
121 IStreams::IStreams(const std::vector< std::istream * > & iStreams) :
122  mData(new IStreams::PrivateData())
123 {
124  mData->streams = iStreams;
125  init();
126  if (!mData->valid || mData->version != 1)
127  {
128  mData->streams.clear();
129  return;
130  }
131 
132  mData->locks = new Alembic::Util::mutex[mData->streams.size()];
133 }
134 
135 void IStreams::init()
136 {
137  // simple temporary endian check
138  union {
139  Util::uint32_t l;
140  char c[4];
141  } u;
142 
143  u.l = 0x01234567;
144 
145  if (u.c[0] != 0x67)
146  {
147  throw std::runtime_error(
148  "Ogawa currently only supports little-endian reading.");
149  }
150 
151  if (mData->streams.empty())
152  {
153  return;
154  }
155 
156  Alembic::Util::uint64_t firstGroupPos = 0;
157 
158  for (std::size_t i = 0; i < mData->streams.size(); ++i)
159  {
160  char header[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
161  mData->offsets.push_back(mData->streams[i]->tellg());
162  mData->streams[i]->read(header, 16);
163  std::string magicStr(header, 5);
164  if (magicStr != "Ogawa")
165  {
166  mData->frozen = false;
167  mData->valid = false;
168  mData->version = 0;
169  return;
170  }
171  bool frozen = (header[5] == char(0xff));
172  Alembic::Util::uint16_t version = (header[6] << 8) | header[7];
173  Alembic::Util::uint64_t groupPos =
174  *((Alembic::Util::uint64_t *)(&(header[8])));
175 
176  if (i == 0)
177  {
178  firstGroupPos = groupPos;
179  mData->frozen = frozen;
180  mData->version = version;
181  }
182  // all the streams have to agree, or we are invalid
183  else if (firstGroupPos != groupPos || mData->frozen != frozen ||
184  mData->version != version)
185  {
186  mData->frozen = false;
187  mData->valid = false;
188  mData->version = 0;
189  return;
190  }
191  }
192  mData->valid = true;
193 }
194 
195 IStreams::~IStreams()
196 {
197 }
198 
199 bool IStreams::isValid()
200 {
201  return mData->valid;
202 }
203 
204 bool IStreams::isFrozen()
205 {
206  return mData->frozen;
207 }
208 
209 Alembic::Util::uint16_t IStreams::getVersion()
210 {
211  return mData->version;
212 }
213 
214 void IStreams::read(std::size_t iThreadId, Alembic::Util::uint64_t iPos,
215  Alembic::Util::uint64_t iSize, void * oBuf)
216 {
217  if (!isValid())
218  {
219  return;
220  }
221 
222  std::size_t threadId = 0;
223  if (iThreadId < mData->streams.size())
224  {
225  threadId = iThreadId;
226  }
227 
228  {
229  Alembic::Util::scoped_lock l(mData->locks[threadId]);
230  std::istream * stream = mData->streams[threadId];
231 
232  // the file hasn't been opened for this id yet
233  if (stream == NULL && !mData->fileName.empty())
234  {
235  std::ifstream * filestream = new std::ifstream;
236  filestream->open(mData->fileName.c_str(), std::ios::binary);
237 
238  if (filestream->is_open())
239  {
240  stream = filestream;
241  mData->streams[threadId] = filestream;
242  mData->offsets[threadId] = filestream->tellg();
243  }
244  // couldnt open the file, do cleanup
245  else
246  {
247  delete filestream;
248 
249  // read from thread 0 instead until it can be opened
250  if (threadId != 0)
251  {
252  read(0, iPos, iSize, oBuf);
253  }
254  return;
255  }
256  }
257  stream->seekg(iPos + mData->offsets[threadId]);
258  stream->read((char *)oBuf, iSize);
259  }
260 }
261 
262 } // End namespace ALEMBIC_VERSION_NS
263 } // End namespace Ogawa
264 } // End namespace Alembic
Alembic
Definition: OgawaFwd.h:77