Merge branch 'develop' of https://github.com/fougue/gmio into develop

This commit is contained in:
Hugues Delorme 2017-01-06 17:02:18 +01:00
commit 579989484c
7 changed files with 642 additions and 210 deletions

View File

@ -632,7 +632,8 @@ static size_t gmio_amf_ostringstream_write_zlib(
int z_retcode = Z_OK;
context->z_uncompressed_size += len;
context->z_crc32 = crc32(context->z_crc32, (const Bytef*)ptr, len);
context->z_crc32 =
gmio_zlib_crc32_update(context->z_crc32, (const uint8_t*)ptr, len);
z_stream->next_in = (z_const Bytef*)ptr;
z_stream->avail_in = len;
@ -866,7 +867,7 @@ int gmio_amf_write(
(uint8_t*)memblock->ptr + mblock_halfsize,
mblock_halfsize,
NULL);
context.z_crc32 = crc32(0, NULL, 0);
context.z_crc32 = gmio_zlib_crc32_initial();
context.error =
gmio_zlib_compress_init(
&context.z_stream, &opts->z_compress_options);

View File

@ -75,14 +75,21 @@ static const struct tm* gmio_nonnull_datetime(const struct tm* datetime)
return datetime;
}
#ifdef GMIO_HAVE_INT64_TYPE
/* Encodes little-endian 64b val in buffer and returns advanced buffer pos */
static uint8_t* gmio_adv_encode_uint64_le(uint64_t val, uint8_t* bytes)
/* Reads 16b uint from memory (little-endian) and advances buffer pos */
static uint16_t gmio_adv_decode_uint16_le(const uint8_t** bytes)
{
gmio_encode_uint64_le(val, bytes);
return bytes + 8;
const uint16_t val = gmio_decode_uint16_le(*bytes);
*bytes += 2;
return val;
}
/* Reads 32b uint from memory (little-endian) and advances buffer pos */
static uint32_t gmio_adv_decode_uint32_le(const uint8_t** bytes)
{
const uint32_t val = gmio_decode_uint32_le(*bytes);
*bytes += 4;
return val;
}
#endif
/* Encodes little-endian 32b val in buffer and returns advanced buffer pos */
static uint8_t* gmio_adv_encode_uint32_le(uint32_t val, uint8_t* bytes)
@ -98,25 +105,251 @@ static uint8_t* gmio_adv_encode_uint16_le(uint16_t val, uint8_t* bytes)
return bytes + 2;
}
#ifdef GMIO_HAVE_INT64_TYPE
/* Reads 64b uint from memory (little-endian) and advances buffer pos */
static uint64_t gmio_adv_decode_uint64_le(const uint8_t** bytes)
{
const uint64_t val = gmio_decode_uint64_le(*bytes);
*bytes += 8;
return val;
}
/* Encodes little-endian 64b val in buffer and returns advanced buffer pos */
static uint8_t* gmio_adv_encode_uint64_le(uint64_t val, uint8_t* bytes)
{
gmio_encode_uint64_le(val, bytes);
return bytes + 8;
}
#endif
/* Helper to facilitate return from gmio_zip_write_xxx() API functions */
static size_t gmio_zip_write_returnhelper(
struct gmio_stream* stream, size_t written, size_t expected, int* error)
struct gmio_stream* stream,
size_t written,
size_t expected,
int* ptr_error)
{
if (error != NULL) {
if (ptr_error != NULL) {
const bool no_error = written == expected && !gmio_stream_error(stream);
*error = no_error ? GMIO_ERROR_OK : GMIO_ERROR_STREAM;
*ptr_error = no_error ? GMIO_ERROR_OK : GMIO_ERROR_STREAM;
}
return written;
}
static bool gmio_zip_read_checkhelper(
struct gmio_stream* stream, size_t read, size_t expected)
{
return read == expected && !gmio_stream_error(stream);
}
/* Helper to facilitate return from gmio_zip_read_xxx() and gmio_zip_write_xxx()
* API functions */
static size_t gmio_zip_io_returnerr(size_t io_len, int error, int* ptr_error)
{
if (ptr_error != NULL)
*ptr_error = error;
return io_len;
}
/* ----------
* API functions
* ---------- */
size_t gmio_zip_read_local_file_header(
struct gmio_stream *stream,
struct gmio_zip_local_file_header *info,
int *ptr_error)
{
uint8_t buff[GMIO_ZIP_SIZE_LOCAL_FILE_HEADER];
uint8_t* buffit = buff;
const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff));
if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff)))
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error);
/* 4-bytes magic */
if (gmio_adv_decode_uint32_le(&buffit) != 0x04034b50) {
return gmio_zip_io_returnerr(
read_len, GMIO_ZIP_UTILS_ERROR_BAD_MAGIC, ptr_error);
}
/* 2-bytes version needed to extract */
info->version_needed_to_extract = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes general purpose bit flag */
info->general_purpose_flags = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes compression method */
info->compress_method = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes last mod file time */
/* 2-bytes last mod file date */
/* TODO: convert DOS datetime to struct tm */
const uint32_t dos_datetime = gmio_adv_decode_uint32_le(&buffit);
GMIO_UNUSED(dos_datetime);
info->lastmod_datetime = NULL;
/* 4-bytes crc-32 */
info->crc32 = gmio_adv_decode_uint32_le(&buffit);
/* 4-bytes compressed size */
info->compressed_size = gmio_adv_decode_uint32_le(&buffit);
/* 4-bytes uncompressed size */
info->uncompressed_size = gmio_adv_decode_uint32_le(&buffit);
/* 2-bytes file name length */
info->filename_len = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes extra field length */
info->extrafield_len = gmio_adv_decode_uint16_le(&buffit);
info->filename = NULL;
info->extrafield = NULL;
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
}
size_t gmio_zip_write_data_descriptor(
struct gmio_stream *stream,
const struct gmio_zip_data_descriptor *info,
int* ptr_error)
{
const size_t fixed_data_len =
info->use_zip64 ?
GMIO_ZIP64_SIZE_DATA_DESCRIPTOR :
GMIO_ZIP_SIZE_DATA_DESCRIPTOR;
uint8_t fixed_data[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR];
uint8_t* buff = fixed_data;
/* 4-bytes crc-32 */
buff = gmio_adv_encode_uint32_le(info->crc32, buff);
/* Compressed size and uncompressed size (4 or 8 bytes) */
if (info->use_zip64) {
#ifdef GMIO_HAVE_INT64_TYPE
buff = gmio_adv_encode_uint64_le(info->compressed_size, buff);
buff = gmio_adv_encode_uint64_le(info->uncompressed_size, buff);
#else
/* TODO: error code */
return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, ptr_error);
#endif
}
else {
if (info->compressed_size <= UINT32_MAX
&& info->uncompressed_size <= UINT32_MAX)
{
buff = gmio_adv_encode_uint32_le((uint32_t)info->compressed_size, buff);
buff = gmio_adv_encode_uint32_le((uint32_t)info->uncompressed_size, buff);
}
else {
/* TODO: error code */
return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, ptr_error);
}
}
/* Write to stream */
const size_t written_len =
gmio_stream_write_bytes(stream, fixed_data, fixed_data_len);
return gmio_zip_write_returnhelper(
stream, written_len, fixed_data_len, ptr_error);
}
size_t gmio_zip_read_central_directory_header(
struct gmio_stream *stream,
struct gmio_zip_central_directory_header *info,
int *ptr_error)
{
uint8_t buff[GMIO_ZIP_SIZE_CENTRAL_DIRECTORY_HEADER];
uint8_t* buffit = buff;
const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff));
if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff)))
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error);
/* 4-bytes magic */
if (gmio_adv_decode_uint32_le(&buffit) != 0x02014b50) {
return gmio_zip_io_returnerr(
read_len, GMIO_ZIP_UTILS_ERROR_BAD_MAGIC, ptr_error);
}
/* 2-bytes version made by */
info->version_made_by = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes version needed to extract */
info->version_needed_to_extract = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes general purpose bit flag */
info->general_purpose_flags = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes compression method */
info->compress_method = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes last mod file time */
/* 2-bytes last mod file date */
/* TODO: convert DOS datetime to struct tm */
const uint32_t dos_datetime = gmio_adv_decode_uint32_le(&buffit);
GMIO_UNUSED(dos_datetime);
info->lastmod_datetime = NULL;
/* 4-bytes crc-32 */
info->crc32 = gmio_adv_decode_uint32_le(&buffit);
/* 4-bytes compressed size */
info->compressed_size = gmio_adv_decode_uint32_le(&buffit);
/* 4-bytes uncompressed size */
info->uncompressed_size = gmio_adv_decode_uint32_le(&buffit);
/* 2-bytes file name length */
info->filename_len = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes extra field length */
info->extrafield_len = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes file comment length */
info->filecomment_len = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes disk number start */
info->disk_nb_start = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes internal file attributes */
info->internal_file_attrs = gmio_adv_decode_uint16_le(&buffit);
/* 4-bytes external file attributes */
info->external_file_attrs = gmio_adv_decode_uint32_le(&buffit);
/* 4-bytes relative offset of local header */
info->relative_offset_local_header = gmio_adv_decode_uint32_le(&buffit);
info->filename = NULL;
info->extrafield = NULL;
info->filecomment = NULL;
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
}
size_t gmio_zip_read_end_of_central_directory_record(
struct gmio_stream *stream,
struct gmio_zip_end_of_central_directory_record *info,
int *ptr_error)
{
uint8_t buff[GMIO_ZIP_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD];
uint8_t* buffit = buff;
const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff));
if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff)))
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error);
/* 4-bytes magic */
if (gmio_adv_decode_uint32_le(&buffit) != 0x06054b50) {
return gmio_zip_io_returnerr(
read_len, GMIO_ZIP_UTILS_ERROR_BAD_MAGIC, ptr_error);
}
/* 2-bytes number of this disk */
info->disk_nb = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes number of the disk with the start of the central directory */
info->disk_nb_with_start_of_central_dir = gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes total number of entries in the central directory on this disk */
info->total_entry_count_in_central_dir_on_disk =
gmio_adv_decode_uint16_le(&buffit);
/* 2-bytes total number of entries in the central directory */
info->total_entry_count_in_central_dir = gmio_adv_decode_uint16_le(&buffit);
/* 4-bytes size of the central directory */
info->central_dir_size = gmio_adv_decode_uint32_le(&buffit);
/* 4-bytes offset of start of central directory with respect to the starting
* disk number */
info->start_offset_central_dir_from_disk_start_nb =
gmio_adv_decode_uint32_le(&buffit);
/* 2-bytes .ZIP file comment length */
info->filecomment_len = gmio_adv_decode_uint16_le(&buffit);
info->filecomment = NULL;
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
}
size_t gmio_zip_write_local_file_header(
struct gmio_stream* stream,
const struct gmio_zip_local_file_header* info,
int* error)
int* ptr_error)
{
uint8_t fixed_data[GMIO_ZIP_SIZE_LOCAL_FILE_HEADER];
uint8_t* buff = fixed_data;
@ -162,55 +395,62 @@ size_t gmio_zip_write_local_file_header(
written_len +=
gmio_stream_write_bytes(stream, info->extrafield, info->extrafield_len);
return gmio_zip_write_returnhelper(
stream, written_len, expected_written_len, error);
stream, written_len, expected_written_len, ptr_error);
}
size_t gmio_zip_write_data_descriptor(
size_t gmio_zip_read_data_descriptor(
struct gmio_stream *stream,
const struct gmio_zip_data_descriptor *info,
int* error)
struct gmio_zip_data_descriptor *info,
int* ptr_error)
{
const size_t fixed_data_len =
info->use_zip64 ?
GMIO_ZIP64_SIZE_DATA_DESCRIPTOR :
GMIO_ZIP_SIZE_DATA_DESCRIPTOR;
uint8_t fixed_data[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR];
uint8_t* buff = fixed_data;
uint8_t buff[GMIO_ZIP_SIZE_DATA_DESCRIPTOR];
uint8_t* buffit = buff;
const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff));
if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff)))
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error);
/* 4-bytes crc-32 */
buff = gmio_adv_encode_uint32_le(info->crc32, buff);
/* Compressed size and uncompressed size (4 or 8 bytes) */
if (info->use_zip64) {
#ifdef GMIO_HAVE_INT64_TYPE
buff = gmio_adv_encode_uint64_le(info->compressed_size, buff);
buff = gmio_adv_encode_uint64_le(info->uncompressed_size, buff);
#else
return GMIO_ERROR_UNKNOWN; /* TODO: error code */
#endif
}
else {
if (info->compressed_size <= UINT32_MAX
&& info->uncompressed_size <= UINT32_MAX)
{
buff = gmio_adv_encode_uint32_le((uint32_t)info->compressed_size, buff);
buff = gmio_adv_encode_uint32_le((uint32_t)info->uncompressed_size, buff);
}
else {
return GMIO_ERROR_UNKNOWN; /* TODO: error code */
}
}
info->crc32 = gmio_adv_decode_uint32_le(&buffit);
/* 4-bytes compressed size */
info->compressed_size = gmio_adv_decode_uint32_le(&buffit);
/* 4-bytes uncompressed size */
info->uncompressed_size = gmio_adv_decode_uint32_le(&buffit);
/* Write to stream */
const size_t written_len =
gmio_stream_write_bytes(stream, fixed_data, fixed_data_len);
return gmio_zip_write_returnhelper(
stream, written_len, fixed_data_len, error);
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
}
size_t gmio_zip64_read_data_descriptor(
struct gmio_stream* stream,
struct gmio_zip_data_descriptor* info,
int* ptr_error)
{
#ifdef GMIO_HAVE_INT64_TYPE
uint8_t buff[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR];
uint8_t* buffit = buff;
const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff));
if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff)))
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error);
/* 4-bytes crc-32 */
info->crc32 = gmio_adv_decode_uint32_le(&buffit);
/* 8-bytes compressed size */
info->compressed_size = gmio_adv_decode_uint64_le(&buffit);
/* 8-bytes uncompressed size */
info->uncompressed_size = gmio_adv_decode_uint64_le(&buffit);
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
#else
/* TODO: error code */
return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, ptr_error);
#endif
}
size_t gmio_zip_write_central_directory_header(
struct gmio_stream *stream,
const struct gmio_zip_central_directory_header *info,
int* error)
int* ptr_error)
{
uint8_t fixed_data[GMIO_ZIP_SIZE_CENTRAL_DIRECTORY_HEADER];
uint8_t* buff = fixed_data;
@ -270,18 +510,18 @@ size_t gmio_zip_write_central_directory_header(
written_len +=
gmio_stream_write_bytes(stream, info->filecomment, info->filecomment_len);
return gmio_zip_write_returnhelper(
stream, written_len, expected_written_len, error);
stream, written_len, expected_written_len, ptr_error);
}
size_t gmio_zip64_write_extrafield_extended_info(
uint8_t *buff,
size_t buff_capacity,
const struct gmio_zip64_extrablock_extended_info *info,
int* error)
int* ptr_error)
{
if (buff_capacity < GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO) {
*error = GMIO_ERROR_INVALID_MEMBLOCK_SIZE;
return 0;
return gmio_zip_io_returnerr(
0, GMIO_ERROR_INVALID_MEMBLOCK_SIZE, ptr_error);
}
#ifdef GMIO_HAVE_INT64_TYPE
/* Tag */
@ -297,18 +537,20 @@ size_t gmio_zip64_write_extrafield_extended_info(
buff = gmio_adv_encode_uint64_le(info->relative_offset_local_header, buff);
/* Number of the disk on which this file starts */
buff = gmio_adv_encode_uint32_le(info->disk_nb_start, buff);
*error = GMIO_ERROR_OK;
return GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO;
return gmio_zip_io_returnerr(
GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO,
GMIO_ERROR_OK,
ptr_error);
#else
*error = GMIO_ERROR_UNKNOWN; /* TODO: error code */
return 0;
/* TODO: error code */
return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, error);
#endif
}
size_t gmio_zip_write_end_of_central_directory_record(
struct gmio_stream *stream,
const struct gmio_zip_end_of_central_directory_record *info,
int* error)
int* ptr_error)
{
uint8_t fixed_data[GMIO_ZIP_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD];
uint8_t* buff = fixed_data;
@ -322,17 +564,24 @@ size_t gmio_zip_write_end_of_central_directory_record(
/* 2-bytes number of the disk with the start of the central directory */
const uint16_t disk_nb_with_start_of_central_dir =
info->use_zip64 ? UINT16_MAX : info->disk_nb_with_start_of_central_dir;
info->use_zip64 ?
UINT16_MAX :
info->disk_nb_with_start_of_central_dir;
buff = gmio_adv_encode_uint16_le(disk_nb_with_start_of_central_dir, buff);
/* 2-bytes total number of entries in the central directory on this disk */
const uint16_t total_entry_count_in_central_dir_on_disk =
info->use_zip64 ? UINT16_MAX : info->total_entry_count_in_central_dir_on_disk;
buff = gmio_adv_encode_uint16_le(total_entry_count_in_central_dir_on_disk, buff);
info->use_zip64 ?
UINT16_MAX :
info->total_entry_count_in_central_dir_on_disk;
buff = gmio_adv_encode_uint16_le(
total_entry_count_in_central_dir_on_disk, buff);
/* 2-bytes total number of entries in the central directory */
const uint16_t total_entry_count_in_central_dir =
info->use_zip64 ? UINT16_MAX : info->total_entry_count_in_central_dir;
info->use_zip64 ?
UINT16_MAX :
info->total_entry_count_in_central_dir;
buff = gmio_adv_encode_uint16_le(total_entry_count_in_central_dir, buff);
/* 4-bytes size of the central directory */
@ -343,8 +592,11 @@ size_t gmio_zip_write_end_of_central_directory_record(
/* 4-bytes offset of start of central directory with respect to the starting
* disk number */
const uint32_t start_offset_central_dir_from_disk_start_nb =
info->use_zip64 ? UINT32_MAX : info->start_offset_central_dir_from_disk_start_nb;
buff = gmio_adv_encode_uint32_le(start_offset_central_dir_from_disk_start_nb, buff);
info->use_zip64 ?
UINT32_MAX :
info->start_offset_central_dir_from_disk_start_nb;
buff = gmio_adv_encode_uint32_le(
start_offset_central_dir_from_disk_start_nb, buff);
/* 2-bytes .ZIP file comment length */
buff = gmio_adv_encode_uint16_le(info->filecomment_len, buff);
@ -358,5 +610,5 @@ size_t gmio_zip_write_end_of_central_directory_record(
written_len +=
gmio_stream_write_bytes(stream, info->filecomment, info->filecomment_len);
return gmio_zip_write_returnhelper(
stream, written_len, expected_written_len, error);
stream, written_len, expected_written_len, ptr_error);
}

View File

@ -165,10 +165,10 @@ struct gmio_zip_local_file_header {
uint32_t crc32;
uint32_t compressed_size;
uint32_t uncompressed_size;
const char* filename;
const uint8_t* extrafield;
uint16_t filename_len;
uint16_t extrafield_len;
const char* filename;
const uint8_t* extrafield;
};
/*! Zip data descriptor */
@ -246,23 +246,58 @@ struct gmio_zip_write_result {
size_t written_len;
};
enum { GMIO_ZIP_UTILS_ERROR_TAG = 0xAA000000 };
enum gmio_zip_utils_error {
GMIO_ZIP_UTILS_ERROR_BAD_MAGIC = GMIO_ZIP_UTILS_ERROR_TAG + 0x01
};
/*! Reads ZIP local file header from \p stream */
size_t gmio_zip_read_local_file_header(
struct gmio_stream* stream,
struct gmio_zip_local_file_header* info,
int* ptr_error);
/*! Reads ZIP data descriptor from \p stream */
size_t gmio_zip_read_data_descriptor(
struct gmio_stream* stream,
struct gmio_zip_data_descriptor* info,
int* ptr_error);
/*! Reads Zip64 data descriptor from \p stream */
size_t gmio_zip64_read_data_descriptor(
struct gmio_stream* stream,
struct gmio_zip_data_descriptor* info,
int* ptr_error);
/*! Reads ZIP central directory header from \p stream */
size_t gmio_zip_read_central_directory_header(
struct gmio_stream* stream,
struct gmio_zip_central_directory_header* info,
int* ptr_error);
/*! Reads ZIP end of central directory record from \p stream */
size_t gmio_zip_read_end_of_central_directory_record(
struct gmio_stream* stream,
struct gmio_zip_end_of_central_directory_record* info,
int* ptr_error);
/*! Writes ZIP local file header to \p stream */
size_t gmio_zip_write_local_file_header(
struct gmio_stream* stream,
const struct gmio_zip_local_file_header* info,
int* error);
int* ptr_error);
/*! Writes ZIP data descriptor to \p stream */
size_t gmio_zip_write_data_descriptor(
struct gmio_stream* stream,
const struct gmio_zip_data_descriptor* info,
int* error);
int* ptr_error);
/*! Writes ZIP central directory header to \p stream */
size_t gmio_zip_write_central_directory_header(
struct gmio_stream* stream,
const struct gmio_zip_central_directory_header* info,
int* error);
int* ptr_error);
/*! Writes ZIP local file header to \p buff
*
@ -273,12 +308,12 @@ size_t gmio_zip64_write_extrafield_extended_info(
uint8_t* buff,
size_t buff_capacity,
const struct gmio_zip64_extrablock_extended_info* info,
int* error);
int* ptr_error);
/*! Writes ZIP end of central directory record \p stream */
/*! Writes ZIP end of central directory record to \p stream */
size_t gmio_zip_write_end_of_central_directory_record(
struct gmio_stream* stream,
const struct gmio_zip_end_of_central_directory_record* info,
int* error);
int* ptr_error);
#endif /* GMIO_INTERNAL_ZIP_UTILS_H */

View File

@ -30,6 +30,18 @@
#include "zlib_utils.h"
#include "../error.h"
/* zlib doc:
* the windowBits parameter is the base two logarithm of the window size
* (the size of the history buffer). It should be in the range 8..15 for
* this version of the library. Larger values of this parameter result in
* better compression at the expense of memory usage. The default value is
* 15 if deflateInit is used instead.
* windowBits can also be 8..15 for raw deflate. In this case, -windowBits
* determines the window size. deflate() will then generate raw deflate
* data with no zlib header or trailer, and will not compute an adler32
* check value. */
static const int z_window_bits_for_no_zlib_wrapper = -15;
static int gmio_to_zlib_compress_level(int gmio_compress_level)
{
if (gmio_compress_level == GMIO_ZLIB_COMPRESS_LEVEL_DEFAULT)
@ -69,23 +81,12 @@ int gmio_zlib_compress_init(
gmio_to_zlib_compress_level(z_opts->level);
const int zlib_compress_memusage =
gmio_to_zlib_compress_memusage(z_opts->memory_usage);
/* zlib doc:
* the windowBits parameter is the base two logarithm of the window size
* (the size of the history buffer). It should be in the range 8..15 for
* this version of the library. Larger values of this parameter result in
* better compression at the expense of memory usage. The default value is
* 15 if deflateInit is used instead.
* windowBits can also be 8..15 for raw deflate. In this case, -windowBits
* determines the window size. deflate() will then generate raw deflate
* data with no zlib header or trailer, and will not compute an adler32
* check value. */
static const int z_window_bits = -15;
const int z_init_error =
deflateInit2_(
z_stream,
zlib_compress_level,
Z_DEFLATED, /* Method */
z_window_bits,
z_window_bits_for_no_zlib_wrapper,
zlib_compress_memusage,
z_opts->strategy,
ZLIB_VERSION,
@ -104,3 +105,60 @@ bool gmio_check_zlib_compress_options(
}
return gmio_no_error(*error);
}
int gmio_zlib_uncompress_buffer(
uint8_t* dest, size_t* dest_len, const uint8_t* src, size_t src_len)
{
z_stream stream;
int err;
stream.next_in = (z_const Bytef *)src;
stream.avail_in = (uInt)src_len;
/* Check for source > 64K on 16-bit machine: */
if ((uLong)stream.avail_in != src_len)
return GMIO_ERROR_ZLIB_BUF;
stream.next_out = dest;
stream.avail_out = (uInt)*dest_len;
if ((uLong)stream.avail_out != *dest_len)
return GMIO_ERROR_ZLIB_BUF;
stream.zalloc = (alloc_func)NULL;
stream.zfree = (free_func)NULL;
err = inflateInit2_(
&stream,
z_window_bits_for_no_zlib_wrapper,
ZLIB_VERSION,
sizeof(z_stream));
if (err != Z_OK)
return err;
err = inflate(&stream, Z_FINISH);
if (err != Z_STREAM_END) {
inflateEnd(&stream);
if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
return GMIO_ERROR_ZLIB_DATA;
return err;
}
*dest_len = stream.total_out;
err = inflateEnd(&stream);
return zlib_error_to_gmio_error(err);
}
uint32_t gmio_zlib_crc32(const uint8_t *buff, size_t buff_len)
{
return gmio_zlib_crc32_update(gmio_zlib_crc32_initial(), buff, buff_len);
}
uint32_t gmio_zlib_crc32_update(
uint32_t crc, const uint8_t *buff, size_t buff_len)
{
return crc32(crc, (const Bytef*)buff, (uInt)buff_len);
}
uint32_t gmio_zlib_crc32_initial()
{
return crc32(0, NULL, 0);
}

View File

@ -46,4 +46,24 @@ int gmio_zlib_compress_init(
bool gmio_check_zlib_compress_options(
int* error, const struct gmio_zlib_compress_options* z_opts);
/* Decompresses the source buffer into the destination buffer.
*
* \p sourceLen is the byte length of the source buffer. Upon entry, \p destLen
* is the total size of the destination buffer, which must be large enough to
* hold the entire uncompressed data.
* Upon exit, destLen is the actual size of the compressed buffer.
*/
int gmio_zlib_uncompress_buffer(
uint8_t* dest, size_t* dest_len, const uint8_t* src, size_t src_len);
/* Computes the CRC-32 value with the bytes buff[0..buff_len-1] */
uint32_t gmio_zlib_crc32(const uint8_t* buff, size_t buff_len);
/* Returns the required initial value for the gmio_zlib_crc32_update() */
uint32_t gmio_zlib_crc32_initial();
/* Updates a running CRC-32 with the bytes buff[0..buff_len-1] */
uint32_t gmio_zlib_crc32_update(
uint32_t crc, const uint8_t* buff, size_t buff_len);
#endif /* GMIO_INTERNAL_ZLIB_UTILS_H */

View File

@ -48,7 +48,8 @@ const char* all_tests()
gmio_memblock_set_default_constructor(gmio_memblock_for_tests);
UTEST_RUN(test_amf_write);
UTEST_RUN(test_amf_write_doc_null);
UTEST_RUN(test_amf_write_doc_1);
return NULL;
}

View File

@ -36,6 +36,7 @@
#include "../src/gmio_core/internal/byte_codec.h"
#include "../src/gmio_core/internal/helper_stream.h"
#include "../src/gmio_core/internal/zip_utils.h"
#include "../src/gmio_core/internal/zlib_utils.h"
#include "../src/gmio_amf/amf_error.h"
#include "../src/gmio_amf/amf_io.h"
@ -178,97 +179,105 @@ static void __tamf__skip_zip_local_file_header()
}
static const char* test_amf_write()
static const char* test_amf_write_doc_null()
{
struct gmio_stream stream = {0};
struct gmio_amf_document doc = {0};
const int error = gmio_amf_write(&stream, &doc, NULL);
UTEST_ASSERT(gmio_error(error));
return NULL;
}
static const char* test_amf_write_doc_1()
{
static const size_t wbuffsize = 8192;
struct gmio_rw_buffer wbuff = {0};
struct gmio_amf_document doc = {0};
const struct __tamf__material testmaterials[] = {
{ { 1., 0., 0. }, "red" },
{ { 0., 1., 0. }, "green" },
{ { 0., 0., 1. }, "blue" },
{ { 1., 1., 1. }, "white" }
};
const struct gmio_vec3d testvertices[] = {
{ 0., 0., 0.},
{ 1., 0., 0.},
{ 1., -1., 0.},
{ 0., -1., 0.},
{ 1., 0., 1.},
{ 1., -1., 1.},
{ 0., 0., 1.},
{ 0., -1., 1.}
};
const struct __tamf__triangle testtriangles[] = {
{ 0, 1, 2},
{ 0, 2, 3},
{ 1, 5, 2},
{ 1, 4, 5},
{ 6, 5, 7},
{ 6, 4, 5},
{ 0, 6, 7},
{ 0, 7, 3},
{ 0, 6, 4},
{ 0, 4, 1},
{ 3, 7, 5},
{ 3, 5, 2}
};
struct __tamf__document testdoc = {0};
wbuff.ptr = calloc(wbuffsize, 1);
wbuff.len = wbuffsize;
testdoc.vec_material = testmaterials;
testdoc.mesh.vec_vertex = testvertices;
testdoc.mesh.vertex_count = GMIO_ARRAY_SIZE(testvertices);
testdoc.mesh.vec_triangle = testtriangles;
testdoc.mesh.triangle_count = GMIO_ARRAY_SIZE(testtriangles);
doc.cookie = &testdoc;
doc.unit = GMIO_AMF_UNIT_MILLIMETER;
doc.func_get_document_element = &__tamf__get_document_element;
doc.func_get_object_mesh = &__tamf__get_object_mesh;
doc.func_get_object_mesh_element = &__tamf__get_object_mesh_element;
doc.func_get_object_mesh_volume_triangle =
&__tamf__get_object_mesh_volume_triangle;
doc.func_get_document_element_metadata =
&__tamf__get_document_element_metadata;
doc.object_count = 1;
doc.material_count = GMIO_ARRAY_SIZE(testmaterials);
/* Write as raw contents(uncompressed) */
{
struct gmio_stream stream = {0};
struct gmio_amf_document doc = {0};
const int error = gmio_amf_write(&stream, &doc, NULL);
UTEST_ASSERT(gmio_error(error));
struct gmio_stream stream = gmio_stream_buffer(&wbuff);
struct gmio_amf_write_options options = {0};
options.float64_prec = 9;
const int error = gmio_amf_write(&stream, &doc, &options);
if (gmio_error(error))
printf("\n0x%x\n", error);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
/* printf("%s\n", wbuff.ptr); */
}
/* Write compressed ZIP */
{
static const size_t wbuffsize = 8192;
struct gmio_rw_buffer wbuff = {0};
struct gmio_stream stream = gmio_stream_buffer(&wbuff);
struct gmio_amf_document doc = {0};
struct gmio_amf_write_options options = {0};
const size_t source_len = wbuff.pos;
const uint32_t crc32_amf_data = gmio_zlib_crc32(wbuff.ptr, source_len);
uint8_t* source = calloc(source_len, 1);
memcpy(source, wbuff.ptr, source_len);
const struct __tamf__material testmaterials[] = {
{ { 1., 0., 0. }, "red" },
{ { 0., 1., 0. }, "green" },
{ { 0., 0., 1. }, "blue" },
{ { 1., 1., 1. }, "white" }
};
const struct gmio_vec3d testvertices[] = {
{ 0., 0., 0.},
{ 1., 0., 0.},
{ 1., -1., 0.},
{ 0., -1., 0.},
{ 1., 0., 1.},
{ 1., -1., 1.},
{ 0., 0., 1.},
{ 0., -1., 1.}
};
const struct __tamf__triangle testtriangles[] = {
{ 0, 1, 2},
{ 0, 2, 3},
{ 1, 5, 2},
{ 1, 4, 5},
{ 6, 5, 7},
{ 6, 4, 5},
{ 0, 6, 7},
{ 0, 7, 3},
{ 0, 6, 4},
{ 0, 4, 1},
{ 3, 7, 5},
{ 3, 5, 2}
};
struct __tamf__document testdoc = {0};
wbuff.ptr = calloc(wbuffsize, 1);
wbuff.len = wbuffsize;
testdoc.vec_material = testmaterials;
testdoc.mesh.vec_vertex = testvertices;
testdoc.mesh.vertex_count = GMIO_ARRAY_SIZE(testvertices);
testdoc.mesh.vec_triangle = testtriangles;
testdoc.mesh.triangle_count = GMIO_ARRAY_SIZE(testtriangles);
doc.cookie = &testdoc;
doc.unit = GMIO_AMF_UNIT_MILLIMETER;
doc.func_get_document_element = &__tamf__get_document_element;
doc.func_get_object_mesh = &__tamf__get_object_mesh;
doc.func_get_object_mesh_element = &__tamf__get_object_mesh_element;
doc.func_get_object_mesh_volume_triangle =
&__tamf__get_object_mesh_volume_triangle;
doc.func_get_document_element_metadata =
&__tamf__get_document_element_metadata;
doc.object_count = 1;
doc.material_count = GMIO_ARRAY_SIZE(testmaterials);
options.float64_prec = 9;
/* Write as raw contents(uncompressed) */
static const char zip_entry_filename[] = "test.amf";
static const uint16_t zip_entry_filename_len =
sizeof(zip_entry_filename) - 1;
{
const int error = gmio_amf_write(&stream, &doc, &options);
if (gmio_error(error))
printf("\n0x%x\n", error);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
/* printf("%s\n", wbuff.ptr); */
}
/* Write compressed ZIP */
{
const size_t source_len = wbuff.pos;
uint8_t* source = calloc(source_len, 1);
memcpy(source, wbuff.ptr, source_len);
wbuff.pos = 0;
struct gmio_stream stream = gmio_stream_buffer(&wbuff);
struct gmio_amf_write_options options = {0};
options.float64_prec = 9;
options.compress = true;
static const char zip_entry_filename[] = "test.amf";
options.zip_entry_filename = zip_entry_filename;
options.zip_entry_filename_len = sizeof(zip_entry_filename) - 1;
options.zip_entry_filename_len = zip_entry_filename_len;
const int error = gmio_amf_write(&stream, &doc, &options);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
#if 0
@ -276,52 +285,108 @@ static const char* test_amf_write()
fwrite(wbuff.ptr, 1, wbuff.pos, file);
fclose(file);
#endif
/* Unzip and compare with source data */
uint8_t* rbuff = wbuff.ptr;
/* -- Read local file header */
UTEST_COMPARE_UINT(gmio_decode_uint32_le(rbuff), 0x04034b50);
rbuff += 8;
/* -- Read compression method */
UTEST_COMPARE_UINT(
gmio_decode_uint16_le(rbuff),
GMIO_ZIP_COMPRESS_METHOD_DEFLATE);
rbuff += 18;
/* -- Read filename length */
UTEST_COMPARE_UINT(
gmio_decode_uint16_le(rbuff),
options.zip_entry_filename_len);
rbuff += 2;
/* -- Read extrafield length */
const uint16_t zip_extrafield_len = gmio_decode_uint16_le(rbuff);
rbuff += 2;
/* -- Read filename */
UTEST_ASSERT(strncmp(
(const char*)rbuff,
options.zip_entry_filename,
options.zip_entry_filename_len)
== 0);
rbuff += options.zip_entry_filename_len;
/* -- Skip extrafield */
rbuff += zip_extrafield_len;
#if 0 /* TODO: check other ZIP records, and uncompress file */
uint8_t* dest = calloc(wbuffsize, 1);
unsigned long dest_len = (unsigned long)wbuffsize;
const unsigned long z_len = wbuff.pos;
const int zerr = uncompress(dest, &dest_len, wbuff.ptr, z_len);
printf("\n-- Info: z_len=%i src_len=%i\n", z_len, source_len);
UTEST_COMPARE_INT(zerr, Z_OK);
UTEST_COMPARE_UINT(source_len, dest_len);
UTEST_COMPARE_INT(memcmp(dest, source, source_len), 0);
free(dest);
#endif
free(source);
}
free(wbuff.ptr);
/* Unzip and compare with source data */
{
/* Total size in bytes of the ZIP archive */
const uintmax_t zip_archive_len = wbuff.pos;
wbuff.pos = 0;
struct gmio_stream stream = gmio_stream_buffer(&wbuff);
int error = GMIO_ERROR_OK;
/* -- Read ZIP local file header */
struct gmio_zip_local_file_header zip_lfh = {0};
const size_t lfh_read_len =
gmio_zip_read_local_file_header(&stream, &zip_lfh, &error);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
UTEST_COMPARE_UINT(
zip_lfh.compress_method,
GMIO_ZIP_COMPRESS_METHOD_DEFLATE);
UTEST_ASSERT(
(zip_lfh.general_purpose_flags &
GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR)
!= 0);
UTEST_COMPARE_UINT(
zip_lfh.filename_len,
sizeof(zip_entry_filename) - 1);
UTEST_ASSERT(strncmp(
(const char*)wbuff.ptr + wbuff.pos,
zip_entry_filename,
zip_entry_filename_len)
== 0);
/* -- Read ZIP end of central directory record */
static const size_t end_of_central_dir_record_len = 22;
wbuff.pos = zip_archive_len - end_of_central_dir_record_len;;
struct gmio_zip_end_of_central_directory_record zip_eocdr = {0};
gmio_zip_read_end_of_central_directory_record(
&stream, &zip_eocdr, &error);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
UTEST_COMPARE_UINT(zip_eocdr.disk_nb, 0);
UTEST_COMPARE_UINT(
zip_eocdr.disk_nb_with_start_of_central_dir, 0);
UTEST_COMPARE_UINT(
zip_eocdr.total_entry_count_in_central_dir_on_disk, 1);
UTEST_COMPARE_UINT(
zip_eocdr.total_entry_count_in_central_dir, 1);
/* -- Read ZIP central directory */
wbuff.pos = zip_eocdr.start_offset_central_dir_from_disk_start_nb;
struct gmio_zip_central_directory_header zip_cdh = {0};
gmio_zip_read_central_directory_header(&stream, &zip_cdh, &error);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
UTEST_COMPARE_UINT(
zip_cdh.compress_method,
GMIO_ZIP_COMPRESS_METHOD_DEFLATE);
UTEST_ASSERT(
(zip_cdh.general_purpose_flags &
GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR)
!= 0);
UTEST_COMPARE_UINT(crc32_amf_data, zip_cdh.crc32);
UTEST_COMPARE_UINT(
zip_cdh.filename_len, zip_entry_filename_len);
UTEST_ASSERT(strncmp(
(const char*)wbuff.ptr + wbuff.pos,
zip_entry_filename,
zip_entry_filename_len)
== 0);
/* -- Read compressed AMF data */
const size_t pos_start_amf_data =
lfh_read_len + zip_lfh.filename_len + zip_lfh.extrafield_len;
wbuff.pos = pos_start_amf_data;
{
uint8_t* dest = calloc(zip_cdh.uncompressed_size, 1);
size_t dest_len = zip_cdh.uncompressed_size;
const int error =
gmio_zlib_uncompress_buffer(
dest,
&dest_len,
(const uint8_t*)wbuff.ptr + wbuff.pos,
zip_cdh.compressed_size);
printf("\n-- Info: z_len=%i src_len=%i\n",
zip_cdh.compressed_size, source_len);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
UTEST_COMPARE_UINT(source_len, dest_len);
UTEST_COMPARE_UINT(dest_len, zip_cdh.uncompressed_size);
UTEST_COMPARE_INT(memcmp(dest, source, source_len), 0);
const uint32_t crc32_uncomp = gmio_zlib_crc32(dest, dest_len);
UTEST_COMPARE_UINT(crc32_amf_data, crc32_uncomp);
free(dest);
}
/* -- Read ZIP data descriptor */
wbuff.pos = pos_start_amf_data + zip_cdh.compressed_size;
struct gmio_zip_data_descriptor zip_dd = {0};
gmio_zip_read_data_descriptor(&stream, &zip_dd, &error);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
UTEST_COMPARE_UINT(zip_dd.crc32, crc32_amf_data);
UTEST_COMPARE_UINT(
zip_dd.compressed_size, zip_cdh.compressed_size);
UTEST_COMPARE_UINT(
zip_dd.uncompressed_size, zip_cdh.uncompressed_size);
}
free(source);
}
free(wbuff.ptr);
return NULL;
}