Beispiel #1
0
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;
}
Beispiel #2
0
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;
}