diff --git a/README.md b/README.md index 9a6397f..b1498cb 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,38 @@ struct zip_t *zip = zip_open("foo.zip", 0, 'r'); zip_close(zip); ``` +* Create a new zip archive in memory. + +```c +char *buf_encode = NULL; +const char *buf = "Append some data here...\0"; +struct zip_t *zip = zip_open_stream(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); +{ + zip_entry_open(zip, "foo-1.txt"); + zip_entry_write(zip, buf, strlen(buf)); + zip_entry_close(zip); + /* copy compressed mem to buf_encode */ + size_t n = zip_copy_stream(zip, (void **)&buf_encode, NULL); +} +zip_close_stream(zip); +free(buf_encode); +``` + +* Extract a zip entry into a memory. + +```c +char *buf = NULL; +struct zip_t *zipStream = zip_open_stream(buf_encode, n, 0, 'r'); +{ + zip_entry_open(zipStream, "foo-1.txt"); + ssize_t bufsize; + bufsize = zip_entry_read(zipStream, (void **)&buf, NULL); + zip_entry_close(zipStream); +} +zip_close_stream(zipStream); +free(buf); +``` + * List of all zip entries ```c struct zip_t *zip = zip_open("foo.zip", 0, 'r'); diff --git a/src/zip.c b/src/zip.c index ceacb4c..fd9a682 100644 --- a/src/zip.c +++ b/src/zip.c @@ -72,7 +72,9 @@ static int file_truncate(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 file_size = pZip->m_archive_size; - + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { + return 0; + } if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { if (pState->m_pFile) { int fd = fileno(pState->m_pFile); @@ -1076,13 +1078,30 @@ struct entry_mark { mz_uint64 lf_length; }; -struct zip_t *zip_open_stream(const char *stream, size_t size) { +struct zip_t *zip_open_stream(const char *stream, size_t size, int level, char mode) { struct zip_t *zip = NULL; zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); if (!zip) { return NULL; } - if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) { + if (level < 0) + level = MZ_DEFAULT_LEVEL; + if ((level & 0xF) > MZ_UBER_COMPRESSION) { + // Wrong compression level + goto cleanup; + } + zip->level = (mz_uint)level; + if((stream != NULL) && (size > 0) && (mode == 'r')){ + if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) { + goto cleanup; + } + }else if((stream == NULL) && (size == 0) && (mode == 'w')){ + // Create a new archive. + if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) { + // Cannot initialize zip_archive writer + goto cleanup; + } + }else{ goto cleanup; } return zip; @@ -1092,6 +1111,31 @@ cleanup: return NULL; } +static inline void zip_write_end(struct zip_t *zip){ + if (zip) { + mz_zip_writer_finalize_archive(&(zip->archive)); + file_truncate(&(zip->archive)); + } +} + +ssize_t zip_copy_stream(struct zip_t *zip, void **buf, ssize_t *bufsize){ + if(zip == NULL) return -1; + zip_write_end(zip); + if(bufsize != NULL) + *bufsize = zip->archive.m_archive_size; + *buf = (char *)calloc(sizeof(unsigned char), zip->archive.m_archive_size); + memcpy(*buf, zip->archive.m_pState->m_pMem, zip->archive.m_archive_size); + return zip->archive.m_archive_size; +} + +void zip_close_stream(struct zip_t *zip){ + if (zip) { + mz_zip_writer_end(&(zip->archive)); + mz_zip_reader_end(&(zip->archive)); + CLEANUP(zip); + } +} + static mz_bool file_name_matches(const char *file_name, const char *delete_name) { int delete_name_length = strlen(delete_name); diff --git a/src/zip.h b/src/zip.h index 16638c3..b771fa7 100644 --- a/src/zip.h +++ b/src/zip.h @@ -331,7 +331,28 @@ extern int zip_extract_stream(const char *stream, size_t size, const char *dir, * * @return the zip archive handler or NULL on error */ -extern struct zip_t *zip_open_stream(const char *stream, size_t size); +extern struct zip_t *zip_open_stream(const char *stream, size_t size, + int level, char mode); + +/** + * Copy zip archive stream output buffer. + * + * @param zip zip archive handler. + * @param buf output buffer. User should free buf. + * @param bufsize output buffer size (in bytes). + * + * @return copy size + */ +extern ssize_t zip_copy_stream(struct zip_t *zip, void **buf, ssize_t *bufsize); + +/** + * Close zip archive releases resources. + * + * @param zip zip archive handler. + * + * @return + */ +extern void zip_close_stream(struct zip_t *zip); /** * Deletes zip archive entries. diff --git a/test/test.c b/test/test.c index e6fc3f4..1185798 100644 --- a/test/test.c +++ b/test/test.c @@ -547,32 +547,20 @@ static void test_open_stream(void) { #if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__) #else remove(ZIPNAME); - - struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + /* COMPRESS MEM TO MEM */ + struct zip_t *zip = zip_open_stream(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); assert(zip != NULL); assert(0 == zip_entry_open(zip, "test/test-1.txt")); assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1))); assert(0 == zip_entry_close(zip)); - zip_close(zip); - - FILE *fp = NULL; - fp = fopen(ZIPNAME, "r"); - assert(fp != NULL); - - fseek(fp, 0L, SEEK_END); - size_t filesize = ftell(fp); - fseek(fp, 0L, SEEK_SET); - - char stream[filesize]; - memset(stream, 0, filesize); - size_t size = fread(stream, sizeof(char), filesize, fp); - assert(filesize == size); - - fclose(fp); - - struct zip_t *zipStream = zip_open_stream(stream, size); + /* write compressed mem to file */ + char *buf_encode = NULL; + size_t n = zip_copy_stream(zip, (void **)&buf_encode, NULL); + zip_close_stream(zip); + /* DECOMPRESS MEM TO MEM */ + struct zip_t *zipStream = zip_open_stream(buf_encode, n, 0, 'r'); assert(zipStream != NULL); assert(0 == zip_entry_open(zipStream, "test/test-1.txt")); @@ -582,9 +570,10 @@ static void test_open_stream(void) { bufsize = zip_entry_read(zipStream, (void **)&buf, NULL); assert(0 == strncmp(buf, TESTDATA1, (size_t)bufsize)); assert(0 == zip_entry_close(zipStream)); + zip_close_stream(zipStream); free(buf); - zip_close(zipStream); + free(buf_encode); remove(ZIPNAME); #endif }