/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 |