zip_tricks 2.6.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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