65 while ((opt = getopt(argc, argv,
"V:")) != -1) {
78static void put16(
unsigned char* p,
unsigned v) {
79 p[0] = (
unsigned char)(v & 0xff);
80 p[1] = (
unsigned char)((v >> 8) & 0xff);
86static void put32(
unsigned char* p,
unsigned long v) {
87 put16(p, (
unsigned)(v & 0xffff));
88 put16(p + 2, (
unsigned)((v >> 16) & 0xffff));
98 const unsigned char*
data;
110 FILE* f = fopen(path,
"wb");
111 unsigned long offsets[4], sizes[4], crcs[4];
112 unsigned char* packed[4];
113 unsigned long dir_at = 0, dir_size = 0, at = 0;
116 if (!f || count > 4)
return -1;
117 for (i = 0; i < count; i++) {
118 unsigned char header[30];
119 size_t n = strlen(m[i].name);
121 crcs[i] = crc32(crc32(0L, Z_NULL, 0), m[i].data, (uInt)m[i].len);
122 packed[i] = (
unsigned char*)malloc(m[i].len + 1024);
123 if (m[i].method == 8) {
126 memset(&zs, 0,
sizeof(zs));
127 deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
128 zs.next_in = (Bytef*)m[i].data;
129 zs.avail_in = (uInt)m[i].len;
130 zs.next_out = packed[i];
131 zs.avail_out = (uInt)(m[i].len + 1024);
132 deflate(&zs, Z_FINISH);
133 sizes[i] = zs.total_out;
136 memcpy(packed[i], m[i].data, m[i].len);
137 sizes[i] = (
unsigned long)m[i].len;
139 memset(header, 0,
sizeof(header));
140 put32(header, 0x04034b50ul);
141 put16(header + 4, 20);
142 put16(header + 8, m[i].method);
143 put32(header + 14, crcs[i]);
144 put32(header + 18, sizes[i]);
145 put32(header + 22, (
unsigned long)m[i].len);
146 put16(header + 26, (
unsigned)n);
148 fwrite(header, 1, 30, f);
149 fwrite(m[i].name, 1, n, f);
150 fwrite(packed[i], 1, sizes[i], f);
151 at += 30 + (
unsigned long)n + sizes[i];
154 for (i = 0; i < count; i++) {
155 unsigned char record[46];
156 size_t n = strlen(m[i].name);
158 memset(record, 0,
sizeof(record));
159 put32(record, 0x02014b50ul);
160 put16(record + 4, 20);
161 put16(record + 6, 20);
162 put16(record + 10, m[i].method);
163 put32(record + 16, crcs[i]);
164 put32(record + 20, sizes[i]);
165 put32(record + 24, (
unsigned long)m[i].len);
166 put16(record + 28, (
unsigned)n);
167 put32(record + 42, offsets[i]);
168 fwrite(record, 1, 46, f);
169 fwrite(m[i].name, 1, n, f);
170 dir_size += 46 + (
unsigned long)n;
174 unsigned char end[22];
176 memset(end, 0,
sizeof(end));
177 put32(end, 0x06054b50ul);
178 put16(end + 8, (
unsigned)count);
179 put16(end + 10, (
unsigned)count);
180 put32(end + 12, dir_size);
181 put32(end + 16, dir_at);
182 fwrite(end, 1, 22, f);
193static int file_is(
const char* path,
const unsigned char* data,
size_t len) {
194 FILE* f = fopen(path,
"rb");
195 unsigned char* got = NULL;
200 got = (
unsigned char*)malloc(len + 1);
202 n = fread(got, 1, len + 1, f);
203 same = n == len && memcmp(got, data, len) == 0;
214int main(
int argc,
char** argv) {
215 static const char* archive =
"ex_zip.tmp.zip";
216 static const char* out =
"ex_zip.tmp.out";
217 static const unsigned char small[] =
"a stored member, as it is";
218 size_t big_len = 700000, i = 0;
219 unsigned char* big = NULL;
225 fprintf(stderr,
"Usage: %s [-V LOG_LEVEL]\n", argv[0]);
229 big = (
unsigned char*)malloc(big_len);
233 for (i = 0; i < big_len; i++) big[i] = (
unsigned char)((i * 7) % 251);
234 members[0].
name =
"notes/small.txt";
236 members[0].
data = small;
237 members[0].
len =
sizeof(small) - 1;
238 members[1].
name =
"model/model.onnx";
240 members[1].
data = big;
241 members[1].
len = big_len;
245 info.
size == big_len);
247 expect_true(
"a stored member comes out as it went in",
252 unsigned char* data = NULL;
256 expect_true(
"a member is read into memory as it went in",
257 n_zip_read(archive,
"model/model.onnx", big_len, &data, &len) == 0 && len == big_len &&
258 memcmp(data, big, big_len) == 0 && data[len] == 0);
262 n_zip_read(archive,
"model/model.onnx", big_len - 1, &data, &len) == -1 && !data);
263 expect_true(
"a member is found by how its name ends",
265 strcmp(name,
"notes/small.txt") == 0);
271 FILE* f = fopen(archive,
"r+b");
276 fseek(f, 30 + 15 + (
long)(
sizeof(small) - 1) + 30 + 16 + 100, SEEK_SET);
277 if (fread(&b, 1, 1, f) == 1) {
279 fseek(f, -1, SEEK_CUR);
284 expect_true(
"a damaged member is refused and removed",
285 n_zip_extract(archive,
"model/model.onnx", out) == -1 && access(out, F_OK) != 0);
292 fprintf(stderr,
"ex_zip: %d failure(s)\n",
failures);
295 printf(
"ex_zip: ok\n");
void process_args(int argc, char **argv)
static void expect_true(const char *label, int cond)
static int write_zip(const char *path, const MEMBER *m, int count)
write a ZIP of stored members, as torch.save() does
static void put16(unsigned char *p, uint16_t v, int be)
Write a 16 bit value in a byte order.
static void put32(unsigned char *p, uint32_t v, int be)
Write a 32 bit value in a byte order.
unsigned method
0 stored, 8 deflated
static int file_is(const char *path, const unsigned char *data, size_t len)
is a file these bytes
const char * name
its name
const unsigned char * data
its bytes
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_INFO
informational
uint64_t size
its size once out
unsigned method
0 stored, 8 deflated
int n_zip_extract(const char *path, const char *name, const char *out)
write a member out to a file, checking its CRC-32
int n_zip_find(const char *path, const char *name, N_ZIP_MEMBER *member)
find a member by its full name in an archive's central directory
int n_zip_find_ending(const char *path, const char *ending, char *name, size_t name_len)
find the first member whose name ends with a given text, as a PyTorch checkpoint's "archive/data....
int n_zip_read(const char *path, const char *name, size_t max_size, unsigned char **data, size_t *len)
read a member into memory, checking its CRC-32
What n_zip_find() says about a member.
Common headers and low-level functions & define.
Reading members out of a ZIP archive, to a file or into memory.