Coverage Report

Created: 2026-04-28 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openexr/src/lib/OpenEXR/ImfContextInit.cpp
Line
Count
Source
1
//
2
// SPDX-License-Identifier: BSD-3-Clause
3
// Copyright (c) Contributors to the OpenEXR Project.
4
//
5
6
#include "ImfContextInit.h"
7
#include "ImfIO.h"
8
9
#include <cinttypes>
10
#include <climits>
11
#include <cstring>
12
#include <mutex>
13
14
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
15
16
struct istream_holder
17
{
18
38.3k
    istream_holder (IStream* s) : _stream (s)
19
38.3k
    {
20
38.3k
    }
21
22
#if ILMTHREAD_THREADING_ENABLED
23
    std::mutex _mx;
24
#endif
25
    IStream* _stream;
26
};
27
28
static int64_t
29
istream_size (
30
    exr_const_context_t         ctxt,
31
    void*                       userdata)
32
38.3k
{
33
38.3k
    istream_holder* ih = static_cast<istream_holder*> (userdata);
34
38.3k
    IStream*        s  = ih->_stream;
35
36
38.3k
    return s->size ();
37
38.3k
}
38
39
static int64_t
40
istream_threadsafe_read (
41
    exr_const_context_t         ctxt,
42
    void*                       userdata,
43
    void*                       buffer,
44
    uint64_t                    sz,
45
    uint64_t                    offset,
46
    exr_stream_error_func_ptr_t error_cb)
47
0
{
48
0
    istream_holder* ih    = static_cast<istream_holder*> (userdata);
49
0
    IStream*        s     = ih->_stream;
50
0
    int64_t         nread = -1;
51
52
0
    try
53
0
    {
54
0
        nread = s->read (buffer, sz, offset);
55
0
    }
56
0
    catch (std::exception &e)
57
0
    {
58
0
        error_cb (
59
0
            ctxt,
60
0
            EXR_ERR_READ_IO,
61
0
            "Unable to seek to desired offset %" PRIu64 ": %s",
62
0
            offset,
63
0
            e.what());
64
0
        nread = -1;
65
0
    }
66
0
    catch (...)
67
0
    {
68
0
        error_cb (
69
0
            ctxt,
70
0
            EXR_ERR_READ_IO,
71
0
            "Unable to seek to desired offset %" PRIu64 ": Unknown error",
72
0
            offset);
73
0
        nread = -1;
74
0
    }
75
0
    return nread;
76
0
}
77
78
static int64_t
79
istream_nonparallel_read (
80
    exr_const_context_t         ctxt,
81
    void*                       userdata,
82
    void*                       buffer,
83
    uint64_t                    sz,
84
    uint64_t                    offset,
85
    exr_stream_error_func_ptr_t error_cb)
86
728k
{
87
728k
    istream_holder* ih = static_cast<istream_holder*> (userdata);
88
728k
    IStream*        s  = ih->_stream;
89
90
728k
    if (sz > INT_MAX)
91
0
    {
92
0
        error_cb (
93
0
            ctxt,
94
0
            EXR_ERR_READ_IO,
95
0
            "Stream interface request to read block too large");
96
0
        return -1;
97
0
    }
98
99
728k
#if ILMTHREAD_THREADING_ENABLED
100
728k
    std::lock_guard<std::mutex> lk{ih->_mx};
101
728k
#endif
102
103
728k
    int64_t         nread = s->tellg ();
104
728k
    try
105
728k
    {
106
728k
        if (offset != static_cast<size_t> (nread))
107
547k
        {
108
547k
            s->seekg (offset);
109
547k
            nread = s->tellg ();
110
547k
            if (offset != static_cast<size_t> (nread))
111
0
            {
112
0
                error_cb (
113
0
                    ctxt,
114
0
                    EXR_ERR_READ_IO,
115
0
                    "Unable to seek to desired offset %" PRIu64,
116
0
                    offset);
117
0
                return -1;
118
0
            }
119
547k
        }
120
121
728k
        int64_t stream_sz = s->size ();
122
728k
        int64_t nend = nread + static_cast<int64_t>(sz);
123
728k
        if (stream_sz > 0 && nend > stream_sz)
124
83.5k
            sz = static_cast<uint64_t>(stream_sz - nread);
125
126
728k
        try
127
728k
        {
128
728k
            if (s->isMemoryMapped ())
129
0
            {
130
0
                char* data = s->readMemoryMapped (static_cast<int> (sz));
131
                // TODO: in a future release, pass this through to
132
                // core directly
133
0
                if (data)
134
0
                    memcpy (buffer, data, sz);
135
0
            }
136
728k
            else
137
728k
            {
138
728k
                s->read (static_cast<char*> (buffer), static_cast<int> (sz));
139
728k
            }
140
728k
        }
141
728k
        catch (...)
142
728k
        {
143
            // bah, there could be two reasons for this, one is a
144
            // legitimate error, the other is a read past the end of file
145
            // (i.e. the core library tries to read a 4k block when
146
            // parsing the header), let's let the core deal with that and
147
            // clear errors
148
0
            ih->_stream->clear ();
149
            //error_cb (
150
            //    ctxt,
151
            //    EXR_ERR_READ_IO,
152
            //    "Unable to read requested bytes: %" PRIu64,
153
            //    sz);
154
0
        }
155
728k
        nread = s->tellg () - nread;
156
634k
    }
157
728k
    catch (std::exception &e)
158
728k
    {
159
93.4k
        error_cb (
160
93.4k
            ctxt,
161
93.4k
            EXR_ERR_READ_IO,
162
93.4k
            "Unable to seek to desired offset %" PRIu64 ": %s",
163
93.4k
            offset,
164
93.4k
            e.what());
165
93.4k
        nread = -1;
166
93.4k
    }
167
728k
    catch (...)
168
728k
    {
169
0
        error_cb (
170
0
            ctxt,
171
0
            EXR_ERR_READ_IO,
172
0
            "Unable to seek to desired offset %" PRIu64 ": Unknown error",
173
0
            offset);
174
0
        nread = -1;
175
0
    }
176
728k
    return nread;
177
728k
}
178
179
static void
180
istream_destroy (exr_const_context_t ctxt, void* userdata, int failed)
181
38.3k
{
182
38.3k
    istream_holder* ih = static_cast<istream_holder*> (userdata);
183
38.3k
    delete ih;
184
38.3k
}
185
186
ContextInitializer&
187
ContextInitializer::setInputStream (IStream* istr)
188
38.3k
{
189
38.3k
    _initializer.user_data  = new istream_holder{istr};
190
38.3k
    if (istr->isStatelessRead ())
191
0
        _initializer.read_fn = istream_threadsafe_read;
192
38.3k
    else
193
38.3k
        _initializer.read_fn = istream_nonparallel_read;
194
38.3k
    _initializer.size_fn    = istream_size;
195
38.3k
    _initializer.write_fn   = nullptr;
196
38.3k
    _initializer.destroy_fn = istream_destroy;
197
38.3k
    _ctxt_type              = ContextFileType::READ;
198
38.3k
    _prov_stream            = istr;
199
38.3k
    return *this;
200
38.3k
}
201
202
struct ostream_holder
203
{
204
0
    ostream_holder (OStream* s) : _stream (s)
205
0
    {
206
0
        if (_stream) _cur_offset = _stream->tellp ();
207
0
    }
208
209
#if ILMTHREAD_THREADING_ENABLED
210
    std::mutex _mx;
211
#endif
212
    uint64_t _cur_offset = 0;
213
    OStream* _stream;
214
};
215
216
static int64_t
217
ostream_write (
218
    exr_const_context_t         ctxt,
219
    void*                       userdata,
220
    const void*                 buffer,
221
    uint64_t                    sz,
222
    uint64_t                    offset,
223
    exr_stream_error_func_ptr_t error_cb)
224
0
{
225
0
    ostream_holder* oh = static_cast<ostream_holder*> (userdata);
226
227
0
    if (sz > INT_MAX)
228
0
    {
229
0
        error_cb (
230
0
            ctxt,
231
0
            EXR_ERR_READ_IO,
232
0
            "Stream interface request to write block too large");
233
0
        return -1;
234
0
    }
235
236
0
#if ILMTHREAD_THREADING_ENABLED
237
0
    std::lock_guard<std::mutex> lk{oh->_mx};
238
0
#endif
239
240
0
    if (offset != oh->_cur_offset)
241
0
    {
242
0
        oh->_stream->seekp (offset);
243
0
        oh->_cur_offset = oh->_stream->tellp ();
244
0
        if (offset != oh->_cur_offset)
245
0
        {
246
0
            error_cb (
247
0
                ctxt,
248
0
                EXR_ERR_READ_IO,
249
0
                "Unable to seek to desired offset %" PRIu64,
250
0
                offset);
251
0
            return -1;
252
0
        }
253
0
    }
254
255
0
    int64_t nwrite = oh->_cur_offset;
256
0
    try
257
0
    {
258
0
        oh->_stream->write (
259
0
            static_cast<const char*> (buffer), static_cast<int> (sz));
260
0
        oh->_cur_offset = oh->_stream->tellp ();
261
0
        nwrite          = oh->_cur_offset - nwrite;
262
0
    }
263
0
    catch (...)
264
0
    {
265
0
        error_cb (
266
0
            ctxt,
267
0
            EXR_ERR_READ_IO,
268
0
            "Unable to seek to desired offset %" PRIu64,
269
0
            offset);
270
0
        nwrite = -1;
271
0
    }
272
0
    return nwrite;
273
0
}
274
275
static void
276
ostream_destroy (exr_const_context_t ctxt, void* userdata, int failed)
277
0
{
278
0
    ostream_holder* oh = static_cast<ostream_holder*> (userdata);
279
280
0
    delete oh;
281
0
}
282
283
ContextInitializer&
284
ContextInitializer::setOutputStream (OStream* ostr)
285
0
{
286
0
    _initializer.user_data  = new ostream_holder{ostr};
287
0
    _initializer.read_fn    = nullptr;
288
0
    _initializer.size_fn    = nullptr;
289
0
    _initializer.write_fn   = ostream_write;
290
0
    _initializer.destroy_fn = ostream_destroy;
291
0
    _ctxt_type              = ContextFileType::WRITE;
292
0
    return *this;
293
0
}
294
295
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT