From e7d138caba5178118c6466584b77d791318d9082 Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Sun, 14 Jul 2019 15:42:30 +0200 Subject: Refactor the data structure Move away from ad-hoc prints to being able to store the entire file structure in memory for later processing or storing. --- src/e2e.c | 376 +++++++++++++++++++++++++++++-------------------------------- src/e2e.h | 63 ++++++++++- src/main.c | 14 +-- 3 files changed, 247 insertions(+), 206 deletions(-) diff --git a/src/e2e.c b/src/e2e.c index 1ab1449..cb5dc62 100644 --- a/src/e2e.c +++ b/src/e2e.c @@ -1,9 +1,21 @@ +#include #include #include +#include #include #include "e2e.h" -uint32_t u32le(const char *src) +static uint16_t u16le(const char *src) +{ + uint8_t bytes[2]; + + memcpy(bytes, src, 2); + + return (uint16_t)bytes[0] + + (((uint16_t)bytes[1]) << 8); +} + +static uint32_t u32le(const char *src) { uint8_t bytes[4]; @@ -15,20 +27,20 @@ uint32_t u32le(const char *src) + (((uint32_t)bytes[3]) << 24); } -static int make_pointer(const char *start, const char *end, - const char *pos, const char **out) +static int make_pointer(const struct e2e_data *data, const char *pos, + const char **out) { - uint32_t ptroffs; + uint32_t offs; - ptroffs = u32le(pos); + offs = u32le(pos); - if (!ptroffs) + if (!offs) *out = NULL; else { - if (ptroffs > end - start) + if (offs > data->end - data->start) return 1; - *out = start + ptroffs; + *out = data->start + offs; } return 0; @@ -61,166 +73,135 @@ static int make_pointer(const char *start, const char *end, #define CHUNK_TYPE 52 #define CHUNK_HEADER_END 60 -#define DATA_IMAGE 0x40000000 - #define IMAGE_SIZE 0 #define IMAGE_TYPE 4 #define IMAGE_WIDTH 12 #define IMAGE_HEIGHT 16 #define IMAGE_IMAGE 20 -#define IMAGE_FUNDUS 0x02010201 -#define IMAGE_TOMOGRAM 0x02200201 - #define UF16_MANTISSA_MASK ((1 << 11) - 1) // 10 most significant bits +#define UF16_EXP_OFFSET -38 // FIXME: pure guess -struct e2e_directory { - size_t num_entries; - const char *current, *next; -}; - -struct e2e_entry { - const char *start; - size_t size; - - uint32_t patient_id, study_id, series_id, slice_id, type; -}; - -struct e2e_chunk { - const char *origin; - const struct e2e_entry *entry; - - size_t size; - uint32_t patient_id, study_id, series_id, slice_id, type; -}; - -struct e2e_fundus { - size_t size, width, height; - - uint32_t type; - -}; - -static int read_fundus(const char *start, const char *end, const char *cursor, - size_t width, size_t height) +static int read_fundus(struct e2e_data *data, const char *cursor, + struct e2e_image *image) { - FILE *fp; - char buffer[1024]; + // This cast is _technically_ illegal, but I can't think of a way in + // which this would cause problems in practice. + image->fundus = (const uint8_t*)cursor; - snprintf(buffer, sizeof(buffer), "out/fundus_%tx.py", cursor - start); - fp = fopen(buffer, "w"); - - fprintf(fp, "data = [\n"); - for (size_t y = 0; y < height; y++) { - fprintf(fp, "\t["); - - for (size_t x = 0; x < width; x++) { - uint8_t px; + return 0; +} - memcpy(&px, cursor + y * width + x, 1); - fprintf(fp, "%s% 3hhu", x ? ", " : "", px); - } - fprintf(fp, "]%s\n", y + 1 >= height ? "" : ","); +static int read_tomogram(struct e2e_data *data, const char *cursor, + struct e2e_image *image) +{ + image->tomogram = calloc(image->width * image->height, sizeof(float)); + if (!image->tomogram) + return ENOMEM; + + for (size_t y = 0; y < image->height; y++) + for (size_t x = 0; x < image->width; x++) { + size_t i = y * image->width + x; + uint16_t raw; + int exp, man; + + raw = u16le(cursor + i * 2); + man = raw & UF16_MANTISSA_MASK; + exp = (raw & ~UF16_MANTISSA_MASK) >> 10; + image->tomogram[i] = ldexpf(man, exp2f(exp + UF16_EXP_OFFSET)); } - fprintf(fp, "]\n"); - fclose(fp); return 0; } -static int read_tomogram(const char *start, const char *end, const char *cursor, - size_t width, size_t height) +static int read_image(struct e2e_data *data, const char *cursor, + struct e2e_image *image) { - FILE *fp; - char buffer[1024]; - - snprintf(buffer, sizeof(buffer), "out/tomogram_%tx.py", cursor - start); - fp = fopen(buffer, "w"); - - fprintf(fp, "data = [\n"); - for (size_t y = 0; y < height; y++) { - fprintf(fp, "\t["); - - for (size_t x = 0; x < width; x++) { - uint16_t raw; - int exp, man; - float px; + if (cursor + IMAGE_IMAGE > data->end) { + fprintf(stderr, "Image data at %td ends past the end of file.\n", + cursor - data->start); + return 1; + } - memcpy(&raw, cursor + (y * width + x) * 2, 2); + image->size = u32le(cursor + IMAGE_SIZE); + image->type = u32le(cursor + IMAGE_TYPE); + image->width = u32le(cursor + IMAGE_WIDTH); + image->height = u32le(cursor + IMAGE_HEIGHT); + cursor += IMAGE_IMAGE; - man = raw & UF16_MANTISSA_MASK; - exp = (raw & ~UF16_MANTISSA_MASK) >> 10; + switch (image->type) { + case IMAGE_FUNDUS: + return read_fundus(data, cursor, image); - px = man * exp2f(exp); - fprintf(fp, "%s%e", x ? ", " : "", px); - } - fprintf(fp, "]%s\n", y + 1 >= height ? "" : ","); + case IMAGE_TOMOGRAM: + return read_tomogram(data, cursor, image); } - fprintf(fp, "]\n"); - fclose(fp); return 0; } -static int read_image(const char *start, const char *end, - const struct e2e_chunk *chunk) +static int read_entry(struct e2e_data *data, const char *cursor, + struct e2e_entry *entry) { - const char *cursor = chunk->origin + CHUNK_HEADER_END; - size_t size, width, height; - uint32_t type; + const char *pos; + struct e2e_chunk *chunk; - if (cursor + IMAGE_IMAGE > end) { - fprintf(stderr, "Image data at %td ends past the end of file.\n", - cursor - start); + if (cursor + ENTRY_END > data->end) { + fprintf(stderr, "Entry at %td data->ends past the data->end of file.\n", + cursor - data->start); return 1; } - size = u32le(cursor + IMAGE_SIZE); - type = u32le(cursor + IMAGE_TYPE); - width = u32le(cursor + IMAGE_WIDTH); - height = u32le(cursor + IMAGE_HEIGHT); - - printf("# %08tx Image data: %zux%zu, size=%zu\n", - cursor - start, width, height, size); - printf("# Slice is %d\n", chunk->slice_id); - printf("# Type is %08x\n", type); + if (make_pointer(data, cursor + ENTRY_POS, &pos)) { + fprintf(stderr, "Entry at %td has a bad 'pos' entry.\n", + cursor - data->start); + return 1; + } - switch (type) { - case IMAGE_FUNDUS: - read_fundus(start, end, cursor + IMAGE_IMAGE, width, height); - break; + if (pos != cursor) { + fprintf(stderr, "Entry at %td has a 'pos' entry that differs from the entry's actual position", + cursor - data->start); + return 1; + } - case IMAGE_TOMOGRAM: - read_tomogram(start, end, cursor + IMAGE_IMAGE, width, height); - break; + if (make_pointer(data, cursor + ENTRY_START, &entry->start)) { + fprintf(stderr, "Entry at %td has a bad 'data->start' entry.\n", + cursor - data->start); + return 1; } - return 0; -} + entry->size = u32le(cursor + ENTRY_SIZE); + entry->patient_id = u32le(cursor + ENTRY_PATIENT_ID); + entry->study_id = u32le(cursor + ENTRY_STUDY_ID); + entry->series_id = u32le(cursor + ENTRY_SERIES_ID); + entry->slice_id = u32le(cursor + ENTRY_SLICE_ID); + entry->type = u32le(cursor + ENTRY_TYPE); -static int read_chunk(const char *start, const char *end, - const struct e2e_entry *entry, struct e2e_chunk *chunk) -{ - const char *cursor = entry->start, *pos; + if (!entry->start || entry->start < data->start + HEADER_END) + return 0; + + cursor = entry->start; + chunk = &entry->chunk; + entry->has_chunk = 1; chunk->entry = entry; chunk->origin = cursor; - if (cursor + CHUNK_HEADER_END > end) { + if (cursor + CHUNK_HEADER_END > data->end) { fprintf(stderr, "Chunk at %td ends past the end of file.\n", - cursor - start); + cursor - data->start); return 1; } - if (make_pointer(start, end, cursor + CHUNK_POS, &pos)) { + if (make_pointer(data, cursor + CHUNK_POS, &pos)) { fprintf(stderr, "Chunk at %td has a bad 'pos' entry.\n", - cursor - start); + cursor - data->start); return 1; } if (pos != cursor) { - fprintf(stderr, "Chunk at %td has a 'pos' entry that differs from the entry's actual position", - cursor - start); + fprintf(stderr, "Chunk at %td has a 'pos' entry that differs from the entry's actual position.\n", + cursor - data->start); return 1; } @@ -230,80 +211,75 @@ static int read_chunk(const char *start, const char *end, chunk->series_id = u32le(cursor + CHUNK_SERIES_ID); chunk->slice_id = u32le(cursor + CHUNK_SLICE_ID); chunk->type = u32le(cursor + CHUNK_TYPE); + cursor += CHUNK_HEADER_END; + + switch (chunk->type) { + case CHUNK_IMAGE: + return read_image(data, cursor, &chunk->image); + } return 0; } -static int read_entry(const char *start, const char *end, - const char *cursor, struct e2e_entry *entry) +static int read_directory(struct e2e_data *data, const char *cursor, + struct e2e_directory *dir) { - const char *pos; - - if (cursor + ENTRY_END > end) { - fprintf(stderr, "Entry at %td ends past the end of file.\n", - cursor - start); + if (cursor + DIRECTORY_END > data->end) { + fprintf(stderr, "Directory at %td data->ends past the data->end of file.\n", + cursor - data->start); return 1; } - if (make_pointer(start, end, cursor + ENTRY_POS, &pos)) { - fprintf(stderr, "Entry at %td has a bad 'pos' entry.\n", - cursor - start); - return 1; - } + dir->num_entries = u32le(cursor + DIRECTORY_NUM_ENTRIES); - if (pos != cursor) { - fprintf(stderr, "Entry at %td has a 'pos' entry that differs from the entry's actual position", - cursor - start); + if (make_pointer(data, cursor + DIRECTORY_CURRENT, &dir->current)) { + fprintf(stderr, "Directory at %td contains a bad 'current' field.\n", + cursor - data->start); return 1; } - if (make_pointer(start, end, cursor + ENTRY_START, &entry->start)) { - fprintf(stderr, "Entry at %td has a bad 'start' entry.\n", - cursor - start); + if (make_pointer(data, cursor + DIRECTORY_NEXT, &dir->next)) { + fprintf(stderr, "Directory at %td contains a bad 'current' field.\n", + cursor - data->start); return 1; } - entry->size = u32le(cursor + ENTRY_SIZE); - entry->patient_id = u32le(cursor + ENTRY_PATIENT_ID); - entry->study_id = u32le(cursor + ENTRY_STUDY_ID); - entry->series_id = u32le(cursor + ENTRY_SERIES_ID); - entry->slice_id = u32le(cursor + ENTRY_SLICE_ID); - entry->type = u32le(cursor + ENTRY_TYPE); - return 0; } -static int read_directory(const char *start, const char *end, - const char *cursor, struct e2e_directory *dir) +// Because entries are a part of a larger array in e2e_data, they're freed +// all at once by e2e_destroy and not here. +void e2e_entry_destroy(struct e2e_entry *entry) { - if (cursor + DIRECTORY_END > end) { - fprintf(stderr, "Directory at %td ends past the end of file.\n", - cursor - start); - return 1; - } + if (entry->has_chunk + && entry->chunk.type == CHUNK_IMAGE + && entry->chunk.image.type == IMAGE_TOMOGRAM) + free(entry->chunk.image.tomogram); +} - dir->num_entries = u32le(cursor + DIRECTORY_NUM_ENTRIES); +void e2e_destroy(struct e2e_data *data) +{ + struct e2e_directory *dir, *next; - if (make_pointer(start, end, cursor + DIRECTORY_CURRENT, &dir->current)) { - fprintf(stderr, "Directory at %td contains a bad 'current' field.\n", - cursor - start); - return 1; - } + for (dir = data->dirs; dir; dir = next) { + next = dir->data_list.next; - if (make_pointer(start, end, cursor + DIRECTORY_NEXT, &dir->next)) { - fprintf(stderr, "Directory at %td contains a bad 'current' field.\n", - cursor - start); - return 1; + for (size_t i = 0; i < dir->num_entries; i++) + e2e_entry_destroy(dir->entries + i); + free(dir->entries); + free(dir); } - - return 0; } -int e2e_read(const char *start, const char *end) +int e2e_read(struct e2e_data *data, const char *start, const char *end) { - int rv; + int rv = 0; const char *cursor; + data->start = start; + data->end = end; + data->dirs = NULL; + if (start + HEADER_END > end) { fprintf(stderr, "File too short to be an E2E file.\n"); return 1; @@ -315,49 +291,53 @@ int e2e_read(const char *start, const char *end) cursor += DIRECTORY_END; // skip the first directory (wtf?) while (cursor) { - struct e2e_directory dir; + struct e2e_directory *dir; + size_t i; + + dir = malloc(sizeof(struct e2e_directory)); + if (!dir) { + rv = ENOMEM; + goto error; + } - rv = read_directory(start, end, cursor, &dir); + rv = read_directory(data, cursor, dir); if (rv) - return rv; + goto error_read_directory; - printf("# %08tx Directory: num_entries=%zu, current=%08td, next=%td\n", - cursor - start, dir.num_entries, dir.current - start, - dir.next ? dir.next - start : 0); + dir->entries = calloc(dir->num_entries, sizeof(struct e2e_entry)); + if (!dir->entries) { + rv = ENOMEM; + goto error_alloc_entries; + } - for (size_t i = 0; i < dir.num_entries; i++) { + for (i = 0; i < dir->num_entries; i++) { const char *entry_cursor; - struct e2e_entry entry; - struct e2e_chunk chunk; + struct e2e_entry *entry = dir->entries + i; entry_cursor = cursor + DIRECTORY_END + i * ENTRY_END; - rv = read_entry(start, end, entry_cursor, &entry); - if (rv) - return rv; - - printf("# %08tx Entry %zu: start=%08tx\n", - entry_cursor - start, i, - entry.start ? entry.start - start : 0); - - if (!entry.start) - continue; - - rv = read_chunk(start, end, &entry, &chunk); + rv = read_entry(data, entry_cursor, entry); if (rv) - continue; + goto error_read_entry; + } - printf("# %08tx Chunk, size=%zu, type=%08x\n", - entry.start - start, chunk.size, chunk.type); + eli_append(&data->dirs, dir, data_list); + cursor = dir->next; + continue; - switch (chunk.type) { - case DATA_IMAGE: - read_image(start, end, &chunk); - break; - } - } - cursor = dir.next; + error_read_entry: + for (size_t j = 0; j < i; j++) + e2e_entry_destroy(dir->entries + j); + free(dir->entries); + error_alloc_entries: + error_read_directory: + free(dir); + goto error; } return 0; -} \ No newline at end of file + +error: + e2e_destroy(data); + return rv; +} diff --git a/src/e2e.h b/src/e2e.h index d7f7a13..167a83a 100644 --- a/src/e2e.h +++ b/src/e2e.h @@ -1,3 +1,64 @@ #include +#include "eli.h" -int e2e_read(const char *start, const char *end); \ No newline at end of file +#define IMAGE_FUNDUS 0x02010201 +#define IMAGE_TOMOGRAM 0x02200201 + +struct e2e_image { + size_t size, width, height; + uint32_t type; + + union { + const uint8_t *fundus; + float *tomogram; + }; +}; + +#define CHUNK_IMAGE 0x40000000 + +struct e2e_chunk { + const char *origin; + const struct e2e_entry *entry; + + size_t size; + uint32_t patient_id, study_id, series_id, slice_id, ind; + uint32_t type; + + union { + struct e2e_image image; + }; +}; + +struct e2e_entry { + const char *start; + size_t size; + + uint32_t patient_id, study_id, series_id, slice_id, type; + + _Bool has_chunk; + struct e2e_chunk chunk; +}; + +struct e2e_fundus { + size_t size, width, height; + + uint32_t type; +}; + +struct e2e_directory { + const char *current, *next; + + size_t num_entries; + struct e2e_entry *entries; + + eli_header data_list; +}; + +struct e2e_data { + const char *start, *end; + + struct e2e_directory *dirs; +}; + +int e2e_read(struct e2e_data *data, const char *start, const char *end); +void e2e_destroy(struct e2e_data *data); diff --git a/src/main.c b/src/main.c index e51d863..19d2957 100644 --- a/src/main.c +++ b/src/main.c @@ -10,15 +10,14 @@ #include "e2e.h" -uint32_t u32le(const char*); - int main(int argc, char **argv) { - int rv = 0; + int ret = 0; char *inpath; int fd; struct stat st; void *input; + struct e2e_data data; if (argc < 2) { fprintf(stderr, "%s [FILE]\n", argv[0]); @@ -35,21 +34,22 @@ int main(int argc, char **argv) if (fstat(fd, &st) == -1) { perror("fstat"); - rv = 1; + ret = 1; goto error_mmap; } input = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (!input) { perror("mmap"); - rv = 1; + ret = 1; goto error_mmap; } - e2e_read(input, input + st.st_size); + e2e_read(&data, input, input + st.st_size); + e2e_destroy(&data); munmap(input, st.st_size); error_mmap: close(fd); - return rv; + return ret; } \ No newline at end of file -- cgit