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