1 #include "HalideRuntime.h"
2 
3 // We support three formats, tiff, mat, and tmp.
4 //
5 // All formats support arbitrary types, and are easy to write in a
6 // small amount of code.
7 //
8 // TIFF:
9 // - 2/3-D only
10 // - Readable by the most tools
11 // mat:
12 // - Arbitrary dimensionality, type
13 // - Readable by matlab, ImageStack, and many other tools
14 // tmp:
15 // - Dirt simple, easy to roll your own parser
16 // - Readable by ImageStack only
17 // - Will probably be deprecated in favor of .mat soon
18 //
19 // It would be nice to use a format that web browsers read and display
20 // directly, but those formats don't tend to satisfy the above goals.
21 
22 namespace Halide {
23 namespace Runtime {
24 namespace Internal {
25 
26 // Mappings from the type_code passed in to the type codes of the
27 // formats. See "type_code" in DebugToFile.cpp
28 
29 // TIFF sample type values are:
30 //     1 => Unsigned int
31 //     2 => Signed int
32 //     3 => Floating-point
33 WEAK int16_t pixel_type_to_tiff_sample_type[] = {
34     // float, double, uint8, int8, ... uint64, int64
35     3, 3, 1, 2, 1, 2, 1, 2, 1, 2};
36 
37 // See the .mat level 5 documentation for matlab class codes.
38 WEAK uint8_t pixel_type_to_matlab_class_code[] = {
39     7, 6, 9, 8, 11, 10, 13, 12, 15, 14};
40 
41 WEAK uint8_t pixel_type_to_matlab_type_code[] = {
42     7, 9, 2, 1, 4, 3, 6, 5, 13, 12};
43 
44 #pragma pack(push)
45 #pragma pack(2)
46 
47 struct tiff_tag {
48     uint16_t tag_code;
49     int16_t type_code;
50     int32_t count;
51     union {
52         int8_t i8;
53         int16_t i16;
54         int32_t i32;
55     } value;
56 
assign16Halide::Runtime::Internal::tiff_tag57     ALWAYS_INLINE void assign16(uint16_t tag_code, int32_t count, int16_t value) {
58         this->tag_code = tag_code;
59         this->type_code = 3;
60         this->count = count;
61         this->value.i16 = value;
62     }
63 
assign32Halide::Runtime::Internal::tiff_tag64     ALWAYS_INLINE void assign32(uint16_t tag_code, int32_t count, int32_t value) {
65         this->tag_code = tag_code;
66         this->type_code = 4;
67         this->count = count;
68         this->value.i32 = value;
69     }
70 
assign32Halide::Runtime::Internal::tiff_tag71     ALWAYS_INLINE void assign32(uint16_t tag_code, int16_t type_code, int32_t count, int32_t value) {
72         this->tag_code = tag_code;
73         this->type_code = type_code;
74         this->count = count;
75         this->value.i32 = value;
76     }
77 };
78 
79 struct halide_tiff_header {
80     int16_t byte_order_marker;
81     int16_t version;
82     int32_t ifd0_offset;
83     int16_t entry_count;
84     tiff_tag entries[15];
85     int32_t ifd0_end;
86     int32_t width_resolution[2];
87     int32_t height_resolution[2];
88 };
89 
90 #pragma pack(pop)
91 
ends_with(const char * filename,const char * suffix)92 WEAK bool ends_with(const char *filename, const char *suffix) {
93     const char *f = filename, *s = suffix;
94     while (*f) {
95         f++;
96     }
97     while (*s) {
98         s++;
99     }
100     while (s != suffix && f != filename) {
101         if (*f != *s) return false;
102         f--;
103         s--;
104     }
105     return *f == *s;
106 }
107 
108 struct ScopedFile {
109     void *f;
ScopedFileHalide::Runtime::Internal::ScopedFile110     ALWAYS_INLINE ScopedFile(const char *filename, const char *mode) {
111         f = fopen(filename, mode);
112     }
~ScopedFileHalide::Runtime::Internal::ScopedFile113     ALWAYS_INLINE ~ScopedFile() {
114         fclose(f);
115     }
writeHalide::Runtime::Internal::ScopedFile116     ALWAYS_INLINE bool write(const void *ptr, size_t bytes) {
117         return fwrite(ptr, bytes, 1, f);
118     }
openHalide::Runtime::Internal::ScopedFile119     ALWAYS_INLINE bool open() const {
120         return f;
121     }
122 };
123 
124 }  // namespace Internal
125 }  // namespace Runtime
126 }  // namespace Halide
127 
halide_debug_to_file(void * user_context,const char * filename,int32_t type_code,struct halide_buffer_t * buf)128 WEAK extern "C" int32_t halide_debug_to_file(void *user_context, const char *filename,
129                                              int32_t type_code, struct halide_buffer_t *buf) {
130 
131     if (buf->is_bounds_query()) {
132         halide_error(user_context, "Bounds query buffer passed to halide_debug_to_file");
133         return -1;
134     }
135 
136     if (buf->dimensions > 4) {
137         halide_error(user_context, "Can't debug_to_file a Func with more than four dimensions\n");
138         return -1;
139     }
140 
141     halide_copy_to_host(user_context, buf);
142 
143     ScopedFile f(filename, "wb");
144     if (!f.open()) return -2;
145 
146     size_t elts = 1;
147     halide_dimension_t shape[4];
148     for (int i = 0; i < buf->dimensions && i < 4; i++) {
149         shape[i] = buf->dim[i];
150         elts *= shape[i].extent;
151     }
152     for (int i = buf->dimensions; i < 4; i++) {
153         shape[i].min = 0;
154         shape[i].extent = 1;
155         shape[i].stride = 0;
156     }
157     int32_t bytes_per_element = buf->type.bytes();
158 
159     uint32_t final_padding_bytes = 0;
160 
161     if (ends_with(filename, ".tiff") || ends_with(filename, ".tif")) {
162         int32_t channels;
163         int32_t width = shape[0].extent;
164         int32_t height = shape[1].extent;
165         int32_t depth;
166 
167         if ((shape[3].extent == 0 || shape[3].extent == 1) && (shape[2].extent < 5)) {
168             channels = shape[2].extent;
169             depth = 1;
170         } else {
171             channels = shape[3].extent;
172             depth = shape[2].extent;
173         }
174 
175         struct halide_tiff_header header;
176 
177         int32_t MMII = 0x4d4d4949;
178         // Select the appropriate two bytes signaling byte order automatically
179         const char *c = (const char *)&MMII;
180         header.byte_order_marker = (c[0] << 8) | c[1];
181 
182         header.version = 42;
183         header.ifd0_offset = __builtin_offsetof(halide_tiff_header, entry_count);
184         header.entry_count = sizeof(header.entries) / sizeof(header.entries[0]);
185 
186         tiff_tag *tag = &header.entries[0];
187         tag++->assign32(256, 1, width);                           // Image width
188         tag++->assign32(257, 1, height);                          // Image height
189         tag++->assign16(258, 1, int16_t(bytes_per_element * 8));  // Bits per sample
190         tag++->assign16(259, 1, 1);                               // Compression -- none
191         tag++->assign16(262, 1, channels >= 3 ? 2 : 1);           // PhotometricInterpretation -- black is zero or RGB
192         tag++->assign32(273, channels, sizeof(header));           // Rows per strip
193         tag++->assign16(277, 1, int16_t(channels));               // Samples per pixel
194         tag++->assign32(278, 1, shape[1].extent);                 // Rows per strip
195         tag++->assign32(279, channels,
196                         (channels == 1) ?
197                             elts * bytes_per_element :
198                             sizeof(header) +
199                                 channels * sizeof(int32_t));  // strip byte counts, bug if 32-bit truncation
200         tag++->assign32(282, 5, 1,
201                         __builtin_offsetof(halide_tiff_header, width_resolution));  // Width resolution
202         tag++->assign32(283, 5, 1,
203                         __builtin_offsetof(halide_tiff_header, height_resolution));  // Height resolution
204         tag++->assign16(284, 1, 2);                                                  // Planar configuration -- planar
205         tag++->assign16(296, 1, 1);                                                  // Resolution Unit -- none
206         tag++->assign16(339, 1,
207                         pixel_type_to_tiff_sample_type[type_code]);  // Sample type
208         tag++->assign32(32997, 1, depth);                            // Image depth
209 
210         header.ifd0_end = 0;
211         header.width_resolution[0] = 1;
212         header.width_resolution[1] = 1;
213         header.height_resolution[0] = 1;
214         header.height_resolution[1] = 1;
215 
216         if (!f.write((void *)(&header), sizeof(header))) {
217             return -3;
218         }
219 
220         if (channels > 1) {
221             int32_t offset = sizeof(header) + channels * sizeof(int32_t) * 2;
222 
223             for (int32_t i = 0; i < channels; i++) {
224                 if (!f.write((void *)(&offset), 4)) {
225                     return -4;
226                 }
227                 offset += shape[0].extent * shape[1].extent * depth * bytes_per_element;
228             }
229             int32_t count = shape[0].extent * shape[1].extent * depth * bytes_per_element;
230             for (int32_t i = 0; i < channels; i++) {
231                 if (!f.write((void *)(&count), 4)) {
232                     return -5;
233                 }
234             }
235         }
236     } else if (ends_with(filename, ".mat")) {
237         // Construct a name for the array from the filename
238         const char *end = filename;
239         while (*end) {
240             end++;
241         }
242         while (*end != '.') {
243             end--;
244         }
245         const char *start = end;
246         while (start != filename && start[-1] != '/') {
247             start--;
248         }
249         uint32_t name_size = (uint32_t)(end - start);
250         char array_name[256];
251         char *dst = array_name;
252         while (start != end) {
253             *dst++ = *start++;
254         }
255         while (dst < array_name + sizeof(array_name)) {
256             *dst++ = 0;
257         }
258 
259         uint32_t padded_name_size = (name_size + 7) & ~7;
260 
261         char header[129] =
262             "MATLAB 5.0 MAT-file, produced by Halide                         "
263             "                                                            \000\001IM";
264 
265         f.write(header, 128);
266 
267         size_t payload_bytes = buf->size_in_bytes();
268 
269         // level 5 .mat files have a size limit
270         if ((uint64_t)payload_bytes >> 32) {
271             halide_error(user_context, "Can't debug_to_file to a .mat file greater than 4GB\n");
272             return -6;
273         }
274 
275         int dims = buf->dimensions;
276         // .mat files require at least two dimensions
277         if (dims < 2) {
278             dims = 2;
279         }
280 
281         int padded_dimensions = (dims + 1) & ~1;
282 
283         uint32_t tags[] = {
284             // This is a matrix
285             14, 40 + padded_dimensions * 4 + padded_name_size + (uint32_t)payload_bytes,
286             // The element type
287             6, 8, pixel_type_to_matlab_class_code[type_code], 1,
288             // The shape
289             5, (uint32_t)(dims * 4)};
290 
291         if (!f.write(&tags, sizeof(tags))) {
292             return -7;
293         }
294 
295         int extents[] = {shape[0].extent, shape[1].extent, shape[2].extent, shape[3].extent};
296         if (!f.write(&extents, padded_dimensions * 4)) {
297             return -8;
298         }
299 
300         // The name
301         uint32_t name_header[2] = {1, name_size};
302         if (!f.write(&name_header, sizeof(name_header))) {
303             return -9;
304         }
305 
306         if (!f.write(array_name, padded_name_size)) {
307             return -10;
308         }
309 
310         final_padding_bytes = 7 - ((payload_bytes - 1) & 7);
311 
312         // Payload header
313         uint32_t payload_header[2] = {
314             pixel_type_to_matlab_type_code[type_code], (uint32_t)payload_bytes};
315         if (!f.write(payload_header, sizeof(payload_header))) {
316             return -11;
317         }
318     } else {
319         int32_t header[] = {shape[0].extent,
320                             shape[1].extent,
321                             shape[2].extent,
322                             shape[3].extent,
323                             type_code};
324         if (!f.write((void *)(&header[0]), sizeof(header))) {
325             return -12;
326         }
327     }
328 
329     // Reorder the data according to the strides.
330     const int TEMP_SIZE = 4 * 1024;
331     uint8_t temp[TEMP_SIZE];
332     int max_elts = TEMP_SIZE / bytes_per_element;
333     int counter = 0;
334 
335     for (int32_t dim3 = shape[3].min; dim3 < shape[3].extent + shape[3].min; ++dim3) {
336         for (int32_t dim2 = shape[2].min; dim2 < shape[2].extent + shape[2].min; ++dim2) {
337             for (int32_t dim1 = shape[1].min; dim1 < shape[1].extent + shape[1].min; ++dim1) {
338                 for (int32_t dim0 = shape[0].min; dim0 < shape[0].extent + shape[0].min; ++dim0) {
339                     counter++;
340                     int idx[] = {dim0, dim1, dim2, dim3};
341                     uint8_t *loc = buf->address_of(idx);
342                     void *dst = temp + (counter - 1) * bytes_per_element;
343                     memcpy(dst, loc, bytes_per_element);
344 
345                     if (counter == max_elts) {
346                         counter = 0;
347                         if (!f.write((void *)temp, max_elts * bytes_per_element)) {
348                             return -13;
349                         }
350                     }
351                 }
352             }
353         }
354     }
355     if (counter > 0) {
356         if (!f.write((void *)temp, counter * bytes_per_element)) {
357             return -14;
358         }
359     }
360 
361     const uint64_t zero = 0;
362     if (final_padding_bytes) {
363         if (final_padding_bytes > sizeof(zero)) {
364             halide_error(user_context, "Unexpectedly large final_padding_bytes");
365             return -15;
366         }
367         if (!f.write(&zero, final_padding_bytes)) {
368             return -16;
369         }
370     }
371 
372     return 0;
373 }
374