zip_tricks 4.4.2 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|