Coverage Report

Created: 2026-06-14 06:57

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
2.89k
    istream_holder (IStream* s) : _stream (s)
19
2.89k
    {
20
2.89k
    }
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
2.89k
{
33
2.89k
    istream_holder* ih = static_cast<istream_holder*> (userdata);
34
2.89k
    IStream*        s  = ih->_stream;
35
36
2.89k
    return s->size ();
37
2.89k
}
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
9.22M
{
87
9.22M
    istream_holder* ih = static_cast<istream_holder*> (userdata);
88
9.22M
    IStream*        s  = ih->_stream;
89
90
9.22M
    if (sz > INT_MAX)
91
2
    {
92
2
        error_cb (
93
2
            ctxt,
94
2
            EXR_ERR_READ_IO,
95
2
            "Stream interface request to read block too large");
96
2
        return -1;
97
2
    }
98
99
9.22M
#if ILMTHREAD_THREADING_ENABLED
100
9.22M
    std::lock_guard<std::mutex> lk{ih->_mx};
101
9.22M
#endif
102
103
9.22M
    int64_t         nread = s->tellg ();
104
9.22M
    try
105
9.22M
    {
106
9.22M
        if (offset != static_cast<size_t> (nread))
107
87.4k
        {
108
87.4k
            s->seekg (offset);
109
87.4k
            nread = s->tellg ();
110
87.4k
            if (offset != static_cast<size_t> (nread))
111
68.2k
            {
112
68.2k
                error_cb (
113
68.2k
                    ctxt,
114
68.2k
                    EXR_ERR_READ_IO,
115
68.2k
                    "Unable to seek to desired offset %" PRIu64,
116
68.2k
                    offset);
117
68.2k
                return -1;
118
68.2k
            }
119
87.4k
        }
120
121
9.15M
        int64_t stream_sz = s->size ();
122
9.15M
        int64_t nend = nread + static_cast<int64_t>(sz);
123
9.15M
        if (stream_sz > 0 && nend > stream_sz)
124
0
            sz = static_cast<uint64_t>(stream_sz - nread);
125
126
9.15M
        try
127
9.15M
        {
128
9.15M
            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
9.15M
            else
137
9.15M
            {
138
9.15M
                s->read (static_cast<char*> (buffer), static_cast<int> (sz));
139
9.15M
            }
140
9.15M
        }
141
9.15M
        catch (...)
142
9.15M
        {
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
1.10k
            ih->_stream->clear ();
149
            //error_cb (
150
            //    ctxt,
151
            //    EXR_ERR_READ_IO,
152
            //    "Unable to read requested bytes: %" PRIu64,
153
            //    sz);
154
1.10k
        }
155
9.15M
        nread = s->tellg () - nread;
156
9.15M
    }
157
9.22M
    catch (std::exception &e)
158
9.22M
    {
159
0
        error_cb (
160
0
            ctxt,
161
0
            EXR_ERR_READ_IO,
162
0
            "Unable to seek to desired offset %" PRIu64 ": %s",
163
0
            offset,
164
0
            e.what());
165
0
        nread = -1;
166
0
    }
167
9.22M
    catch (...)
168
9.22M
    {
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
9.15M
    return nread;
177
9.22M
}
178
179
static void
180
istream_destroy (exr_const_context_t ctxt, void* userdata, int failed)
181
2.89k
{
182
2.89k
    istream_holder* ih = static_cast<istream_holder*> (userdata);
183
2.89k
    delete ih;
184
2.89k
}
185
186
ContextInitializer&
187
ContextInitializer::setInputStream (IStream* istr)
188
2.89k
{
189
2.89k
    _initializer.user_data  = new istream_holder{istr};
190
2.89k
    if (istr->isStatelessRead ())
191
0
        _initializer.read_fn = istream_threadsafe_read;
192
2.89k
    else
193
2.89k
        _initializer.read_fn = istream_nonparallel_read;
194
2.89k
    _initializer.size_fn    = istream_size;
195
2.89k
    _initializer.write_fn   = nullptr;
196
2.89k
    _initializer.destroy_fn = istream_destroy;
197
2.89k
    _ctxt_type              = ContextFileType::READ;
198
2.89k
    _prov_stream            = istr;
199
2.89k
    return *this;
200
2.89k
}
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