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