Coverage Report

Created: 2025-10-12 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/alembic/lib/Alembic/Ogawa/OStream.cpp
Line
Count
Source
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/OStream.h>
37
#include <fstream>
38
#include <stdexcept>
39
40
// for mingw support
41
#if defined _WIN32 || defined _WIN64
42
    #include <Windows.h>
43
#endif
44
45
namespace Alembic {
46
namespace Ogawa {
47
namespace ALEMBIC_VERSION_NS {
48
49
class OStream::PrivateData
50
{
51
public:
52
    PrivateData(const std::string & iFileName) :
53
0
        stream(NULL), fileName(iFileName), startPos(0), curPos(0), maxPos(0)
54
0
    {
55
#ifdef _WIN32
56
        // to wchar_t
57
        // get the size of the UTF8 string
58
        int wLength = MultiByteToWideChar(CP_UTF8, 0, fileName.c_str(), -1, NULL, 0);
59
60
        // allocate buffer
61
        wchar_t* wFileName = (wchar_t*)malloc(wLength * sizeof(wchar_t));
62
        if (!wFileName)
63
          throw std::runtime_error("Unable to convert to wchar_t file name");
64
65
        // convert to UTF8
66
        MultiByteToWideChar(CP_UTF8, 0, fileName.c_str(), -1, wFileName, wLength);
67
68
        // open stream with UTF8 string
69
        std::ofstream * filestream = new std::ofstream(wFileName, std::ios_base::trunc | std::ios_base::binary);
70
71
        // free conversion buffer
72
        free(wFileName);
73
#else
74
0
        std::ofstream * filestream = new std::ofstream(fileName.c_str(),
75
0
            std::ios_base::trunc | std::ios_base::binary);
76
0
#endif
77
0
        if (filestream->is_open())
78
0
        {
79
0
            stream = filestream;
80
#if defined _WIN32 || defined _WIN64
81
            filestream->rdbuf()->pubsetbuf(buffer, sizeof(buffer));
82
#endif
83
0
            stream->exceptions ( std::ofstream::failbit |
84
0
                                 std::ofstream::badbit );
85
0
        }
86
0
        else
87
0
        {
88
0
            filestream->close();
89
0
            delete filestream;
90
0
        }
91
0
    }
92
93
    PrivateData(std::ostream * iStream) :
94
0
        stream(iStream), startPos(0), curPos(0), maxPos(0)
95
0
    {
96
0
        if (stream)
97
0
        {
98
0
            stream->exceptions ( std::ostream::failbit |
99
0
                                 std::ostream::badbit );
100
101
0
            startPos = stream->tellp();
102
0
            if (startPos == INVALID_DATA)
103
0
            {
104
0
                throw std::runtime_error("Illegal start of Ogawa stream");
105
0
            }
106
0
        }
107
0
    }
108
109
    ~PrivateData()
110
0
    {
111
        // if this was done via file, try to clean it up
112
0
        if (!fileName.empty() && stream)
113
0
        {
114
0
            std::ofstream * filestream = dynamic_cast<std::ofstream *>(stream);
115
0
            if (filestream)
116
0
            {
117
0
                filestream->close();
118
0
                delete filestream;
119
0
            }
120
0
        }
121
0
    }
122
123
#if defined _WIN32 || defined _WIN64
124
    char buffer [STREAM_BUF_SIZE];
125
#endif
126
    std::ostream * stream;
127
    std::string fileName;
128
    Alembic::Util::uint64_t startPos;
129
    Alembic::Util::uint64_t curPos;
130
    Alembic::Util::uint64_t maxPos;
131
    Alembic::Util::mutex lock;
132
};
133
134
OStream::OStream(const std::string & iFileName) :
135
0
    mData(new PrivateData(iFileName))
136
0
{
137
0
    init();
138
0
}
139
140
// we'll be writing from this already open stream which we don't own
141
0
OStream::OStream(std::ostream * iStream) : mData(new PrivateData(iStream))
142
0
{
143
0
    init();
144
0
}
145
146
OStream::~OStream()
147
0
{
148
    // write our "frozen" byte (totally done writing)
149
0
    if (isValid())
150
0
    {
151
0
        char frozen = 0xff;
152
0
        mData->stream->seekp(mData->startPos + 5).write(&frozen, 1).flush();
153
0
    }
154
0
}
155
156
bool OStream::isValid()
157
0
{
158
0
    return mData->stream != NULL;
159
0
}
160
161
void OStream::init()
162
0
{
163
    // simple temporary endian check
164
0
    union {
165
0
        Util::uint32_t l;
166
0
        char c[4];
167
0
    } u;
168
169
0
    u.l = 0x01234567;
170
171
0
    if (u.c[0] != 0x67)
172
0
    {
173
0
        throw std::runtime_error(
174
0
            "Ogawa currently only supports little-endian writing.");
175
0
    }
176
177
0
    if (isValid())
178
0
    {
179
0
        const char header[] = {
180
0
            'O', 'g', 'a', 'w', 'a',  // special magic number
181
0
            0,       // this will be 0xff when the entire archive is done
182
0
            0, 1,    // 16 bit format version number
183
0
            0, 0, 0, 0, 0, 0, 0, 0}; // position of the first group
184
0
        mData->stream->write(header, sizeof(header)).flush();
185
0
        mData->curPos += sizeof(header);
186
0
        if( mData->curPos > mData->maxPos )
187
0
        {
188
0
            mData->maxPos = mData->curPos;
189
0
        }
190
0
    }
191
0
}
192
193
Alembic::Util::uint64_t OStream::getAndSeekEndPos()
194
0
{
195
0
    if (isValid())
196
0
    {
197
0
        Alembic::Util::scoped_lock l(mData->lock);
198
199
0
        mData->curPos = mData->maxPos;
200
0
        mData->stream->seekp(mData->curPos + mData->startPos);
201
0
        return mData->curPos;
202
0
    }
203
0
    return 0;
204
0
}
205
206
void OStream::seek(Alembic::Util::uint64_t iPos)
207
0
{
208
0
    if (isValid())
209
0
    {
210
0
        Alembic::Util::scoped_lock l(mData->lock);
211
0
        mData->stream->seekp(iPos + mData->startPos);
212
0
        mData->curPos = iPos;
213
0
    }
214
0
}
215
216
void OStream::write(const void * iBuf, Alembic::Util::uint64_t iSize)
217
0
{
218
0
    if (isValid())
219
0
    {
220
0
        Alembic::Util::scoped_lock l(mData->lock);
221
0
        mData->stream->write((const char *)iBuf, iSize).flush();
222
0
        mData->curPos += iSize;
223
0
        if(mData->curPos > mData->maxPos)
224
0
        {
225
0
            mData->maxPos = mData->curPos;
226
0
        }
227
0
    }
228
0
}
229
230
} // End namespace ALEMBIC_VERSION_NS
231
} // End namespace Ogawa
232
} // End namespace Alembic