static void oskar_binary_read_header(FILE* stream, oskar_BinaryHeader* header, int* status) { /* Read the header from the stream. */ rewind(stream); if (fread(header, sizeof(oskar_BinaryHeader), 1, stream) != 1) { *status = OSKAR_ERR_BINARY_READ_FAIL; return; } /* Check if this is a valid header. */ if (strncmp("OSKARBIN", header->magic, 8) != 0) { *status = OSKAR_ERR_BINARY_FILE_INVALID; return; } /* Check if the format is compatible. */ if ((int)(header->bin_version) > OSKAR_BINARY_FORMAT_VERSION) { *status = OSKAR_ERR_BINARY_VERSION_UNKNOWN; return; } if (header->bin_version == 1) { /* Check if the architecture is compatible. */ if (oskar_endian() != (int)(header->endian)) { *status = OSKAR_ERR_BINARY_ENDIAN_MISMATCH; return; } /* Check size of data types. */ if (sizeof(int) != (size_t)(header->size_int)) *status = OSKAR_ERR_BINARY_INT_UNKNOWN; if (sizeof(float) != (size_t)(header->size_float)) *status = OSKAR_ERR_BINARY_FLOAT_UNKNOWN; if (sizeof(double) != (size_t)(header->size_double)) *status = OSKAR_ERR_BINARY_DOUBLE_UNKNOWN; } }
oskar_Binary* oskar_binary_create(const char* filename, char mode, int* status) { oskar_Binary* handle; oskar_BinaryHeader header; FILE* stream; int i; /* Open the file and check or write the header, depending on the mode. */ if (mode == 'r') { stream = fopen(filename, "rb"); if (!stream) { *status = OSKAR_ERR_BINARY_OPEN_FAIL; return 0; } oskar_binary_read_header(stream, &header, status); if (*status) return 0; } else if (mode == 'w') { stream = fopen(filename, "wb"); if (!stream) { *status = OSKAR_ERR_BINARY_OPEN_FAIL; return 0; } oskar_binary_write_header(stream, &header, status); } else if (mode == 'a') { stream = fopen(filename, "a+b"); if (!stream) { *status = OSKAR_ERR_BINARY_OPEN_FAIL; return 0; } /* Write header only if the file is empty. */ fseek(stream, 0, SEEK_END); if (ftell(stream) == 0) oskar_binary_write_header(stream, &header, status); } else { *status = OSKAR_ERR_BINARY_OPEN_FAIL; return 0; } /* Allocate index and store the stream handle. */ handle = (oskar_Binary*) malloc(sizeof(oskar_Binary)); handle->stream = stream; handle->open_mode = mode; handle->query_search_start = 0; /* Create the CRC lookup tables. */ handle->crc_data = oskar_crc_create(OSKAR_CRC_32C); /* Initialise tag index. */ handle->num_chunks = 0; handle->extended = 0; handle->data_type = 0; handle->id_group = 0; handle->id_tag = 0; handle->name_group = 0; handle->name_tag = 0; handle->user_index = 0; handle->payload_offset_bytes = 0; handle->payload_size_bytes = 0; handle->block_size_bytes = 0; handle->crc = 0; handle->crc_header = 0; /* Store the contents of the header for later use. */ handle->bin_version = header.bin_version; /* Finish if writing. */ if (mode == 'w') return handle; /* Read all tags in the stream. */ for (i = 0;; ++i) { oskar_BinaryTag tag; unsigned long crc; int format_version, element_size; size_t memcpy_size = 0; /* Try to read a tag, and end the loop if unsuccessful. */ if (fread(&tag, sizeof(oskar_BinaryTag), 1, stream) != 1) break; /* If the bytes read are not a tag, or the reserved flag bits * are not zero, then return an error. */ if (tag.magic[0] != 'T' || tag.magic[2] != 'G' || (tag.flags & 0x1F) != 0) { *status = OSKAR_ERR_BINARY_FILE_INVALID; break; } /* Get the binary format version. */ format_version = tag.magic[1] - 0x40; if (format_version < 1 || format_version > OSKAR_BINARY_FORMAT_VERSION) { *status = OSKAR_ERR_BINARY_VERSION_UNKNOWN; break; } /* Additional checks if format version > 1. */ if (format_version > 1) { /* Check system byte order is compatible. */ if (oskar_endian() && !(tag.flags & (1 << 5))) { *status = OSKAR_ERR_BINARY_ENDIAN_MISMATCH; break; } /* Check data size is compatible. */ element_size = tag.magic[3]; if (tag.data_type & OSKAR_MATRIX) element_size /= 4; if (tag.data_type & OSKAR_COMPLEX) element_size /= 2; if (tag.data_type & OSKAR_CHAR) { if (element_size != sizeof(char)) *status = OSKAR_ERR_BINARY_FORMAT_BAD; } else if (tag.data_type & OSKAR_INT) { if (element_size != sizeof(int)) *status = OSKAR_ERR_BINARY_INT_UNKNOWN; } else if (tag.data_type & OSKAR_SINGLE) { if (element_size != sizeof(float)) *status = OSKAR_ERR_BINARY_FLOAT_UNKNOWN; } else if (tag.data_type & OSKAR_DOUBLE) { if (element_size != sizeof(double)) *status = OSKAR_ERR_BINARY_DOUBLE_UNKNOWN; } else *status = OSKAR_ERR_BINARY_TYPE_UNKNOWN; } /* Check if we need to allocate more storage for the tag data. */ if (i % 10 == 0) oskar_binary_resize(handle, i + 10); /* Initialise the tag index data. */ handle->extended[i] = 0; handle->data_type[i] = 0; handle->id_group[i] = 0; handle->id_tag[i] = 0; handle->name_group[i] = 0; handle->name_tag[i] = 0; handle->user_index[i] = 0; handle->payload_offset_bytes[i] = 0; handle->payload_size_bytes[i] = 0; handle->block_size_bytes[i] = 0; handle->crc[i] = 0; handle->crc_header[i] = 0; /* Start computing the CRC code. */ crc = oskar_crc_compute(handle->crc_data, &tag, sizeof(oskar_BinaryTag)); /* Store the data type and IDs. */ handle->data_type[i] = (int) tag.data_type; handle->id_group[i] = (int) tag.group.id; handle->id_tag[i] = (int) tag.tag.id; /* Store the index in native byte order. */ memcpy_size = MIN(sizeof(int), sizeof(tag.user_index)); memcpy(&handle->user_index[i], tag.user_index, memcpy_size); if (oskar_endian() != OSKAR_LITTLE_ENDIAN) oskar_endian_swap(&handle->user_index[i], sizeof(int)); /* Store the number of bytes in the block in native byte order. */ memcpy_size = MIN(sizeof(size_t), sizeof(tag.size_bytes)); memcpy(&handle->block_size_bytes[i], tag.size_bytes, memcpy_size); if (oskar_endian() != OSKAR_LITTLE_ENDIAN) oskar_endian_swap(&handle->block_size_bytes[i], sizeof(size_t)); /* Set payload size to block size, minus 4 bytes if CRC-32 present. */ handle->payload_size_bytes[i] = handle->block_size_bytes[i]; handle->payload_size_bytes[i] -= (tag.flags & (1 << 6) ? 4 : 0); /* Check if the tag is extended. */ if (tag.flags & (1 << 7)) { /* Extended tag: set the extended flag. */ handle->extended[i] = 1; /* Reduce payload size by sum of length of tag names. */ handle->payload_size_bytes[i] -= (tag.group.bytes + tag.tag.bytes); /* Allocate memory for the tag names. */ handle->name_group[i] = (char*) malloc(tag.group.bytes); handle->name_tag[i] = (char*) malloc(tag.tag.bytes); /* Store the tag names. */ if (fread(handle->name_group[i], tag.group.bytes, 1, stream) != 1) *status = OSKAR_ERR_BINARY_FILE_INVALID; if (fread(handle->name_tag[i], tag.tag.bytes, 1, stream) != 1) *status = OSKAR_ERR_BINARY_FILE_INVALID; if (*status) break; /* Update the CRC code. */ crc = oskar_crc_update(handle->crc_data, crc, handle->name_group[i], tag.group.bytes); crc = oskar_crc_update(handle->crc_data, crc, handle->name_tag[i], tag.tag.bytes); } /* Store the current stream pointer as the payload offset. */ handle->payload_offset_bytes[i] = ftell(stream); /* Increment stream pointer by payload size. */ if (fseek(stream, handle->payload_size_bytes[i], SEEK_CUR) != 0) { *status = OSKAR_ERR_BINARY_FILE_INVALID; break; } /* Store header CRC code and get file CRC code in native byte order. */ handle->crc_header[i] = crc; if (tag.flags & (1 << 6)) { if (fread(&handle->crc[i], 4, 1, stream) != 1) { *status = OSKAR_ERR_BINARY_FILE_INVALID; break; } if (oskar_endian() != OSKAR_LITTLE_ENDIAN) oskar_endian_swap(&handle->crc[i], sizeof(unsigned long)); } /* Save the number of tags read from the stream. */ handle->num_chunks = i + 1; } return handle; }
void oskar_binary_write(oskar_Binary* handle, unsigned char data_type, unsigned char id_group, unsigned char id_tag, int user_index, size_t data_size, const void* data, int* status) { oskar_BinaryTag tag; size_t block_size; unsigned long crc = 0; /* Check if safe to proceed. */ if (*status) return; /* Check file was opened for writing. */ if (handle->open_mode != 'w' && handle->open_mode != 'a') { *status = OSKAR_ERR_BINARY_NOT_OPEN_FOR_WRITE; return; } /* Initialise the tag. */ tag.magic[0] = 'T'; tag.magic[1] = 0x40 + OSKAR_BINARY_FORMAT_VERSION; tag.magic[2] = 'G'; tag.magic[3] = 0; memset(tag.size_bytes, 0, sizeof(tag.size_bytes)); memset(tag.user_index, 0, sizeof(tag.user_index)); /* Set the size of the payload element. */ if (data_type & OSKAR_CHAR) tag.magic[3] = sizeof(char); else if (data_type & OSKAR_INT) tag.magic[3] = sizeof(int); else if (data_type & OSKAR_SINGLE) tag.magic[3] = sizeof(float); else if (data_type & OSKAR_DOUBLE) tag.magic[3] = sizeof(double); else { *status = OSKAR_ERR_BINARY_TYPE_UNKNOWN; return; } if (data_type & OSKAR_COMPLEX) tag.magic[3] *= 2; if (data_type & OSKAR_MATRIX) tag.magic[3] *= 4; /* Set up the tag identifiers */ tag.flags = 0; tag.flags |= (1 << 6); /* Set bit 6 to indicate CRC-32C code added. */ tag.data_type = data_type; tag.group.id = id_group; tag.tag.id = id_tag; /* Get the number of bytes in the block and user index in * little-endian byte order (add 4 for CRC). */ block_size = data_size + 4; if (sizeof(size_t) != 4 && sizeof(size_t) != 8) { *status = OSKAR_ERR_BINARY_FORMAT_BAD; return; } if (oskar_endian() != OSKAR_LITTLE_ENDIAN) { oskar_endian_swap(&block_size, sizeof(size_t)); oskar_endian_swap(&user_index, sizeof(int)); } /* Copy user index and block size to the tag, as little endian values. */ memcpy(tag.user_index, &user_index, sizeof(int)); memcpy(tag.size_bytes, &block_size, sizeof(size_t)); /* Tag is complete at this point, so calculate CRC. */ crc = oskar_crc_compute(handle->crc_data, &tag, sizeof(oskar_BinaryTag)); crc = oskar_crc_update(handle->crc_data, crc, data, data_size); if (oskar_endian() != OSKAR_LITTLE_ENDIAN) oskar_endian_swap(&crc, sizeof(unsigned long)); /* Write the tag to the file. */ if (fwrite(&tag, sizeof(oskar_BinaryTag), 1, handle->stream) != 1) { *status = OSKAR_ERR_BINARY_WRITE_FAIL; return; } /* Check there is data to write. */ if (data && data_size > 0) { /* Write the data to the file. */ if (fwrite(data, 1, data_size, handle->stream) != data_size) { *status = OSKAR_ERR_BINARY_WRITE_FAIL; return; } } /* Write the 4-byte CRC-32C code. */ if (fwrite(&crc, 4, 1, handle->stream) != 1) *status = OSKAR_ERR_BINARY_WRITE_FAIL; }