Skip to content

Commit

Permalink
Added zip_entry_noallocreadwithoffset (#359)
Browse files Browse the repository at this point in the history
* Added zip_entry_noallocreadwithoffset
  • Loading branch information
JCash authored Nov 29, 2024
1 parent 209840b commit a91b5a3
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 1 deletion.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,27 @@ zip_stream_close(zip);
free(buf);
```
* Extract a partial zip entry
```c
unsigned char buf[16];
size_t bufsize = sizeof(buf);
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
size_t offset = 4;
ssize_t nread = zip_entry_noallocreadwithoffset(zip, offset, bufsize, (void *)buf);
}
zip_entry_close(zip);
}
zip_close(zip);
free(buf);
```

* List of all zip entries

```c
Expand Down
73 changes: 72 additions & 1 deletion src/zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ struct zip_entry_mark_t {
size_t lf_length;
};

static const char *const zip_errlist[33] = {
static const char *const zip_errlist[35] = {
NULL,
"not initialized\0",
"invalid entry name\0",
Expand Down Expand Up @@ -148,6 +148,8 @@ static const char *const zip_errlist[33] = {
"cannot initialize reader\0",
"cannot initialize writer\0",
"cannot initialize writer from reader\0",
"invalid argument\0",
"cannot initialize reader iterator\0",
};

const char *zip_strerror(int errnum) {
Expand Down Expand Up @@ -1654,6 +1656,75 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
return (ssize_t)zip->entry.uncomp_size;
}

ssize_t zip_entry_noallocreadwithoffset(struct zip_t *zip, size_t offset,
size_t size, void *buf) {
mz_zip_archive *pzip = NULL;

if (!zip) {
// zip_t handler is not initialized
return (ssize_t)ZIP_ENOINIT;
}

if (offset >= (size_t)zip->entry.uncomp_size) {
return (ssize_t)ZIP_EINVAL;
}

if ((offset + size) > (size_t)zip->entry.uncomp_size) {
size = (ssize_t)zip->entry.uncomp_size - offset;
}

pzip = &(zip->archive);
if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
zip->entry.index < (ssize_t)0) {
// the entry is not found or we do not have read access
return (ssize_t)ZIP_ENOENT;
}

mz_zip_reader_extract_iter_state *iter =
mz_zip_reader_extract_iter_new(pzip, (mz_uint)zip->entry.index, 0);
if (!iter) {
return (ssize_t)ZIP_ENORITER;
}

mz_uint8 *writebuf = (mz_uint8 *)buf;
size_t file_offset = 0;
size_t write_cursor = 0;
size_t to_read = size;

// iterate until the requested offset is in range
while (file_offset < zip->entry.uncomp_size && to_read > 0) {
size_t nread = mz_zip_reader_extract_iter_read(
iter, (void *)&writebuf[write_cursor], to_read);

if (nread == 0)
break;

if (offset < (file_offset + nread)) {
size_t read_cursor = offset - file_offset;
MZ_ASSERT(read_cursor < size);
size_t read_size = nread - read_cursor;

if (to_read < read_size)
read_size = to_read;
MZ_ASSERT(read_size <= size);

// If it's an unaligned read (i.e. the first one)
if (read_cursor != 0) {
memmove(&writebuf[write_cursor], &writebuf[read_cursor], read_size);
}

write_cursor += read_size;
offset += read_size;
to_read -= read_size;
}

file_offset += nread;
}

mz_zip_reader_extract_iter_free(iter);
return (ssize_t)write_cursor;
}

int zip_entry_fread(struct zip_t *zip, const char *filename) {
mz_zip_archive *pzip = NULL;
mz_uint idx;
Expand Down
23 changes: 23 additions & 0 deletions src/zip.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ typedef long ssize_t; /* byte count or error */
#define ZIP_ERINIT -30 // cannot initialize reader
#define ZIP_EWINIT -31 // cannot initialize writer
#define ZIP_EWRINIT -32 // cannot initialize writer from reader
#define ZIP_EINVAL -33 // invalid argument
#define ZIP_ENORITER -34 // cannot initialize reader iterator

/**
* Looks up the error message string corresponding to an error number.
Expand Down Expand Up @@ -373,6 +375,27 @@ extern ZIP_EXPORT ssize_t zip_entry_read(struct zip_t *zip, void **buf,
extern ZIP_EXPORT ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
size_t bufsize);

/**
* Extracts the part of the current zip entry into a memory buffer using no
* memory allocation for the buffer.
*
* @param zip zip archive handler.
* @param offset the offset of the entry (in bytes).
* @param size requested number of bytes (in bytes).
* @param buf preallocated output buffer.
*
* @note the iterator api uses an allocation to create its state
* @note each call will iterate from the start of the entry
*
* @return the return code - the number of bytes actually read on success.
* Otherwise a negative number (< 0) on error (e.g. offset is too
* large).
*/
extern ZIP_EXPORT ssize_t zip_entry_noallocreadwithoffset(struct zip_t *zip,
size_t offset,
size_t size,
void *buf);

/**
* Extracts the current zip entry into output file.
*
Expand Down
42 changes: 42 additions & 0 deletions test/test_read.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,53 @@ MU_TEST(test_noallocread) {
zip_close(zip);
}

MU_TEST(test_noallocreadwithoffset) {
size_t expected_size = strlen(TESTDATA2);
char *expected_data = calloc(expected_size, sizeof(char));

struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
mu_assert_int_eq(1, zip_is64(zip));

mu_assert_int_eq(0, zip_entry_open(zip, "test/test-2.txt"));
zip_entry_noallocread(zip, (void *)expected_data, expected_size);

// Read the file in different chunk sizes
for (size_t i = 1; i <= expected_size; ++i) {
size_t buflen = i;
char *tmpbuf = calloc(buflen, sizeof(char));

for (size_t j = 0; j < expected_size; ++j) {
// we test starting from different offsets, to make sure we hit the
// "unaligned" code path
size_t offset = j;
while (offset < expected_size) {

ssize_t nread =
zip_entry_noallocreadwithoffset(zip, offset, buflen, tmpbuf);

mu_assert(nread <= buflen, "too many bytes read");
mu_assert(0u != nread, "no bytes read");

// check the data
for (ssize_t j = 0; j < nread; ++j) {
mu_assert_int_eq(expected_data[offset + j], tmpbuf[j]);
}

offset += nread;
}
}
}

zip_close(zip);
}

MU_TEST_SUITE(test_read_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);

MU_RUN_TEST(test_read);
MU_RUN_TEST(test_noallocread);
MU_RUN_TEST(test_noallocreadwithoffset);
}

#define UNUSED(x) (void)x
Expand Down

0 comments on commit a91b5a3

Please sign in to comment.