zip_kit 6.0.1 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,7 +27,7 @@
27
27
  class ZipKit::ZipWriter
28
28
  FOUR_BYTE_MAX_UINT = 0xFFFFFFFF
29
29
  TWO_BYTE_MAX_UINT = 0xFFFF
30
- ZIP_TRICKS_COMMENT = "Written using ZipKit %<version>s" % {version: ZipKit::VERSION}
30
+ ZIP_KIT_COMMENT = "Written using ZipKit %<version>s" % {version: ZipKit::VERSION}
31
31
  VERSION_MADE_BY = 52
32
32
  VERSION_NEEDED_TO_EXTRACT = 20
33
33
  VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
@@ -58,7 +58,7 @@ class ZipKit::ZipWriter
58
58
  :C_UINT4,
59
59
  :C_UINT2,
60
60
  :C_UINT8,
61
- :ZIP_TRICKS_COMMENT
61
+ :ZIP_KIT_COMMENT
62
62
 
63
63
  # Writes the local file header, that precedes the actual file _data_.
64
64
  #
@@ -74,29 +74,41 @@ class ZipKit::ZipWriter
74
74
  def write_local_file_header(io:, filename:, compressed_size:, uncompressed_size:, crc32:, gp_flags:, mtime:, storage_mode:)
75
75
  requires_zip64 = compressed_size > FOUR_BYTE_MAX_UINT || uncompressed_size > FOUR_BYTE_MAX_UINT
76
76
 
77
- io << [0x04034b50].pack(C_UINT4) # local file header signature 4 bytes (0x04034b50)
78
- io << if requires_zip64 # version needed to extract 2 bytes
77
+ # local file header signature 4 bytes (0x04034b50)
78
+ io << [0x04034b50].pack(C_UINT4)
79
+ # version needed to extract 2 bytes
80
+ io << if requires_zip64
79
81
  [VERSION_NEEDED_TO_EXTRACT_ZIP64].pack(C_UINT2)
80
82
  else
81
83
  [VERSION_NEEDED_TO_EXTRACT].pack(C_UINT2)
82
84
  end
83
85
 
84
- io << [gp_flags].pack(C_UINT2) # general purpose bit flag 2 bytes
85
- io << [storage_mode].pack(C_UINT2) # compression method 2 bytes
86
- io << [to_binary_dos_time(mtime)].pack(C_UINT2) # last mod file time 2 bytes
87
- io << [to_binary_dos_date(mtime)].pack(C_UINT2) # last mod file date 2 bytes
88
- io << [crc32].pack(C_UINT4) # crc-32 4 bytes
86
+ # general purpose bit flag 2 bytes
87
+ io << [gp_flags].pack(C_UINT2)
88
+ # compression method 2 bytes
89
+ io << [storage_mode].pack(C_UINT2)
90
+ # last mod file time 2 bytes
91
+ io << [to_binary_dos_time(mtime)].pack(C_UINT2)
92
+ # last mod file date 2 bytes
93
+ io << [to_binary_dos_date(mtime)].pack(C_UINT2)
94
+ # crc-32 4 bytes
95
+ io << [crc32].pack(C_UINT4)
89
96
 
90
97
  if requires_zip64
91
- io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4) # compressed size 4 bytes
92
- io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4) # uncompressed size 4 bytes
98
+ # compressed size 4 bytes
99
+ io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
100
+ # uncompressed size 4 bytes
101
+ io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
93
102
  else
94
- io << [compressed_size].pack(C_UINT4) # compressed size 4 bytes
95
- io << [uncompressed_size].pack(C_UINT4) # uncompressed size 4 bytes
103
+ # compressed size 4 bytes
104
+ io << [compressed_size].pack(C_UINT4)
105
+ # uncompressed size 4 bytes
106
+ io << [uncompressed_size].pack(C_UINT4)
96
107
  end
97
108
 
98
109
  # Filename should not be longer than 0xFFFF otherwise this wont fit here
99
- io << [filename.bytesize].pack(C_UINT2) # file name length 2 bytes
110
+ # file name length 2 bytes
111
+ io << [filename.bytesize].pack(C_UINT2)
100
112
 
101
113
  extra_fields = StringIO.new
102
114
 
@@ -109,9 +121,13 @@ class ZipKit::ZipWriter
109
121
  end
110
122
  extra_fields << timestamp_extra_for_local_file_header(mtime)
111
123
 
112
- io << [extra_fields.size].pack(C_UINT2) # extra field length 2 bytes
124
+ # extra field length 2 bytes
125
+ io << [extra_fields.size].pack(C_UINT2)
113
126
 
114
- io << filename # file name (variable size)
127
+ # file name (variable size)
128
+ io << filename
129
+
130
+ # Contents of the extra fields (variable size)
115
131
  io << extra_fields.string
116
132
  end
117
133
 
@@ -125,7 +141,7 @@ class ZipKit::ZipWriter
125
141
  # @param crc32[Fixnum] The CRC32 checksum of the file
126
142
  # @param mtime[Time] the modification time to be recorded in the ZIP
127
143
  # @param gp_flags[Fixnum] bit-packed general purpose flags
128
- # @param unix_permissions[Fixnum?] the permissions for the file, or nil for the default to be used
144
+ # @param unix_permissions[Integer] the permissions for the file, or nil for the default to be used
129
145
  # @return [void]
130
146
  def write_central_directory_file_header(io:,
131
147
  local_file_header_location:,
@@ -142,30 +158,42 @@ class ZipKit::ZipWriter
142
158
  add_zip64 = (local_file_header_location > FOUR_BYTE_MAX_UINT) ||
143
159
  (compressed_size > FOUR_BYTE_MAX_UINT) || (uncompressed_size > FOUR_BYTE_MAX_UINT)
144
160
 
145
- io << [0x02014b50].pack(C_UINT4) # central file header signature 4 bytes (0x02014b50)
146
- io << MADE_BY_SIGNATURE # version made by 2 bytes
161
+ # central file header signature 4 bytes (0x02014b50)
162
+ io << [0x02014b50].pack(C_UINT4)
163
+ # version made by 2 bytes
164
+ io << MADE_BY_SIGNATURE
165
+
166
+ # version needed to extract 2 bytes
147
167
  io << if add_zip64
148
- [VERSION_NEEDED_TO_EXTRACT_ZIP64].pack(C_UINT2) # version needed to extract 2 bytes
168
+ [VERSION_NEEDED_TO_EXTRACT_ZIP64].pack(C_UINT2)
149
169
  else
150
- [VERSION_NEEDED_TO_EXTRACT].pack(C_UINT2) # version needed to extract 2 bytes
170
+ [VERSION_NEEDED_TO_EXTRACT].pack(C_UINT2)
151
171
  end
152
172
 
153
- io << [gp_flags].pack(C_UINT2) # general purpose bit flag 2 bytes
154
- io << [storage_mode].pack(C_UINT2) # compression method 2 bytes
155
- io << [to_binary_dos_time(mtime)].pack(C_UINT2) # last mod file time 2 bytes
156
- io << [to_binary_dos_date(mtime)].pack(C_UINT2) # last mod file date 2 bytes
157
- io << [crc32].pack(C_UINT4) # crc-32 4 bytes
158
-
173
+ # general purpose bit flag 2 bytes
174
+ io << [gp_flags].pack(C_UINT2)
175
+ # compression method 2 bytes
176
+ io << [storage_mode].pack(C_UINT2)
177
+ # last mod file time 2 bytes
178
+ io << [to_binary_dos_time(mtime)].pack(C_UINT2)
179
+ # last mod file date 2 bytes
180
+ io << [to_binary_dos_date(mtime)].pack(C_UINT2)
181
+ # crc-32 4 bytes
182
+ io << [crc32].pack(C_UINT4)
183
+
184
+ # compressed size 4 bytes
185
+ # uncompressed size 4 bytes
159
186
  if add_zip64
160
- io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4) # compressed size 4 bytes
161
- io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4) # uncompressed size 4 bytes
187
+ io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
188
+ io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
162
189
  else
163
- io << [compressed_size].pack(C_UINT4) # compressed size 4 bytes
164
- io << [uncompressed_size].pack(C_UINT4) # uncompressed size 4 bytes
190
+ io << [compressed_size].pack(C_UINT4)
191
+ io << [uncompressed_size].pack(C_UINT4)
165
192
  end
166
193
 
167
194
  # Filename should not be longer than 0xFFFF otherwise this wont fit here
168
- io << [filename.bytesize].pack(C_UINT2) # file name length 2 bytes
195
+ # file name length 2 bytes
196
+ io << [filename.bytesize].pack(C_UINT2)
169
197
 
170
198
  extra_fields = StringIO.new
171
199
  if add_zip64
@@ -175,23 +203,25 @@ class ZipKit::ZipWriter
175
203
  end
176
204
  extra_fields << timestamp_extra_for_central_directory_entry(mtime)
177
205
 
178
- io << [extra_fields.size].pack(C_UINT2) # extra field length 2 bytes
179
-
180
- io << [0].pack(C_UINT2) # file comment length 2 bytes
206
+ # extra field length 2 bytes
207
+ io << [extra_fields.size].pack(C_UINT2)
208
+ # file comment length 2 bytes
209
+ io << [0].pack(C_UINT2)
181
210
 
182
211
  # For The Unarchiver < 3.11.1 this field has to be set to the overflow value if zip64 is used
183
212
  # because otherwise it does not properly advance the pointer when reading the Zip64 extra field
184
213
  # https://bitbucket.org/WAHa_06x36/theunarchiver/pull-requests/2/bug-fix-for-zip64-extra-field-parser/diff
185
- io << if add_zip64 # disk number start 2 bytes
214
+ # disk number start 2 bytes
215
+ io << if add_zip64
186
216
  [TWO_BYTE_MAX_UINT].pack(C_UINT2)
187
217
  else
188
218
  [0].pack(C_UINT2)
189
219
  end
190
- io << [0].pack(C_UINT2) # internal file attributes 2 bytes
220
+ # internal file attributes 2 bytes
221
+ io << [0].pack(C_UINT2)
191
222
 
192
223
  # Because the add_empty_directory method will create a directory with a trailing "/",
193
224
  # this check can be used to assign proper permissions to the created directory.
194
- # external file attributes 4 bytes
195
225
  external_attrs = if filename.end_with?("/")
196
226
  unix_permissions ||= DEFAULT_DIRECTORY_UNIX_PERMISSIONS
197
227
  generate_external_attrs(unix_permissions, FILE_TYPE_DIRECTORY)
@@ -199,17 +229,23 @@ class ZipKit::ZipWriter
199
229
  unix_permissions ||= DEFAULT_FILE_UNIX_PERMISSIONS
200
230
  generate_external_attrs(unix_permissions, FILE_TYPE_FILE)
201
231
  end
232
+
233
+ # external file attributes 4 bytes
202
234
  io << [external_attrs].pack(C_UINT4)
203
235
 
204
- io << if add_zip64 # relative offset of local header 4 bytes
236
+ # relative offset of local header 4 bytes
237
+ io << if add_zip64
205
238
  [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
206
239
  else
207
240
  [local_file_header_location].pack(C_UINT4)
208
241
  end
209
242
 
210
- io << filename # file name (variable size)
211
- io << extra_fields.string # extra field (variable size)
212
- # (empty) # file comment (variable size)
243
+ # file name (variable size)
244
+ io << filename
245
+ # extra field (variable size)
246
+ io << extra_fields.string
247
+ # file comment (variable size)
248
+ # (empty)
213
249
  end
214
250
 
215
251
  # Writes the data descriptor following the file data for a file whose local file header
@@ -222,19 +258,25 @@ class ZipKit::ZipWriter
222
258
  # @param uncompressed_size[Fixnum] The size of the file once extracted
223
259
  # @return [void]
224
260
  def write_data_descriptor(io:, compressed_size:, uncompressed_size:, crc32:)
225
- io << [0x08074b50].pack(C_UINT4) # Although not originally assigned a signature, the value
261
+ # Although not originally assigned a signature, the value
226
262
  # 0x08074b50 has commonly been adopted as a signature value
227
263
  # for the data descriptor record.
228
- io << [crc32].pack(C_UINT4) # crc-32 4 bytes
264
+ io << [0x08074b50].pack(C_UINT4)
265
+
266
+ # crc-32 4 bytes
267
+ io << [crc32].pack(C_UINT4)
229
268
 
230
269
  # If one of the sizes is above 0xFFFFFFF use ZIP64 lengths (8 bytes) instead. A good unarchiver
231
270
  # will decide to unpack it as such if it finds the Zip64 extra for the file in the central directory.
232
- # So also use the opportune moment to switch the entry to Zip64 if needed
271
+ # So also use the opportune moment to switch the entry to Zip64 if needed.
272
+ # We switch if either of the sizes requires ZIP64, so that both values are encoded similarly.
233
273
  requires_zip64 = compressed_size > FOUR_BYTE_MAX_UINT || uncompressed_size > FOUR_BYTE_MAX_UINT
234
274
  pack_spec = requires_zip64 ? C_UINT8 : C_UINT4
235
275
 
236
- io << [compressed_size].pack(pack_spec) # compressed size 4 bytes, or 8 bytes for ZIP64
237
- io << [uncompressed_size].pack(pack_spec) # uncompressed size 4 bytes, or 8 bytes for ZIP64
276
+ # compressed size 4 bytes, or 8 bytes for ZIP64
277
+ io << [compressed_size].pack(pack_spec)
278
+ # uncompressed size 4 bytes, or 8 bytes for ZIP64
279
+ io << [uncompressed_size].pack(pack_spec)
238
280
  end
239
281
 
240
282
  # Writes the "end of central directory record" (including the Zip6 salient bits if necessary)
@@ -243,9 +285,9 @@ class ZipKit::ZipWriter
243
285
  # @param start_of_central_directory_location[Fixnum] byte offset of the start of central directory form the beginning of ZIP file
244
286
  # @param central_directory_size[Fixnum] the size of the central directory (only file headers) in bytes
245
287
  # @param num_files_in_archive[Fixnum] How many files the archive contains
246
- # @param comment[String] the comment for the archive (defaults to ZIP_TRICKS_COMMENT)
288
+ # @param comment[String] the comment for the archive (defaults to ZIP_KIT_COMMENT)
247
289
  # @return [void]
248
- def write_end_of_central_directory(io:, start_of_central_directory_location:, central_directory_size:, num_files_in_archive:, comment: ZIP_TRICKS_COMMENT)
290
+ def write_end_of_central_directory(io:, start_of_central_directory_location:, central_directory_size:, num_files_in_archive:, comment: ZIP_KIT_COMMENT)
249
291
  zip64_eocdr_offset = start_of_central_directory_location + central_directory_size
250
292
 
251
293
  zip64_required = central_directory_size > FOUR_BYTE_MAX_UINT ||
@@ -256,71 +298,88 @@ class ZipKit::ZipWriter
256
298
  # Then, if zip64 is used
257
299
  if zip64_required
258
300
  # [zip64 end of central directory record]
259
- # zip64 end of central dir
260
- io << [0x06064b50].pack(C_UINT4) # signature 4 bytes (0x06064b50)
261
- io << [44].pack(C_UINT8) # size of zip64 end of central
301
+ # zip64 end of central dir signature 4 bytes (0x06064b50)
302
+ io << [0x06064b50].pack(C_UINT4)
303
+
304
+ # size of zip64 end of central
262
305
  # directory record 8 bytes
263
306
  # (this is ex. the 12 bytes of the signature and the size value itself).
264
307
  # Without the extensible data sector (which we are not using)
265
308
  # it is always 44 bytes.
266
- io << MADE_BY_SIGNATURE # version made by 2 bytes
267
- io << [VERSION_NEEDED_TO_EXTRACT_ZIP64].pack(C_UINT2) # version needed to extract 2 bytes
268
- io << [0].pack(C_UINT4) # number of this disk 4 bytes
269
- io << [0].pack(C_UINT4) # number of the disk with the
270
- # start of the central directory 4 bytes
271
- io << [num_files_in_archive].pack(C_UINT8) # total number of entries in the
309
+ io << [44].pack(C_UINT8)
310
+
311
+ # version made by 2 bytes
312
+ io << MADE_BY_SIGNATURE
313
+ # version needed to extract 2 bytes
314
+ io << [VERSION_NEEDED_TO_EXTRACT_ZIP64].pack(C_UINT2)
315
+ # number of this disk 4 bytes
316
+ io << [0].pack(C_UINT4)
317
+ # number of the disk with the start of the central directory 4 bytes
318
+ io << [0].pack(C_UINT4)
319
+ # total number of entries in the
272
320
  # central directory on this disk 8 bytes
273
- io << [num_files_in_archive].pack(C_UINT8) # total number of entries in the
321
+ io << [num_files_in_archive].pack(C_UINT8)
322
+ # total number of entries in the
274
323
  # central directory 8 bytes
275
- io << [central_directory_size].pack(C_UINT8) # size of the central directory 8 bytes
276
- # offset of start of central
277
- # directory with respect to
278
- io << [start_of_central_directory_location].pack(C_UINT8) # the starting disk number 8 bytes
279
- # zip64 extensible data sector (variable size), blank for us
280
-
281
- # [zip64 end of central directory locator]
282
- io << [0x07064b50].pack(C_UINT4) # zip64 end of central dir locator
324
+ io << [num_files_in_archive].pack(C_UINT8)
325
+ # size of the central directory 8 bytes
326
+ io << [central_directory_size].pack(C_UINT8)
327
+ # offset of start of central directory with respect to
328
+ # the starting disk number 8 bytes
329
+ io << [start_of_central_directory_location].pack(C_UINT8)
330
+ # zip64 extensible data sector (variable size)
331
+ # (blank for us)
332
+
333
+ # zip64 end of central dir locator
283
334
  # signature 4 bytes (0x07064b50)
284
- io << [0].pack(C_UINT4) # number of the disk with the
285
- # start of the zip64 end of
335
+ io << [0x07064b50].pack(C_UINT4)
336
+ # number of the disk with the start of the zip64 end of
286
337
  # central directory 4 bytes
287
- io << [zip64_eocdr_offset].pack(C_UINT8) # relative offset of the zip64
338
+ io << [0].pack(C_UINT4)
339
+ # relative offset of the zip64
288
340
  # end of central directory record 8 bytes
289
341
  # (note: "relative" is actually "from the start of the file")
290
- io << [1].pack(C_UINT4) # total number of disks 4 bytes
342
+ io << [zip64_eocdr_offset].pack(C_UINT8)
343
+ # total number of disks 4 bytes
344
+ io << [1].pack(C_UINT4)
291
345
  end
292
346
 
293
347
  # Then the end of central directory record:
294
- io << [0x06054b50].pack(C_UINT4) # end of central dir signature 4 bytes (0x06054b50)
295
- io << [0].pack(C_UINT2) # number of this disk 2 bytes
296
- io << [0].pack(C_UINT2) # number of the disk with the
348
+ # end of central dir signature 4 bytes (0x06054b50)
349
+ io << [0x06054b50].pack(C_UINT4)
350
+ # number of this disk 2 bytes
351
+ io << [0].pack(C_UINT2)
352
+ # number of the disk with the
297
353
  # start of the central directory 2 bytes
354
+ io << [0].pack(C_UINT2)
298
355
 
299
- if zip64_required # the number of entries will be read from the zip64 part of the central directory
300
- io << [TWO_BYTE_MAX_UINT].pack(C_UINT2) # total number of entries in the
301
- # central directory on this disk 2 bytes
302
- io << [TWO_BYTE_MAX_UINT].pack(C_UINT2) # total number of entries in
356
+ # total number of entries in the
357
+ # central directory on this disk 2 bytes
358
+ # total number of entries in
303
359
  # the central directory 2 bytes
360
+ if zip64_required # the number of entries will be read from the zip64 part of the central directory
361
+ io << [TWO_BYTE_MAX_UINT].pack(C_UINT2)
362
+ io << [TWO_BYTE_MAX_UINT].pack(C_UINT2)
304
363
  else
305
- io << [num_files_in_archive].pack(C_UINT2) # total number of entries in the
306
- # central directory on this disk 2 bytes
307
- io << [num_files_in_archive].pack(C_UINT2) # total number of entries in
308
- # the central directory 2 bytes
364
+ io << [num_files_in_archive].pack(C_UINT2)
365
+ io << [num_files_in_archive].pack(C_UINT2)
309
366
  end
310
367
 
311
- if zip64_required
312
- io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4) # size of the central directory 4 bytes
313
- io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4) # offset of start of central
368
+ # size of the central directory 4 bytes
369
+ # offset of start of central
314
370
  # directory with respect to
315
371
  # the starting disk number 4 bytes
372
+ if zip64_required
373
+ io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
374
+ io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
316
375
  else
317
- io << [central_directory_size].pack(C_UINT4) # size of the central directory 4 bytes
318
- io << [start_of_central_directory_location].pack(C_UINT4) # offset of start of central
319
- # directory with respect to
320
- # the starting disk number 4 bytes
376
+ io << [central_directory_size].pack(C_UINT4)
377
+ io << [start_of_central_directory_location].pack(C_UINT4)
321
378
  end
322
- io << [comment.bytesize].pack(C_UINT2) # .ZIP file comment length 2 bytes
323
- io << comment # .ZIP file comment (variable size)
379
+ # .ZIP file comment length 2 bytes
380
+ io << [comment.bytesize].pack(C_UINT2)
381
+ # .ZIP file comment (variable size)
382
+ io << comment
324
383
  end
325
384
 
326
385
  private
@@ -332,10 +391,14 @@ class ZipKit::ZipWriter
332
391
  # @return [String]
333
392
  def zip_64_extra_for_local_file_header(compressed_size:, uncompressed_size:)
334
393
  data_and_packspecs = [
335
- 0x0001, C_UINT2, # 2 bytes Tag for this "extra" block type
336
- 16, C_UINT2, # 2 bytes Size of this "extra" block. For us it will always be 16 (2x8)
337
- uncompressed_size, C_UINT8, # 8 bytes Original uncompressed file size
338
- compressed_size, C_UINT8 # 8 bytes Size of compressed data
394
+ # 2 bytes Tag for this "extra" block type
395
+ 0x0001, C_UINT2,
396
+ # 2 bytes Size of this "extra" block. For us it will always be 16 (2x8)
397
+ 16, C_UINT2,
398
+ # 8 bytes Original uncompressed file size
399
+ uncompressed_size, C_UINT8,
400
+ # 8 bytes Size of compressed data
401
+ compressed_size, C_UINT8
339
402
  ]
340
403
  pack_array(data_and_packspecs)
341
404
  end
@@ -377,10 +440,14 @@ class ZipKit::ZipWriter
377
440
  # bits 3-7 reserved for additional timestamps; not set
378
441
  flags = 0b00000001 # Set the lowest bit only, to indicate that only mtime is present
379
442
  data_and_packspecs = [
380
- 0x5455, C_UINT2, # tag for this extra block type ("UT")
381
- (1 + 4), C_UINT2, # the size of this block (1 byte used for the Flag + 3 longs used for the timestamp)
382
- flags, C_CHAR, # encode a single byte
383
- mtime.utc.to_i, C_INT4 # Use a signed int, not the unsigned one used by the rest of the ZIP spec.
443
+ # tag for this extra block type ("UT")
444
+ 0x5455, C_UINT2,
445
+ # the size of this block (1 byte used for the Flag + 3 longs used for the timestamp)
446
+ (1 + 4), C_UINT2,
447
+ # encode a single byte
448
+ flags, C_CHAR,
449
+ # Use a signed int, not the unsigned one used by the rest of the ZIP spec.
450
+ mtime.utc.to_i, C_INT4
384
451
  ]
385
452
  # The atime and ctime can be omitted if not present
386
453
  pack_array(data_and_packspecs)
@@ -399,12 +466,18 @@ class ZipKit::ZipWriter
399
466
  # @return [String]
400
467
  def zip_64_extra_for_central_directory_file_header(compressed_size:, uncompressed_size:, local_file_header_location:)
401
468
  data_and_packspecs = [
402
- 0x0001, C_UINT2, # 2 bytes Tag for this "extra" block type
403
- 28, C_UINT2, # 2 bytes Size of this "extra" block. For us it will always be 28
404
- uncompressed_size, C_UINT8, # 8 bytes Original uncompressed file size
405
- compressed_size, C_UINT8, # 8 bytes Size of compressed data
406
- local_file_header_location, C_UINT8, # 8 bytes Offset of local header record
407
- 0, C_UINT4 # 4 bytes Number of the disk on which this file starts
469
+ # 2 bytes Tag for this "extra" block type
470
+ 0x0001, C_UINT2,
471
+ # 2 bytes Size of this "extra" block. For us it will always be 28
472
+ 28, C_UINT2,
473
+ # 8 bytes Original uncompressed file size
474
+ uncompressed_size, C_UINT8,
475
+ # 8 bytes Size of compressed data
476
+ compressed_size, C_UINT8,
477
+ # 8 bytes Offset of local header record
478
+ local_file_header_location, C_UINT8,
479
+ # 4 bytes Number of the disk on which this file starts
480
+ 0, C_UINT4
408
481
  ]
409
482
  pack_array(data_and_packspecs)
410
483
  end
@@ -424,7 +497,10 @@ class ZipKit::ZipWriter
424
497
  #
425
498
  # will do the following two transforms:
426
499
  #
427
- # [1, 'V', 2, 'v', 148, 'v] -> [1,2,148], ['V','v','v'] -> [1,2,148].pack('Vvv') -> "\x01\x00\x00\x00\x02\x00\x94\x00"
500
+ # [1, 'V', 2, 'v', 148, 'v] -> [1,2,148], ['V','v','v'] -> [1,2,148].pack('Vvv') -> "\x01\x00\x00\x00\x02\x00\x94\x00".
501
+ # This might seem like a "clever optimisation" but the issue is that `pack` needs an array allocated per call, and
502
+ # we output very verbosely - value-by-value. This might be quite a few array allocs. Using something like this
503
+ # helps us save the array allocs
428
504
  def pack_array(values_to_packspecs)
429
505
  values, packspecs = values_to_packspecs.partition.each_with_index { |_, i| i.even? }
430
506
  values.pack(packspecs.join)