1 /*
2 ** SPDX-License-Identifier: BSD-3-Clause
3 ** Copyright Contributors to the OpenEXR Project.
4 */
5 
6 #include "internal_attr.h"
7 
8 #include "internal_constants.h"
9 #include "internal_structs.h"
10 
11 #include <string.h>
12 
13 struct _internal_exr_attr_map
14 {
15     const char*          name;
16     uint32_t             name_len;
17     exr_attribute_type_t type;
18     size_t               exp_size;
19 };
20 
21 static struct _internal_exr_attr_map the_predefined_attr_typenames[] = {
22     { "box2i", 5, EXR_ATTR_BOX2I, sizeof (exr_attr_box2i_t) },
23     { "box2f", 5, EXR_ATTR_BOX2F, sizeof (exr_attr_box2f_t) },
24     { "chlist", 6, EXR_ATTR_CHLIST, sizeof (exr_attr_chlist_t) },
25     { "chromaticities",
26       14,
27       EXR_ATTR_CHROMATICITIES,
28       sizeof (exr_attr_chromaticities_t) },
29     { "compression", 11, EXR_ATTR_COMPRESSION, 0 },
30     { "double", 6, EXR_ATTR_DOUBLE, 0 },
31     { "envmap", 6, EXR_ATTR_ENVMAP, 0 },
32     { "float", 5, EXR_ATTR_FLOAT, 0 },
33     { "floatvector",
34       11,
35       EXR_ATTR_FLOAT_VECTOR,
36       sizeof (exr_attr_float_vector_t) },
37     { "int", 3, EXR_ATTR_INT, 0 },
38     { "keycode", 7, EXR_ATTR_KEYCODE, sizeof (exr_attr_keycode_t) },
39     { "lineOrder", 9, EXR_ATTR_LINEORDER, 0 },
40     { "m33f", 4, EXR_ATTR_M33F, sizeof (exr_attr_m33f_t) },
41     { "m33d", 4, EXR_ATTR_M33D, sizeof (exr_attr_m33d_t) },
42     { "m44f", 4, EXR_ATTR_M44F, sizeof (exr_attr_m44f_t) },
43     { "m44d", 4, EXR_ATTR_M44D, sizeof (exr_attr_m44d_t) },
44     { "preview", 7, EXR_ATTR_PREVIEW, sizeof (exr_attr_preview_t) },
45     { "rational", 8, EXR_ATTR_RATIONAL, sizeof (exr_attr_rational_t) },
46     { "string", 6, EXR_ATTR_STRING, sizeof (exr_attr_string_t) },
47     { "stringvector",
48       12,
49       EXR_ATTR_STRING_VECTOR,
50       sizeof (exr_attr_string_vector_t) },
51     { "tiledesc", 8, EXR_ATTR_TILEDESC, sizeof (exr_attr_tiledesc_t) },
52     { "timecode", 8, EXR_ATTR_TIMECODE, sizeof (exr_attr_timecode_t) },
53     { "v2i", 3, EXR_ATTR_V2I, sizeof (exr_attr_v2i_t) },
54     { "v2f", 3, EXR_ATTR_V2F, sizeof (exr_attr_v2f_t) },
55     { "v2d", 3, EXR_ATTR_V2D, sizeof (exr_attr_v2d_t) },
56     { "v3i", 3, EXR_ATTR_V3I, sizeof (exr_attr_v3i_t) },
57     { "v3f", 3, EXR_ATTR_V3F, sizeof (exr_attr_v3f_t) },
58     { "v3d", 3, EXR_ATTR_V3D, sizeof (exr_attr_v3d_t) }
59 };
60 static int the_predefined_attr_count = sizeof (the_predefined_attr_typenames) /
61                                        sizeof (struct _internal_exr_attr_map);
62 
63 /**************************************/
64 
65 static exr_result_t
attr_init(struct _internal_exr_context * ctxt,exr_attribute_t * nattr)66 attr_init (struct _internal_exr_context* ctxt, exr_attribute_t* nattr)
67 {
68     switch (nattr->type)
69     {
70         case EXR_ATTR_BOX2I: {
71             exr_attr_box2i_t nil = { 0 };
72             *(nattr->box2i)      = nil;
73             break;
74         }
75         case EXR_ATTR_BOX2F: {
76             exr_attr_box2f_t nil = { 0 };
77             *(nattr->box2f)      = nil;
78             break;
79         }
80         case EXR_ATTR_CHLIST: {
81             exr_attr_chlist_t nil = { 0 };
82             *(nattr->chlist)      = nil;
83             break;
84         }
85         case EXR_ATTR_CHROMATICITIES: {
86             exr_attr_chromaticities_t nil = { 0 };
87             *(nattr->chromaticities)      = nil;
88             break;
89         }
90         case EXR_ATTR_COMPRESSION:
91         case EXR_ATTR_ENVMAP:
92         case EXR_ATTR_LINEORDER: nattr->uc = 0; break;
93         case EXR_ATTR_DOUBLE: nattr->d = 0.0; break;
94         case EXR_ATTR_FLOAT: nattr->f = 0.0f; break;
95         case EXR_ATTR_FLOAT_VECTOR: {
96             exr_attr_float_vector_t nil = { 0 };
97             *(nattr->floatvector)       = nil;
98             break;
99         }
100         case EXR_ATTR_INT: nattr->i = 0; break;
101         case EXR_ATTR_KEYCODE: {
102             exr_attr_keycode_t nil = { 0 };
103             *(nattr->keycode)      = nil;
104             break;
105         }
106         case EXR_ATTR_M33F: {
107             exr_attr_m33f_t nil = { 0 };
108             *(nattr->m33f)      = nil;
109             break;
110         }
111         case EXR_ATTR_M33D: {
112             exr_attr_m33d_t nil = { 0 };
113             *(nattr->m33d)      = nil;
114             break;
115         }
116         case EXR_ATTR_M44F: {
117             exr_attr_m44f_t nil = { 0 };
118             *(nattr->m44f)      = nil;
119             break;
120         }
121         case EXR_ATTR_M44D: {
122             exr_attr_m44f_t nil = { 0 };
123             *(nattr->m44f)      = nil;
124             break;
125         }
126         case EXR_ATTR_PREVIEW: {
127             exr_attr_preview_t nil = { 0 };
128             *(nattr->preview)      = nil;
129             break;
130         }
131         case EXR_ATTR_RATIONAL: {
132             exr_attr_rational_t nil = { 0 };
133             *(nattr->rational)      = nil;
134             break;
135         }
136         case EXR_ATTR_STRING: {
137             exr_attr_string_t nil = { 0 };
138             *(nattr->string)      = nil;
139             break;
140         }
141         case EXR_ATTR_STRING_VECTOR: {
142             exr_attr_string_vector_t nil = { 0 };
143             *(nattr->stringvector)       = nil;
144             break;
145         }
146         case EXR_ATTR_TILEDESC: {
147             exr_attr_tiledesc_t nil = { 0 };
148             *(nattr->tiledesc)      = nil;
149             break;
150         }
151         case EXR_ATTR_TIMECODE: {
152             exr_attr_timecode_t nil = { 0 };
153             *(nattr->timecode)      = nil;
154             break;
155         }
156         case EXR_ATTR_V2I: {
157             exr_attr_v2i_t nil = { 0 };
158             *(nattr->v2i)      = nil;
159             break;
160         }
161         case EXR_ATTR_V2F: {
162             exr_attr_v2f_t nil = { 0 };
163             *(nattr->v2f)      = nil;
164             break;
165         }
166         case EXR_ATTR_V2D: {
167             exr_attr_v2d_t nil = { 0 };
168             *(nattr->v2d)      = nil;
169             break;
170         }
171         case EXR_ATTR_V3I: {
172             exr_attr_v3i_t nil = { 0 };
173             *(nattr->v3i)      = nil;
174             break;
175         }
176         case EXR_ATTR_V3F: {
177             exr_attr_v3f_t nil = { 0 };
178             *(nattr->v3f)      = nil;
179             break;
180         }
181         case EXR_ATTR_V3D: {
182             exr_attr_v3d_t nil = { 0 };
183             *(nattr->v3d)      = nil;
184             break;
185         }
186         case EXR_ATTR_OPAQUE: {
187             exr_attr_opaquedata_t nil = { 0 };
188             *(nattr->opaque)          = nil;
189             break;
190         }
191         case EXR_ATTR_UNKNOWN:
192         case EXR_ATTR_LAST_KNOWN_TYPE:
193         default:
194             if (ctxt)
195                 ctxt->print_error (
196                     ctxt,
197                     EXR_ERR_INVALID_ARGUMENT,
198                     "Invalid / unimplemented type (%s) in attr_init",
199                     nattr->type_name);
200             return EXR_ERR_INVALID_ARGUMENT;
201     }
202     return EXR_ERR_SUCCESS;
203 }
204 
205 /**************************************/
206 
207 static exr_result_t
attr_destroy(struct _internal_exr_context * ctxt,exr_attribute_t * attr)208 attr_destroy (struct _internal_exr_context* ctxt, exr_attribute_t* attr)
209 {
210     exr_result_t rv = EXR_ERR_SUCCESS;
211     switch (attr->type)
212     {
213         case EXR_ATTR_CHLIST:
214             rv = exr_attr_chlist_destroy ((exr_context_t) ctxt, attr->chlist);
215             break;
216         case EXR_ATTR_FLOAT_VECTOR:
217             rv = exr_attr_float_vector_destroy (
218                 (exr_context_t) ctxt, attr->floatvector);
219             break;
220         case EXR_ATTR_PREVIEW:
221             rv = exr_attr_preview_destroy ((exr_context_t) ctxt, attr->preview);
222             break;
223         case EXR_ATTR_STRING:
224             rv = exr_attr_string_destroy ((exr_context_t) ctxt, attr->string);
225             break;
226         case EXR_ATTR_STRING_VECTOR:
227             rv = exr_attr_string_vector_destroy (
228                 (exr_context_t) ctxt, attr->stringvector);
229             break;
230         case EXR_ATTR_OPAQUE:
231             rv = exr_attr_opaquedata_destroy (
232                 (exr_context_t) ctxt, attr->opaque);
233             break;
234         case EXR_ATTR_BOX2I:
235         case EXR_ATTR_BOX2F:
236         case EXR_ATTR_CHROMATICITIES:
237         case EXR_ATTR_COMPRESSION:
238         case EXR_ATTR_ENVMAP:
239         case EXR_ATTR_LINEORDER:
240         case EXR_ATTR_DOUBLE:
241         case EXR_ATTR_FLOAT:
242         case EXR_ATTR_INT:
243         case EXR_ATTR_KEYCODE:
244         case EXR_ATTR_M33F:
245         case EXR_ATTR_M33D:
246         case EXR_ATTR_M44F:
247         case EXR_ATTR_M44D:
248         case EXR_ATTR_RATIONAL:
249         case EXR_ATTR_TILEDESC:
250         case EXR_ATTR_TIMECODE:
251         case EXR_ATTR_V2I:
252         case EXR_ATTR_V2F:
253         case EXR_ATTR_V2D:
254         case EXR_ATTR_V3I:
255         case EXR_ATTR_V3F:
256         case EXR_ATTR_V3D:
257         case EXR_ATTR_UNKNOWN:
258         case EXR_ATTR_LAST_KNOWN_TYPE:
259         default: break;
260     }
261     /* we don't care about the string because they were built into the
262      * allocation block of the attribute as necessary */
263     ctxt->free_fn (attr);
264     return rv;
265 }
266 
267 /**************************************/
268 
269 int
internal_exr_is_standard_type(const char * typen)270 internal_exr_is_standard_type (const char* typen)
271 {
272     for (int i = 0; i < the_predefined_attr_count; ++i)
273     {
274         if (0 == strcmp (typen, the_predefined_attr_typenames[i].name))
275             return 1;
276     }
277     return 0;
278 }
279 
280 /**************************************/
281 
282 exr_result_t
exr_attr_list_destroy(exr_context_t ctxt,exr_attribute_list_t * list)283 exr_attr_list_destroy (exr_context_t ctxt, exr_attribute_list_t* list)
284 {
285     exr_attribute_list_t nil = { 0 };
286     exr_result_t         arv;
287     exr_result_t         rv = EXR_ERR_SUCCESS;
288 
289     INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR (ctxt);
290 
291     if (list)
292     {
293         if (list->entries)
294         {
295             for (int i = 0; i < list->num_attributes; ++i)
296             {
297                 arv = attr_destroy (pctxt, list->entries[i]);
298                 if (arv != EXR_ERR_SUCCESS) rv = arv;
299             }
300             pctxt->free_fn (list->entries);
301         }
302         *list = nil;
303     }
304     return rv;
305 }
306 
307 /**************************************/
308 
309 exr_result_t
exr_attr_list_compute_size(exr_context_t ctxt,exr_attribute_list_t * list,uint64_t * out)310 exr_attr_list_compute_size (
311     exr_context_t ctxt, exr_attribute_list_t* list, uint64_t* out)
312 {
313     uint64_t     retval = 0;
314     exr_result_t rv     = EXR_ERR_SUCCESS;
315 
316     INTERN_EXR_PROMOTE_CONST_CONTEXT_OR_ERROR (ctxt);
317 
318     if (!list)
319         return pctxt->report_error (
320             pctxt, EXR_ERR_INVALID_ARGUMENT, "Missing list to compute size");
321 
322     if (!out)
323         return pctxt->report_error (
324             pctxt, EXR_ERR_INVALID_ARGUMENT, "Expected output pointer");
325 
326     *out = 0;
327     for (int i = 0; i < list->num_attributes; ++i)
328     {
329         const exr_attribute_t* cur = list->entries[i];
330         retval += (size_t) cur->name_length + 1;
331         retval += (size_t) cur->type_name_length + 1;
332         retval += sizeof (int32_t);
333         switch (cur->type)
334         {
335             case EXR_ATTR_BOX2I: retval += sizeof (*(cur->box2i)); break;
336             case EXR_ATTR_BOX2F: retval += sizeof (*(cur->box2f)); break;
337             case EXR_ATTR_CHLIST:
338                 for (int c = 0; c < cur->chlist->num_channels; ++c)
339                 {
340                     retval += (size_t) cur->chlist->entries[c].name.length + 1;
341                     retval += sizeof (int32_t) * 4;
342                 }
343                 break;
344             case EXR_ATTR_CHROMATICITIES:
345                 retval += sizeof (*(cur->chromaticities));
346                 break;
347             case EXR_ATTR_COMPRESSION:
348             case EXR_ATTR_ENVMAP:
349             case EXR_ATTR_LINEORDER: retval += sizeof (uint8_t); break;
350             case EXR_ATTR_DOUBLE: retval += sizeof (double); break;
351             case EXR_ATTR_FLOAT: retval += sizeof (float); break;
352             case EXR_ATTR_FLOAT_VECTOR:
353                 retval += sizeof (float) * (size_t) (cur->floatvector->length);
354                 break;
355             case EXR_ATTR_INT: retval += sizeof (int32_t); break;
356             case EXR_ATTR_KEYCODE: retval += sizeof (*(cur->keycode)); break;
357             case EXR_ATTR_M33F: retval += sizeof (*(cur->m33f)); break;
358             case EXR_ATTR_M33D: retval += sizeof (*(cur->m33d)); break;
359             case EXR_ATTR_M44F: retval += sizeof (*(cur->m44f)); break;
360             case EXR_ATTR_M44D: retval += sizeof (*(cur->m44d)); break;
361             case EXR_ATTR_PREVIEW:
362                 retval += (size_t) cur->preview->width *
363                           (size_t) cur->preview->height * (size_t) 4;
364                 break;
365             case EXR_ATTR_RATIONAL: retval += sizeof (*(cur->rational)); break;
366             case EXR_ATTR_STRING: retval += (size_t) cur->string->length; break;
367             case EXR_ATTR_STRING_VECTOR:
368                 for (int s = 0; s < cur->stringvector->n_strings; ++s)
369                 {
370                     retval += (size_t) cur->stringvector->strings[s].length;
371                     retval += sizeof (int32_t);
372                 }
373                 break;
374             case EXR_ATTR_TILEDESC: retval += sizeof (*(cur->tiledesc)); break;
375             case EXR_ATTR_TIMECODE: retval += sizeof (*(cur->timecode)); break;
376             case EXR_ATTR_V2I: retval += sizeof (*(cur->v2i)); break;
377             case EXR_ATTR_V2F: retval += sizeof (*(cur->v2f)); break;
378             case EXR_ATTR_V2D: retval += sizeof (*(cur->v2d)); break;
379             case EXR_ATTR_V3I: retval += sizeof (*(cur->v3i)); break;
380             case EXR_ATTR_V3F: retval += sizeof (*(cur->v3f)); break;
381             case EXR_ATTR_V3D: retval += sizeof (*(cur->v3d)); break;
382             case EXR_ATTR_OPAQUE:
383                 if (cur->opaque->packed_data)
384                     retval += (size_t) cur->opaque->size;
385                 else if (cur->opaque->unpacked_data)
386                 {
387                     int32_t sz = 0;
388                     rv =
389                         exr_attr_opaquedata_pack (ctxt, cur->opaque, &sz, NULL);
390                     if (rv != EXR_ERR_SUCCESS) return rv;
391 
392                     retval += (size_t) sz;
393                 }
394                 break;
395             case EXR_ATTR_UNKNOWN:
396             case EXR_ATTR_LAST_KNOWN_TYPE:
397             default:
398                 return pctxt->print_error (
399                     pctxt,
400                     EXR_ERR_INVALID_ARGUMENT,
401                     "Invalid / unhandled type '%s' for attribute '%s', unable to compute size",
402                     cur->type_name,
403                     cur->name);
404         }
405     }
406 
407     *out = retval;
408     return rv;
409 }
410 
411 /**************************************/
412 
413 exr_result_t
exr_attr_list_find_by_name(exr_const_context_t ctxt,exr_attribute_list_t * list,const char * name,exr_attribute_t ** out)414 exr_attr_list_find_by_name (
415     exr_const_context_t   ctxt,
416     exr_attribute_list_t* list,
417     const char*           name,
418     exr_attribute_t**     out)
419 {
420     exr_attribute_t** it    = NULL;
421     exr_attribute_t** first = NULL;
422     exr_attribute_t** end   = NULL;
423     int               step, count, cmp;
424     INTERN_EXR_PROMOTE_CONST_CONTEXT_OR_ERROR (ctxt);
425 
426     if (!out)
427         return pctxt->report_error (
428             pctxt,
429             EXR_ERR_INVALID_ARGUMENT,
430             "Invalid output pointer passed to find_by_name");
431 
432     if (!name || name[0] == '\0')
433         return pctxt->report_error (
434             pctxt,
435             EXR_ERR_INVALID_ARGUMENT,
436             "Invalid name passed to find_by_name");
437 
438     if (!list)
439         return pctxt->report_error (
440             pctxt,
441             EXR_ERR_INVALID_ARGUMENT,
442             "Invalid list pointer passed to find_by_name");
443 
444     if (list->sorted_entries)
445     {
446         first = list->sorted_entries;
447         count = list->num_attributes;
448         end   = first + count;
449         /* lower bound search w/ equality check */
450         while (count > 0)
451         {
452             it   = first;
453             step = count / 2;
454             it += step;
455             cmp = strcmp ((*it)->name, name);
456             if (cmp == 0)
457             {
458                 // early exit
459                 *out = (*it);
460                 return EXR_ERR_SUCCESS;
461             }
462 
463             if (cmp < 0)
464             {
465                 first = ++it;
466                 count -= step + 1;
467             }
468             else
469                 count = step;
470         }
471 
472         if (first && first < end && 0 == strcmp ((*first)->name, name))
473         {
474             *out = (*first);
475             return EXR_ERR_SUCCESS;
476         }
477     }
478 
479     return EXR_ERR_NO_ATTR_BY_NAME;
480 }
481 
482 /**************************************/
483 
484 static exr_result_t
add_to_list(struct _internal_exr_context * ctxt,exr_attribute_list_t * list,exr_attribute_t * nattr,const char * name)485 add_to_list (
486     struct _internal_exr_context* ctxt,
487     exr_attribute_list_t*         list,
488     exr_attribute_t*              nattr,
489     const char*                   name)
490 {
491     int               cattrsz = list->num_attributes;
492     int               nattrsz = cattrsz + 1;
493     int               insertpos;
494     exr_attribute_t** attrs        = list->entries;
495     exr_attribute_t** sorted_attrs = list->sorted_entries;
496     exr_result_t      rv           = EXR_ERR_SUCCESS;
497 
498     (void) name;
499     if (nattrsz > list->num_alloced)
500     {
501         size_t nsize = (size_t) (list->num_alloced) * 2;
502         if ((size_t) nattrsz > nsize) nsize = (size_t) (nattrsz) + 1;
503         attrs = (exr_attribute_t**) ctxt->alloc_fn (
504             sizeof (exr_attribute_t*) * nsize * 2);
505         if (!attrs)
506         {
507             ctxt->free_fn (nattr);
508             return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
509         }
510 
511         list->num_alloced = (int32_t) nsize;
512         sorted_attrs      = attrs + nsize;
513 
514         for (int i = 0; i < cattrsz; ++i)
515         {
516             attrs[i]        = list->entries[i];
517             sorted_attrs[i] = list->sorted_entries[i];
518         }
519 
520         if (list->entries) ctxt->free_fn (list->entries);
521         list->entries        = attrs;
522         list->sorted_entries = sorted_attrs;
523     }
524     attrs[cattrsz]        = nattr;
525     sorted_attrs[cattrsz] = nattr;
526     insertpos             = cattrsz - 1;
527 
528     // FYI: qsort is shockingly slow, just do a quick search and
529     // bubble it up until it's in the correct location
530     while (insertpos >= 0)
531     {
532         exr_attribute_t* prev = sorted_attrs[insertpos];
533 
534         if (strcmp (nattr->name, prev->name) >= 0) break;
535 
536         sorted_attrs[insertpos + 1] = prev;
537         sorted_attrs[insertpos]     = nattr;
538         --insertpos;
539     }
540 
541     list->num_attributes = nattrsz;
542     rv                   = attr_init (ctxt, nattr);
543     if (rv != EXR_ERR_SUCCESS)
544         exr_attr_list_remove ((exr_context_t) ctxt, list, nattr);
545     return rv;
546 }
547 
548 /**************************************/
549 
550 static exr_result_t
validate_attr_arguments(struct _internal_exr_context * ctxt,exr_attribute_list_t * list,const char * name,int32_t data_len,uint8_t ** data_ptr,exr_attribute_t ** attr)551 validate_attr_arguments (
552     struct _internal_exr_context* ctxt,
553     exr_attribute_list_t*         list,
554     const char*                   name,
555     int32_t                       data_len,
556     uint8_t**                     data_ptr,
557     exr_attribute_t**             attr)
558 {
559     exr_attribute_t* nattr = NULL;
560     exr_result_t     rv;
561     if (!list)
562     {
563         return ctxt->report_error (
564             ctxt,
565             EXR_ERR_INVALID_ARGUMENT,
566             "Invalid list pointer to attr_list_add");
567     }
568 
569     if (!attr)
570     {
571         return ctxt->report_error (
572             ctxt,
573             EXR_ERR_INVALID_ARGUMENT,
574             "Invalid output attribute pointer location to attr_list_add");
575     }
576 
577     *attr = NULL;
578 
579     if (data_len < 0)
580     {
581         return ctxt->print_error (
582             ctxt,
583             EXR_ERR_INVALID_ARGUMENT,
584             "Extra data storage requested negative length (%d)",
585             data_len);
586     }
587     else if (data_len > 0 && !data_ptr)
588     {
589         return ctxt->print_error (
590             ctxt,
591             EXR_ERR_INVALID_ARGUMENT,
592             "Extra data storage output pointer must be provided when requesting extra data (%d)",
593             data_len);
594     }
595     else if (data_ptr)
596         *data_ptr = NULL;
597 
598     if (!name || name[0] == '\0')
599     {
600         return ctxt->report_error (
601             ctxt, EXR_ERR_INVALID_ARGUMENT, "Invalid name to add_by_type");
602     }
603 
604     /* is it already in the list? */
605     rv = exr_attr_list_find_by_name (
606         (exr_const_context_t) ctxt, list, name, &nattr);
607 
608     if (rv == EXR_ERR_SUCCESS)
609     {
610         if (data_ptr && data_len > 0)
611         {
612             return ctxt->print_error (
613                 ctxt,
614                 EXR_ERR_INVALID_ARGUMENT,
615                 "Attribute '%s' (type %s) already in list but requesting additional data",
616                 name,
617                 nattr->type_name);
618         }
619 
620         *attr = nattr;
621         return -1;
622     }
623 
624     return EXR_ERR_SUCCESS;
625 }
626 
627 /**************************************/
628 
629 static void
check_attr_handler(struct _internal_exr_context * pctxt,exr_attribute_t * attr)630 check_attr_handler (struct _internal_exr_context* pctxt, exr_attribute_t* attr)
631 {
632     if (attr->type == EXR_ATTR_OPAQUE)
633     {
634         exr_attribute_t* handler = NULL;
635         exr_result_t     rv      = exr_attr_list_find_by_name (
636             (exr_const_context_t) pctxt,
637             &(pctxt->custom_handlers),
638             attr->type_name,
639             &handler);
640         if (rv == EXR_ERR_SUCCESS && handler)
641         {
642             attr->opaque->unpack_func_ptr = handler->opaque->unpack_func_ptr;
643             attr->opaque->pack_func_ptr   = handler->opaque->pack_func_ptr;
644             attr->opaque->destroy_unpacked_func_ptr =
645                 handler->opaque->destroy_unpacked_func_ptr;
646         }
647     }
648 }
649 
650 /**************************************/
651 
652 static exr_result_t
create_attr_block(struct _internal_exr_context * pctxt,exr_attribute_t ** attr,size_t dblocksize,int32_t data_len,uint8_t ** data_ptr,const char * name,int32_t nlen,const char * type,int32_t tlen)653 create_attr_block (
654     struct _internal_exr_context* pctxt,
655     exr_attribute_t**             attr,
656     size_t                        dblocksize,
657     int32_t                       data_len,
658     uint8_t**                     data_ptr,
659     const char*                   name,
660     int32_t                       nlen,
661     const char*                   type,
662     int32_t                       tlen)
663 {
664     size_t           alignpad1, alignpad2;
665     size_t           attrblocksz = sizeof (exr_attribute_t);
666     uint8_t*         ptr;
667     exr_attribute_t* nattr;
668     exr_attribute_t  nil = { 0 };
669     // not all compilers have this :(
670     //const size_t ptralign = _Alignof(void*);
671     const size_t ptralign = 8;
672 
673     if (nlen > 0) attrblocksz += (size_t) (nlen + 1);
674     if (tlen > 0) attrblocksz += (size_t) (tlen + 1);
675 
676     if (dblocksize > 0)
677     {
678         alignpad1 = ptralign - (attrblocksz % ptralign);
679         if (alignpad1 == ptralign) alignpad1 = 0;
680         attrblocksz += alignpad1;
681         attrblocksz += dblocksize;
682     }
683     else
684         alignpad1 = 0;
685 
686     if (data_len > 0)
687     {
688         /* align the extra data to a pointer */
689         alignpad2 = ptralign - (attrblocksz % ptralign);
690         if (alignpad2 == ptralign) alignpad2 = 0;
691         attrblocksz += alignpad2;
692         attrblocksz += (size_t) data_len;
693     }
694     else
695         alignpad2 = 0;
696 
697     ptr = (uint8_t*) pctxt->alloc_fn (attrblocksz);
698     if (!ptr) return pctxt->standard_error (pctxt, EXR_ERR_OUT_OF_MEMORY);
699 
700     nattr  = (exr_attribute_t*) ptr;
701     *nattr = nil;
702     *attr  = nattr;
703     ptr += sizeof (exr_attribute_t);
704     if (nlen > 0)
705     {
706         memcpy (ptr, name, (size_t) (nlen + 1));
707         nattr->name        = (char*) ptr;
708         nattr->name_length = (uint8_t) nlen;
709 
710         ptr += nlen + 1;
711     }
712     if (tlen > 0)
713     {
714         memcpy (ptr, type, (size_t) (tlen + 1));
715         nattr->type_name        = (char*) ptr;
716         nattr->type_name_length = (uint8_t) tlen;
717 
718         ptr += tlen + 1;
719     }
720     ptr += alignpad1;
721     if (dblocksize > 0)
722     {
723         nattr->rawptr = ptr;
724         ptr += dblocksize;
725     }
726     if (data_ptr)
727     {
728         if (data_len > 0)
729         {
730             ptr += alignpad2;
731             *data_ptr = ptr;
732         }
733         else
734             *data_ptr = NULL;
735     }
736     return EXR_ERR_SUCCESS;
737 }
738 
739 /**************************************/
740 
741 exr_result_t
exr_attr_list_add_by_type(exr_context_t ctxt,exr_attribute_list_t * list,const char * name,const char * type,int32_t data_len,uint8_t ** data_ptr,exr_attribute_t ** attr)742 exr_attr_list_add_by_type (
743     exr_context_t         ctxt,
744     exr_attribute_list_t* list,
745     const char*           name,
746     const char*           type,
747     int32_t               data_len,
748     uint8_t**             data_ptr,
749     exr_attribute_t**     attr)
750 {
751     const struct _internal_exr_attr_map* known = NULL;
752 
753     exr_result_t     rval = EXR_ERR_INVALID_ARGUMENT;
754     int32_t          nlen, tlen, mlen;
755     size_t           slen;
756     exr_attribute_t* nattr = NULL;
757 
758     INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR (ctxt);
759 
760     if (!type || type[0] == '\0')
761     {
762         return pctxt->report_error (
763             pctxt, EXR_ERR_INVALID_ARGUMENT, "Invalid type to add_by_type");
764     }
765 
766     rval =
767         validate_attr_arguments (pctxt, list, name, data_len, data_ptr, attr);
768     if (rval != EXR_ERR_SUCCESS)
769     {
770         if (rval < 0)
771         {
772             if (0 != strcmp (type, (*attr)->type_name))
773             {
774                 nattr = *attr;
775                 *attr = NULL;
776                 return pctxt->print_error (
777                     pctxt,
778                     EXR_ERR_INVALID_ARGUMENT,
779                     "Entry '%s' already in list but with different type ('%s' vs requested '%s')",
780                     name,
781                     nattr->type_name,
782                     type);
783             }
784             return EXR_ERR_SUCCESS;
785         }
786         return rval;
787     }
788 
789     slen = strlen (name);
790     mlen = (int32_t) pctxt->max_name_length;
791 
792     if (slen > (size_t) mlen)
793     {
794         return pctxt->print_error (
795             pctxt,
796             EXR_ERR_NAME_TOO_LONG,
797             "Provided name '%s' too long for file (len %d, max %d)",
798             name,
799             (int) slen,
800             mlen);
801     }
802     nlen = (int32_t) slen;
803 
804     slen = strlen (type);
805     if (slen > (size_t) mlen)
806     {
807         return pctxt->print_error (
808             pctxt,
809             EXR_ERR_NAME_TOO_LONG,
810             "Provided type name '%s' too long for file (len %d, max %d)",
811             type,
812             (int) slen,
813             mlen);
814     }
815     tlen = (int32_t) slen;
816 
817     for (int i = 0; i < the_predefined_attr_count; ++i)
818     {
819         if (0 == strcmp (type, the_predefined_attr_typenames[i].name))
820         {
821             known = &(the_predefined_attr_typenames[i]);
822             break;
823         }
824     }
825 
826     if (known)
827     {
828         rval = create_attr_block (
829             pctxt,
830             &nattr,
831             known->exp_size,
832             data_len,
833             data_ptr,
834             name,
835             nlen,
836             NULL,
837             0);
838 
839         if (rval == EXR_ERR_SUCCESS)
840         {
841             nattr->type_name        = known->name;
842             nattr->type_name_length = (uint8_t) known->name_len;
843             nattr->type             = known->type;
844         }
845     }
846     else
847     {
848         rval = create_attr_block (
849             pctxt,
850             &nattr,
851             sizeof (exr_attr_opaquedata_t),
852             data_len,
853             data_ptr,
854             name,
855             nlen,
856             type,
857             tlen);
858 
859         if (rval == EXR_ERR_SUCCESS) nattr->type = EXR_ATTR_OPAQUE;
860     }
861     if (rval == EXR_ERR_SUCCESS) rval = add_to_list (pctxt, list, nattr, name);
862     if (rval == EXR_ERR_SUCCESS)
863     {
864         *attr = nattr;
865         check_attr_handler (pctxt, nattr);
866     }
867     else if (data_ptr)
868         *data_ptr = NULL;
869 
870     return rval;
871 }
872 
873 /**************************************/
874 
875 exr_result_t
exr_attr_list_add(exr_context_t ctxt,exr_attribute_list_t * list,const char * name,exr_attribute_type_t type,int32_t data_len,uint8_t ** data_ptr,exr_attribute_t ** attr)876 exr_attr_list_add (
877     exr_context_t         ctxt,
878     exr_attribute_list_t* list,
879     const char*           name,
880     exr_attribute_type_t  type,
881     int32_t               data_len,
882     uint8_t**             data_ptr,
883     exr_attribute_t**     attr)
884 {
885     const struct _internal_exr_attr_map* known = NULL;
886 
887     exr_result_t     rval = EXR_ERR_INVALID_ARGUMENT;
888     int32_t          nlen, tidx, mlen;
889     size_t           slen;
890     exr_attribute_t* nattr = NULL;
891 
892     INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR (ctxt);
893 
894     rval =
895         validate_attr_arguments (pctxt, list, name, data_len, data_ptr, attr);
896     if (rval != EXR_ERR_SUCCESS)
897     {
898         if (rval < 0)
899         {
900             if ((*attr)->type != type)
901             {
902                 nattr = *attr;
903                 *attr = NULL;
904                 return pctxt->print_error (
905                     pctxt,
906                     EXR_ERR_INVALID_ARGUMENT,
907                     "Entry '%s' already in list but with different type ('%s')",
908                     name,
909                     nattr->type_name);
910             }
911             return EXR_ERR_SUCCESS;
912         }
913         return rval;
914     }
915 
916     slen = strlen (name);
917     mlen = (int32_t) pctxt->max_name_length;
918     if (slen > (size_t) mlen)
919     {
920         return pctxt->print_error (
921             pctxt,
922             EXR_ERR_NAME_TOO_LONG,
923             "Provided name '%s' too long for file (len %d, max %d)",
924             name,
925             (int) slen,
926             mlen);
927     }
928     nlen = (int32_t) slen;
929 
930     tidx = ((int) type) - 1;
931     if (tidx < 0 || tidx >= the_predefined_attr_count)
932     {
933         if (type == EXR_ATTR_OPAQUE)
934             return pctxt->print_error (
935                 pctxt,
936                 EXR_ERR_INVALID_ARGUMENT,
937                 "Invalid type enum for '%s': the opaque type is not actually a built-in type",
938                 name);
939 
940         return pctxt->print_error (
941             pctxt,
942             EXR_ERR_INVALID_ARGUMENT,
943             "Invalid type enum for '%s' in create by builtin type (type %d)",
944             name,
945             (int) type);
946     }
947 
948     known = &(the_predefined_attr_typenames[tidx]);
949 
950     rval = create_attr_block (
951         pctxt,
952         &nattr,
953         known->exp_size,
954         data_len,
955         data_ptr,
956         name,
957         nlen,
958         NULL,
959         0);
960 
961     if (rval == EXR_ERR_SUCCESS)
962     {
963         nattr->type_name        = known->name;
964         nattr->type_name_length = (uint8_t) known->name_len;
965         nattr->type             = known->type;
966         rval                    = add_to_list (pctxt, list, nattr, name);
967     }
968 
969     if (rval == EXR_ERR_SUCCESS)
970     {
971         *attr = nattr;
972         check_attr_handler (pctxt, nattr);
973     }
974     else if (data_ptr)
975         *data_ptr = NULL;
976     return rval;
977 }
978 
979 /**************************************/
980 
981 exr_result_t
exr_attr_list_add_static_name(exr_context_t ctxt,exr_attribute_list_t * list,const char * name,exr_attribute_type_t type,int32_t data_len,uint8_t ** data_ptr,exr_attribute_t ** attr)982 exr_attr_list_add_static_name (
983     exr_context_t         ctxt,
984     exr_attribute_list_t* list,
985     const char*           name,
986     exr_attribute_type_t  type,
987     int32_t               data_len,
988     uint8_t**             data_ptr,
989     exr_attribute_t**     attr)
990 {
991     const struct _internal_exr_attr_map* known = NULL;
992 
993     int              rval = EXR_ERR_INVALID_ARGUMENT;
994     int32_t          nlen, tidx, mlen;
995     size_t           slen;
996     exr_attribute_t* nattr = NULL;
997 
998     INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR (ctxt);
999 
1000     rval =
1001         validate_attr_arguments (pctxt, list, name, data_len, data_ptr, attr);
1002     if (rval != EXR_ERR_SUCCESS)
1003     {
1004         if (rval < 0)
1005         {
1006             if ((*attr)->type != type)
1007             {
1008                 nattr = *attr;
1009                 *attr = NULL;
1010                 return pctxt->print_error (
1011                     pctxt,
1012                     EXR_ERR_INVALID_ARGUMENT,
1013                     "Entry '%s' already in list but with different type ('%s')",
1014                     name,
1015                     nattr->type_name);
1016             }
1017             return EXR_ERR_SUCCESS;
1018         }
1019         return rval;
1020     }
1021 
1022     mlen = (int32_t) pctxt->max_name_length;
1023     slen = strlen (name);
1024     if (slen > (size_t) mlen)
1025     {
1026         return pctxt->print_error (
1027             pctxt,
1028             EXR_ERR_NAME_TOO_LONG,
1029             "Provided name '%s' too long for file (len %d, max %d)",
1030             name,
1031             (int) slen,
1032             mlen);
1033     }
1034     nlen = (int32_t) slen;
1035 
1036     tidx = ((int) type) - 1;
1037     if (tidx < 0 || tidx >= the_predefined_attr_count)
1038     {
1039         if (type == EXR_ATTR_OPAQUE)
1040             return pctxt->print_error (
1041                 pctxt,
1042                 EXR_ERR_INVALID_ARGUMENT,
1043                 "Invalid type enum for '%s': the opaque type is not actually a built-in type",
1044                 name);
1045 
1046         return pctxt->print_error (
1047             pctxt,
1048             EXR_ERR_INVALID_ARGUMENT,
1049             "Invalid type enum for '%s' in create by builtin type (type %d)",
1050             name,
1051             (int) type);
1052     }
1053     known = &(the_predefined_attr_typenames[tidx]);
1054 
1055     rval = create_attr_block (
1056         pctxt, &nattr, known->exp_size, data_len, data_ptr, NULL, 0, NULL, 0);
1057 
1058     if (rval == EXR_ERR_SUCCESS)
1059     {
1060         nattr->name             = name;
1061         nattr->type_name        = known->name;
1062         nattr->name_length      = (uint8_t) nlen;
1063         nattr->type_name_length = (uint8_t) known->name_len;
1064         nattr->type             = known->type;
1065         rval                    = add_to_list (pctxt, list, nattr, name);
1066     }
1067 
1068     if (rval == EXR_ERR_SUCCESS)
1069     {
1070         *attr = nattr;
1071         check_attr_handler (pctxt, nattr);
1072     }
1073     else if (data_ptr)
1074         *data_ptr = NULL;
1075     return rval;
1076 }
1077 
1078 /**************************************/
1079 
1080 exr_result_t
exr_attr_list_remove(exr_context_t ctxt,exr_attribute_list_t * list,exr_attribute_t * attr)1081 exr_attr_list_remove (
1082     exr_context_t ctxt, exr_attribute_list_t* list, exr_attribute_t* attr)
1083 {
1084     int               cattrsz, attridx = -1;
1085     exr_attribute_t** attrs;
1086 
1087     INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR (ctxt);
1088 
1089     if (!attr)
1090     {
1091         return pctxt->report_error (
1092             pctxt, EXR_ERR_INVALID_ARGUMENT, "NULL attribute passed to remove");
1093     }
1094 
1095     if (!list)
1096     {
1097         return pctxt->report_error (
1098             pctxt,
1099             EXR_ERR_INVALID_ARGUMENT,
1100             "Invalid list pointer to remove attribute");
1101     }
1102 
1103     cattrsz = list->num_attributes;
1104     attrs   = list->entries;
1105     for (int i = 0; i < cattrsz; ++i)
1106     {
1107         if (attrs[i] == attr)
1108         {
1109             attridx = i;
1110             break;
1111         }
1112     }
1113 
1114     if (attridx == -1)
1115     {
1116         return pctxt->report_error (
1117             pctxt, EXR_ERR_INVALID_ARGUMENT, "Attribute not in list");
1118     }
1119 
1120     list->entries[attridx] = NULL;
1121     for (int i = attridx; i < (cattrsz - 1); ++i)
1122         attrs[i] = attrs[i + 1];
1123     list->num_attributes = cattrsz - 1;
1124 
1125     attrs   = list->sorted_entries;
1126     attridx = 0;
1127     for (int i = 0; i < cattrsz; ++i)
1128     {
1129         if (attrs[i] == attr) continue;
1130         attrs[attridx++] = attrs[i];
1131     }
1132 
1133     return attr_destroy (pctxt, attr);
1134 }
1135