zip_tricks 4.4.2 → 4.5.0
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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +7 -0
- data/.gitignore +6 -0
- data/.rubocop.yml +79 -0
- data/.rubocop_todo.yml +43 -0
- data/.travis.yml +3 -1
- data/CHANGELOG.md +9 -0
- data/Rakefile +7 -4
- data/examples/archive_size_estimate.rb +8 -6
- data/examples/config.ru +3 -1
- data/examples/parallel_compression_with_block_deflate.rb +31 -20
- data/examples/rack_application.rb +25 -17
- data/lib/zip_tricks.rb +4 -2
- data/lib/zip_tricks/block_deflate.rb +43 -25
- data/lib/zip_tricks/block_write.rb +20 -10
- data/lib/zip_tricks/file_reader.rb +241 -145
- data/lib/zip_tricks/file_reader/inflating_reader.rb +4 -1
- data/lib/zip_tricks/file_reader/stored_reader.rb +4 -1
- data/lib/zip_tricks/null_writer.rb +5 -5
- data/lib/zip_tricks/rack_body.rb +7 -4
- data/lib/zip_tricks/rails_streaming.rb +3 -1
- data/lib/zip_tricks/remote_io.rb +9 -5
- data/lib/zip_tricks/remote_uncap.rb +10 -5
- data/lib/zip_tricks/size_estimator.rb +39 -27
- data/lib/zip_tricks/stream_crc32.rb +2 -0
- data/lib/zip_tricks/streamer.rb +254 -98
- data/lib/zip_tricks/streamer/deflated_writer.rb +6 -9
- data/lib/zip_tricks/streamer/entry.rb +11 -3
- data/lib/zip_tricks/streamer/stored_writer.rb +5 -7
- data/lib/zip_tricks/streamer/writable.rb +30 -7
- data/lib/zip_tricks/version.rb +3 -1
- data/lib/zip_tricks/write_and_tell.rb +2 -0
- data/lib/zip_tricks/zip_writer.rb +54 -44
- data/testing/generate_test_files.rb +68 -38
- data/testing/support.rb +21 -16
- data/testing/test-report.txt +28 -0
- data/zip_tricks.gemspec +24 -22
- metadata +23 -5
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ZipTricks::Streamer::DeflatedWriter
|
2
4
|
# After how many bytes of incoming data the deflater for the
|
3
5
|
# contents must be flushed. This is done to prevent unreasonable
|
4
6
|
# memory use when archiving large files.
|
5
7
|
FLUSH_EVERY_N_BYTES = 1024 * 1024 * 5
|
6
|
-
|
8
|
+
|
7
9
|
def initialize(io)
|
8
10
|
@io = io
|
9
11
|
@uncompressed_size = 0
|
@@ -15,9 +17,9 @@ class ZipTricks::Streamer::DeflatedWriter
|
|
15
17
|
|
16
18
|
def finish
|
17
19
|
@io << @deflater.finish until @deflater.finished?
|
18
|
-
|
20
|
+
{crc32: @crc.to_i, compressed_size: @io.tell - @started_at, uncompressed_size: @uncompressed_size}
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
def <<(data)
|
22
24
|
@uncompressed_size += data.bytesize
|
23
25
|
@bytes_since_last_flush += data.bytesize
|
@@ -27,11 +29,6 @@ class ZipTricks::Streamer::DeflatedWriter
|
|
27
29
|
self
|
28
30
|
end
|
29
31
|
|
30
|
-
def write(data)
|
31
|
-
self << data
|
32
|
-
data.bytesize
|
33
|
-
end
|
34
|
-
|
35
32
|
private
|
36
33
|
|
37
34
|
def interim_flush
|
@@ -39,4 +36,4 @@ class ZipTricks::Streamer::DeflatedWriter
|
|
39
36
|
@io << @deflater.flush
|
40
37
|
@bytes_since_last_flush = 0
|
41
38
|
end
|
42
|
-
end
|
39
|
+
end
|
@@ -1,10 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Is used internally by Streamer to keep track of entries in the archive during writing.
|
2
4
|
# Normally you will not have to use this class directly
|
3
|
-
class ZipTricks::Streamer::Entry < Struct.new(:filename, :crc32, :compressed_size,
|
5
|
+
class ZipTricks::Streamer::Entry < Struct.new(:filename, :crc32, :compressed_size,
|
6
|
+
:uncompressed_size, :storage_mode, :mtime,
|
7
|
+
:use_data_descriptor)
|
4
8
|
def initialize(*)
|
5
9
|
super
|
6
10
|
filename.force_encoding(Encoding::UTF_8)
|
7
|
-
|
11
|
+
# Rubocop: convention: Avoid using rescue in its modifier form.
|
12
|
+
@requires_efs_flag = !(begin
|
13
|
+
filename.encode(Encoding::ASCII)
|
14
|
+
rescue
|
15
|
+
false
|
16
|
+
end)
|
8
17
|
end
|
9
18
|
|
10
19
|
# Set the general purpose flags for the entry. We care about is the EFS
|
@@ -18,5 +27,4 @@ class ZipTricks::Streamer::Entry < Struct.new(:filename, :crc32, :compressed_siz
|
|
18
27
|
flag |= 0x0008 if use_data_descriptor # bit 3
|
19
28
|
flag
|
20
29
|
end
|
21
|
-
|
22
30
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Rubocop: convention: Missing top-level class documentation comment.
|
1
4
|
class ZipTricks::Streamer::StoredWriter
|
2
5
|
def initialize(io)
|
3
6
|
@io = io
|
@@ -13,13 +16,8 @@ class ZipTricks::Streamer::StoredWriter
|
|
13
16
|
self
|
14
17
|
end
|
15
18
|
|
16
|
-
def write(data)
|
17
|
-
self << data
|
18
|
-
data.bytesize
|
19
|
-
end
|
20
|
-
|
21
19
|
def finish
|
22
20
|
size = @io.tell - @started_at
|
23
|
-
|
21
|
+
{crc32: @crc.to_i, compressed_size: size, uncompressed_size: size}
|
24
22
|
end
|
25
|
-
end
|
23
|
+
end
|
@@ -1,20 +1,43 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Gets yielded from the writing methods of the Streamer
|
4
|
+
# and accepts the data being written into the ZIP for deflate
|
5
|
+
# or stored modes. Can be used as a destination for `IO.copy_stream`
|
6
|
+
#
|
7
|
+
# IO.copy_stream(File.open('source.bin', 'rb), writable)
|
3
8
|
class ZipTricks::Streamer::Writable
|
4
9
|
# Initializes a new Writable with the object it delegates the writes to.
|
5
|
-
# Normally you would not need to use this method directly
|
6
|
-
def initialize(writer)
|
10
|
+
# Normally you would not need to use this method directly
|
11
|
+
def initialize(streamer, writer)
|
12
|
+
@streamer = streamer
|
7
13
|
@writer = writer
|
14
|
+
@closed = false
|
8
15
|
end
|
16
|
+
|
9
17
|
# Writes the given data to the output stream
|
10
18
|
#
|
11
19
|
# @param d[String] the binary string to write (part of the uncompressed file)
|
12
20
|
# @return [self]
|
13
|
-
def <<(d)
|
14
|
-
|
21
|
+
def <<(d)
|
22
|
+
raise 'Trying to write to a closed Writable' if @closed
|
23
|
+
@writer << d
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
15
27
|
# Writes the given data to the output stream
|
16
28
|
#
|
17
29
|
# @param d[String] the binary string to write (part of the uncompressed file)
|
18
30
|
# @return [Fixnum] the number of bytes written
|
19
|
-
def write(d)
|
31
|
+
def write(d)
|
32
|
+
self << d
|
33
|
+
d.bytesize
|
34
|
+
end
|
35
|
+
|
36
|
+
# Flushes the writer and recovers the CRC32/size values. It then calls
|
37
|
+
# `update_last_entry_and_write_data_descriptor` on the given Streamer.
|
38
|
+
def close
|
39
|
+
return if @closed
|
40
|
+
@streamer.update_last_entry_and_write_data_descriptor(**@writer.finish)
|
41
|
+
@closed = true
|
42
|
+
end
|
20
43
|
end
|
data/lib/zip_tricks/version.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Layout/CommentIndentation, Metrics/LineLength, Metrics/AbcSize, Style/RedundantParentheses, Metrics/PerceivedComplexity, Layout/MultilineOperationIndentation, Layout/AlignParameters, Style/ConditionalAssignment, Layout/ExtraSpacing, Metrics/CyclomaticComplexity, Lint/UselessAssignment, Metrics/ParameterLists, Layout/LeadingCommentSpace, Naming/ConstantName
|
4
|
+
#
|
1
5
|
# A low-level ZIP file data writer. You can use it to write out various headers and central directory elements
|
2
6
|
# separately. The class handles the actual encoding of the data according to the ZIP format APPNOTE document.
|
3
7
|
#
|
@@ -35,15 +39,15 @@ class ZipTricks::ZipWriter
|
|
35
39
|
# but for now just putting in sane defaults will do. For example, Trac with zipinfo does this:
|
36
40
|
# zipinfo.external_attr = 0644 << 16L # permissions -r-wr--r--.
|
37
41
|
# We snatch the incantations from Rubyzip for this.
|
38
|
-
unix_perms =
|
39
|
-
file_type_file =
|
40
|
-
external_attrs = (file_type_file << 12 | (unix_perms &
|
42
|
+
unix_perms = 0o644
|
43
|
+
file_type_file = 0o10
|
44
|
+
external_attrs = (file_type_file << 12 | (unix_perms & 0o7777)) << 16
|
41
45
|
end
|
42
46
|
EMPTY_DIRECTORY_EXTERNAL_ATTRS = begin
|
43
47
|
# Applies permissions to an empty directory.
|
44
|
-
unix_perms =
|
45
|
-
file_type_dir =
|
46
|
-
external_attrs = (file_type_file << 12 | (unix_perms &
|
48
|
+
unix_perms = 0o755
|
49
|
+
file_type_dir = 0o04
|
50
|
+
external_attrs = (file_type_file << 12 | (unix_perms & 0o7777)) << 16
|
47
51
|
end
|
48
52
|
MADE_BY_SIGNATURE = begin
|
49
53
|
# A combination of the VERSION_MADE_BY low byte and the OS type high byte
|
@@ -51,19 +55,19 @@ class ZipTricks::ZipWriter
|
|
51
55
|
[VERSION_MADE_BY, os_type].pack('CC')
|
52
56
|
end
|
53
57
|
|
54
|
-
C_V = 'V'
|
55
|
-
C_v = 'v'
|
56
|
-
C_Qe = 'Q<'
|
57
|
-
C_C = 'C'
|
58
|
-
C_N = 'N'
|
59
|
-
|
58
|
+
C_V = 'V' # Encode a 4-byte unsigned little-endian uint
|
59
|
+
C_v = 'v' # Encode a 2-byte unsigned little-endian uint
|
60
|
+
C_Qe = 'Q<' # Encode an 8-byte unsigned little-endian uint
|
61
|
+
C_C = 'C' # For bit-encoded strings
|
62
|
+
C_N = 'N' # Encode a 4-byte signed little-endian int
|
63
|
+
|
60
64
|
private_constant :FOUR_BYTE_MAX_UINT, :TWO_BYTE_MAX_UINT,
|
61
65
|
:VERSION_MADE_BY, :VERSION_NEEDED_TO_EXTRACT, :VERSION_NEEDED_TO_EXTRACT_ZIP64,
|
62
66
|
:DEFAULT_EXTERNAL_ATTRS, :MADE_BY_SIGNATURE,
|
63
67
|
:C_V, :C_v, :C_Qe, :ZIP_TRICKS_COMMENT
|
64
68
|
|
65
|
-
# Writes the local file header, that precedes the actual file _data_.
|
66
|
-
#
|
69
|
+
# Writes the local file header, that precedes the actual file _data_.
|
70
|
+
#
|
67
71
|
# @param io[#<<] the buffer to write the local file header to
|
68
72
|
# @param filename[String] the name of the file in the archive
|
69
73
|
# @param compressed_size[Fixnum] The size of the compressed (or stored) data - how much space it uses in the ZIP
|
@@ -100,21 +104,21 @@ class ZipTricks::ZipWriter
|
|
100
104
|
# Filename should not be longer than 0xFFFF otherwise this wont fit here
|
101
105
|
io << [filename.bytesize].pack(C_v) # file name length 2 bytes
|
102
106
|
|
107
|
+
extra_fields = StringIO.new
|
108
|
+
|
103
109
|
# Interesting tidbit:
|
104
110
|
# https://social.technet.microsoft.com/Forums/windows/en-US/6a60399f-2879-4859-b7ab-6ddd08a70948
|
105
111
|
# TL;DR of it is: Windows 7 Explorer _will_ open Zip64 entries. However, it desires to have the
|
106
112
|
# Zip64 extra field as _the first_ extra field.
|
107
|
-
|
108
|
-
zip_64_extra_for_local_file_header(compressed_size: compressed_size, uncompressed_size: uncompressed_size)
|
109
|
-
else
|
110
|
-
''
|
113
|
+
if requires_zip64
|
114
|
+
extra_fields << zip_64_extra_for_local_file_header(compressed_size: compressed_size, uncompressed_size: uncompressed_size)
|
111
115
|
end
|
112
116
|
extra_fields << timestamp_extra(mtime)
|
113
117
|
|
114
|
-
io << [extra_fields.
|
118
|
+
io << [extra_fields.size].pack(C_v) # extra field length 2 bytes
|
115
119
|
|
116
120
|
io << filename # file name (variable size)
|
117
|
-
io << extra_fields
|
121
|
+
io << extra_fields.string
|
118
122
|
end
|
119
123
|
|
120
124
|
# Writes the file header for the central directory, for a particular file in the archive. When writing out this data,
|
@@ -128,8 +132,15 @@ class ZipTricks::ZipWriter
|
|
128
132
|
# @param mtime[Time] the modification time to be recorded in the ZIP
|
129
133
|
# @param gp_flags[Fixnum] bit-packed general purpose flags
|
130
134
|
# @return [void]
|
131
|
-
def write_central_directory_file_header(io:,
|
132
|
-
|
135
|
+
def write_central_directory_file_header(io:,
|
136
|
+
local_file_header_location:,
|
137
|
+
gp_flags:,
|
138
|
+
storage_mode:,
|
139
|
+
compressed_size:,
|
140
|
+
uncompressed_size:,
|
141
|
+
mtime:,
|
142
|
+
crc32:,
|
143
|
+
filename:)
|
133
144
|
# At this point if the header begins somewhere beyound 0xFFFFFFFF we _have_ to record the offset
|
134
145
|
# of the local file header as a zip64 extra field, so we give up, give in, you loose, love will always win...
|
135
146
|
add_zip64 = (local_file_header_location > FOUR_BYTE_MAX_UINT) ||
|
@@ -142,7 +153,7 @@ class ZipTricks::ZipWriter
|
|
142
153
|
else
|
143
154
|
io << [VERSION_NEEDED_TO_EXTRACT].pack(C_v) # version needed to extract 2 bytes
|
144
155
|
end
|
145
|
-
|
156
|
+
|
146
157
|
io << [gp_flags].pack(C_v) # general purpose bit flag 2 bytes
|
147
158
|
io << [storage_mode].pack(C_v) # compression method 2 bytes
|
148
159
|
io << [to_binary_dos_time(mtime)].pack(C_v) # last mod file time 2 bytes
|
@@ -160,15 +171,15 @@ class ZipTricks::ZipWriter
|
|
160
171
|
# Filename should not be longer than 0xFFFF otherwise this wont fit here
|
161
172
|
io << [filename.bytesize].pack(C_v) # file name length 2 bytes
|
162
173
|
|
163
|
-
extra_fields =
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
174
|
+
extra_fields = StringIO.new
|
175
|
+
if add_zip64
|
176
|
+
extra_fields << zip_64_extra_for_central_directory_file_header(local_file_header_location: local_file_header_location,
|
177
|
+
compressed_size: compressed_size,
|
178
|
+
uncompressed_size: uncompressed_size)
|
168
179
|
end
|
169
180
|
extra_fields << timestamp_extra(mtime)
|
170
|
-
|
171
|
-
io << [extra_fields.
|
181
|
+
|
182
|
+
io << [extra_fields.size].pack(C_v) # extra field length 2 bytes
|
172
183
|
|
173
184
|
io << [0].pack(C_v) # file comment length 2 bytes
|
174
185
|
|
@@ -181,10 +192,10 @@ class ZipTricks::ZipWriter
|
|
181
192
|
io << [0].pack(C_v)
|
182
193
|
end
|
183
194
|
io << [0].pack(C_v) # internal file attributes 2 bytes
|
184
|
-
|
195
|
+
|
185
196
|
# Because the add_empty_directory method will create a directory with a trailing "/",
|
186
197
|
# this check can be used to assign proper permissions to the created directory.
|
187
|
-
if filename.end_with?(
|
198
|
+
if filename.end_with?('/')
|
188
199
|
io << [EMPTY_DIRECTORY_EXTERNAL_ATTRS].pack(C_V)
|
189
200
|
else
|
190
201
|
io << [DEFAULT_EXTERNAL_ATTRS].pack(C_V) # external file attributes 4 bytes
|
@@ -196,7 +207,7 @@ class ZipTricks::ZipWriter
|
|
196
207
|
io << [local_file_header_location].pack(C_V)
|
197
208
|
end
|
198
209
|
io << filename # file name (variable size)
|
199
|
-
io << extra_fields
|
210
|
+
io << extra_fields.string # extra field (variable size)
|
200
211
|
#(empty) # file comment (variable size)
|
201
212
|
end
|
202
213
|
|
@@ -215,7 +226,6 @@ class ZipTricks::ZipWriter
|
|
215
226
|
# for the data descriptor record.
|
216
227
|
io << [crc32].pack(C_V) # crc-32 4 bytes
|
217
228
|
|
218
|
-
|
219
229
|
# If one of the sizes is above 0xFFFFFFF use ZIP64 lengths (8 bytes) instead. A good unarchiver
|
220
230
|
# will decide to unpack it as such if it finds the Zip64 extra for the file in the central directory.
|
221
231
|
# So also use the opportune moment to switch the entry to Zip64 if needed
|
@@ -236,7 +246,7 @@ class ZipTricks::ZipWriter
|
|
236
246
|
# @return [void]
|
237
247
|
def write_end_of_central_directory(io:, start_of_central_directory_location:, central_directory_size:, num_files_in_archive:, comment: ZIP_TRICKS_COMMENT)
|
238
248
|
zip64_eocdr_offset = start_of_central_directory_location + central_directory_size
|
239
|
-
|
249
|
+
|
240
250
|
zip64_required = central_directory_size > FOUR_BYTE_MAX_UINT ||
|
241
251
|
start_of_central_directory_location > FOUR_BYTE_MAX_UINT ||
|
242
252
|
zip64_eocdr_offset > FOUR_BYTE_MAX_UINT ||
|
@@ -311,7 +321,7 @@ class ZipTricks::ZipWriter
|
|
311
321
|
io << [comment.bytesize].pack(C_v) # .ZIP file comment length 2 bytes
|
312
322
|
io << comment # .ZIP file comment (variable size)
|
313
323
|
end
|
314
|
-
|
324
|
+
|
315
325
|
private
|
316
326
|
|
317
327
|
# Writes the Zip64 extra field for the local file header. Will be used by `write_local_file_header` when any sizes given to it warrant that.
|
@@ -336,7 +346,7 @@ class ZipTricks::ZipWriter
|
|
336
346
|
# remaining times
|
337
347
|
def timestamp_extra(mtime)
|
338
348
|
# Local-header version:
|
339
|
-
#
|
349
|
+
#
|
340
350
|
# Value Size Description
|
341
351
|
# ----- ---- -----------
|
342
352
|
# (time) 0x5455 Short tag for this extra block type ("UT")
|
@@ -345,9 +355,9 @@ class ZipTricks::ZipWriter
|
|
345
355
|
# (ModTime) Long time of last modification (UTC/GMT)
|
346
356
|
# (AcTime) Long time of last access (UTC/GMT)
|
347
357
|
# (CrTime) Long time of original creation (UTC/GMT)
|
348
|
-
#
|
358
|
+
#
|
349
359
|
# Central-header version:
|
350
|
-
#
|
360
|
+
#
|
351
361
|
# Value Size Description
|
352
362
|
# ----- ---- -----------
|
353
363
|
# (time) 0x5455 Short tag for this extra block type ("UT")
|
@@ -357,7 +367,7 @@ class ZipTricks::ZipWriter
|
|
357
367
|
#
|
358
368
|
# The lower three bits of Flags in both headers indicate which time-
|
359
369
|
# stamps are present in the LOCAL extra field:
|
360
|
-
#
|
370
|
+
#
|
361
371
|
# bit 0 if set, modification time is present
|
362
372
|
# bit 1 if set, access time is present
|
363
373
|
# bit 2 if set, creation time is present
|
@@ -371,7 +381,7 @@ class ZipTricks::ZipWriter
|
|
371
381
|
]
|
372
382
|
pack_array(data_and_packspecs)
|
373
383
|
end
|
374
|
-
|
384
|
+
|
375
385
|
# Writes the Zip64 extra field for the central directory header.It differs from the extra used in the local file header because it
|
376
386
|
# also contains the location of the local file header in the ZIP as an 8-byte int.
|
377
387
|
#
|
@@ -390,15 +400,15 @@ class ZipTricks::ZipWriter
|
|
390
400
|
]
|
391
401
|
pack_array(data_and_packspecs)
|
392
402
|
end
|
393
|
-
|
403
|
+
|
394
404
|
def to_binary_dos_time(t)
|
395
|
-
(t.sec/2) + (t.min << 5) + (t.hour << 11)
|
405
|
+
(t.sec / 2) + (t.min << 5) + (t.hour << 11)
|
396
406
|
end
|
397
407
|
|
398
408
|
def to_binary_dos_date(t)
|
399
409
|
(t.day) + (t.month << 5) + ((t.year - 1980) << 9)
|
400
410
|
end
|
401
|
-
|
411
|
+
|
402
412
|
# Unzips a given array of tuples of "numeric value, pack specifier" and then packs all the odd
|
403
413
|
# values using specifiers from all the even values. It is harder to explain than to show:
|
404
414
|
#
|
@@ -1,96 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'support'
|
2
4
|
|
3
|
-
build_test
|
4
|
-
zip.add_stored_entry(filename: 'text.txt',
|
5
|
+
build_test 'Two small stored files' do |zip|
|
6
|
+
zip.add_stored_entry(filename: 'text.txt',
|
7
|
+
size: $war_and_peace.bytesize,
|
8
|
+
crc32: $war_and_peace_crc)
|
5
9
|
zip << $war_and_peace
|
6
10
|
|
7
|
-
zip.add_stored_entry(filename: 'image.jpg',
|
11
|
+
zip.add_stored_entry(filename: 'image.jpg',
|
12
|
+
size: $image_file.bytesize,
|
13
|
+
crc32: $image_file_crc)
|
8
14
|
zip << $image_file
|
9
15
|
end
|
10
16
|
|
11
|
-
build_test
|
12
|
-
zip.add_stored_entry(filename: 'text.txt',
|
17
|
+
build_test 'Two small stored files and an empty directory' do |zip|
|
18
|
+
zip.add_stored_entry(filename: 'text.txt',
|
19
|
+
size: $war_and_peace.bytesize,
|
20
|
+
crc32: $war_and_peace_crc)
|
13
21
|
zip << $war_and_peace
|
14
22
|
|
15
|
-
zip.add_stored_entry(filename: 'image.jpg',
|
23
|
+
zip.add_stored_entry(filename: 'image.jpg',
|
24
|
+
size: $image_file.bytesize,
|
25
|
+
crc32: $image_file_crc)
|
16
26
|
zip << $image_file
|
17
|
-
|
18
|
-
zip.add_empty_directory(dirname:
|
27
|
+
|
28
|
+
zip.add_empty_directory(dirname: 'Chekov')
|
19
29
|
end
|
20
30
|
|
21
|
-
build_test
|
22
|
-
zip.add_stored_entry(filename: 'Kungälv.txt',
|
31
|
+
build_test 'Filename with diacritics' do |zip|
|
32
|
+
zip.add_stored_entry(filename: 'Kungälv.txt',
|
33
|
+
size: $war_and_peace.bytesize,
|
34
|
+
crc32: $war_and_peace_crc)
|
23
35
|
zip << $war_and_peace
|
24
36
|
end
|
25
37
|
|
26
|
-
build_test
|
27
|
-
zip.add_stored_entry(filename: 'Война и мир.txt',
|
38
|
+
build_test 'Purely UTF-8 filename' do |zip|
|
39
|
+
zip.add_stored_entry(filename: 'Война и мир.txt',
|
40
|
+
size: $war_and_peace.bytesize,
|
41
|
+
crc32: $war_and_peace_crc)
|
28
42
|
zip << $war_and_peace
|
29
43
|
end
|
30
44
|
|
31
45
|
# The trick of this test is that each file of the 2, on it's own, does _not_ exceed the
|
32
46
|
# size threshold for Zip64. Together, however, they do.
|
33
|
-
build_test
|
34
|
-
big = generate_big_entry((0xFFFFFFFF / 2) +
|
35
|
-
zip.add_stored_entry(filename: 'repeated-A.txt',
|
47
|
+
build_test 'Two entries larger than the overall Zip64 offset' do |zip|
|
48
|
+
big = generate_big_entry((0xFFFFFFFF / 2) + 1_024)
|
49
|
+
zip.add_stored_entry(filename: 'repeated-A.txt',
|
50
|
+
size: big.size,
|
51
|
+
crc32: big.crc32)
|
36
52
|
big.write_to(zip)
|
37
53
|
|
38
|
-
zip.add_stored_entry(filename: 'repeated-B.txt',
|
54
|
+
zip.add_stored_entry(filename: 'repeated-B.txt',
|
55
|
+
size: big.size,
|
56
|
+
crc32: big.crc32)
|
39
57
|
big.write_to(zip)
|
40
58
|
end
|
41
59
|
|
42
|
-
build_test
|
43
|
-
big = generate_big_entry(0xFFFFFFFF +
|
44
|
-
zip.add_stored_entry(filename: 'large-requires-zip64.bin',
|
60
|
+
build_test 'One entry that requires Zip64 and a tiny entry following it' do |zip|
|
61
|
+
big = generate_big_entry(0xFFFFFFFF + 2_048)
|
62
|
+
zip.add_stored_entry(filename: 'large-requires-zip64.bin',
|
63
|
+
size: big.size,
|
64
|
+
crc32: big.crc32)
|
45
65
|
big.write_to(zip)
|
46
66
|
|
47
|
-
zip.add_stored_entry(filename: 'tiny-after.txt',
|
67
|
+
zip.add_stored_entry(filename: 'tiny-after.txt',
|
68
|
+
size: $war_and_peace.bytesize,
|
69
|
+
crc32: $war_and_peace_crc)
|
48
70
|
zip << $war_and_peace
|
49
71
|
end
|
50
72
|
|
51
|
-
build_test
|
52
|
-
zip.add_stored_entry(filename: 'tiny-at-start.txt',
|
73
|
+
build_test 'One tiny entry followed by second that requires Zip64' do |zip|
|
74
|
+
zip.add_stored_entry(filename: 'tiny-at-start.txt',
|
75
|
+
size: $war_and_peace.bytesize,
|
76
|
+
crc32: $war_and_peace_crc)
|
53
77
|
zip << $war_and_peace
|
54
78
|
|
55
|
-
big = generate_big_entry(0xFFFFFFFF +
|
56
|
-
zip.add_stored_entry(filename: 'large-requires-zip64.bin',
|
79
|
+
big = generate_big_entry(0xFFFFFFFF + 2_048)
|
80
|
+
zip.add_stored_entry(filename: 'large-requires-zip64.bin',
|
81
|
+
size: big.size,
|
82
|
+
crc32: big.crc32)
|
57
83
|
big.write_to(zip)
|
58
84
|
end
|
59
85
|
|
60
|
-
build_test
|
61
|
-
big = generate_big_entry(0xFFFFFFFF +
|
62
|
-
zip.add_stored_entry(filename: 'huge-file-1.bin',
|
86
|
+
build_test 'Two entries both requiring Zip64' do |zip|
|
87
|
+
big = generate_big_entry(0xFFFFFFFF + 2_048)
|
88
|
+
zip.add_stored_entry(filename: 'huge-file-1.bin',
|
89
|
+
size: big.size,
|
90
|
+
crc32: big.crc32)
|
63
91
|
big.write_to(zip)
|
64
92
|
|
65
|
-
zip.add_stored_entry(filename: 'huge-file-2.bin',
|
93
|
+
zip.add_stored_entry(filename: 'huge-file-2.bin',
|
94
|
+
size: big.size,
|
95
|
+
crc32: big.crc32)
|
66
96
|
big.write_to(zip)
|
67
97
|
end
|
68
98
|
|
69
|
-
build_test
|
99
|
+
build_test 'Two stored entries using data descriptors' do |zip|
|
70
100
|
zip.write_stored_file('stored.1.bin') do |sink|
|
71
|
-
sink << Random.new.bytes(
|
101
|
+
sink << Random.new.bytes(1_024 * 1_024 * 4)
|
72
102
|
end
|
73
103
|
zip.write_stored_file('stored.2.bin') do |sink|
|
74
|
-
sink << Random.new.bytes(
|
104
|
+
sink << Random.new.bytes(1_024 * 1_024 * 3)
|
75
105
|
end
|
76
106
|
end
|
77
107
|
|
78
|
-
build_test
|
108
|
+
build_test 'One entry deflated using data descriptors' do |zip|
|
79
109
|
big = generate_big_entry(0xFFFFFFFF / 64)
|
80
110
|
zip.write_deflated_file('war-and-peace-repeated-compressed.txt') do |sink|
|
81
111
|
big.write_to(sink)
|
82
112
|
end
|
83
113
|
end
|
84
114
|
|
85
|
-
build_test
|
86
|
-
big = generate_big_entry((0xFFFFFFFF / 2) +
|
87
|
-
|
115
|
+
build_test 'Two entries larger than the overall Zip64 offset using data descriptors' do |zip|
|
116
|
+
big = generate_big_entry((0xFFFFFFFF / 2) + 1_024)
|
117
|
+
|
88
118
|
zip.write_stored_file('repeated-A.txt') { |sink| big.write_to(sink) }
|
89
119
|
zip.write_stored_file('repeated-B.txt') { |sink| big.write_to(sink) }
|
90
120
|
end
|
91
121
|
|
92
|
-
build_test
|
93
|
-
big = generate_big_entry(0xFFFFFFFF +
|
94
|
-
|
122
|
+
build_test 'One stored entry larger than Zip64 threshold using data descriptors' do |zip|
|
123
|
+
big = generate_big_entry(0xFFFFFFFF + 64_000)
|
124
|
+
|
95
125
|
zip.write_stored_file('repeated-A.txt') { |sink| big.write_to(sink) }
|
96
126
|
end
|