]> git.sesse.net Git - nageru/blob - futatabi/jpeglib_error_wrapper.h
Fix an issue where scrubbing in Futatabi could lock up if there were no inputs going...
[nageru] / futatabi / jpeglib_error_wrapper.h
1 #ifndef _JPEGLIB_ERROR_WRAPPER
2 #define _JPEGLIB_ERROR_WRAPPER 1
3
4 /*
5   A wrapper class for libjpeg's very cumbersome error handling.
6   By default, any error will simply exit(); you can set your own
7   error handler, but it can't return. You can't throw exceptions
8   through C code legally, so the only real choice is setjmp/longjmp,
9   which is also what libjpeg recommends. However, longjmp has
10   undefined behavior if a similar try/catch pair would invoke
11   running any nontrivial destructors, so it's better to wrap it
12   into a common class where we know for sure there are no such
13   destructors; we choose to simply convert it into a normal
14   true/false idiom for success/failure.
15
16   Use as:
17
18   JPEGWrapErrorManager error_mgr(&cinfo);
19   if (!error_mgr.run([&cinfo]{ jpeg_read_header(&cinfo, true); })) {
20           // Something went wrong.
21           return nullptr;
22   }
23   if (!error_mgr.run([&cinfo]{ jpeg_start_decompress(&cinfo); })) {
24           // Something went wrong.
25           return nullptr;
26   }
27   // etc.
28
29   If you call libjpeg calls outside of run() and they fail, or if
30   you declare objects with nontrivial destructors in your lambda
31   (including in the capture), you end up in undefined behavior.
32  */
33
34 #include <jpeglib.h>
35 #include <setjmp.h>
36
37 struct JPEGWrapErrorManager {
38         struct jpeg_error_mgr pub;
39         jmp_buf setjmp_buffer;
40
41         explicit JPEGWrapErrorManager(jpeg_compress_struct *cinfo)  // Does not take ownership.
42         {
43                 cinfo->err = jpeg_std_error(&pub);
44                 pub.error_exit = error_exit_thunk;
45         }
46
47         explicit JPEGWrapErrorManager(jpeg_decompress_struct *dinfo)  // Does not take ownership.
48         {
49                 dinfo->err = jpeg_std_error(&pub);
50                 pub.error_exit = error_exit_thunk;
51         }
52
53         static void error_exit_thunk(jpeg_common_struct *cinfo)
54         {
55                 ((JPEGWrapErrorManager *)cinfo->err)->error_exit(cinfo);
56         }
57
58         void error_exit(jpeg_common_struct *cinfo)
59         {
60                 (pub.output_message)(cinfo);
61                 longjmp(setjmp_buffer, 1);
62         }
63
64         // Returns false if and only if the call failed.
65         template<class T>
66         inline bool run(T &&func)
67         {
68                 if (setjmp(setjmp_buffer)) {
69                         return false;
70                 }
71                 func();
72                 return true;
73         }
74 };
75
76 #endif