zip_tricks 2.6.0 → 2.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 33d767b5968d6743916c79ae85953e802a3f3e4a
4
- data.tar.gz: 30bcfd521a5db58cfea176d6b36c82c09609c485
3
+ metadata.gz: bbb6bf8e96402a401c0bd282c77f48adb8b6e797
4
+ data.tar.gz: 7fea8830d270a647f66edd6d89c2c04a4674c2f7
5
5
  SHA512:
6
- metadata.gz: 41b5f45f0ae560ca28386e939da7cbb4648fe34fe0c84acbff1a761786b797d1a17a30a33cf72201596783a0dfa307d8d7910089872d76aecaf8b10986a9ccd9
7
- data.tar.gz: 0539bde9484ba1cb6bf580cfbc252271a1498bdbca9703701d22333a67f6d495360558590e6eaf2e945742a46e63bbe0d519f32664bdc329908a1b80f6ad8b76
6
+ metadata.gz: f79662655eeccdaeede66019b345fa9274c264beedf48dbcb7098dcaceba10f86d881e5d27ce720c6c875adb5eb7c0d8352d79d9beb1dd4d320932896c164b34
7
+ data.tar.gz: 0070e683f68a0f72c07dee4992d2213e604d73ee01df0ca7dcbbcd55dc9ab6ff66a26f0a8ecf0098495575b2e9fbda8b52044fb51ee436d58f7e53d20159428a
@@ -27,7 +27,7 @@ module ZipTricks::BlockDeflate
27
27
  output_io << END_MARKER
28
28
  END_MARKER.bytesize
29
29
  end
30
-
30
+
31
31
  # Compress a given binary string and flush the deflate stream at byte boundary.
32
32
  # The returned string can be spliced into another deflate stream.
33
33
  #
@@ -40,11 +40,11 @@ module ZipTricks::BlockDeflate
40
40
  compressed_blob = z.deflate(bytes, Zlib::SYNC_FLUSH)
41
41
  compressed_blob << z.finish
42
42
  z.close
43
-
43
+
44
44
  # Remove the header (2 bytes), the [3,0] end marker and the adler (4 bytes)
45
45
  compressed_blob[2...-6]
46
46
  end
47
-
47
+
48
48
  # Compress the contents of input_io into output_io, in blocks
49
49
  # of block_size. Aligns the parts so that they can be concatenated later.
50
50
  # Writes deflate end marker (\x3\x0) into `output_io` as the final step, so
@@ -64,7 +64,7 @@ module ZipTricks::BlockDeflate
64
64
  bytes_written = deflate_in_blocks(input_io, output_io, level: level, block_size: block_size)
65
65
  bytes_written + write_terminator(output_io)
66
66
  end
67
-
67
+
68
68
  # Compress the contents of input_io into output_io, in blocks
69
69
  # of block_size. Align the parts so that they can be concatenated later.
70
70
  # Will not write the deflate end marker (\x3\x0) so more parts can be written
@@ -17,7 +17,7 @@ class ZipTricks::BlockWrite
17
17
  # Every time this object gets written to, call the Rack body each() block with the bytes given instead.
18
18
  def <<(buf)
19
19
  return if buf.nil?
20
-
20
+
21
21
  # Ensure we ALWAYS write in binary encoding.
22
22
  encoded = if buf.encoding != Encoding::BINARY
23
23
  # If we got a frozen string we can't force_encoding on it
@@ -25,14 +25,14 @@ class ZipTricks::BlockWrite
25
25
  else
26
26
  buf
27
27
  end
28
-
28
+
29
29
  # buf.dup.force_encoding(Encoding::BINARY)
30
30
  return if encoded.bytesize.zero? # Zero-size output has a special meaning when using chunked encoding
31
-
31
+
32
32
  @block.call(encoded)
33
33
  self
34
34
  end
35
-
35
+
36
36
  # Does nothing
37
37
  def close
38
38
  nil
@@ -1,10 +1,10 @@
1
1
  # Helps to estimate archive sizes
2
2
  class ZipTricks::Manifest < Struct.new(:zip_streamer, :io, :part_list)
3
-
3
+
4
4
  # Describes a span within the ZIP bytestream
5
5
  class ZipSpan < Struct.new(:part_type, :byte_range_in_zip, :filename, :additional_metadata)
6
6
  end
7
-
7
+
8
8
  # Builds an array of spans within the ZIP file and computes the size of the resulting archive in bytes.
9
9
  #
10
10
  # zip_spans, bytesize = Manifest.build do | b |
@@ -26,14 +26,14 @@ class ZipTricks::Manifest < Struct.new(:zip_streamer, :io, :part_list)
26
26
  yield(manifest)
27
27
  last_range_end = part_list[-1].byte_range_in_zip.end
28
28
  end
29
-
29
+
30
30
  # Record the position of the central directory
31
31
  directory_location = (last_range_end + 1)..(output_io.tell - 1)
32
32
  part_list << ZipSpan.new(:central_directory, directory_location, :central_directory, nil)
33
-
33
+
34
34
  [part_list, output_io.tell]
35
35
  end
36
-
36
+
37
37
  # Add a fake entry to the archive, to see how big it is going to be in the end.
38
38
  #
39
39
  # @param name [String] the name of the file (filenames are variable-width in the ZIP)
@@ -45,14 +45,14 @@ class ZipTricks::Manifest < Struct.new(:zip_streamer, :io, :part_list)
45
45
  register_part(:entry_header, name, segment_info) do
46
46
  zip_streamer.add_stored_entry(name, size_uncompressed, C_fake_crc)
47
47
  end
48
-
48
+
49
49
  register_part(:entry_body, name, segment_info) do
50
50
  zip_streamer.simulate_write(size_uncompressed)
51
51
  end
52
-
52
+
53
53
  self
54
54
  end
55
-
55
+
56
56
  # Add a fake entry to the archive, to see how big it is going to be in the end.
57
57
  #
58
58
  # @param name [String] the name of the file (filenames are variable-width in the ZIP)
@@ -65,19 +65,19 @@ class ZipTricks::Manifest < Struct.new(:zip_streamer, :io, :part_list)
65
65
  register_part(:entry_header, name, segment_info) do
66
66
  zip_streamer.add_compressed_entry(name, size_uncompressed, C_fake_crc, size_compressed)
67
67
  end
68
-
68
+
69
69
  register_part(:entry_body, name, segment_info) do
70
70
  zip_streamer.simulate_write(size_compressed)
71
71
  end
72
-
72
+
73
73
  self
74
74
  end
75
-
75
+
76
76
  private
77
-
77
+
78
78
  C_fake_crc = Zlib.crc32('Mary had a little lamb')
79
79
  private_constant :C_fake_crc
80
-
80
+
81
81
  def register_part(span_type, filename, metadata)
82
82
  before, _, after = io.tell, yield, (io.tell - 1)
83
83
  part_list << ZipSpan.new(span_type, (before..after), filename, metadata)
@@ -12,19 +12,19 @@ class ZipTricks::RackBody
12
12
  # content_length = ZipTricks::StoredSizeEstimator.perform_fake_archiving do | estimator |
13
13
  # estimator.add_stored_entry('large.tif', size=1289894)
14
14
  # end
15
- #
15
+ #
16
16
  # # Prepare the response body. The block will only be called when the response starts to be written.
17
17
  # body = ZipTricks::RackBody.new do | streamer |
18
18
  # streamer.add_stored_entry('large.tif', size=1289894, crc32=198210)
19
19
  # streamer << large_file.read(1024*1024) until large_file.eof?
20
20
  # ...
21
21
  # end
22
- #
22
+ #
23
23
  # return [200, {'Content-Type' => 'binary/octet-stream', 'Content-Length' => content_length.to_s}, body]
24
24
  def initialize(&blk)
25
25
  @archiving_block = blk
26
26
  end
27
-
27
+
28
28
  # Connects a {ZipTricks::BlockWrite} to the Rack webserver output,
29
29
  # and calls the proc given to the constructor with a {ZipTricks::Streamer}
30
30
  # for archive writing.
@@ -32,7 +32,7 @@ class ZipTricks::RackBody
32
32
  fake_io = ZipTricks::BlockWrite.new(&body_chunk_block)
33
33
  ZipTricks::Streamer.open(fake_io, &@archiving_block)
34
34
  end
35
-
35
+
36
36
  # Does nothing because nothing has to be deallocated or canceled
37
37
  # even if the zip output is incomplete. The archive gets closed
38
38
  # automatically as part of {ZipTricks::Streamer.open}
@@ -2,14 +2,14 @@
2
2
  # - or, more precisely, to be useful as a source for the RubyZip
3
3
  # central directory parser
4
4
  class ZipTricks::RemoteIO
5
-
5
+
6
6
  # @param fetcher[#request_object_size, #request_range] an object that can fetch
7
7
  def initialize(fetcher = :NOT_SET)
8
8
  @pos = 0
9
9
  @fetcher = fetcher
10
10
  @remote_size = false
11
11
  end
12
-
12
+
13
13
  # Emulates IO#seek
14
14
  def seek(offset, mode = IO::SEEK_SET)
15
15
  case mode
@@ -28,16 +28,17 @@ class ZipTricks::RemoteIO
28
28
  # Emulates IO#read
29
29
  def read(n_bytes = nil)
30
30
  @remote_size ||= request_object_size
31
-
31
+
32
32
  # If the resource is empty there is nothing to read
33
33
  return nil if @remote_size.zero?
34
-
34
+
35
35
  maximum_avaialable = @remote_size - @pos
36
36
  n_bytes ||= maximum_avaialable # nil == read to the end of file
37
+ return '' if n_bytes.zero?
37
38
  raise ArgumentError, "No negative reads(#{n_bytes})" if n_bytes < 0
38
-
39
+
39
40
  n_bytes = clamp(0, n_bytes, maximum_avaialable)
40
-
41
+
41
42
  read_n_bytes_from_remote(@pos, n_bytes).tap do |data|
42
43
  if data.bytesize != n_bytes
43
44
  raise "Remote read returned #{data.bytesize} bytes instead of #{n_bytes} as requested"
@@ -53,30 +54,30 @@ class ZipTricks::RemoteIO
53
54
  def pos
54
55
  @pos
55
56
  end
56
-
57
+
57
58
  protected
58
59
 
59
60
  def request_range(range)
60
61
  @fetcher.request_range(range)
61
62
  end
62
-
63
+
63
64
  def request_object_size
64
65
  @fetcher.request_object_size
65
66
  end
66
-
67
+
67
68
  # Reads N bytes at offset from remote
68
69
  def read_n_bytes_from_remote(start_at, n_bytes)
69
70
  range = (start_at..(start_at + n_bytes - 1))
70
71
  request_range(range)
71
72
  end
72
-
73
+
73
74
  # Reads the Content-Length and caches it
74
75
  def remote_size
75
76
  @remote_size ||= request_object_size
76
77
  end
77
-
78
+
78
79
  private
79
-
80
+
80
81
  def clamp(a,b,c)
81
82
  return a if b < a
82
83
  return c if b > c
@@ -3,59 +3,59 @@
3
3
  # offsets at which the actual file contents is located. You can then
4
4
  # use the `Range:` HTTP headers to download those entries separately.
5
5
  class ZipTricks::RemoteUncap
6
-
6
+
7
7
  # Represents a file embedded within a remote ZIP archive
8
8
  class RemoteZipEntry
9
-
9
+
10
10
  # @return [String] filename of the file in the remote ZIP
11
11
  attr_accessor :name
12
-
12
+
13
13
  # @return [Fixnum] size in bytes of the file when uncompressed
14
14
  attr_accessor :size_uncompressed
15
15
 
16
16
  # @return [Fixnum] size in bytes of the file when compressed (the segment in the ZIP)
17
17
  attr_accessor :size_compressed
18
-
18
+
19
19
  # @return [Fixnum] compression method (0 for stored, 8 for deflate)
20
20
  attr_accessor :compression_method
21
-
21
+
22
22
  # @return [Fixnum] where the file data starts within the ZIP
23
23
  attr_accessor :starts_at_offset
24
-
24
+
25
25
  # @return [Fixnum] where the file data ends within the zip.
26
26
  # Will be equal to starts_at_offset if the file is empty
27
27
  attr_accessor :ends_at_offset
28
-
28
+
29
29
  # Yields the object during initialization
30
30
  def initialize
31
31
  yield self
32
32
  end
33
33
  end
34
-
34
+
35
35
  # @param uri[String] the HTTP(S) URL to read the ZIP footer from
36
36
  # @return [Array<RemoteZipEntry>] metadata about the files within the remote archive
37
37
  def self.files_within_zip_at(uri)
38
38
  fetcher = new(uri)
39
39
  fake_io = ZipTricks::RemoteIO.new(fetcher)
40
40
  dir = Zip::CentralDirectory.read_from_stream(fake_io)
41
-
41
+
42
42
  dir.entries.map do | rubyzip_entry |
43
43
  RemoteZipEntry.new do | entry |
44
44
  entry.name = rubyzip_entry.name
45
45
  entry.size_uncompressed = rubyzip_entry.size
46
46
  entry.size_compressed = rubyzip_entry.compressed_size
47
47
  entry.compression_method = rubyzip_entry.compression_method
48
-
48
+
49
49
  entry.starts_at_offset = rubyzip_entry.local_header_offset + rubyzip_entry.calculate_local_header_size
50
50
  entry.ends_at_offset = entry.starts_at_offset + rubyzip_entry.compressed_size
51
51
  end
52
52
  end
53
53
  end
54
-
54
+
55
55
  def initialize(uri)
56
56
  @uri = URI(uri)
57
57
  end
58
-
58
+
59
59
  # @param range[Range] the HTTP range of data to fetch from remote
60
60
  # @return [String] the response body of the ranged request
61
61
  def request_range(range)
@@ -1,6 +1,6 @@
1
1
  # Helps to estimate archive sizes
2
2
  class ZipTricks::StoredSizeEstimator < Struct.new(:manifest)
3
-
3
+
4
4
  # Performs the estimate using fake archiving. It needs to know the sizes of the
5
5
  # entries upfront. Usage:
6
6
  #
@@ -20,7 +20,7 @@ class ZipTricks::StoredSizeEstimator < Struct.new(:manifest)
20
20
  end
21
21
  bytes
22
22
  end
23
-
23
+
24
24
  # Add a fake entry to the archive, to see how big it is going to be in the end.
25
25
  #
26
26
  # @param name [String] the name of the file (filenames are variable-width in the ZIP)
@@ -30,7 +30,7 @@ class ZipTricks::StoredSizeEstimator < Struct.new(:manifest)
30
30
  manifest.add_stored_entry(name: name, size_uncompressed: size_uncompressed)
31
31
  self
32
32
  end
33
-
33
+
34
34
  # Add a fake entry to the archive, to see how big it is going to be in the end.
35
35
  #
36
36
  # @param name [String] the name of the file (filenames are variable-width in the ZIP)
@@ -9,12 +9,12 @@ class ZipTricks::StreamCRC32
9
9
  crc << io.read(1024 * 512) until io.eof?
10
10
  crc.to_i
11
11
  end
12
-
12
+
13
13
  # Creates a new streaming CRC32 calculator
14
14
  def initialize
15
15
  @crc = Zlib.crc32('')
16
16
  end
17
-
17
+
18
18
  # Append data to the CRC32. Updates the contained CRC32 value in place.
19
19
  #
20
20
  # @param blob[String] the string to compute the CRC32 from
@@ -23,14 +23,14 @@ class ZipTricks::StreamCRC32
23
23
  @crc = Zlib.crc32_combine(@crc, Zlib.crc32(blob), blob.bytesize)
24
24
  self
25
25
  end
26
-
26
+
27
27
  # Returns the CRC32 value computed so far
28
28
  #
29
29
  # @return crc[Fixnum] the updated CRC32 value for all the blobs so far
30
30
  def to_i
31
31
  @crc
32
32
  end
33
-
33
+
34
34
  # Appends a known CRC32 value to the current one, and combines the
35
35
  # contained CRC32 value in-place.
36
36
  #
@@ -13,13 +13,13 @@
13
13
  class ZipTricks::Streamer
14
14
  EntryBodySizeMismatch = Class.new(StandardError)
15
15
  InvalidOutput = Class.new(ArgumentError)
16
-
16
+
17
17
  # Language encoding flag (EFS) bit (general purpose bit 11)
18
18
  EFS = 0b100000000000
19
-
19
+
20
20
  # Default general purpose flags for each entry.
21
21
  DEFAULT_GP_FLAGS = 0b00000000000
22
-
22
+
23
23
  # Creates a new Streamer on top of the given IO-ish object and yields it. Once the given block
24
24
  # returns, the Streamer will have it's `close` method called, which will write out the central
25
25
  # directory of the archive to the output.
@@ -31,7 +31,7 @@ class ZipTricks::Streamer
31
31
  yield(archive)
32
32
  archive.close
33
33
  end
34
-
34
+
35
35
  # Creates a new Streamer on top of the given IO-ish object.
36
36
  #
37
37
  # @param stream [IO] the destination IO for the ZIP (should respond to `tell` and `<<`)
@@ -39,7 +39,7 @@ class ZipTricks::Streamer
39
39
  raise InvalidOutput, "The stream should respond to #<<" unless stream.respond_to?(:<<)
40
40
  stream = ZipTricks::WriteAndTell.new(stream) unless stream.respond_to?(:tell) && stream.respond_to?(:advance_position_by)
41
41
  @output_stream = stream
42
-
42
+
43
43
  @state_monitor = VeryTinyStateMachine.new(:before_entry, callbacks_to=self)
44
44
  @state_monitor.permit_state :in_entry_header, :in_entry_body, :in_central_directory, :closed
45
45
  @state_monitor.permit_transition :before_entry => :in_entry_header
@@ -47,7 +47,7 @@ class ZipTricks::Streamer
47
47
  @state_monitor.permit_transition :in_entry_body => :in_entry_header
48
48
  @state_monitor.permit_transition :in_entry_body => :in_central_directory
49
49
  @state_monitor.permit_transition :in_central_directory => :closed
50
-
50
+
51
51
  @entry_set = ::Zip::EntrySet.new
52
52
  end
53
53
 
@@ -61,7 +61,7 @@ class ZipTricks::Streamer
61
61
  @bytes_written_for_entry += binary_data.bytesize
62
62
  self
63
63
  end
64
-
64
+
65
65
  # Advances the internal IO pointer to keep the offsets of the ZIP file in check. Use this if you are going
66
66
  # to use accelerated writes to the socket (like the `sendfile()` call) after writing the headers, or if you
67
67
  # just need to figure out the size of the archive.
@@ -74,7 +74,7 @@ class ZipTricks::Streamer
74
74
  @bytes_written_for_entry += num_bytes
75
75
  @output_stream.tell
76
76
  end
77
-
77
+
78
78
  # Writes out the local header for an entry (file in the ZIP) that is using the deflated storage model (is compressed).
79
79
  # Once this method is called, the `<<` method has to be called to write the actual contents of the body.
80
80
  #
@@ -88,21 +88,21 @@ class ZipTricks::Streamer
88
88
  # @return [Fixnum] the offset the output IO is at after writing the entry header
89
89
  def add_compressed_entry(entry_name, uncompressed_size, crc32, compressed_size)
90
90
  @state_monitor.transition! :in_entry_header
91
-
91
+
92
92
  entry = ::Zip::Entry.new(@file_name, entry_name)
93
93
  entry.compression_method = Zip::Entry::DEFLATED
94
94
  entry.crc = crc32
95
95
  entry.size = uncompressed_size
96
96
  entry.compressed_size = compressed_size
97
97
  set_gp_flags_for_filename(entry, entry_name)
98
-
98
+
99
99
  @entry_set << entry
100
100
  entry.write_local_entry(@output_stream)
101
101
  @expected_bytes_for_entry = compressed_size
102
102
  @bytes_written_for_entry = 0
103
103
  @output_stream.tell
104
104
  end
105
-
105
+
106
106
  # Writes out the local header for an entry (file in the ZIP) that is using the stored storage model (is stored as-is).
107
107
  # Once this method is called, the `<<` method has to be called one or more times to write the actual contents of the body.
108
108
  #
@@ -112,7 +112,7 @@ class ZipTricks::Streamer
112
112
  # @return [Fixnum] the offset the output IO is at after writing the entry header
113
113
  def add_stored_entry(entry_name, uncompressed_size, crc32)
114
114
  @state_monitor.transition! :in_entry_header
115
-
115
+
116
116
  entry = ::Zip::Entry.new(@file_name, entry_name)
117
117
  entry.compression_method = Zip::Entry::STORED
118
118
  entry.crc = crc32
@@ -125,8 +125,8 @@ class ZipTricks::Streamer
125
125
  @expected_bytes_for_entry = uncompressed_size
126
126
  @output_stream.tell
127
127
  end
128
-
129
-
128
+
129
+
130
130
  # Writes out the global footer and the directory entry header and the global directory of the ZIP
131
131
  # archive using the information about the entries added using `add_stored_entry` and `add_compressed_entry`.
132
132
  #
@@ -139,7 +139,7 @@ class ZipTricks::Streamer
139
139
  cdir.write_to_stream(@output_stream)
140
140
  @output_stream.tell
141
141
  end
142
-
142
+
143
143
  # Closes the archive. Writes the central directory if it has not yet been written.
144
144
  # Switches the Streamer into a state where it can no longer be written to.
145
145
  #
@@ -151,9 +151,9 @@ class ZipTricks::Streamer
151
151
  @state_monitor.transition! :closed
152
152
  @output_stream.tell
153
153
  end
154
-
154
+
155
155
  private
156
-
156
+
157
157
  # Set the general purpose flags for the entry. The only flag we care about is the EFS
158
158
  # bit (bit 11) which should be set if the filename is UTF8. If it is, we need to set the
159
159
  # bit so that the unarchiving application knows that the filename in the archive is UTF-8
@@ -164,7 +164,7 @@ class ZipTricks::Streamer
164
164
  rescue Encoding::UndefinedConversionError #=> UTF8 filename
165
165
  entry.gp_flags = DEFAULT_GP_FLAGS | EFS
166
166
  end
167
-
167
+
168
168
  # Checks whether the number of bytes written conforms to the declared entry size
169
169
  def leaving_in_entry_body_state
170
170
  if @bytes_written_for_entry != @expected_bytes_for_entry
@@ -5,7 +5,7 @@ class ZipTricks::WriteAndTell
5
5
  @io = io
6
6
  @pos = 0
7
7
  end
8
-
8
+
9
9
  def <<(bytes)
10
10
  return self if bytes.nil?
11
11
  binary_bytes = binary(bytes)
@@ -13,17 +13,17 @@ class ZipTricks::WriteAndTell
13
13
  @pos += binary_bytes.bytesize
14
14
  self
15
15
  end
16
-
16
+
17
17
  def advance_position_by(num_bytes)
18
18
  @pos += num_bytes
19
19
  end
20
-
20
+
21
21
  def tell
22
22
  @pos
23
23
  end
24
-
24
+
25
25
  private
26
-
26
+
27
27
  def binary(str)
28
28
  return str if str.encoding == Encoding::BINARY
29
29
  str.force_encoding(Encoding::BINARY)
data/lib/zip_tricks.rb CHANGED
@@ -2,7 +2,7 @@ require 'zip'
2
2
  require 'very_tiny_state_machine'
3
3
 
4
4
  module ZipTricks
5
- VERSION = '2.6.0'
5
+ VERSION = '2.6.1'
6
6
 
7
7
  # Require all the sub-components except myself
8
8
  Dir.glob(__dir__ + '/**/*.rb').sort.each {|p| require p unless p == __FILE__ }
@@ -4,7 +4,7 @@ describe ZipTricks::BlockDeflate do
4
4
  def tag_deflated(deflated_string, raw_string)
5
5
  [120, 156].pack("C*") + deflated_string + [3,0].pack("C*") + [Zlib.adler32(raw_string)].pack("N")
6
6
  end
7
-
7
+
8
8
  describe '.deflate_chunk' do
9
9
  it 'compresses a blob that can be inflated later, when the header, footer and adler32 are added' do
10
10
  blob = 'compressible' * 1024 * 4
@@ -13,26 +13,26 @@ describe ZipTricks::BlockDeflate do
13
13
  complete_deflated_segment = tag_deflated(compressed, blob)
14
14
  expect(Zlib.inflate(complete_deflated_segment)).to eq(blob)
15
15
  end
16
-
16
+
17
17
  it 'removes the header' do
18
18
  blob = 'compressible' * 1024 * 4
19
19
  compressed = described_class.deflate_chunk(blob)
20
20
  expect(compressed[0..1]).not_to eq([120, 156].pack("C*"))
21
21
  end
22
-
22
+
23
23
  it 'removes the adler32' do
24
24
  blob = 'compressible' * 1024 * 4
25
25
  compressed = described_class.deflate_chunk(blob)
26
26
  adler = [Zlib.adler32(blob)].pack("N")
27
27
  expect(compressed).not_to end_with(adler)
28
28
  end
29
-
29
+
30
30
  it 'removes the end marker' do
31
31
  blob = 'compressible' * 1024 * 4
32
32
  compressed = described_class.deflate_chunk(blob)
33
33
  expect(compressed[-7..-5]).not_to eq([3,0].pack("C*"))
34
34
  end
35
-
35
+
36
36
  it 'honors the compression level' do
37
37
  deflater = Zlib::Deflate.new
38
38
  expect(Zlib::Deflate).to receive(:new).with(2) { deflater }
@@ -40,7 +40,7 @@ describe ZipTricks::BlockDeflate do
40
40
  compressed = described_class.deflate_chunk(blob, level: 2)
41
41
  end
42
42
  end
43
-
43
+
44
44
  describe 'deflate_in_blocks_and_terminate' do
45
45
  it 'uses deflate_in_blocks' do
46
46
  data = 'compressible' * 1024 * 1024 * 10
@@ -50,7 +50,7 @@ describe ZipTricks::BlockDeflate do
50
50
  expect(described_class).to receive(:deflate_in_blocks).with(input, output, level: -1, block_size: block_size).and_call_original
51
51
  described_class.deflate_in_blocks_and_terminate(input, output, block_size: block_size)
52
52
  end
53
-
53
+
54
54
  it 'passes a custom compression level' do
55
55
  data = 'compressible' * 1024 * 1024 * 10
56
56
  input = StringIO.new(data)
@@ -58,7 +58,7 @@ describe ZipTricks::BlockDeflate do
58
58
  expect(described_class).to receive(:deflate_in_blocks).with(input, output, level: 9, block_size: 5242880).and_call_original
59
59
  described_class.deflate_in_blocks_and_terminate(input, output, level: Zlib::BEST_COMPRESSION)
60
60
  end
61
-
61
+
62
62
  it 'writes the end marker' do
63
63
  data = 'compressible' * 1024 * 1024 * 10
64
64
  input = StringIO.new(data)
@@ -67,7 +67,7 @@ describe ZipTricks::BlockDeflate do
67
67
  expect(output.string[-2..-1]).to eq([3,0].pack("C*"))
68
68
  end
69
69
  end
70
-
70
+
71
71
  describe '.write_terminator' do
72
72
  it 'writes the terminator and returns 2 for number of bytes written' do
73
73
  buf = double('IO')
@@ -75,35 +75,35 @@ describe ZipTricks::BlockDeflate do
75
75
  expect(described_class.write_terminator(buf)).to eq(2)
76
76
  end
77
77
  end
78
-
78
+
79
79
  describe '.deflate_in_blocks' do
80
80
  it 'honors the block size' do
81
81
  data = 'compressible' * 1024 * 1024 * 10
82
82
  input = StringIO.new(data)
83
83
  output = StringIO.new
84
84
  block_size = 1024 * 64
85
-
85
+
86
86
  num_chunks = (data.bytesize / block_size.to_f).ceil
87
87
  expect(described_class).to receive(:deflate_chunk).exactly(num_chunks).times.and_call_original
88
88
  expect(input).to receive(:read).with(block_size).exactly(num_chunks + 1).times.and_call_original
89
89
  expect(output).to receive(:<<).exactly(num_chunks).times.and_call_original
90
-
90
+
91
91
  described_class.deflate_in_blocks(input, output, block_size: block_size)
92
92
  end
93
-
93
+
94
94
  it 'does not write the end marker' do
95
95
  input_string = 'compressible' * 1024 * 1024 * 10
96
96
  output_string = ''
97
-
97
+
98
98
  described_class.deflate_in_blocks(StringIO.new(input_string), StringIO.new(output_string))
99
99
  expect(output_string).not_to be_empty
100
100
  expect(output_string).not_to end_with([3,0].pack("C*"))
101
101
  end
102
-
102
+
103
103
  it 'returns the number of bytes written' do
104
104
  input_string = 'compressible' * 1024 * 1024 * 10
105
105
  output_string = ''
106
-
106
+
107
107
  num_bytes = described_class.deflate_in_blocks(StringIO.new(input_string), StringIO.new(output_string))
108
108
  expect(num_bytes).to eq(245016)
109
109
  end