1 /*
2 ** SPDX-License-Identifier: BSD-3-Clause
3 ** Copyright Contributors to the OpenEXR Project.
4 */
5 
6 #ifndef OPENEXR_PRIVATE_STRUCTS_H
7 #define OPENEXR_PRIVATE_STRUCTS_H
8 
9 #include "internal_attr.h"
10 
11 #include <IlmThreadConfig.h>
12 
13 #ifdef ILMTHREAD_THREADING_ENABLED
14 #    ifdef _WIN32
15 #        include <windows.h>
16 #        include <synchapi.h>
17 #    else
18 #        include <pthread.h>
19 #    endif
20 #endif
21 
22 #include <inttypes.h>
23 
24 #ifdef _MSC_VER
25 #    ifndef PRId64
26 #        define PRId64 "I64d"
27 #    endif
28 #    ifndef PRIu64
29 #        define PRIu64 "I64u"
30 #    endif
31 #endif
32 
33 /* for testing, we include a bunch of internal stuff into the unit tests which are in c++ */
34 #ifdef __cplusplus
35 #    include <atomic>
36 using atomic_uintptr_t = std::atomic_uintptr_t;
37 #else
38 #    if defined __has_include
39 #        if __has_include(<stdatomic.h>)
40 #            define EXR_HAS_STD_ATOMICS 1
41 #        endif
42 #    endif
43 
44 #    ifdef EXR_HAS_STD_ATOMICS
45 #        include <stdatomic.h>
46 #    elif defined(_MSC_VER)
47 /* msvc w/ c11 support is only very new, until we know what the preprocessor checks are, provide defaults */
48 #        include <windows.h>
49 /* yeah, yeah, might be a 32-bit pointer, but if we make it the same, we
50  * can write less since we know support is coming (eventually) */
51 typedef uint64_t atomic_uintptr_t;
52 #    else
53 #        error OS unimplemented support for atomics
54 #    endif
55 #endif
56 
57 struct _internal_exr_part
58 {
59     int part_index;
60     exr_storage_t
61         storage_mode; /**< Part of the file version flag declaring scanlines or tiled mode */
62 
63     exr_attribute_list_t attributes;
64 
65     /*  required attributes  */
66     exr_attribute_t* channels;
67     exr_attribute_t* compression;
68     exr_attribute_t* dataWindow;
69     exr_attribute_t* displayWindow;
70     exr_attribute_t* lineOrder;
71     exr_attribute_t* pixelAspectRatio;
72     exr_attribute_t* screenWindowCenter;
73     exr_attribute_t* screenWindowWidth;
74 
75     /** required for tiled files (@see storage_mode) */
76     exr_attribute_t* tiles;
77 
78     /** required for deep or multipart files */
79     exr_attribute_t* name;
80     exr_attribute_t* type;
81     exr_attribute_t* version;
82     exr_attribute_t* chunkCount;
83     /* in the file layout doc, but not required any more? */
84     exr_attribute_t* maxSamplesPerPixel;
85 
86     /* copy commonly accessed required attributes to local struct memory for quick access */
87     exr_attr_box2i_t  data_window;
88     exr_attr_box2i_t  display_window;
89     exr_compression_t comp_type;
90     exr_lineorder_t   lineorder;
91 
92     int32_t  zip_compression_level;
93     float    dwa_compression_level;
94 
95     int32_t  num_tile_levels_x;
96     int32_t  num_tile_levels_y;
97     int32_t* tile_level_tile_count_x;
98     int32_t* tile_level_tile_count_y;
99     int32_t* tile_level_tile_size_x;
100     int32_t* tile_level_tile_size_y;
101 
102     uint64_t unpacked_size_per_chunk;
103     int16_t  lines_per_chunk;
104     int16_t  chan_has_line_sampling;
105 
106     int32_t          chunk_count;
107     uint64_t         chunk_table_offset;
108     atomic_uintptr_t chunk_table;
109 };
110 
111 enum _INTERNAL_EXR_READ_MODE
112 {
113     EXR_MUST_READ_ALL    = 0,
114     EXR_ALLOW_SHORT_READ = 1
115 };
116 
117 enum _INTERNAL_EXR_CONTEXT_MODE
118 {
119     EXR_CONTEXT_READ          = 0,
120     EXR_CONTEXT_WRITE         = 1,
121     EXR_CONTEXT_UPDATE_HEADER = 2,
122     EXR_CONTEXT_WRITING_DATA  = 3,
123     EXR_CONTEXT_WRITE_FINISHED
124 };
125 
126 struct _internal_exr_context
127 {
128     uint8_t mode;
129     uint8_t version;
130     uint8_t max_name_length;
131 
132     uint8_t is_singlepart_tiled;
133     uint8_t has_nonimage_data;
134     uint8_t is_multipart;
135 
136     uint8_t pad[2];
137 
138     exr_attr_string_t filename;
139     exr_attr_string_t tmp_filename;
140 
141     exr_result_t (*do_read) (
142         const struct _internal_exr_context* file,
143         void*,
144         uint64_t,
145         uint64_t*,
146         int64_t*,
147         enum _INTERNAL_EXR_READ_MODE);
148     exr_result_t (*do_write) (
149         struct _internal_exr_context* file, const void*, uint64_t, uint64_t*);
150 
151     exr_result_t (*standard_error) (
152         const struct _internal_exr_context* ctxt, exr_result_t code);
153     exr_result_t (*report_error) (
154         const struct _internal_exr_context* ctxt,
155         exr_result_t                        code,
156         const char*                         msg);
157     exr_result_t (*print_error) (
158         const struct _internal_exr_context* ctxt,
159         exr_result_t                        code,
160         const char*                         msg,
161         ...) EXR_PRINTF_FUNC_ATTRIBUTE;
162 
163     exr_error_handler_cb_t error_handler_fn;
164 
165     exr_memory_allocation_func_t alloc_fn;
166     exr_memory_free_func_t       free_fn;
167 
168     int max_image_w;
169     int max_image_h;
170     int max_tile_w;
171     int max_tile_h;
172 
173     int default_zip_level;
174     float default_dwa_quality;
175 
176     void*                         real_user_data;
177     void*                         user_data;
178     exr_destroy_stream_func_ptr_t destroy_fn;
179 
180     int64_t             file_size;
181     exr_read_func_ptr_t read_fn;
182 
183     exr_write_func_ptr_t write_fn;
184     /* used when writing under a mutex, is there a better way? */
185     uint64_t output_file_offset;
186     int      cur_output_part;
187     int      last_output_chunk;
188     int      output_chunk_count;
189 
190     /** all files have at least one part */
191     int num_parts;
192 
193     struct _internal_exr_part first_part;
194     /* cheap array of one */
195     struct _internal_exr_part*  init_part;
196     struct _internal_exr_part** parts;
197 
198     exr_attribute_list_t custom_handlers;
199 
200     /* mostly needed for writing, but used during read to ensure
201      * custom attribute handlers are safe */
202 #ifdef ILMTHREAD_THREADING_ENABLED
203 #    ifdef _WIN32
204     CRITICAL_SECTION mutex;
205 #    else
206     pthread_mutex_t mutex;
207 #    endif
208 #endif
209 };
210 
211 #define EXR_CTXT(c) ((struct _internal_exr_context*) (c))
212 #define EXR_CCTXT(c) ((const struct _internal_exr_context*) (c))
213 
214 #define EXR_CONST_CAST(t, v) ((t) (uintptr_t) v)
215 
216 static inline void
internal_exr_lock(const struct _internal_exr_context * c)217 internal_exr_lock (const struct _internal_exr_context* c)
218 {
219 #ifdef ILMTHREAD_THREADING_ENABLED
220     struct _internal_exr_context* nonc =
221         EXR_CONST_CAST (struct _internal_exr_context*, c);
222 #    ifdef _WIN32
223     EnterCriticalSection (&nonc->mutex);
224 #    else
225     pthread_mutex_lock (&nonc->mutex);
226 #    endif
227 #endif
228 }
229 
230 static inline void
internal_exr_unlock(const struct _internal_exr_context * c)231 internal_exr_unlock (const struct _internal_exr_context* c)
232 {
233 #ifdef ILMTHREAD_THREADING_ENABLED
234     struct _internal_exr_context* nonc =
235         EXR_CONST_CAST (struct _internal_exr_context*, c);
236 #    ifdef _WIN32
237     LeaveCriticalSection (&nonc->mutex);
238 #    else
239     pthread_mutex_unlock (&nonc->mutex);
240 #    endif
241 #endif
242 }
243 
244 #define EXR_LOCK(c) internal_exr_lock ((const struct _internal_exr_context*) c)
245 #define EXR_UNLOCK(c)                                                          \
246     internal_exr_unlock ((const struct _internal_exr_context*) c)
247 
248 #define EXR_LOCK_WRITE(c)                                                      \
249     if (c->mode == EXR_CONTEXT_WRITE) internal_exr_lock (c)
250 
251 #define EXR_UNLOCK_WRITE(c)                                                    \
252     if (c->mode == EXR_CONTEXT_WRITE) internal_exr_unlock (c)
253 
254 #define EXR_RETURN_WRITE(c)                                                    \
255     ((c->mode == EXR_CONTEXT_WRITE) ? internal_exr_unlock (c) : ((void) 0))
256 
257 #define EXR_UNLOCK_AND_RETURN_PCTXT(v) ((void) (EXR_UNLOCK (pctxt)), v)
258 #define EXR_UNLOCK_WRITE_AND_RETURN_PCTXT(v)                                   \
259     ((void) (EXR_RETURN_WRITE (pctxt)), v)
260 
261 #define INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR(c)                                 \
262     struct _internal_exr_context* pctxt = EXR_CTXT (c);                        \
263     if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG
264 
265 #define INTERN_EXR_PROMOTE_CONST_CONTEXT_OR_ERROR(c)                           \
266     const struct _internal_exr_context* pctxt = EXR_CCTXT (c);                 \
267     if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG
268 
269 #define EXR_PROMOTE_LOCKED_CONTEXT_OR_ERROR(c)                                 \
270     INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR (c);                                   \
271     EXR_LOCK (c)
272 
273 #define EXR_PROMOTE_CONST_CONTEXT_OR_ERROR(c)                                  \
274     INTERN_EXR_PROMOTE_CONST_CONTEXT_OR_ERROR (c);                             \
275     EXR_LOCK_WRITE (pctxt)
276 
277 #define EXR_PROMOTE_LOCKED_CONTEXT_AND_PART_OR_ERROR(c, pi)                    \
278     struct _internal_exr_context* pctxt = EXR_CTXT (c);                        \
279     struct _internal_exr_part*    part;                                        \
280     if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG;                            \
281     EXR_LOCK (pctxt);                                                          \
282     if (pi < 0 || pi >= pctxt->num_parts)                                      \
283         return (                                                               \
284             (void) (EXR_UNLOCK (pctxt)),                                       \
285             pctxt->print_error (                                               \
286                 pctxt,                                                         \
287                 EXR_ERR_ARGUMENT_OUT_OF_RANGE,                                 \
288                 "Part index (%d) out of range",                                \
289                 pi));                                                          \
290     part = pctxt->parts[pi]
291 
292 #define EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR(c, pi)                     \
293     const struct _internal_exr_context* pctxt = EXR_CCTXT (c);                 \
294     const struct _internal_exr_part*    part;                                  \
295     if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG;                            \
296     EXR_LOCK_WRITE (pctxt);                                                    \
297     if (pi < 0 || pi >= pctxt->num_parts)                                      \
298         return (                                                               \
299             (void) (EXR_RETURN_WRITE (pctxt)),                                 \
300             pctxt->print_error (                                               \
301                 pctxt,                                                         \
302                 EXR_ERR_ARGUMENT_OUT_OF_RANGE,                                 \
303                 "Part index (%d) out of range",                                \
304                 pi));                                                          \
305     part = pctxt->parts[pi]
306 
307 #define EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR_NO_LOCK(c, pi)             \
308     const struct _internal_exr_context* pctxt = EXR_CCTXT (c);                 \
309     const struct _internal_exr_part*    part;                                  \
310     if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG;                            \
311     if (pi < 0 || pi >= pctxt->num_parts)                                      \
312         return (                                                               \
313             (void) (EXR_RETURN_WRITE (pctxt)),                                 \
314             pctxt->print_error (                                               \
315                 pctxt,                                                         \
316                 EXR_ERR_ARGUMENT_OUT_OF_RANGE,                                 \
317                 "Part index (%d) out of range",                                \
318                 pi));                                                          \
319     part = pctxt->parts[pi]
320 
321 #define EXR_PROMOTE_READ_CONST_CONTEXT_AND_PART_OR_ERROR(c, pi)                \
322     const struct _internal_exr_context* pctxt = EXR_CCTXT (c);                 \
323     const struct _internal_exr_part*    part;                                  \
324     if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG;                            \
325     if (pctxt->mode != EXR_CONTEXT_READ)                                       \
326         return pctxt->standard_error (pctxt, EXR_ERR_NOT_OPEN_READ);           \
327     if (pi < 0 || pi >= pctxt->num_parts)                                      \
328         return pctxt->print_error (                                            \
329             pctxt,                                                             \
330             EXR_ERR_ARGUMENT_OUT_OF_RANGE,                                     \
331             "Part index (%d) out of range",                                    \
332             pi);                                                               \
333     part = pctxt->parts[pi]
334 
335 void internal_exr_update_default_handlers (exr_context_initializer_t* inits);
336 
337 exr_result_t internal_exr_add_part (
338     struct _internal_exr_context*, struct _internal_exr_part**, int* new_index);
339 void internal_exr_revert_add_part (
340     struct _internal_exr_context*, struct _internal_exr_part**, int* new_index);
341 
342 exr_result_t internal_exr_alloc_context (
343     struct _internal_exr_context**   out,
344     const exr_context_initializer_t* initializers,
345     enum _INTERNAL_EXR_CONTEXT_MODE  mode,
346     size_t                           extra_data);
347 void internal_exr_destroy_context (struct _internal_exr_context* ctxt);
348 
349 #endif /* OPENEXR_PRIVATE_STRUCTS_H */
350