OSDN Git Service

Camera: Add opaque user pointer to camera metadata buffer
[android-x86/system-media.git] / camera / src / camera_metadata.c
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #define _GNU_SOURCE // for fdprintf
17 #include <system/camera_metadata.h>
18 #include <cutils/log.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <errno.h>
22
23 #define OK         0
24 #define ERROR      1
25 #define NOT_FOUND -ENOENT
26 /**
27  * A single metadata entry, storing an array of values of a given type. If the
28  * array is no larger than 4 bytes in size, it is stored in the data.value[]
29  * array; otherwise, it can found in the parent's data array at index
30  * data.offset.
31  */
32 typedef struct camera_metadata_buffer_entry {
33     uint32_t tag;
34     size_t   count;
35     union {
36         size_t  offset;
37         uint8_t value[4];
38     } data;
39     uint8_t  type;
40     uint8_t  reserved[3];
41 } __attribute__((packed)) camera_metadata_buffer_entry_t;
42
43 /**
44  * A packet of metadata. This is a list of entries, each of which may point to
45  * its values stored at an offset in data.
46  *
47  * It is assumed by the utility functions that the memory layout of the packet
48  * is as follows:
49  *
50  *   |-----------------------------------------------|
51  *   | camera_metadata_t                             |
52  *   |                                               |
53  *   |-----------------------------------------------|
54  *   | reserved for future expansion                 |
55  *   |-----------------------------------------------|
56  *   | camera_metadata_buffer_entry_t #0             |
57  *   |-----------------------------------------------|
58  *   | ....                                          |
59  *   |-----------------------------------------------|
60  *   | camera_metadata_buffer_entry_t #entry_count-1 |
61  *   |-----------------------------------------------|
62  *   | free space for                                |
63  *   | (entry_capacity-entry_count) entries          |
64  *   |-----------------------------------------------|
65  *   | start of camera_metadata.data                 |
66  *   |                                               |
67  *   |-----------------------------------------------|
68  *   | free space for                                |
69  *   | (data_capacity-data_count) bytes              |
70  *   |-----------------------------------------------|
71  *
72  * With the total length of the whole packet being camera_metadata.size bytes.
73  *
74  * In short, the entries and data are contiguous in memory after the metadata
75  * header.
76  */
77 struct camera_metadata {
78     size_t                   size;
79     uint32_t                 version;
80     uint32_t                 flags;
81     size_t                   entry_count;
82     size_t                   entry_capacity;
83     camera_metadata_buffer_entry_t *entries;
84     size_t                   data_count;
85     size_t                   data_capacity;
86     uint8_t                 *data;
87     void                    *user; // User set pointer, not copied with buffer
88     uint8_t                  reserved[0];
89 };
90
91 /** Versioning information */
92 #define CURRENT_METADATA_VERSION 1
93
94 /** Flag definitions */
95 #define FLAG_SORTED 0x00000001
96
97 /** Tag information */
98
99 typedef struct tag_info {
100     const char *tag_name;
101     uint8_t     tag_type;
102 } tag_info_t;
103
104 #include "camera_metadata_tag_info.c"
105
106 size_t camera_metadata_type_size[NUM_TYPES] = {
107     [TYPE_BYTE]     = sizeof(uint8_t),
108     [TYPE_INT32]    = sizeof(int32_t),
109     [TYPE_FLOAT]    = sizeof(float),
110     [TYPE_INT64]    = sizeof(int64_t),
111     [TYPE_DOUBLE]   = sizeof(double),
112     [TYPE_RATIONAL] = sizeof(camera_metadata_rational_t)
113 };
114
115 char *camera_metadata_type_names[NUM_TYPES] = {
116     [TYPE_BYTE]     = "byte",
117     [TYPE_INT32]    = "int32",
118     [TYPE_FLOAT]    = "float",
119     [TYPE_INT64]    = "int64",
120     [TYPE_DOUBLE]   = "double",
121     [TYPE_RATIONAL] = "rational"
122 };
123
124 camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,
125                                             size_t data_capacity) {
126     size_t memory_needed = calculate_camera_metadata_size(entry_capacity,
127                                                           data_capacity);
128     void *buffer = malloc(memory_needed);
129     return place_camera_metadata(buffer, memory_needed,
130                                  entry_capacity,
131                                  data_capacity);
132 }
133
134 camera_metadata_t *place_camera_metadata(void *dst,
135                                          size_t dst_size,
136                                          size_t entry_capacity,
137                                          size_t data_capacity) {
138     if (dst == NULL) return NULL;
139     if (entry_capacity == 0) return NULL;
140
141     size_t memory_needed = calculate_camera_metadata_size(entry_capacity,
142                                                           data_capacity);
143     if (memory_needed > dst_size) return NULL;
144
145     camera_metadata_t *metadata = (camera_metadata_t*)dst;
146     metadata->version = CURRENT_METADATA_VERSION;
147     metadata->flags = 0;
148     metadata->entry_count = 0;
149     metadata->entry_capacity = entry_capacity;
150     metadata->entries = (camera_metadata_buffer_entry_t*)(metadata + 1);
151     metadata->data_count = 0;
152     metadata->data_capacity = data_capacity;
153     metadata->size = memory_needed;
154     if (metadata->data_capacity != 0) {
155         metadata->data =
156                 (uint8_t*)(metadata->entries + metadata->entry_capacity);
157     } else {
158         metadata->data = NULL;
159     }
160     metadata->user = NULL;
161
162     return metadata;
163 }
164 void free_camera_metadata(camera_metadata_t *metadata) {
165     free(metadata);
166 }
167
168 size_t calculate_camera_metadata_size(size_t entry_count,
169                                       size_t data_count) {
170     size_t memory_needed = sizeof(camera_metadata_t);
171     memory_needed += sizeof(camera_metadata_buffer_entry_t[entry_count]);
172     memory_needed += sizeof(uint8_t[data_count]);
173     return memory_needed;
174 }
175
176 size_t get_camera_metadata_size(const camera_metadata_t *metadata) {
177     if (metadata == NULL) return ERROR;
178
179     return metadata->size;
180 }
181
182 size_t get_camera_metadata_compact_size(const camera_metadata_t *metadata) {
183     if (metadata == NULL) return ERROR;
184
185     ptrdiff_t reserved_size = metadata->size -
186             calculate_camera_metadata_size(metadata->entry_capacity,
187                                            metadata->data_capacity);
188
189     return calculate_camera_metadata_size(metadata->entry_count,
190                                           metadata->data_count) + reserved_size;
191 }
192
193 size_t get_camera_metadata_entry_count(const camera_metadata_t *metadata) {
194     return metadata->entry_count;
195 }
196
197 size_t get_camera_metadata_entry_capacity(const camera_metadata_t *metadata) {
198     return metadata->entry_capacity;
199 }
200
201 size_t get_camera_metadata_data_count(const camera_metadata_t *metadata) {
202     return metadata->data_count;
203 }
204
205 size_t get_camera_metadata_data_capacity(const camera_metadata_t *metadata) {
206     return metadata->data_capacity;
207 }
208
209 camera_metadata_t* copy_camera_metadata(void *dst, size_t dst_size,
210         const camera_metadata_t *src) {
211     size_t memory_needed = get_camera_metadata_compact_size(src);
212
213     if (dst == NULL) return NULL;
214     if (dst_size < memory_needed) return NULL;
215
216     // If copying a newer version of the structure, there may be additional
217     // header fields we don't know about but need to copy
218     ptrdiff_t reserved_size = src->size -
219             calculate_camera_metadata_size(src->entry_capacity,
220                                            src->data_capacity);
221
222     camera_metadata_t *metadata = (camera_metadata_t*)dst;
223     metadata->version = CURRENT_METADATA_VERSION;
224     metadata->flags = src->flags;
225     metadata->entry_count = src->entry_count;
226     metadata->entry_capacity = src->entry_count;
227     metadata->entries = (camera_metadata_buffer_entry_t*)
228              ((uint8_t *)(metadata + 1) + reserved_size);
229     metadata->data_count = src->data_count;
230     metadata->data_capacity = src->data_count;
231     metadata->data = (uint8_t *)(metadata->entries + metadata->entry_capacity);
232     metadata->size = memory_needed;
233
234     if (reserved_size > 0) {
235         memcpy(metadata->reserved, src->reserved, reserved_size);
236     }
237     memcpy(metadata->entries, src->entries,
238             sizeof(camera_metadata_buffer_entry_t[metadata->entry_count]));
239     memcpy(metadata->data, src->data,
240             sizeof(uint8_t[metadata->data_count]));
241     metadata->user = NULL;
242
243     return metadata;
244 }
245
246 int append_camera_metadata(camera_metadata_t *dst,
247         const camera_metadata_t *src) {
248     if (dst == NULL || src == NULL ) return ERROR;
249
250     if (dst->entry_capacity < src->entry_count + dst->entry_count) return ERROR;
251     if (dst->data_capacity < src->data_count + dst->data_count) return ERROR;
252
253     memcpy(dst->entries + dst->entry_count, src->entries,
254             sizeof(camera_metadata_buffer_entry_t[src->entry_count]));
255     memcpy(dst->data + dst->data_count, src->data,
256             sizeof(uint8_t[src->data_count]));
257     if (dst->data_count != 0) {
258         unsigned int i;
259         for (i = dst->entry_count;
260              i < dst->entry_count + src->entry_count;
261              i++) {
262             camera_metadata_buffer_entry_t *entry = dst->entries + i;
263             if ( camera_metadata_type_size[entry->type] * entry->count > 4 ) {
264                 entry->data.offset += dst->data_count;
265             }
266         }
267     }
268     if (dst->entry_count == 0) {
269         // Appending onto empty buffer, keep sorted state
270         dst->flags |= src->flags & FLAG_SORTED;
271     } else if (src->entry_count != 0) {
272         // Both src, dst are nonempty, cannot assume sort remains
273         dst->flags &= ~FLAG_SORTED;
274     } else {
275         // Src is empty, keep dst sorted state
276     }
277     dst->entry_count += src->entry_count;
278     dst->data_count += src->data_count;
279
280     return OK;
281 }
282
283 size_t calculate_camera_metadata_entry_data_size(uint8_t type,
284         size_t data_count) {
285     if (type >= NUM_TYPES) return 0;
286     size_t data_bytes = data_count *
287             camera_metadata_type_size[type];
288     return data_bytes <= 4 ? 0 : data_bytes;
289 }
290
291 static int add_camera_metadata_entry_raw(camera_metadata_t *dst,
292         uint32_t tag,
293         uint8_t  type,
294         const void *data,
295         size_t data_count) {
296
297     if (dst == NULL) return ERROR;
298     if (dst->entry_count == dst->entry_capacity) return ERROR;
299     if (data == NULL) return ERROR;
300
301     size_t data_bytes =
302             calculate_camera_metadata_entry_data_size(type, data_count);
303
304     camera_metadata_buffer_entry_t *entry = dst->entries + dst->entry_count;
305     entry->tag = tag;
306     entry->type = type;
307     entry->count = data_count;
308
309     if (data_bytes == 0) {
310         memcpy(entry->data.value, data,
311                 data_count * camera_metadata_type_size[type] );
312     } else {
313         entry->data.offset = dst->data_count;
314         memcpy(dst->data + entry->data.offset, data, data_bytes);
315         dst->data_count += data_bytes;
316     }
317     dst->entry_count++;
318     dst->flags &= ~FLAG_SORTED;
319     return OK;
320 }
321
322 int add_camera_metadata_entry(camera_metadata_t *dst,
323         uint32_t tag,
324         const void *data,
325         size_t data_count) {
326
327     int type = get_camera_metadata_tag_type(tag);
328     if (type == -1) {
329         ALOGE("%s: Unknown tag %04x.", __FUNCTION__, tag);
330         return ERROR;
331     }
332
333     return add_camera_metadata_entry_raw(dst,
334             tag,
335             type,
336             data,
337             data_count);
338 }
339
340 static int compare_entry_tags(const void *p1, const void *p2) {
341     uint32_t tag1 = ((camera_metadata_buffer_entry_t*)p1)->tag;
342     uint32_t tag2 = ((camera_metadata_buffer_entry_t*)p2)->tag;
343     return  tag1 < tag2 ? -1 :
344             tag1 == tag2 ? 0 :
345             1;
346 }
347
348 int sort_camera_metadata(camera_metadata_t *dst) {
349     if (dst == NULL) return ERROR;
350     if (dst->flags & FLAG_SORTED) return OK;
351
352     qsort(dst->entries, dst->entry_count,
353             sizeof(camera_metadata_buffer_entry_t),
354             compare_entry_tags);
355     dst->flags |= FLAG_SORTED;
356
357     return OK;
358 }
359
360 int get_camera_metadata_entry(camera_metadata_t *src,
361         size_t index,
362         camera_metadata_entry_t *entry) {
363     if (src == NULL || entry == NULL) return ERROR;
364     if (index >= src->entry_count) return ERROR;
365
366     camera_metadata_buffer_entry_t *buffer_entry = src->entries + index;
367
368     entry->index = index;
369     entry->tag = buffer_entry->tag;
370     entry->type = buffer_entry->type;
371     entry->count = buffer_entry->count;
372     if (buffer_entry->count *
373             camera_metadata_type_size[buffer_entry->type] > 4) {
374         entry->data.u8 = src->data + buffer_entry->data.offset;
375     } else {
376         entry->data.u8 = buffer_entry->data.value;
377     }
378     return OK;
379 }
380
381 int find_camera_metadata_entry(camera_metadata_t *src,
382         uint32_t tag,
383         camera_metadata_entry_t *entry) {
384     if (src == NULL) return ERROR;
385
386     uint32_t index;
387     if (src->flags & FLAG_SORTED) {
388         // Sorted entries, do a binary search
389         camera_metadata_buffer_entry_t *search_entry = NULL;
390         camera_metadata_buffer_entry_t key;
391         key.tag = tag;
392         search_entry = bsearch(&key,
393                 src->entries,
394                 src->entry_count,
395                 sizeof(camera_metadata_buffer_entry_t),
396                 compare_entry_tags);
397         if (search_entry == NULL) return NOT_FOUND;
398         index = search_entry - src->entries;
399     } else {
400         // Not sorted, linear search
401         for (index = 0; index < src->entry_count; index++) {
402             if (src->entries[index].tag == tag) {
403                 break;
404             }
405         }
406         if (index == src->entry_count) return NOT_FOUND;
407     }
408
409     return get_camera_metadata_entry(src, index,
410             entry);
411 }
412
413 int delete_camera_metadata_entry(camera_metadata_t *dst,
414         size_t index) {
415     if (dst == NULL) return ERROR;
416     if (index >= dst->entry_count) return ERROR;
417
418     camera_metadata_buffer_entry_t *entry = dst->entries + index;
419     size_t data_bytes = calculate_camera_metadata_entry_data_size(entry->type,
420             entry->count);
421
422     if (data_bytes > 0) {
423         // Shift data buffer to overwrite deleted data
424         uint8_t *start = dst->data + entry->data.offset;
425         uint8_t *end = start + data_bytes;
426         size_t length = dst->data_count - entry->data.offset - data_bytes;
427         memmove(start, end, length);
428
429         // Update all entry indices to account for shift
430         camera_metadata_buffer_entry_t *e = dst->entries;
431         size_t i;
432         for (i = 0; i < dst->entry_count; i++) {
433             if (calculate_camera_metadata_entry_data_size(
434                     e->type, e->count) > 0 &&
435                     e->data.offset > entry->data.offset) {
436                 e->data.offset -= data_bytes;
437             }
438             ++e;
439         }
440         dst->data_count -= data_bytes;
441     }
442     // Shift entry array
443     memmove(entry, entry + 1,
444             sizeof(camera_metadata_buffer_entry_t) *
445             (dst->entry_count - index - 1) );
446     dst->entry_count -= 1;
447
448     return OK;
449 }
450
451 int update_camera_metadata_entry(camera_metadata_t *dst,
452         size_t index,
453         const void *data,
454         size_t data_count,
455         camera_metadata_entry_t *updated_entry) {
456     if (dst == NULL) return ERROR;
457     if (index >= dst->entry_count) return ERROR;
458
459     camera_metadata_buffer_entry_t *entry = dst->entries + index;
460
461     size_t data_bytes =
462             calculate_camera_metadata_entry_data_size(entry->type,
463                     data_count);
464     size_t entry_bytes =
465             calculate_camera_metadata_entry_data_size(entry->type,
466                     entry->count);
467     if (data_bytes != entry_bytes) {
468         // May need to shift/add to data array
469         if (dst->data_capacity < dst->data_count + data_bytes - entry_bytes) {
470             // No room
471             return ERROR;
472         }
473         if (entry_bytes != 0) {
474             // Remove old data
475             uint8_t *start = dst->data + entry->data.offset;
476             uint8_t *end = start + entry_bytes;
477             size_t length = dst->data_count - entry->data.offset - entry_bytes;
478             memmove(start, end, length);
479             dst->data_count -= entry_bytes;
480
481             // Update all entry indices to account for shift
482             camera_metadata_buffer_entry_t *e = dst->entries;
483             size_t i;
484             for (i = 0; i < dst->entry_count; i++) {
485                 if (calculate_camera_metadata_entry_data_size(
486                         e->type, e->count) > 0 &&
487                         e->data.offset > entry->data.offset) {
488                     e->data.offset -= entry_bytes;
489                 }
490                 ++e;
491             }
492         }
493
494         if (data_bytes != 0) {
495             // Append new data
496             entry->data.offset = dst->data_count;
497
498             memcpy(dst->data + entry->data.offset, data, data_bytes);
499             dst->data_count += data_bytes;
500         }
501     } else if (data_bytes != 0) {
502         // data size unchanged, reuse same data location
503         memcpy(dst->data + entry->data.offset, data, data_bytes);
504     }
505
506     if (data_bytes == 0) {
507         // Data fits into entry
508         memcpy(entry->data.value, data,
509                 data_count * camera_metadata_type_size[entry->type]);
510     }
511
512     entry->count = data_count;
513
514     if (updated_entry != NULL) {
515         get_camera_metadata_entry(dst,
516                 index,
517                 updated_entry);
518     }
519
520     return OK;
521 }
522
523 int set_camera_metadata_user_pointer(camera_metadata_t *dst, void* user) {
524     if (dst == NULL) return ERROR;
525     dst->user = user;
526     return OK;
527 }
528
529 int get_camera_metadata_user_pointer(camera_metadata_t *dst, void** user) {
530     if (dst == NULL) return ERROR;
531     *user = dst->user;
532     return OK;
533 }
534
535 static const vendor_tag_query_ops_t *vendor_tag_ops = NULL;
536
537 const char *get_camera_metadata_section_name(uint32_t tag) {
538     uint32_t tag_section = tag >> 16;
539     if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
540         return vendor_tag_ops->get_camera_vendor_section_name(
541             vendor_tag_ops,
542             tag);
543     }
544     if (tag_section >= ANDROID_SECTION_COUNT) {
545         return NULL;
546     }
547     return camera_metadata_section_names[tag_section];
548 }
549
550 const char *get_camera_metadata_tag_name(uint32_t tag) {
551     uint32_t tag_section = tag >> 16;
552     if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
553         return vendor_tag_ops->get_camera_vendor_tag_name(
554             vendor_tag_ops,
555             tag);
556     }
557     if (tag_section >= ANDROID_SECTION_COUNT ||
558         tag >= camera_metadata_section_bounds[tag_section][1] ) {
559         return NULL;
560     }
561     uint32_t tag_index = tag & 0xFFFF;
562     return tag_info[tag_section][tag_index].tag_name;
563 }
564
565 int get_camera_metadata_tag_type(uint32_t tag) {
566     uint32_t tag_section = tag >> 16;
567     if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
568         return vendor_tag_ops->get_camera_vendor_tag_type(
569             vendor_tag_ops,
570             tag);
571     }
572     if (tag_section >= ANDROID_SECTION_COUNT ||
573             tag >= camera_metadata_section_bounds[tag_section][1] ) {
574         return -1;
575     }
576     uint32_t tag_index = tag & 0xFFFF;
577     return tag_info[tag_section][tag_index].tag_type;
578 }
579
580 int set_camera_metadata_vendor_tag_ops(const vendor_tag_query_ops_t *query_ops) {
581     vendor_tag_ops = query_ops;
582     return OK;
583 }
584
585 static void print_data(int fd, const uint8_t *data_ptr, int type, int count);
586
587 void dump_camera_metadata(const camera_metadata_t *metadata,
588         int fd,
589         int verbosity) {
590     if (metadata == NULL) {
591         ALOGE("%s: Metadata is null.", __FUNCTION__);
592         return;
593     }
594     unsigned int i;
595     fdprintf(fd,
596             "Dumping camera metadata array. %d entries, "
597             "%d bytes of extra data.\n",
598             metadata->entry_count, metadata->data_count);
599     fdprintf(fd, "  (%d entries and %d bytes data reserved)\n",
600             metadata->entry_capacity, metadata->data_capacity);
601     fdprintf(fd, "  Version: %d, Flags: %08x\n",
602             metadata->version, metadata->flags);
603     for (i=0; i < metadata->entry_count; i++) {
604         camera_metadata_buffer_entry_t *entry = metadata->entries + i;
605
606         const char *tag_name, *tag_section;
607         tag_section = get_camera_metadata_section_name(entry->tag);
608         if (tag_section == NULL) {
609             tag_section = "unknownSection";
610         }
611         tag_name = get_camera_metadata_tag_name(entry->tag);
612         if (tag_name == NULL) {
613             tag_name = "unknownTag";
614         }
615         const char *type_name;
616         if (entry->type >= NUM_TYPES) {
617             type_name = "unknown";
618         } else {
619             type_name = camera_metadata_type_names[entry->type];
620         }
621         fdprintf(fd, "Tag: %s.%s (%05x): %s[%d]\n",
622              tag_section,
623              tag_name,
624              entry->tag,
625              type_name,
626              entry->count);
627
628         if (verbosity < 1) continue;
629
630         if (entry->type >= NUM_TYPES) continue;
631
632         size_t type_size = camera_metadata_type_size[entry->type];
633         uint8_t *data_ptr;
634         if ( type_size * entry->count > 4 ) {
635             if (entry->data.offset >= metadata->data_count) {
636                 ALOGE("%s: Malformed entry data offset: %d (max %d)",
637                         __FUNCTION__,
638                         entry->data.offset,
639                         metadata->data_count);
640                 continue;
641             }
642             data_ptr = metadata->data + entry->data.offset;
643         } else {
644             data_ptr = entry->data.value;
645         }
646         int count = entry->count;
647         if (verbosity < 2 && count > 16) count = 16;
648
649         print_data(fd, data_ptr, entry->type, count);
650     }
651 }
652
653 static void print_data(int fd, const uint8_t *data_ptr, int type, int count) {
654     static int values_per_line[NUM_TYPES] = {
655         [TYPE_BYTE]     = 16,
656         [TYPE_INT32]    = 4,
657         [TYPE_FLOAT]    = 8,
658         [TYPE_INT64]    = 2,
659         [TYPE_DOUBLE]   = 4,
660         [TYPE_RATIONAL] = 2,
661     };
662     size_t type_size = camera_metadata_type_size[type];
663
664     int lines = count / values_per_line[type];
665     if (count % values_per_line[type] != 0) lines++;
666
667     int index = 0;
668     int j, k;
669     for (j = 0; j < lines; j++) {
670         fdprintf(fd, " [");
671         for (k = 0;
672              k < values_per_line[type] && count > 0;
673              k++, count--, index += type_size) {
674
675             switch (type) {
676                 case TYPE_BYTE:
677                     fdprintf(fd, "%hhu ",
678                             *(data_ptr + index));
679                     break;
680                 case TYPE_INT32:
681                     fdprintf(fd, "%d ",
682                             *(int32_t*)(data_ptr + index));
683                     break;
684                 case TYPE_FLOAT:
685                     fdprintf(fd, "%0.2f ",
686                             *(float*)(data_ptr + index));
687                     break;
688                 case TYPE_INT64:
689                     fdprintf(fd, "%lld ",
690                             *(int64_t*)(data_ptr + index));
691                     break;
692                 case TYPE_DOUBLE:
693                     fdprintf(fd, "%0.2f ",
694                             *(float*)(data_ptr + index));
695                     break;
696                 case TYPE_RATIONAL: {
697                     int32_t numerator = *(int32_t*)(data_ptr + index);
698                     int32_t denominator = *(int32_t*)(data_ptr + index + 4);
699                     fdprintf(fd, "(%d / %d) ",
700                             numerator, denominator);
701                     break;
702                 }
703                 default:
704                     fdprintf(fd, "??? ");
705             }
706         }
707         fdprintf(fd, "]\n");
708     }
709 }