You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

storezip.cpp 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. // Tencent is pleased to support the open source community by making ncnn available.
  2. //
  3. // Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
  4. //
  5. // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
  6. // in compliance with the License. You may obtain a copy of the License at
  7. //
  8. // https://opensource.org/licenses/BSD-3-Clause
  9. //
  10. // Unless required by applicable law or agreed to in writing, software distributed
  11. // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  12. // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  13. // specific language governing permissions and limitations under the License.
  14. #include "storezip.h"
  15. #include <stdio.h>
  16. #include <stdint.h>
  17. #include <map>
  18. #include <string>
  19. #include <vector>
  20. namespace pnnx {
  21. // https://stackoverflow.com/questions/1537964/visual-c-equivalent-of-gccs-attribute-packed
  22. #ifdef _MSC_VER
  23. #define PACK(__Declaration__) __pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop))
  24. #else
  25. #define PACK(__Declaration__) __Declaration__ __attribute__((__packed__))
  26. #endif
  27. PACK(struct local_file_header {
  28. uint16_t version;
  29. uint16_t flag;
  30. uint16_t compression;
  31. uint16_t last_modify_time;
  32. uint16_t last_modify_date;
  33. uint32_t crc32;
  34. uint32_t compressed_size;
  35. uint32_t uncompressed_size;
  36. uint16_t file_name_length;
  37. uint16_t extra_field_length;
  38. });
  39. PACK(struct zip64_extended_extra_field {
  40. uint64_t uncompressed_size;
  41. uint64_t compressed_size;
  42. uint64_t lfh_offset;
  43. uint32_t disk_number;
  44. });
  45. PACK(struct central_directory_file_header {
  46. uint16_t version_made;
  47. uint16_t version;
  48. uint16_t flag;
  49. uint16_t compression;
  50. uint16_t last_modify_time;
  51. uint16_t last_modify_date;
  52. uint32_t crc32;
  53. uint32_t compressed_size;
  54. uint32_t uncompressed_size;
  55. uint16_t file_name_length;
  56. uint16_t extra_field_length;
  57. uint16_t file_comment_length;
  58. uint16_t start_disk;
  59. uint16_t internal_file_attrs;
  60. uint32_t external_file_attrs;
  61. uint32_t lfh_offset;
  62. });
  63. PACK(struct zip64_end_of_central_directory_record {
  64. uint64_t size_of_eocd64_m12;
  65. uint16_t version_made_by;
  66. uint16_t version_min_required;
  67. uint32_t disk_number;
  68. uint32_t start_disk;
  69. uint64_t cd_records;
  70. uint64_t total_cd_records;
  71. uint64_t cd_size;
  72. uint64_t cd_offset;
  73. });
  74. PACK(struct zip64_end_of_central_directory_locator {
  75. uint32_t eocdr64_disk_number;
  76. uint64_t eocdr64_offset;
  77. uint32_t disk_count;
  78. });
  79. PACK(struct end_of_central_directory_record {
  80. uint16_t disk_number;
  81. uint16_t start_disk;
  82. uint16_t cd_records;
  83. uint16_t total_cd_records;
  84. uint32_t cd_size;
  85. uint32_t cd_offset;
  86. uint16_t comment_length;
  87. });
  88. static uint32_t CRC32_TABLE[256];
  89. static void CRC32_TABLE_INIT()
  90. {
  91. for (int i = 0; i < 256; i++)
  92. {
  93. uint32_t c = i;
  94. for (int j = 0; j < 8; j++)
  95. {
  96. if (c & 1)
  97. c = (c >> 1) ^ 0xedb88320;
  98. else
  99. c >>= 1;
  100. }
  101. CRC32_TABLE[i] = c;
  102. }
  103. }
  104. static uint32_t CRC32(uint32_t x, unsigned char ch)
  105. {
  106. return (x >> 8) ^ CRC32_TABLE[(x ^ ch) & 0xff];
  107. }
  108. static uint32_t CRC32_buffer(const unsigned char* data, uint64_t len)
  109. {
  110. uint32_t x = 0xffffffff;
  111. for (uint64_t i = 0; i < len; i++)
  112. x = CRC32(x, data[i]);
  113. return x ^ 0xffffffff;
  114. }
  115. StoreZipReader::StoreZipReader()
  116. {
  117. fp = 0;
  118. }
  119. StoreZipReader::~StoreZipReader()
  120. {
  121. close();
  122. }
  123. int StoreZipReader::open(const std::string& path)
  124. {
  125. close();
  126. fp = fopen(path.c_str(), "rb");
  127. if (!fp)
  128. {
  129. fprintf(stderr, "open failed\n");
  130. return -1;
  131. }
  132. while (!feof(fp))
  133. {
  134. // peek signature
  135. uint32_t signature;
  136. int nread = fread((char*)&signature, sizeof(signature), 1, fp);
  137. if (nread != 1)
  138. break;
  139. // fprintf(stderr, "signature = %x\n", signature);
  140. if (signature == 0x04034b50)
  141. {
  142. local_file_header lfh;
  143. fread((char*)&lfh, sizeof(lfh), 1, fp);
  144. if (lfh.flag & 0x08)
  145. {
  146. fprintf(stderr, "zip file contains data descriptor, this is not supported yet\n");
  147. return -1;
  148. }
  149. if (lfh.compression != 0 || lfh.compressed_size != lfh.uncompressed_size)
  150. {
  151. fprintf(stderr, "not stored zip file %d %d\n", lfh.compressed_size, lfh.uncompressed_size);
  152. return -1;
  153. }
  154. // file name
  155. std::string name;
  156. name.resize(lfh.file_name_length);
  157. fread((char*)name.data(), name.size(), 1, fp);
  158. uint64_t compressed_size = lfh.compressed_size;
  159. uint64_t uncompressed_size = lfh.uncompressed_size;
  160. if (compressed_size == 0xffffffff && uncompressed_size == 0xffffffff)
  161. {
  162. uint16_t extra_offset = 0;
  163. while (extra_offset < lfh.extra_field_length)
  164. {
  165. uint16_t extra_id;
  166. uint16_t extra_size;
  167. fread((char*)&extra_id, sizeof(extra_id), 1, fp);
  168. fread((char*)&extra_size, sizeof(extra_size), 1, fp);
  169. if (extra_id != 0x0001)
  170. {
  171. // skip this extra field block
  172. fseek(fp, extra_size - 4, SEEK_CUR);
  173. extra_offset += extra_size;
  174. continue;
  175. }
  176. // zip64 extra field
  177. zip64_extended_extra_field zip64_eef;
  178. fread((char*)&zip64_eef, sizeof(zip64_eef), 1, fp);
  179. compressed_size = zip64_eef.compressed_size;
  180. uncompressed_size = zip64_eef.uncompressed_size;
  181. // skip remaining extra field blocks
  182. fseek(fp, lfh.extra_field_length - extra_offset - 4 - sizeof(zip64_eef), SEEK_CUR);
  183. break;
  184. }
  185. }
  186. else
  187. {
  188. // skip extra field
  189. fseek(fp, lfh.extra_field_length, SEEK_CUR);
  190. }
  191. StoreZipMeta fm;
  192. fm.offset = ftell(fp);
  193. fm.size = compressed_size;
  194. filemetas[name] = fm;
  195. // fprintf(stderr, "%s = %d %d\n", name.c_str(), fm.offset, fm.size);
  196. fseek(fp, compressed_size, SEEK_CUR);
  197. }
  198. else if (signature == 0x02014b50)
  199. {
  200. central_directory_file_header cdfh;
  201. fread((char*)&cdfh, sizeof(cdfh), 1, fp);
  202. // skip file name
  203. fseek(fp, cdfh.file_name_length, SEEK_CUR);
  204. // skip extra field
  205. fseek(fp, cdfh.extra_field_length, SEEK_CUR);
  206. // skip file comment
  207. fseek(fp, cdfh.file_comment_length, SEEK_CUR);
  208. }
  209. else if (signature == 0x06054b50)
  210. {
  211. end_of_central_directory_record eocdr;
  212. fread((char*)&eocdr, sizeof(eocdr), 1, fp);
  213. // skip comment
  214. fseek(fp, eocdr.comment_length, SEEK_CUR);
  215. }
  216. else if (signature == 0x06064b50)
  217. {
  218. zip64_end_of_central_directory_record eocdr64;
  219. fread((char*)&eocdr64, sizeof(eocdr64), 1, fp);
  220. // skip comment
  221. fseek(fp, eocdr64.size_of_eocd64_m12 - 44, SEEK_CUR);
  222. }
  223. else if (signature == 0x07064b50)
  224. {
  225. zip64_end_of_central_directory_locator eocdl64;
  226. fread((char*)&eocdl64, sizeof(eocdl64), 1, fp);
  227. }
  228. else
  229. {
  230. fprintf(stderr, "unsupported signature %x\n", signature);
  231. return -1;
  232. }
  233. }
  234. return 0;
  235. }
  236. std::vector<std::string> StoreZipReader::get_names() const
  237. {
  238. std::vector<std::string> names;
  239. for (std::map<std::string, StoreZipMeta>::const_iterator it = filemetas.begin(); it != filemetas.end(); ++it)
  240. {
  241. names.push_back(it->first);
  242. }
  243. return names;
  244. }
  245. uint64_t StoreZipReader::get_file_size(const std::string& name) const
  246. {
  247. if (filemetas.find(name) == filemetas.end())
  248. {
  249. fprintf(stderr, "no such file %s\n", name.c_str());
  250. return 0;
  251. }
  252. return filemetas.at(name).size;
  253. }
  254. int StoreZipReader::read_file(const std::string& name, char* data)
  255. {
  256. if (filemetas.find(name) == filemetas.end())
  257. {
  258. fprintf(stderr, "no such file %s\n", name.c_str());
  259. return -1;
  260. }
  261. uint64_t offset = filemetas[name].offset;
  262. uint64_t size = filemetas[name].size;
  263. fseek(fp, offset, SEEK_SET);
  264. fread(data, size, 1, fp);
  265. return 0;
  266. }
  267. int StoreZipReader::close()
  268. {
  269. if (!fp)
  270. return 0;
  271. fclose(fp);
  272. fp = 0;
  273. return 0;
  274. }
  275. StoreZipWriter::StoreZipWriter()
  276. {
  277. fp = 0;
  278. CRC32_TABLE_INIT();
  279. }
  280. StoreZipWriter::~StoreZipWriter()
  281. {
  282. close();
  283. }
  284. int StoreZipWriter::open(const std::string& path)
  285. {
  286. close();
  287. fp = fopen(path.c_str(), "wb");
  288. if (!fp)
  289. {
  290. fprintf(stderr, "open failed\n");
  291. return -1;
  292. }
  293. return 0;
  294. }
  295. int StoreZipWriter::write_file(const std::string& name, const char* data, uint64_t size)
  296. {
  297. long offset = ftell(fp);
  298. uint32_t signature = 0x04034b50;
  299. fwrite((char*)&signature, sizeof(signature), 1, fp);
  300. uint32_t crc32 = CRC32_buffer((const unsigned char*)data, size);
  301. local_file_header lfh;
  302. lfh.version = 0;
  303. lfh.flag = 0;
  304. lfh.compression = 0;
  305. lfh.last_modify_time = 0;
  306. lfh.last_modify_date = 0;
  307. lfh.crc32 = crc32;
  308. lfh.compressed_size = 0xffffffff;
  309. lfh.uncompressed_size = 0xffffffff;
  310. lfh.file_name_length = name.size();
  311. // zip64 extra field
  312. zip64_extended_extra_field zip64_eef;
  313. zip64_eef.uncompressed_size = size;
  314. zip64_eef.compressed_size = size;
  315. zip64_eef.lfh_offset = 0;
  316. zip64_eef.disk_number = 0;
  317. uint16_t extra_id = 0x0001;
  318. uint16_t extra_size = sizeof(zip64_eef);
  319. lfh.extra_field_length = sizeof(extra_id) + sizeof(extra_size) + sizeof(zip64_eef);
  320. fwrite((char*)&lfh, sizeof(lfh), 1, fp);
  321. fwrite((char*)name.c_str(), name.size(), 1, fp);
  322. fwrite((char*)&extra_id, sizeof(extra_id), 1, fp);
  323. fwrite((char*)&extra_size, sizeof(extra_size), 1, fp);
  324. fwrite((char*)&zip64_eef, sizeof(zip64_eef), 1, fp);
  325. fwrite(data, size, 1, fp);
  326. StoreZipMeta szm;
  327. szm.name = name;
  328. szm.lfh_offset = offset;
  329. szm.crc32 = crc32;
  330. szm.size = size;
  331. filemetas.push_back(szm);
  332. return 0;
  333. }
  334. int StoreZipWriter::close()
  335. {
  336. if (!fp)
  337. return 0;
  338. long offset = ftell(fp);
  339. for (const StoreZipMeta& szm : filemetas)
  340. {
  341. uint32_t signature = 0x02014b50;
  342. fwrite((char*)&signature, sizeof(signature), 1, fp);
  343. central_directory_file_header cdfh;
  344. cdfh.version_made = 0;
  345. cdfh.version = 0;
  346. cdfh.flag = 0;
  347. cdfh.compression = 0;
  348. cdfh.last_modify_time = 0;
  349. cdfh.last_modify_date = 0;
  350. cdfh.crc32 = szm.crc32;
  351. cdfh.compressed_size = 0xffffffff;
  352. cdfh.uncompressed_size = 0xffffffff;
  353. cdfh.file_name_length = szm.name.size();
  354. cdfh.file_comment_length = 0;
  355. cdfh.start_disk = 0xffff;
  356. cdfh.internal_file_attrs = 0;
  357. cdfh.external_file_attrs = 0;
  358. cdfh.lfh_offset = 0xffffffff;
  359. // zip64 extra field
  360. zip64_extended_extra_field zip64_eef;
  361. zip64_eef.uncompressed_size = szm.size;
  362. zip64_eef.compressed_size = szm.size;
  363. zip64_eef.lfh_offset = szm.lfh_offset;
  364. zip64_eef.disk_number = 0;
  365. uint16_t extra_id = 0x0001;
  366. uint16_t extra_size = sizeof(zip64_eef);
  367. cdfh.extra_field_length = sizeof(extra_id) + sizeof(extra_size) + sizeof(zip64_eef);
  368. fwrite((char*)&cdfh, sizeof(cdfh), 1, fp);
  369. fwrite((char*)szm.name.c_str(), szm.name.size(), 1, fp);
  370. fwrite((char*)&extra_id, sizeof(extra_id), 1, fp);
  371. fwrite((char*)&extra_size, sizeof(extra_size), 1, fp);
  372. fwrite((char*)&zip64_eef, sizeof(zip64_eef), 1, fp);
  373. }
  374. long offset2 = ftell(fp);
  375. {
  376. uint32_t signature = 0x06064b50;
  377. fwrite((char*)&signature, sizeof(signature), 1, fp);
  378. zip64_end_of_central_directory_record eocdr64;
  379. eocdr64.size_of_eocd64_m12 = sizeof(eocdr64) - 8;
  380. eocdr64.version_made_by = 0;
  381. eocdr64.version_min_required = 0;
  382. eocdr64.disk_number = 0;
  383. eocdr64.start_disk = 0;
  384. eocdr64.cd_records = filemetas.size();
  385. eocdr64.total_cd_records = filemetas.size();
  386. eocdr64.cd_size = offset2 - offset;
  387. eocdr64.cd_offset = offset;
  388. fwrite((char*)&eocdr64, sizeof(eocdr64), 1, fp);
  389. }
  390. {
  391. uint32_t signature = 0x07064b50;
  392. fwrite((char*)&signature, sizeof(signature), 1, fp);
  393. zip64_end_of_central_directory_locator eocdl64;
  394. eocdl64.eocdr64_disk_number = 0;
  395. eocdl64.eocdr64_offset = offset2;
  396. eocdl64.disk_count = 1;
  397. fwrite((char*)&eocdl64, sizeof(eocdl64), 1, fp);
  398. }
  399. {
  400. uint32_t signature = 0x06054b50;
  401. fwrite((char*)&signature, sizeof(signature), 1, fp);
  402. end_of_central_directory_record eocdr;
  403. eocdr.disk_number = 0xffff;
  404. eocdr.start_disk = 0xffff;
  405. eocdr.cd_records = 0xffff;
  406. eocdr.total_cd_records = 0xffff;
  407. eocdr.cd_size = 0xffffffff;
  408. eocdr.cd_offset = 0xffffffff;
  409. eocdr.comment_length = 0;
  410. fwrite((char*)&eocdr, sizeof(eocdr), 1, fp);
  411. }
  412. fclose(fp);
  413. fp = 0;
  414. return 0;
  415. }
  416. } // namespace pnnx
  417. #if 0
  418. int main()
  419. {
  420. using namespace pnnx;
  421. {
  422. uint64_t len = 1*1024*1024*1024;
  423. // uint64_t len = 1*1024*1024;
  424. char* data1g = new char[len];
  425. StoreZipWriter szw;
  426. szw.open("szw.zip");
  427. szw.write_file("a.py", data1g, len);
  428. szw.write_file("b.param", data1g, 44);
  429. szw.write_file("c.bin", data1g, len);
  430. szw.write_file("d.txt", data1g, len);
  431. szw.write_file("e.jpg", data1g, len);
  432. szw.write_file("f.png", data1g, len);
  433. szw.close();
  434. delete[] data1g;
  435. }
  436. {
  437. StoreZipReader sz;
  438. sz.open("szw.zip");
  439. std::vector<std::string> names = sz.get_names();
  440. for (size_t i = 0; i < names.size(); i++)
  441. {
  442. uint64_t size = sz.get_file_size(names[i]);
  443. fprintf(stderr, "%s %lu\n", names[i].c_str(), size);
  444. }
  445. sz.close();
  446. }
  447. return 0;
  448. }
  449. #endif