test.zig 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. const std = @import("std");
  2. const zip = @import("../zip.zig");
  3. pub const File = struct {
  4. name: []const u8,
  5. content: []const u8,
  6. compression: zip.CompressionMethod,
  7. };
  8. pub const FileCache = struct {
  9. offset: u32,
  10. crc: u32,
  11. compressed_size: u32,
  12. };
  13. pub fn writeFile(
  14. out_file: std.fs.File,
  15. files: []const File,
  16. cache: []FileCache,
  17. ) !void {
  18. if (cache.len < files.len) return error.FileCacheTooSmall;
  19. var bw = std.io.bufferedWriter(out_file.writer());
  20. var counting = std.io.countingWriter(bw.writer());
  21. const writer = counting.writer();
  22. for (files, 0..) |file, i| {
  23. cache[i].offset = @intCast(counting.bytes_written);
  24. cache[i].crc = std.hash.Crc32.hash(file.content);
  25. {
  26. const hdr: zip.LocalFileHeader = .{
  27. .signature = zip.local_file_header_sig,
  28. .minimum_version = 0,
  29. .flags = 0,
  30. .compression_method = file.compression,
  31. .last_modification_time = 0,
  32. .last_modification_date = 0,
  33. .crc32 = cache[i].crc,
  34. .compressed_size = 0,
  35. .uncompressed_size = @intCast(file.content.len),
  36. .filename_len = @intCast(file.name.len),
  37. .extra_len = 0,
  38. };
  39. try writer.writeAll(&hdr.serialize());
  40. }
  41. try writer.writeAll(file.name);
  42. switch (file.compression) {
  43. .store => {
  44. try writer.writeAll(file.content);
  45. cache[i].compressed_size = @intCast(file.content.len);
  46. },
  47. .deflate, .deflate64 => {
  48. const offset = counting.bytes_written;
  49. var fbs = std.io.fixedBufferStream(file.content);
  50. try std.compress.flate.deflate.compress(.raw, fbs.reader(), writer, .{});
  51. std.debug.assert(fbs.pos == file.content.len);
  52. cache[i].compressed_size = @intCast(counting.bytes_written - offset);
  53. },
  54. else => unreachable,
  55. }
  56. }
  57. const cd_offset = counting.bytes_written;
  58. for (files, 0..) |file, i| {
  59. {
  60. const hdr: zip.CentralDirectoryFileHeader = .{
  61. .signature = zip.central_file_header_sig,
  62. .version = 0,
  63. .minimum_version = 0,
  64. .flags = 0,
  65. .compression_method = file.compression,
  66. .last_modification_time = 0,
  67. .last_modification_date = 0,
  68. .crc32 = cache[i].crc,
  69. .compressed_size = cache[i].compressed_size,
  70. .uncompressed_size = @intCast(file.content.len),
  71. .filename_len = @intCast(file.name.len),
  72. .extra_len = 0,
  73. .comment_len = 0,
  74. .disk_number = 0,
  75. .internal_file_attributes = 0,
  76. .external_file_attributes = 0,
  77. .local_file_header_offset = cache[i].offset,
  78. };
  79. try writer.writeAll(&hdr.serialize());
  80. }
  81. try writer.writeAll(file.name);
  82. }
  83. const cd_end = counting.bytes_written;
  84. {
  85. const hdr: zip.EndOfCentralDirectoryRecord = .{
  86. .disk_number = 0,
  87. .central_directory_disk_number = 0,
  88. .record_count_disk = @intCast(files.len),
  89. .record_count_total = @intCast(files.len),
  90. .central_directory_size = @intCast(cd_end - cd_offset),
  91. .central_directory_offset = @intCast(cd_offset),
  92. .comment_len = 0,
  93. };
  94. try writer.writeAll(&hdr.serialize());
  95. }
  96. try bw.flush();
  97. }