1 /*
2 ** SPDX-License-Identifier: BSD-3-Clause
3 ** Copyright Contributors to the OpenEXR Project.
4 */
5
6 #include "internal_structs.h"
7 #include "internal_attr.h"
8 #include "internal_constants.h"
9 #include "internal_memory.h"
10 #include "backward_compatibility.h"
11
12 #include <IlmThreadConfig.h>
13
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include <string.h>
17
18 #ifdef ILMTHREAD_THREADING_ENABLED
19 # ifdef _WIN32
20 # include <synchapi.h>
21 # include <windows.h>
22 # else
23 # include <pthread.h>
24 # endif
25 #endif
26
27 /**************************************/
28
29 static void
default_error_handler(exr_const_context_t ctxt,exr_result_t code,const char * msg)30 default_error_handler (
31 exr_const_context_t ctxt, exr_result_t code, const char* msg)
32 {
33 const struct _internal_exr_context* pctxt = EXR_CCTXT (ctxt);
34
35 #ifdef ILMBASE_THREADING_ENABLED
36 # ifdef _WIN32
37 static CRITICAL_SECTION sMutex;
38 volatile static long initialized = 0;
39 if (InterlockedIncrement (&initialized) == 1)
40 InitializeCriticalSection (&sMutex);
41 initialized = 1; // avoids overflow on long running programs...
42 # else
43 static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER;
44 # endif
45 #endif
46
47 #ifdef ILMBASE_THREADING_ENABLED
48 # ifdef _WIN32
49 EnterCriticalSection (&sMutex);
50 # else
51 pthread_mutex_lock (&sMutex);
52 # endif
53 #endif
54 if (pctxt)
55 {
56 if (pctxt->filename.str)
57 fprintf (
58 stderr,
59 "%s: (%s) %s\n",
60 pctxt->filename.str,
61 exr_get_error_code_as_string (code),
62 msg);
63 else
64 fprintf (
65 stderr,
66 "Context 0x%p: (%s) %s\n",
67 (const void*) ctxt,
68 exr_get_error_code_as_string (code),
69 msg);
70 }
71 else
72 fprintf (stderr, "<ERROR>: %s\n", msg);
73 fflush (stderr);
74
75 #ifdef ILMBASE_THREADING_ENABLED
76 # ifdef _WIN32
77 LeaveCriticalSection (&sMutex);
78 # else
79 pthread_mutex_unlock (&sMutex);
80 # endif
81 #endif
82 }
83
84 static exr_result_t
dispatch_error(const struct _internal_exr_context * pctxt,exr_result_t code,const char * msg)85 dispatch_error (
86 const struct _internal_exr_context* pctxt,
87 exr_result_t code,
88 const char* msg)
89 {
90 exr_const_context_t ctxt = (exr_const_context_t) (pctxt);
91 if (pctxt)
92 {
93 pctxt->error_handler_fn (ctxt, code, msg);
94 return code;
95 }
96
97 default_error_handler (ctxt, code, msg);
98 return code;
99 }
100
101 /**************************************/
102
103 static exr_result_t
dispatch_standard_error(const struct _internal_exr_context * pctxt,exr_result_t code)104 dispatch_standard_error (
105 const struct _internal_exr_context* pctxt, exr_result_t code)
106 {
107 return dispatch_error (pctxt, code, exr_get_default_error_message (code));
108 }
109
110 /**************************************/
111
112 static exr_result_t dispatch_print_error (
113 const struct _internal_exr_context* pctxt,
114 exr_result_t code,
115 const char* msg,
116 ...) EXR_PRINTF_FUNC_ATTRIBUTE;
117
118 static exr_result_t
dispatch_print_error(const struct _internal_exr_context * pctxt,exr_result_t code,const char * msg,...)119 dispatch_print_error (
120 const struct _internal_exr_context* pctxt,
121 exr_result_t code,
122 const char* msg,
123 ...)
124 {
125 char stackbuf[256];
126 char* heapbuf = NULL;
127 int nwrit = 0;
128 va_list fmtargs;
129
130 va_start (fmtargs, msg);
131 {
132 va_list stkargs;
133
134 va_copy (stkargs, fmtargs);
135 nwrit = vsnprintf (stackbuf, 256, msg, stkargs);
136 va_end (stkargs);
137 if (nwrit >= 256)
138 {
139 heapbuf = pctxt->alloc_fn ((size_t) (nwrit + 1));
140 if (heapbuf)
141 {
142 (void) vsnprintf (heapbuf, (size_t) (nwrit + 1), msg, fmtargs);
143 dispatch_error (pctxt, code, heapbuf);
144 pctxt->free_fn (heapbuf);
145 }
146 else
147 dispatch_error (
148 pctxt, code, "Unable to allocate temporary memory");
149 }
150 else
151 dispatch_error (pctxt, code, stackbuf);
152 }
153 va_end (fmtargs);
154 return code;
155 }
156
157 /**************************************/
158
159 static void
internal_exr_destroy_part(struct _internal_exr_context * ctxt,struct _internal_exr_part * cur)160 internal_exr_destroy_part (
161 struct _internal_exr_context* ctxt, struct _internal_exr_part* cur)
162 {
163 exr_memory_free_func_t dofree = ctxt->free_fn;
164 uint64_t* ctable;
165
166 exr_attr_list_destroy ((exr_context_t) ctxt, &(cur->attributes));
167
168 /* we stack x and y together so only have to free the first */
169 if (cur->tile_level_tile_count_x) dofree (cur->tile_level_tile_count_x);
170
171 #if defined(_MSC_VER)
172 ctable = (uint64_t*) InterlockedOr64 (
173 (int64_t volatile*) &(cur->chunk_table), 0);
174 cur->chunk_table = 0;
175 #else
176 ctable = (uint64_t*) atomic_load (&(cur->chunk_table));
177 atomic_store (&(cur->chunk_table), (uintptr_t)(0));
178 #endif
179 if (ctable) dofree (ctable);
180 }
181
182 /**************************************/
183
184 static void
internal_exr_destroy_parts(struct _internal_exr_context * ctxt)185 internal_exr_destroy_parts (struct _internal_exr_context* ctxt)
186 {
187 exr_memory_free_func_t dofree = ctxt->free_fn;
188 for (int p = 0; p < ctxt->num_parts; ++p)
189 {
190 struct _internal_exr_part* cur = ctxt->parts[p];
191
192 internal_exr_destroy_part (ctxt, cur);
193
194 /* the first one is always the one that is part of the file */
195 if (cur != &(ctxt->first_part)) { dofree (cur); }
196 else
197 {
198 memset (cur, 0, sizeof (struct _internal_exr_part));
199 }
200 }
201
202 if (ctxt->num_parts > 1) dofree (ctxt->parts);
203 ctxt->parts = NULL;
204 ctxt->num_parts = 0;
205 }
206
207 /**************************************/
208
209 exr_result_t
internal_exr_add_part(struct _internal_exr_context * f,struct _internal_exr_part ** outpart,int * new_index)210 internal_exr_add_part (
211 struct _internal_exr_context* f,
212 struct _internal_exr_part** outpart,
213 int* new_index)
214 {
215 int ncount = f->num_parts + 1;
216 struct _internal_exr_part* part;
217 struct _internal_exr_part** nptrs = NULL;
218
219 if (new_index) *new_index = f->num_parts;
220
221 if (ncount == 1)
222 {
223 /* no need to zilch, the parent struct will have already been zero'ed */
224 part = &(f->first_part);
225 f->init_part = part;
226 nptrs = &(f->init_part);
227 }
228 else
229 {
230 struct _internal_exr_part nil = { 0 };
231
232 part = f->alloc_fn (sizeof (struct _internal_exr_part));
233 if (!part) return f->standard_error (f, EXR_ERR_OUT_OF_MEMORY);
234
235 nptrs =
236 f->alloc_fn (sizeof (struct _internal_exr_part*) * (size_t) ncount);
237 if (!nptrs)
238 {
239 f->free_fn (part);
240 return f->standard_error (f, EXR_ERR_OUT_OF_MEMORY);
241 }
242 *part = nil;
243 }
244
245 /* assign appropriately invalid values */
246 part->storage_mode = EXR_STORAGE_LAST_TYPE;
247 part->data_window.max.x = -1;
248 part->data_window.max.y = -1;
249 part->display_window.max.x = -1;
250 part->display_window.max.y = -1;
251 part->chunk_count = -1;
252
253 part->zip_compression_level = f->default_zip_level;
254 part->dwa_compression_level = f->default_dwa_quality;
255
256 /* put it into the part table */
257 if (ncount > 1)
258 {
259 for (int p = 0; p < f->num_parts; ++p)
260 {
261 nptrs[p] = f->parts[p];
262 }
263 nptrs[ncount - 1] = part;
264 }
265
266 if (f->num_parts > 1) { f->free_fn (f->parts); }
267 f->parts = nptrs;
268 f->num_parts = ncount;
269 if (outpart) *outpart = part;
270
271 return EXR_ERR_SUCCESS;
272 }
273
274 /**************************************/
275
276 void
internal_exr_revert_add_part(struct _internal_exr_context * ctxt,struct _internal_exr_part ** outpart,int * new_index)277 internal_exr_revert_add_part (
278 struct _internal_exr_context* ctxt,
279 struct _internal_exr_part** outpart,
280 int* new_index)
281 {
282 int ncount = ctxt->num_parts - 1;
283 struct _internal_exr_part* part = *outpart;
284
285 *outpart = NULL;
286 *new_index = -1;
287
288 internal_exr_destroy_part (ctxt, part);
289 if (ncount == 0)
290 {
291 ctxt->num_parts = 0;
292 ctxt->init_part = NULL;
293 ctxt->parts = NULL;
294 }
295 else if (ncount == 1)
296 {
297 if (part == &(ctxt->first_part))
298 ctxt->first_part = *(ctxt->parts[1]);
299 ctxt->init_part = &(ctxt->first_part);
300 ctxt->free_fn (ctxt->parts);
301 ctxt->parts = &(ctxt->init_part);
302 }
303 else
304 {
305 int np = 0;
306 for (int p = 0; p < ctxt->num_parts; ++p)
307 {
308 if (ctxt->parts[p] == part) continue;
309 ctxt->parts[np] = ctxt->parts[p];
310 ++np;
311 }
312 }
313 ctxt->num_parts = ncount;
314 }
315
316 /**************************************/
317
318 exr_result_t
internal_exr_alloc_context(struct _internal_exr_context ** out,const exr_context_initializer_t * initializers,enum _INTERNAL_EXR_CONTEXT_MODE mode,size_t default_size)319 internal_exr_alloc_context (
320 struct _internal_exr_context** out,
321 const exr_context_initializer_t* initializers,
322 enum _INTERNAL_EXR_CONTEXT_MODE mode,
323 size_t default_size)
324 {
325 void* memptr;
326 exr_result_t rv;
327 struct _internal_exr_context* ret;
328 *out = NULL;
329 int gmaxw, gmaxh;
330 size_t extra_data;
331
332 if (initializers->read_fn || initializers->write_fn)
333 extra_data = 0;
334 else
335 extra_data = default_size;
336
337 memptr = (initializers->alloc_fn) (
338 sizeof (struct _internal_exr_context) + extra_data);
339 if (memptr)
340 {
341 memset (memptr, 0, sizeof (struct _internal_exr_context));
342
343 ret = memptr;
344 ret->mode = (uint8_t) mode;
345 /* stash this separately so when a user queries they don't see
346 * any of our internal hijinx */
347 ret->real_user_data = initializers->user_data;
348 if (initializers->read_fn || initializers->write_fn)
349 ret->user_data = initializers->user_data;
350 else if (extra_data > 0)
351 ret->user_data =
352 (((uint8_t*) memptr) + sizeof (struct _internal_exr_context));
353
354 ret->standard_error = &dispatch_standard_error;
355 ret->report_error = &dispatch_error;
356 ret->print_error = &dispatch_print_error;
357 ret->error_handler_fn = initializers->error_handler_fn;
358 ret->alloc_fn = initializers->alloc_fn;
359 ret->free_fn = initializers->free_fn;
360
361 exr_get_default_maximum_image_size (&gmaxw, &gmaxh);
362 if (initializers->max_image_width <= 0)
363 ret->max_image_w = gmaxw;
364 else
365 ret->max_image_w = initializers->max_image_width;
366 if (ret->max_image_w > 0 && gmaxw > 0 && ret->max_image_w &&
367 ret->max_image_w > gmaxw)
368 ret->max_image_w = gmaxw;
369
370 if (initializers->max_image_height <= 0)
371 ret->max_image_h = gmaxh;
372 else
373 ret->max_image_h = initializers->max_image_height;
374 if (ret->max_image_h > 0 && gmaxh > 0 && ret->max_image_h &&
375 ret->max_image_h > gmaxh)
376 ret->max_image_h = gmaxh;
377
378 exr_get_default_maximum_tile_size (&gmaxw, &gmaxh);
379 if (initializers->max_tile_width <= 0)
380 ret->max_tile_w = gmaxw;
381 else
382 ret->max_tile_w = initializers->max_tile_width;
383 if (ret->max_tile_w > 0 && gmaxw > 0 && ret->max_tile_w &&
384 ret->max_tile_w > gmaxw)
385 ret->max_tile_w = gmaxw;
386
387 if (initializers->max_tile_height <= 0)
388 ret->max_tile_h = gmaxh;
389 else
390 ret->max_tile_h = initializers->max_tile_height;
391 if (ret->max_tile_h > 0 && gmaxh > 0 && ret->max_tile_h &&
392 ret->max_tile_h > gmaxh)
393 ret->max_tile_h = gmaxh;
394
395 exr_get_default_zip_compression_level (&ret->default_zip_level);
396 exr_get_default_dwa_compression_quality (&ret->default_dwa_quality);
397 if (initializers->size >= sizeof(struct _exr_context_initializer_v2))
398 {
399 if (initializers->zip_level >= 0)
400 ret->default_zip_level = initializers->zip_level;
401 if (initializers->dwa_quality >= 0.f)
402 ret->default_dwa_quality = initializers->dwa_quality;
403 }
404
405 ret->file_size = -1;
406 ret->max_name_length = EXR_SHORTNAME_MAXLEN;
407
408 ret->destroy_fn = initializers->destroy_fn;
409 ret->read_fn = initializers->read_fn;
410 ret->write_fn = initializers->write_fn;
411
412 #ifdef ILMTHREAD_THREADING_ENABLED
413 # ifdef _WIN32
414 InitializeCriticalSection (&(ret->mutex));
415 # else
416 rv = pthread_mutex_init (&(ret->mutex), NULL);
417 if (rv != 0)
418 {
419 /* fairly unlikely... */
420 (initializers->free_fn) (memptr);
421 *out = NULL;
422 return EXR_ERR_OUT_OF_MEMORY;
423 }
424 # endif
425 #endif
426
427 *out = ret;
428 rv = EXR_ERR_SUCCESS;
429
430 /* if we are reading the file, go ahead and set up the first
431 * part to make parsing logic easier */
432 if (mode != EXR_CONTEXT_WRITE)
433 {
434 struct _internal_exr_part* part;
435 rv = internal_exr_add_part (ret, &part, NULL);
436 if (rv != EXR_ERR_SUCCESS)
437 {
438 /* this should never happen since we reserve space for
439 * one in the struct, but maybe we changed
440 * something */
441 (initializers->free_fn) (memptr);
442 *out = NULL;
443 }
444 }
445 }
446 else
447 {
448 (initializers->error_handler_fn) (
449 NULL,
450 EXR_ERR_OUT_OF_MEMORY,
451 exr_get_default_error_message (EXR_ERR_OUT_OF_MEMORY));
452 rv = EXR_ERR_OUT_OF_MEMORY;
453 }
454
455 return rv;
456 }
457
458 /**************************************/
459
460 void
internal_exr_destroy_context(struct _internal_exr_context * ctxt)461 internal_exr_destroy_context (struct _internal_exr_context* ctxt)
462 {
463 exr_memory_free_func_t dofree = ctxt->free_fn;
464
465 exr_attr_string_destroy ((exr_context_t) ctxt, &(ctxt->filename));
466 exr_attr_string_destroy ((exr_context_t) ctxt, &(ctxt->tmp_filename));
467 exr_attr_list_destroy ((exr_context_t) ctxt, &(ctxt->custom_handlers));
468 internal_exr_destroy_parts (ctxt);
469 #ifdef ILMTHREAD_THREADING_ENABLED
470 # ifdef _WIN32
471 DeleteCriticalSection (&(ctxt->mutex));
472 # else
473 pthread_mutex_destroy (&(ctxt->mutex));
474 # endif
475 #endif
476
477 dofree (ctxt);
478 }
479
480 /**************************************/
481
482 void
internal_exr_update_default_handlers(exr_context_initializer_t * inits)483 internal_exr_update_default_handlers (exr_context_initializer_t* inits)
484 {
485 if (!inits->error_handler_fn)
486 inits->error_handler_fn = &default_error_handler;
487
488 if (!inits->alloc_fn) inits->alloc_fn = &internal_exr_alloc;
489 if (!inits->free_fn) inits->free_fn = &internal_exr_free;
490 }
491