zip_tricks 2.8.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -3
  3. data/IMPLEMENTATION_DETAILS.md +2 -10
  4. data/README.md +62 -59
  5. data/examples/archive_size_estimate.rb +4 -4
  6. data/examples/rack_application.rb +3 -5
  7. data/lib/zip_tricks/block_deflate.rb +21 -0
  8. data/lib/zip_tricks/file_reader.rb +491 -0
  9. data/lib/zip_tricks/null_writer.rb +7 -2
  10. data/lib/zip_tricks/rack_body.rb +3 -3
  11. data/lib/zip_tricks/remote_io.rb +30 -20
  12. data/lib/zip_tricks/remote_uncap.rb +10 -10
  13. data/lib/zip_tricks/size_estimator.rb +64 -0
  14. data/lib/zip_tricks/stream_crc32.rb +2 -2
  15. data/lib/zip_tricks/streamer/deflated_writer.rb +26 -0
  16. data/lib/zip_tricks/streamer/entry.rb +21 -0
  17. data/lib/zip_tricks/streamer/stored_writer.rb +25 -0
  18. data/lib/zip_tricks/streamer/writable.rb +20 -0
  19. data/lib/zip_tricks/streamer.rb +172 -66
  20. data/lib/zip_tricks/zip_writer.rb +346 -0
  21. data/lib/zip_tricks.rb +1 -4
  22. data/spec/spec_helper.rb +1 -38
  23. data/spec/zip_tricks/file_reader_spec.rb +47 -0
  24. data/spec/zip_tricks/rack_body_spec.rb +2 -2
  25. data/spec/zip_tricks/remote_io_spec.rb +8 -20
  26. data/spec/zip_tricks/remote_uncap_spec.rb +4 -4
  27. data/spec/zip_tricks/size_estimator_spec.rb +31 -0
  28. data/spec/zip_tricks/streamer_spec.rb +59 -36
  29. data/spec/zip_tricks/zip_writer_spec.rb +408 -0
  30. data/zip_tricks.gemspec +20 -14
  31. metadata +33 -16
  32. data/lib/zip_tricks/manifest.rb +0 -85
  33. data/lib/zip_tricks/microzip.rb +0 -339
  34. data/lib/zip_tricks/stored_size_estimator.rb +0 -44
  35. data/spec/zip_tricks/manifest_spec.rb +0 -60
  36. data/spec/zip_tricks/microzip_interop_spec.rb +0 -48
  37. data/spec/zip_tricks/microzip_spec.rb +0 -546
  38. data/spec/zip_tricks/stored_size_estimator_spec.rb +0 -22
@@ -1,48 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe 'Microzip in interop context' do
4
- let(:described_class) { ZipTricks::Microzip}
5
-
6
- it 'creates an archive that can be opened by Rubyzip, with a small number of very tiny text files' do
7
- tf = ManagedTempfile.new('zip')
8
- z = described_class.new
9
-
10
- test_str = Random.new.bytes(64)
11
- crc = Zlib.crc32(test_str)
12
- t = Time.now.utc
13
-
14
- 3.times do |i|
15
- fn = "test-#{i}"
16
- z.add_local_file_header(io: tf, filename: fn, crc32: crc, compressed_size: test_str.bytesize,
17
- uncompressed_size: test_str.bytesize, storage_mode: 0, mtime: t)
18
- tf << test_str
19
- end
20
- z.write_central_directory(tf)
21
- tf.flush
22
-
23
- Zip::File.open(tf.path) do |zip_file|
24
- entries = zip_file.to_a
25
- expect(entries.length).to eq(3)
26
- entries.each do |entry|
27
- # Make sure it is tagged as UNIX
28
- expect(entry.fstype).to eq(3)
29
-
30
- # Check the file contents
31
- readback = entry.get_input_stream.read
32
- readback.force_encoding(Encoding::BINARY)
33
- expect(readback).to eq(test_str)
34
-
35
- # The CRC
36
- expect(entry.crc).to eq(crc)
37
-
38
- # Check the name
39
- expect(entry.name).to match(/test/)
40
-
41
- # Check the right external attributes (non-executable on UNIX)
42
- expect(entry.external_file_attributes).to eq(2175008768)
43
- end
44
- end
45
-
46
- inspect_zip_with_external_tool(tf.path)
47
- end
48
- end
@@ -1,546 +0,0 @@
1
- require_relative '../spec_helper'
2
- require_relative '../../testing/support'
3
-
4
- describe ZipTricks::Microzip do
5
- class ByteReader < Struct.new(:io)
6
- def read_2b
7
- io.read(2).unpack('v').first
8
- end
9
-
10
- def read_2c
11
- io.read(2).unpack('CC').first
12
- end
13
-
14
- def read_4b
15
- io.read(4).unpack('V').first
16
- end
17
-
18
- def read_8b
19
- io.read(8).unpack('Q<').first
20
- end
21
-
22
- def read_n(n)
23
- io.read(n)
24
- end
25
- end
26
-
27
- class IOWrapper < ZipTricks::WriteAndTell
28
- def read(n)
29
- @io.read(n)
30
- end
31
- end
32
-
33
- it 'raises an exception if the filename is non-unique in the already existing set' do
34
- z = described_class.new
35
- z.add_local_file_header(io: StringIO.new, filename: 'foo.txt', crc32: 0, compressed_size: 0, uncompressed_size: 0, storage_mode: 0)
36
- expect {
37
- z.add_local_file_header(io: StringIO.new, filename: 'foo.txt', crc32: 0, compressed_size: 0, uncompressed_size: 0, storage_mode: 0)
38
- }.to raise_error(/already/)
39
- end
40
-
41
- it 'raises an exception if the filename contains backward slashes' do
42
- z = described_class.new
43
- expect {
44
- z.add_local_file_header(io: StringIO.new, filename: 'windows\not\welcome.txt',
45
- crc32: 0, compressed_size: 0, uncompressed_size: 0, storage_mode: 0)
46
- }.to raise_error(/UNIX/)
47
- end
48
-
49
- it 'raises an exception if the filename does not fit in 0xFFFF bytes' do
50
- longest_filename_in_the_universe = "x" * (0xFFFF + 1)
51
- z = described_class.new
52
- expect {
53
- z.add_local_file_header(io: StringIO.new, filename: longest_filename_in_the_universe,
54
- crc32: 0, compressed_size: 0, uncompressed_size: 0, storage_mode: 0)
55
- }.to raise_error(/is too long/)
56
- end
57
-
58
- describe '#add_local_file_header' do
59
- it 'writes out the local file header for an entry that fits into a standard ZIP' do
60
- buf = StringIO.new
61
- zip = described_class.new
62
- mtime = Time.utc(2016, 7, 17, 13, 48)
63
- zip.add_local_file_header(io: buf, filename: 'first-file.bin', crc32: 123, compressed_size: 8981,
64
- uncompressed_size: 90981, storage_mode: 8, mtime: mtime)
65
-
66
- buf.rewind
67
- br = ByteReader.new(buf)
68
- expect(br.read_4b).to eq(0x04034b50) # Signature
69
- expect(br.read_2b).to eq(20) # Version needed to extract
70
- expect(br.read_2b).to eq(0) # gp flags
71
- expect(br.read_2b).to eq(8) # storage mode
72
- expect(br.read_2b).to eq(28160) # DOS time
73
- expect(br.read_2b).to eq(18673) # DOS date
74
- expect(br.read_4b).to eq(123) # CRC32
75
- expect(br.read_4b).to eq(8981) # compressed size
76
- expect(br.read_4b).to eq(90981) # uncompressed size
77
- expect(br.read_2b).to eq('first-file.bin'.bytesize) # byte length of the filename
78
- expect(br.read_2b).to be_zero # size of extra fields
79
- expect(br.read_n('first-file.bin'.bytesize)).to eq('first-file.bin') # the filename
80
- expect(buf).to be_eof
81
- end
82
-
83
- it 'writes out the local file header for an entry with a UTF-8 filename, setting the proper GP flag bit' do
84
- buf = StringIO.new
85
- zip = described_class.new
86
- mtime = Time.utc(2016, 7, 17, 13, 48)
87
- zip.add_local_file_header(io: buf, filename: 'файл.bin', crc32: 123, compressed_size: 8981,
88
- uncompressed_size: 90981, storage_mode: 8, mtime: mtime)
89
-
90
- buf.rewind
91
- br = ByteReader.new(buf)
92
- br.read_4b # Signature
93
- br.read_2b # Version needed to extract
94
- expect(br.read_2b).to eq(2048) # gp flags
95
- end
96
-
97
- it "correctly recognizes UTF-8 filenames even if they are tagged as ASCII" do
98
- name = 'файл.bin'
99
- name.force_encoding(Encoding::US_ASCII)
100
-
101
- buf = StringIO.new
102
- zip = described_class.new
103
- mtime = Time.utc(2016, 7, 17, 13, 48)
104
- zip.add_local_file_header(io: buf, filename: name, crc32: 123, compressed_size: 8981,
105
- uncompressed_size: 90981, storage_mode: 8, mtime: mtime)
106
-
107
- buf.rewind
108
- br = ByteReader.new(buf)
109
- br.read_4b # Signature
110
- br.read_2b # Version needed to extract
111
- expect(br.read_2b).to eq(2048) # gp flags
112
- end
113
-
114
- it 'writes out the local file header for an entry with a filename with diacritics, setting the proper GP flag bit' do
115
- buf = StringIO.new
116
- zip = described_class.new
117
- mtime = Time.utc(2016, 7, 17, 13, 48)
118
- zip.add_local_file_header(io: buf, filename: 'Kungälv', crc32: 123, compressed_size: 8981,
119
- uncompressed_size: 90981, storage_mode: 8, mtime: mtime)
120
-
121
- buf.rewind
122
- br = ByteReader.new(buf)
123
- br.read_4b # Signature
124
- br.read_2b # Version needed to extract
125
- expect(br.read_2b).to eq(2048) # gp flags
126
- br.read_2b
127
- br.read_2b
128
- br.read_2b
129
- br.read_4b
130
- br.read_4b
131
- br.read_4b
132
- br.read_2b
133
- br.read_2b
134
- filename_readback = br.read_n('Kungälv'.bytesize)
135
- expect(filename_readback.force_encoding(Encoding::UTF_8)).to eq('Kungälv')
136
- end
137
-
138
- it 'writes out the local file header for an entry that requires Zip64 based on its compressed size _only_' do
139
- buf = StringIO.new
140
- zip = described_class.new
141
- mtime = Time.utc(2016, 7, 17, 13, 48)
142
- zip.add_local_file_header(io: buf, filename: 'first-file.bin', crc32: 123, compressed_size: (0xFFFFFFFF + 1),
143
- uncompressed_size: 90981, storage_mode: 8, mtime: mtime)
144
-
145
- buf.rewind
146
- br = ByteReader.new(buf)
147
- expect(br.read_4b).to eq(0x04034b50) # Signature
148
- expect(br.read_2b).to eq(45) # Version needed to extract (require Zip64 support)
149
- expect(br.read_2b).to eq(0) # gp flags
150
- expect(br.read_2b).to eq(8) # storage mode
151
- expect(br.read_2b).to eq(28160) # DOS time
152
- expect(br.read_2b).to eq(18673) # DOS date
153
- expect(br.read_4b).to eq(123) # CRC32
154
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size (blanked out)
155
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size (blanked out)
156
- expect(br.read_2b).to eq('first-file.bin'.bytesize) # byte length of the filename
157
- expect(br.read_2b).to eq(20) # size of extra fields
158
- expect(br.read_n('first-file.bin'.bytesize)).to eq('first-file.bin') # the filename
159
- expect(br.read_2b).to eq(1) # Zip64 extra field signature
160
- expect(br.read_2b).to eq(16) # Size of the Zip64 extra field
161
- expect(br.read_8b).to eq(90981) # True compressed size
162
- expect(br.read_8b).to eq(0xFFFFFFFF + 1) # True uncompressed size
163
- expect(buf).to be_eof
164
- end
165
-
166
- it 'writes out the local file header for an entry that requires Zip64 based on its uncompressed size _only_' do
167
- buf = StringIO.new
168
- zip = described_class.new
169
- mtime = Time.utc(2016, 7, 17, 13, 48)
170
- zip.add_local_file_header(io: buf, filename: 'first-file.bin', crc32: 123, compressed_size: 90981,
171
- uncompressed_size: (0xFFFFFFFF + 1), storage_mode: 8, mtime: mtime)
172
-
173
- buf.rewind
174
- br = ByteReader.new(buf)
175
- expect(br.read_4b).to eq(0x04034b50) # Signature
176
- expect(br.read_2b).to eq(45) # Version needed to extract (require Zip64 support)
177
- expect(br.read_2b).to eq(0) # gp flags
178
- expect(br.read_2b).to eq(8) # storage mode
179
- expect(br.read_2b).to eq(28160) # DOS time
180
- expect(br.read_2b).to eq(18673) # DOS date
181
- expect(br.read_4b).to eq(123) # CRC32
182
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size (blanked out)
183
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size (blanked out)
184
- expect(br.read_2b).to eq('first-file.bin'.bytesize) # byte length of the filename
185
- expect(br.read_2b).to eq(20) # size of extra fields
186
- expect(br.read_n('first-file.bin'.bytesize)).to eq('first-file.bin') # the filename
187
- expect(br.read_2b).to eq(1) # Zip64 extra field signature
188
- expect(br.read_2b).to eq(16) # Size of the Zip64 extra field
189
- expect(br.read_8b).to eq(0xFFFFFFFF + 1) # True uncompressed size
190
- expect(br.read_8b).to eq(90981) # True compressed size
191
- expect(buf).to be_eof
192
- end
193
-
194
- it 'does not write out the Zip64 extra if the position in the destination IO is beyond the Zip64 size limit' do
195
- buf = StringIO.new
196
- zip = described_class.new
197
- mtime = Time.utc(2016, 7, 17, 13, 48)
198
- expect(buf).to receive(:tell).and_return(0xFFFFFFFF + 1)
199
- zip.add_local_file_header(io: buf, filename: 'first-file.bin', crc32: 123, compressed_size: 123,
200
- uncompressed_size: 456, storage_mode: 8, mtime: mtime)
201
-
202
- buf.rewind
203
- br = ByteReader.new(buf)
204
- expect(br.read_4b).to eq(0x04034b50) # Signature
205
- expect(br.read_2b).to eq(20) # Version needed to extract (require Zip64 support)
206
- br.read_2b
207
- br.read_2b
208
- br.read_2b
209
- br.read_2b
210
- br.read_4b
211
- br.read_4b
212
- br.read_4b
213
- br.read_2b
214
- expect(br.read_2b).to be_zero
215
- end
216
- end
217
-
218
- describe '#write_central_directory' do
219
- it 'writes the central directory and makes it a valid one even if there were no files' do
220
- buf = StringIO.new
221
-
222
- zip = described_class.new
223
- zip.write_central_directory(buf)
224
-
225
- buf.rewind
226
- br = ByteReader.new(buf)
227
- expect(br.read_4b).to eq(0x06054b50) # EOCD signature
228
- expect(br.read_2b).to eq(0) # disk number
229
- expect(br.read_2b).to eq(0) # disk number of the disk containing EOCD
230
- expect(br.read_2b).to eq(0) # num files in the central directory of this disk
231
- expect(br.read_2b).to eq(0) # num files in the central directories of all disks
232
- expect(br.read_4b).to eq(0) # central directorys size
233
- expect(br.read_4b).to eq(0) # offset of start of central directory from the beginning of the disk
234
- expect(br.read_2b).to eq(0) # ZIP file comment length
235
- expect(buf).to be_eof
236
- end
237
-
238
- it 'writes the central directory for 2 files' do
239
- zip = described_class.new
240
-
241
- mtime = Time.utc(2016, 7, 17, 13, 48)
242
-
243
- buf = StringIO.new
244
- zip.add_local_file_header(io: buf, filename: 'first-file.bin', crc32: 123, compressed_size: 5,
245
- uncompressed_size: 8, storage_mode: 8, mtime: mtime)
246
- buf << Random.new.bytes(5)
247
- zip.add_local_file_header(io: buf, filename: 'second-file.txt', crc32: 546, compressed_size: 9,
248
- uncompressed_size: 9, storage_mode: 0, mtime: mtime)
249
- buf << Random.new.bytes(5)
250
-
251
- central_dir_offset = buf.tell
252
- zip.write_central_directory(buf)
253
-
254
- # Seek to where the central directory begins
255
- buf.rewind
256
- buf.seek(central_dir_offset)
257
-
258
- br = ByteReader.new(buf)
259
-
260
- # Central directory entry for the first file
261
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
262
- expect(br.read_2b).to eq(820) # version made by
263
- expect(br.read_2b).to eq(20) # version need to extract
264
- expect(br.read_2b).to eq(0) # general purpose bit flag
265
- expect(br.read_2b).to eq(8) # compression method (deflated here)
266
- expect(br.read_2b).to eq(28160) # last mod file time
267
- expect(br.read_2b).to eq(18673) # last mod file date
268
- expect(br.read_4b).to eq(123) # crc32
269
- expect(br.read_4b).to eq(5) # compressed size
270
- expect(br.read_4b).to eq(8) # uncompressed size
271
- expect(br.read_2b).to eq(14) # filename length
272
- expect(br.read_2b).to eq(0) # extra field length
273
- expect(br.read_2b).to eq(0) # file comment
274
- expect(br.read_2b).to eq(0) # disk number, must be blanked to the maximum value because of The Unarchiver bug
275
- expect(br.read_2b).to eq(0) # internal file attributes
276
- expect(br.read_4b).to eq(2175008768) # external file attributes
277
- expect(br.read_4b).to eq(0) # relative offset of local header
278
- expect(br.read_n(14)).to eq('first-file.bin') # the filename
279
-
280
- # Central directory entry for the second file
281
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
282
- expect(br.read_2b).to eq(820) # version made by
283
- expect(br.read_2b).to eq(20) # version need to extract
284
- expect(br.read_2b).to eq(0) # general purpose bit flag
285
- expect(br.read_2b).to eq(0) # compression method (stored here)
286
- expect(br.read_2b).to eq(28160) # last mod file time
287
- expect(br.read_2b).to eq(18673) # last mod file date
288
- expect(br.read_4b).to eq(546) # crc32
289
- expect(br.read_4b).to eq(9) # compressed size
290
- expect(br.read_4b).to eq(9) # uncompressed size
291
- expect(br.read_2b).to eq('second-file.bin'.bytesize) # filename length
292
- expect(br.read_2b).to eq(0) # extra field length
293
- expect(br.read_2b).to eq(0) # file comment
294
- expect(br.read_2b).to eq(0) # disk number, must be blanked to the maximum value because of The Unarchiver bug
295
- expect(br.read_2b).to eq(0) # internal file attributes
296
- expect(br.read_4b).to eq(2175008768) # external file attributes
297
- expect(br.read_4b).to eq(49) # relative offset of local header
298
- expect(br.read_n('second-file.txt'.bytesize)).to eq('second-file.txt') # the filename
299
-
300
- expect(br.read_4b).to eq(0x06054b50) # end of central dir signature
301
- br.read_2b
302
- br.read_2b
303
- br.read_2b
304
- br.read_2b
305
- br.read_4b
306
- br.read_4b
307
- br.read_2b
308
-
309
- expect(buf).to be_eof
310
- end
311
-
312
- it 'writes the central directory for 1 file that is larger than 4GB' do
313
- zip = described_class.new
314
- buf = StringIO.new
315
- big = 0xFFFFFFFF + 2048
316
- mtime = Time.utc(2016, 7, 17, 13, 48)
317
-
318
- zip.add_local_file_header(io: buf, filename: 'big-file.bin', crc32: 12345, compressed_size: big,
319
- uncompressed_size: big, storage_mode: 0, mtime: mtime)
320
-
321
- central_dir_offset = buf.tell
322
-
323
- zip.write_central_directory(buf)
324
-
325
- # Seek to where the central directory begins
326
- buf.rewind
327
- buf.seek(central_dir_offset)
328
-
329
- br = ByteReader.new(buf)
330
-
331
- # Standard central directory entry (similar to the local file header)
332
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
333
- expect(br.read_2b).to eq(820) # version made by
334
- expect(br.read_2b).to eq(45) # version need to extract (45 for Zip64)
335
- expect(br.read_2b).to eq(0) # general purpose bit flag
336
- expect(br.read_2b).to eq(0) # compression method (stored here)
337
- expect(br.read_2b).to eq(28160) # last mod file time
338
- expect(br.read_2b).to eq(18673) # last mod file date
339
- expect(br.read_4b).to eq(12345) # crc32
340
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size
341
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size
342
- expect(br.read_2b).to eq(12) # filename length
343
- expect(br.read_2b).to eq(32) # extra field length (we store the Zip64 extra field for this file)
344
- expect(br.read_2b).to eq(0) # file comment
345
- expect(br.read_2b).to eq(0xFFFF) # disk number, must be blanked to the maximum value because of The Unarchiver bug
346
- expect(br.read_2b).to eq(0) # internal file attributes
347
- expect(br.read_4b).to eq(2175008768) # external file attributes
348
- expect(br.read_4b).to eq(0xFFFFFFFF) # relative offset of local header
349
- expect(br.read_n(12)).to eq('big-file.bin') # the filename
350
-
351
- # Zip64 extra field
352
- expect(br.read_2b).to eq(0x0001) # Tag for the "extra" block
353
- expect(br.read_2b).to eq(28) # Size of this "extra" block. For us it will always be 28
354
- expect(br.read_8b).to eq(big) # Original uncompressed file size
355
- expect(br.read_8b).to eq(big) # Original compressed file size
356
- expect(br.read_8b).to eq(0) # Offset of local header record
357
- expect(br.read_4b).to eq(0) # Number of the disk on which this file starts
358
- end
359
-
360
- it 'writes the central directory for 2 files which, together, make the central directory start beyound the 4GB threshold' do
361
- zip = described_class.new
362
- raw_buf = StringIO.new
363
-
364
- zip_write_buf = IOWrapper.new(raw_buf)
365
- big1 = 0xFFFFFFFF/2 + 512
366
- big2 = 0xFFFFFFFF/2 + 1024
367
- mtime = Time.utc(2016, 7, 17, 13, 48)
368
-
369
- zip.add_local_file_header(io: zip_write_buf, filename: 'first-big-file.bin', crc32: 12345, compressed_size: big1,
370
- uncompressed_size: big1, storage_mode: 0, mtime: mtime)
371
- zip_write_buf.advance_position_by(big1)
372
-
373
- zip.add_local_file_header(io: zip_write_buf, filename: 'second-big-file.bin', crc32: 54321, compressed_size: big2,
374
- uncompressed_size: big2, storage_mode: 0, mtime: mtime)
375
- zip_write_buf.advance_position_by(big2)
376
-
377
- fake_central_dir_offset = zip_write_buf.tell # Grab the position in the underlying buffer
378
- actual_central_dir_offset = raw_buf.tell # Grab the position in the underlying buffer
379
-
380
- zip.write_central_directory(zip_write_buf)
381
-
382
- # Seek to where the central directory begins
383
- raw_buf.seek(actual_central_dir_offset, IO::SEEK_SET)
384
-
385
- br = ByteReader.new(raw_buf)
386
-
387
- # Standard central directory entry (similar to the local file header)
388
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
389
- expect(br.read_2b).to eq(820) # version made by
390
- expect(br.read_2b).to eq(20) # version need to extract (45 for Zip64)
391
- expect(br.read_2b).to eq(0) # general purpose bit flag
392
- expect(br.read_2b).to eq(0) # compression method (stored here)
393
- expect(br.read_2b).to eq(28160) # last mod file time
394
- expect(br.read_2b).to eq(18673) # last mod file date
395
- expect(br.read_4b).to eq(12345) # crc32
396
- expect(br.read_4b).to eq(2147484159) # compressed size
397
- expect(br.read_4b).to eq(2147484159) # uncompressed size
398
- expect(br.read_2b).to eq(18) # filename length
399
- expect(br.read_2b).to eq(0) # extra field length
400
- expect(br.read_2b).to eq(0) # file comment length
401
- expect(br.read_2b).to eq(0) # disk number, must be blanked to the maximum value because of The Unarchiver bug
402
- expect(br.read_2b).to eq(0) # internal file attributes
403
- expect(br.read_4b).to eq(2175008768) # external file attributes
404
- expect(br.read_4b).to eq(0) # relative offset of local header
405
- expect(br.read_n(18)).to eq("first-big-file.bin") # the filename
406
-
407
- # Standard central directory entry (similar to the local file header)
408
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
409
- expect(br.read_2b).to eq(820) # version made by
410
- expect(br.read_2b).to eq(20) # version need to extract (45 for Zip64)
411
- expect(br.read_2b).to eq(0) # general purpose bit flag
412
- expect(br.read_2b).to eq(0) # compression method (stored here)
413
- expect(br.read_2b).to eq(28160) # last mod file time
414
- expect(br.read_2b).to eq(18673) # last mod file date
415
- expect(br.read_4b).to eq(54321) # crc32
416
- expect(br.read_4b).to eq(2147484671) # compressed size
417
- expect(br.read_4b).to eq(2147484671) # uncompressed size
418
- expect(br.read_2b).to eq(19) # filename length
419
- expect(br.read_2b).to eq(0) # extra field length
420
- expect(br.read_2b).to eq(0) # file comment length
421
- expect(br.read_2b).to eq(0) # disk number, must be blanked to the maximum value because of The Unarchiver bug
422
- expect(br.read_2b).to eq(0) # internal file attributes
423
- expect(br.read_4b).to eq(2175008768) # external file attributes
424
- expect(br.read_4b).to eq(2147484207) # relative offset of local header
425
- expect(br.read_n(19)).to eq('second-big-file.bin') # the filename
426
-
427
- # zip64 specific values for a whole central directory
428
- expect(br.read_4b).to eq(0x06064b50) # zip64 end of central dir signature
429
- expect(br.read_8b).to eq(44) # size of zip64 end of central directory record
430
- expect(br.read_2b).to eq(820) # version made by
431
- expect(br.read_2b).to eq(45) # version need to extract
432
- expect(br.read_4b).to eq(0) # number of this disk
433
- expect(br.read_4b).to eq(0) # another number related to disk
434
- expect(br.read_8b).to eq(2) # total number of entries in the central directory on this disk
435
- expect(br.read_8b).to eq(2) # total number of entries in the central directory
436
- expect(br.read_8b).to eq(129) # size of central directory
437
- expect(br.read_8b).to eq(4294968927) # starting disk number
438
- expect(br.read_4b).to eq(0x07064b50) # zip64 end of central dir locator signature
439
- expect(br.read_4b).to eq(0) # number of disk ...
440
- expect(br.read_8b).to eq(4294969056) # relative offset zip64
441
- expect(br.read_4b).to eq(1) # total number of disks
442
- end
443
-
444
- it 'writes the central directory for 3 files which, the third of which will require the Zip64 extra since it is past the 4GB offset' do
445
- zip = described_class.new
446
- raw_buf = StringIO.new
447
-
448
- zip_write_buf = IOWrapper.new(raw_buf)
449
- big1 = 0xFFFFFFFF/2 + 512
450
- big2 = 0xFFFFFFFF/2 + 1024
451
- big3 = 0xFFFFFFFF/2 + 1024
452
- mtime = Time.utc(2016, 7, 17, 13, 48)
453
-
454
- zip.add_local_file_header(io: zip_write_buf, filename: 'one', crc32: 12345, compressed_size: big1,
455
- uncompressed_size: big1, storage_mode: 0, mtime: mtime)
456
- zip_write_buf.advance_position_by(big1)
457
-
458
- zip.add_local_file_header(io: zip_write_buf, filename: 'two', crc32: 54321, compressed_size: big2,
459
- uncompressed_size: big2, storage_mode: 0, mtime: mtime)
460
- zip_write_buf.advance_position_by(big2)
461
-
462
- big3_offset = zip_write_buf.tell
463
-
464
- zip.add_local_file_header(io: zip_write_buf, filename: 'three', crc32: 54321, compressed_size: big2,
465
- uncompressed_size: big2, storage_mode: 0, mtime: mtime)
466
- zip_write_buf.advance_position_by(big3)
467
-
468
- fake_central_dir_offset = zip_write_buf.tell # Grab the position in the underlying buffer
469
- actual_central_dir_offset = raw_buf.tell # Grab the position in the underlying buffer
470
-
471
- zip.write_central_directory(zip_write_buf)
472
-
473
- # Seek to where the central directory begins
474
- raw_buf.seek(actual_central_dir_offset, IO::SEEK_SET)
475
-
476
- br = ByteReader.new(raw_buf)
477
-
478
- # Standard central directory entry (similar to the local file header)
479
- # Skip over two entries, because the other example has a 1-to-1 repeat of this
480
- 2.times {
481
- br.read_4b
482
- br.read_2b
483
- br.read_2b
484
- br.read_2b
485
- br.read_2b
486
- br.read_2b
487
- br.read_2b
488
- br.read_4b
489
- br.read_4b
490
- br.read_4b
491
- br.read_2b
492
- br.read_2b
493
- br.read_2b
494
- br.read_2b
495
- br.read_2b
496
- br.read_4b
497
- br.read_4b
498
- br.read_n(3)
499
- }
500
-
501
- # Entry for the third file DOES bear the Zip64 extra field
502
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
503
- expect(br.read_2b).to eq(820) # version made by
504
- expect(br.read_2b).to eq(45) # version need to extract (45 for Zip64) - this entry requires it
505
- expect(br.read_2b).to eq(0) # general purpose bit flag
506
- expect(br.read_2b).to eq(0) # compression method (stored here)
507
- expect(br.read_2b).to eq(28160) # last mod file time
508
- expect(br.read_2b).to eq(18673) # last mod file date
509
- expect(br.read_4b).to eq(54321) # crc32
510
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size - blanked for Zip64
511
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size - blanked for Zip64
512
- expect(br.read_2b).to eq(5) # filename length
513
- expect(br.read_2b).to eq(32) # extra field length (length of the ZIp64 extra)
514
- expect(br.read_2b).to eq(0) # file comment length
515
- expect(br.read_2b).to eq(0xFFFF) # disk number, with Zip64 must be blanked to the maximum value because of The Unarchiver bug
516
- expect(br.read_2b).to eq(0) # internal file attributes
517
- expect(br.read_4b).to eq(2175008768) # external file attributes
518
- expect(br.read_4b).to eq(4294967295) # relative offset of local header
519
- expect(br.read_n(5)).to eq('three') # the filename
520
- # then the Zip64 extra for that last file _only_
521
- expect(br.read_2b).to eq(0x0001) # Tag for the "extra" block
522
- expect(br.read_2b).to eq(28) # Size of this "extra" block. For us it will always be 28
523
- expect(br.read_8b).to eq(big3) # Original uncompressed file size
524
- expect(br.read_8b).to eq(big3) # Original compressed file size
525
- expect(br.read_8b).to eq(big3_offset) # Offset of local header record
526
- expect(br.read_4b).to eq(0) # Number of the disk on which this file starts
527
-
528
- # zip64 specific values for a whole central directory
529
- expect(br.read_4b).to eq(0x06064b50) # zip64 end of central dir signature
530
- expect(br.read_8b).to eq(44) # size of zip64 end of central directory record
531
- expect(br.read_2b).to eq(820) # version made by
532
- expect(br.read_2b).to eq(45) # version need to extract
533
- expect(br.read_4b).to eq(0) # number of this disk
534
- expect(br.read_4b).to eq(0) # another number related to disk
535
- expect(br.read_8b).to eq(3) # total number of entries in the central directory on this disk
536
- expect(br.read_8b).to eq(3) # total number of entries in the central directory
537
- expect(br.read_8b).to eq(181) # size of central directory
538
- expect(br.read_8b).to eq(6442453602) # central directory offset from start of disk
539
-
540
- expect(br.read_4b).to eq(0x07064b50) # Zip64 EOCD locator signature
541
- expect(br.read_4b).to eq(0) # Disk number with the start of central directory
542
- expect(br.read_8b).to eq(6442453783) # relative offset of the zip64 end of central directory record
543
- expect(br.read_4b).to eq(1) # total number of disks
544
- end
545
- end
546
- end
@@ -1,22 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe ZipTricks::StoredSizeEstimator do
4
- it 'accurately predicts the output zip size' do
5
- # Generate a couple of random files
6
- raw_file_1 = SecureRandom.random_bytes(1024 * 20)
7
- raw_file_2 = SecureRandom.random_bytes(1024 * 128)
8
- raw_file_3 = SecureRandom.random_bytes(1258695)
9
-
10
- predicted_size = described_class.perform_fake_archiving do | estimator |
11
- r = estimator.add_stored_entry("first-file.bin", raw_file_1.size)
12
- expect(r).to eq(estimator), "add_stored_entry should return self"
13
-
14
- estimator.add_stored_entry("second-file.bin", raw_file_2.size)
15
-
16
- r = estimator.add_compressed_entry("second-flie.bin", raw_file_2.size, raw_file_3.size)
17
- expect(r).to eq(estimator), "add_compressed_entry should return self"
18
- end
19
-
20
- expect(predicted_size).to eq(1410585)
21
- end
22
- end