archive-zip 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. data/HACKING +25 -42
  2. data/NEWS +25 -0
  3. data/README +2 -2
  4. data/Rakefile +202 -0
  5. data/TODO +5 -0
  6. data/default.mspec +8 -0
  7. data/lib/archive/support/binary_stringio.rb +23 -0
  8. data/lib/archive/support/integer.rb +13 -0
  9. data/lib/archive/support/io-like.rb +3 -1
  10. data/lib/archive/support/ioextensions.rb +16 -0
  11. data/lib/archive/support/iowindow.rb +10 -18
  12. data/lib/archive/support/time.rb +2 -0
  13. data/lib/archive/support/zlib.rb +298 -71
  14. data/lib/archive/zip.rb +161 -139
  15. data/lib/archive/zip/codec.rb +2 -0
  16. data/lib/archive/zip/codec/deflate.rb +59 -11
  17. data/lib/archive/zip/codec/null_encryption.rb +75 -14
  18. data/lib/archive/zip/codec/store.rb +75 -26
  19. data/lib/archive/zip/codec/traditional_encryption.rb +146 -35
  20. data/lib/archive/zip/data_descriptor.rb +6 -4
  21. data/lib/archive/zip/entry.rb +184 -132
  22. data/lib/archive/zip/error.rb +2 -0
  23. data/lib/archive/zip/extra_field.rb +20 -6
  24. data/lib/archive/zip/extra_field/extended_timestamp.rb +141 -60
  25. data/lib/archive/zip/extra_field/raw.rb +70 -12
  26. data/lib/archive/zip/extra_field/unix.rb +58 -16
  27. data/lib/archive/zip/version.rb +6 -0
  28. data/spec/archive/zip/codec/deflate/compress/checksum_spec.rb +42 -0
  29. data/spec/archive/zip/codec/deflate/compress/close_spec.rb +44 -0
  30. data/spec/archive/zip/codec/deflate/compress/crc32_spec.rb +21 -0
  31. data/spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb +67 -0
  32. data/spec/archive/zip/codec/deflate/compress/new_spec.rb +37 -0
  33. data/spec/archive/zip/codec/deflate/compress/open_spec.rb +46 -0
  34. data/spec/archive/zip/codec/deflate/compress/write_spec.rb +109 -0
  35. data/spec/archive/zip/codec/deflate/decompress/checksum_spec.rb +18 -0
  36. data/spec/archive/zip/codec/deflate/decompress/close_spec.rb +33 -0
  37. data/spec/archive/zip/codec/deflate/decompress/crc32_spec.rb +18 -0
  38. data/spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb +67 -0
  39. data/spec/archive/zip/codec/deflate/decompress/new_spec.rb +14 -0
  40. data/spec/archive/zip/codec/deflate/decompress/open_spec.rb +27 -0
  41. data/spec/archive/zip/codec/deflate/fixtures/classes.rb +25 -0
  42. data/spec/archive/zip/codec/deflate/fixtures/compressed_file.bin +1 -0
  43. data/spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin +0 -0
  44. data/spec/archive/zip/codec/deflate/fixtures/raw_file.txt +10 -0
  45. data/spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb +33 -0
  46. data/spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb +14 -0
  47. data/spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb +27 -0
  48. data/spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb +24 -0
  49. data/spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb +25 -0
  50. data/spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb +57 -0
  51. data/spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb +21 -0
  52. data/spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb +33 -0
  53. data/spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb +14 -0
  54. data/spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb +27 -0
  55. data/spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb +26 -0
  56. data/spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb +50 -0
  57. data/spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb +29 -0
  58. data/spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb +29 -0
  59. data/spec/archive/zip/codec/null_encryption/fixtures/classes.rb +12 -0
  60. data/spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt +10 -0
  61. data/spec/archive/zip/codec/store/compress/close_spec.rb +33 -0
  62. data/spec/archive/zip/codec/store/compress/data_descriptor_spec.rb +68 -0
  63. data/spec/archive/zip/codec/store/compress/new_spec.rb +14 -0
  64. data/spec/archive/zip/codec/store/compress/open_spec.rb +27 -0
  65. data/spec/archive/zip/codec/store/compress/rewind_spec.rb +26 -0
  66. data/spec/archive/zip/codec/store/compress/seek_spec.rb +50 -0
  67. data/spec/archive/zip/codec/store/compress/tell_spec.rb +29 -0
  68. data/spec/archive/zip/codec/store/compress/write_spec.rb +29 -0
  69. data/spec/archive/zip/codec/store/decompress/close_spec.rb +33 -0
  70. data/spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb +68 -0
  71. data/spec/archive/zip/codec/store/decompress/new_spec.rb +14 -0
  72. data/spec/archive/zip/codec/store/decompress/open_spec.rb +27 -0
  73. data/spec/archive/zip/codec/store/decompress/read_spec.rb +24 -0
  74. data/spec/archive/zip/codec/store/decompress/rewind_spec.rb +25 -0
  75. data/spec/archive/zip/codec/store/decompress/seek_spec.rb +57 -0
  76. data/spec/archive/zip/codec/store/decompress/tell_spec.rb +21 -0
  77. data/spec/archive/zip/codec/store/fixtures/classes.rb +12 -0
  78. data/spec/archive/zip/codec/store/fixtures/raw_file.txt +10 -0
  79. data/spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb +64 -0
  80. data/spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb +18 -0
  81. data/spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb +39 -0
  82. data/spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb +126 -0
  83. data/spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb +38 -0
  84. data/spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb +82 -0
  85. data/spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb +25 -0
  86. data/spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb +64 -0
  87. data/spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb +18 -0
  88. data/spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb +39 -0
  89. data/spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb +41 -0
  90. data/spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb +75 -0
  91. data/spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb +42 -0
  92. data/spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb +127 -0
  93. data/spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb +27 -0
  94. data/spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin +0 -0
  95. data/spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt +10 -0
  96. data/spec/binary_stringio/new_spec.rb +34 -0
  97. data/spec/binary_stringio/set_encoding_spec.rb +14 -0
  98. data/spec/ioextensions/read_exactly_spec.rb +50 -0
  99. data/spec/zlib/fixtures/classes.rb +65 -0
  100. data/spec/zlib/fixtures/compressed_file.bin +1 -0
  101. data/spec/zlib/fixtures/compressed_file_gzip.bin +0 -0
  102. data/spec/zlib/fixtures/compressed_file_huffman.bin +2 -0
  103. data/spec/zlib/fixtures/compressed_file_minmem.bin +0 -0
  104. data/spec/zlib/fixtures/compressed_file_minwin.bin +1 -0
  105. data/spec/zlib/fixtures/compressed_file_nocomp.bin +0 -0
  106. data/spec/zlib/fixtures/compressed_file_raw.bin +1 -0
  107. data/spec/zlib/fixtures/raw_file.txt +10 -0
  108. data/spec/zlib/zreader/checksum_spec.rb +40 -0
  109. data/spec/zlib/zreader/close_spec.rb +14 -0
  110. data/spec/zlib/zreader/compressed_size_spec.rb +18 -0
  111. data/spec/zlib/zreader/new_spec.rb +41 -0
  112. data/spec/zlib/zreader/open_spec.rb +49 -0
  113. data/spec/zlib/zreader/read_spec.rb +47 -0
  114. data/spec/zlib/zreader/rewind_spec.rb +23 -0
  115. data/spec/zlib/zreader/seek_spec.rb +55 -0
  116. data/spec/zlib/zreader/tell_spec.rb +21 -0
  117. data/spec/zlib/zreader/uncompressed_size_spec.rb +18 -0
  118. data/spec/zlib/zwriter/checksum_spec.rb +41 -0
  119. data/spec/zlib/zwriter/close_spec.rb +14 -0
  120. data/spec/zlib/zwriter/compressed_size_spec.rb +19 -0
  121. data/spec/zlib/zwriter/new_spec.rb +64 -0
  122. data/spec/zlib/zwriter/open_spec.rb +68 -0
  123. data/spec/zlib/zwriter/rewind_spec.rb +26 -0
  124. data/spec/zlib/zwriter/seek_spec.rb +54 -0
  125. data/spec/zlib/zwriter/tell_spec.rb +29 -0
  126. data/spec/zlib/zwriter/uncompressed_size_spec.rb +19 -0
  127. data/spec/zlib/zwriter/write_spec.rb +28 -0
  128. data/spec_helper.rb +49 -0
  129. metadata +296 -74
  130. data/MANIFEST +0 -27
  131. data/lib/archive/support/io.rb +0 -14
  132. data/lib/archive/support/stringio.rb +0 -22
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  class Time
2
4
  # Returns a DOSTime representing this time object as a DOS date-time
3
5
  # structure. Times are bracketed by the limits of the ability of the DOS
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'zlib'
2
4
 
3
5
  require 'archive/support/io-like'
@@ -7,6 +9,14 @@ module Zlib # :nodoc:
7
9
  MAX_WBITS = Deflate::MAX_WBITS
8
10
  end
9
11
 
12
+ # A deflate strategy which limits match distances to 1, also known as
13
+ # run-length encoding.
14
+ RLE = 3
15
+
16
+ # A deflate strategy which does not use dynamic Huffman codes, allowing for a
17
+ # simpler decoder to be used to inflate.
18
+ FIXED = 4
19
+
10
20
  # Zlib::ZWriter is a writable, IO-like object (includes IO::Like) which wraps
11
21
  # other writable, IO-like objects in order to facilitate writing data to those
12
22
  # objects using the deflate method of compression.
@@ -18,8 +28,8 @@ module Zlib # :nodoc:
18
28
  # guaranteed to be called after the block completes.
19
29
  #
20
30
  # Equivalent to #new if no block is given.
21
- def self.open(io, level = Zlib::DEFAULT_COMPRESSION, window_bits = nil, mem_level = nil, strategy = nil)
22
- zw = new(io, level, window_bits, mem_level, strategy)
31
+ def self.open(delegate, level = nil, window_bits = nil, mem_level = nil, strategy = nil)
32
+ zw = new(delegate, level, window_bits, mem_level, strategy)
23
33
  return zw unless block_given?
24
34
 
25
35
  begin
@@ -29,28 +39,112 @@ module Zlib # :nodoc:
29
39
  end
30
40
  end
31
41
 
32
- # Creates a new instance of this class. _io_ must respond to the _write_
33
- # method as an instance of IO would. _level_, _window_bits_, _mem_level_,
34
- # and _strategy_ are all passed directly to Zlib::Deflate.new(). See the
35
- # documentation of that method for their meanings.
42
+ # Creates a new instance of this class. _delegate_ must respond to the
43
+ # _write_ method as an instance of IO would. _level_, _window_bits_,
44
+ # _mem_level_, and _strategy_ are all passed directly to
45
+ # Zlib::Deflate.new().
46
+ #
47
+ # <b>
48
+ # The following descriptions of _level_, _window_bits_, _mem_level_, and
49
+ # _strategy_ are based upon or pulled largely verbatim from descriptions
50
+ # found in zlib.h version 1.2.3 with changes made to account for different
51
+ # parameter names and to improve readability. Some of the statements
52
+ # concerning default settings or value ranges may not be accurate depending
53
+ # on the version of the zlib library used by a given Ruby interpreter.
54
+ # </b>
55
+ #
56
+ # The _level_ parameter must be +nil+, Zlib::DEFAULT_COMPRESSION, or between
57
+ # <tt>0</tt> and <tt>9</tt>: <tt>1</tt> gives best speed, <tt>9</tt> gives
58
+ # best compression, <tt>0</tt> gives no compression at all (the input data
59
+ # is simply copied a block at a time). Zlib::DEFAULT_COMPRESSION requests a
60
+ # default compromise between speed and compression (currently equivalent to
61
+ # level <tt>6</tt>). If unspecified or +nil+, _level_ defaults to
62
+ # Zlib::DEFAULT_COMPRESSION.
63
+ #
64
+ # The _window_bits_ parameter specifies the size of the history buffer, the
65
+ # format of the compressed stream, and the kind of checksum returned by the
66
+ # checksum method. The size of the history buffer is specified by setting
67
+ # the value of _window_bits_ in the range of <tt>8</tt>..<tt>15</tt>,
68
+ # inclusive. A value of <tt>8</tt> indicates a small window which reduces
69
+ # memory usage but lowers the compression ratio while a value of <tt>15</tt>
70
+ # indicates a larger window which increases memory usage but raises the
71
+ # compression ratio. Modification of this base value for _window_bits_ as
72
+ # noted below dictates what kind of compressed stream and checksum will be
73
+ # produced <b>while preserving the setting for the history buffer</b>.
74
+ #
75
+ # If nothing else is done to the base value of _window_bits_, a zlib stream
76
+ # is to be produced with an appropriate header and trailer. In this case
77
+ # the checksum method of this object will be an adler32.
78
+ #
79
+ # Adding <tt>16</tt> to the base value of _window_bits_ indicates that a
80
+ # gzip stream is to be produced with an appropriate header and trailer. The
81
+ # gzip header will have no file name, no extra data, no comment, no
82
+ # modification time (set to zero), no header crc, and the operating system
83
+ # will be set to <tt>255</tt> (unknown). In this case the checksum
84
+ # attribute of this object will be a crc32.
85
+ #
86
+ # Finally, negating the base value of _window_bits_ indicates that a raw
87
+ # zlib stream is to be produced without any header or trailer. In this case
88
+ # the checksum method of this object will always return <tt>1</tt>. This is
89
+ # for use with other formats that use the deflate compressed data format
90
+ # such as zip. Such formats should provide their own check values.
91
+ #
92
+ # If unspecified or +nil+, _window_bits_ defaults to <tt>15</tt>.
93
+ #
94
+ # The _mem_level_ parameter specifies how much memory should be allocated
95
+ # for the internal compression state. A value of <tt>1</tt> uses minimum
96
+ # memory but is slow and reduces compression ratio; a value of <tt>9</tt>
97
+ # uses maximum memory for optimal speed. The default value is <tt>8</tt> if
98
+ # unspecified or +nil+.
99
+ #
100
+ # The _strategy_ parameter is used to tune the compression algorithm. It
101
+ # only affects the compression ratio but not the correctness of the
102
+ # compressed output even if it is not set appropriately. The default value
103
+ # is Zlib::DEFAULT_STRATEGY if unspecified or +nil+.
104
+ #
105
+ # Use the value Zlib::DEFAULT_STRATEGY for normal data, Zlib::FILTERED for
106
+ # data produced by a filter (or predictor), Zlib::HUFFMAN_ONLY to force
107
+ # Huffman encoding only (no string match), Zlib::RLE to limit match
108
+ # distances to 1 (run-length encoding), or Zlib::FIXED to simplify decoder
109
+ # requirements.
110
+ #
111
+ # The effect of Zlib::FILTERED is to force more Huffman coding and less
112
+ # string matching; it is somewhat intermediate between
113
+ # Zlib::DEFAULT_STRATEGY and Zlib::HUFFMAN_ONLY. Filtered data consists
114
+ # mostly of small values with a somewhat random distribution. In this case,
115
+ # the compression algorithm is tuned to compress them better.
116
+ #
117
+ # Zlib::RLE is designed to be almost as fast as Zlib::HUFFMAN_ONLY, but give
118
+ # better compression for PNG image data.
119
+ #
120
+ # Zlib::FIXED prevents the use of dynamic Huffman codes, allowing for a
121
+ # simpler decoder for special applications.
122
+ #
123
+ # This class has extremely limited seek capabilities. It is possible to
124
+ # seek with an offset of <tt>0</tt> and a whence of <tt>IO::SEEK_CUR</tt>.
125
+ # As a result, the _pos_ and _tell_ methods also work as expected.
126
+ #
127
+ # If _delegate_ also responds to _rewind_, then the _rewind_ method of this
128
+ # class can be used to reset the whole stream back to the beginning. Using
129
+ # _seek_ of this class to seek directly to offset <tt>0</tt> using
130
+ # <tt>IO::SEEK_SET</tt> for whence will also work in this case.
36
131
  #
37
132
  # <b>NOTE:</b> Due to limitations in Ruby's finalization capabilities, the
38
133
  # #close method is _not_ automatically called when this object is garbage
39
134
  # collected. Make sure to call #close when finished with this object.
40
- def initialize(io, level = Zlib::DEFAULT_COMPRESSION, window_bits = nil, mem_level = nil, strategy = nil)
41
- @delegate = io
42
- @deflater = Zlib::Deflate.new(level, window_bits, mem_level, strategy)
135
+ def initialize(delegate, level = nil, window_bits = nil, mem_level = nil, strategy = nil)
136
+ @delegate = delegate
137
+ @level = level
138
+ @window_bits = window_bits
139
+ @mem_level = mem_level
140
+ @strategy = strategy
141
+ @deflater = Zlib::Deflate.new(@level, @window_bits, @mem_level, @strategy)
43
142
  @deflate_buffer = ''
44
- @crc32 = 0
143
+ @checksum = nil
144
+ @compressed_size = nil
145
+ @uncompressed_size = nil
45
146
  end
46
147
 
47
- # The CRC32 checksum of the uncompressed data written using this object.
48
- #
49
- # <b>NOTE:</b> Anything still in the internal write buffer has not been
50
- # processed, so calling #flush prior to examining this attribute may be
51
- # necessary for an accurate computation.
52
- attr_reader :crc32
53
-
54
148
  protected
55
149
 
56
150
  # The delegate object to which compressed data is written.
@@ -58,49 +152,100 @@ module Zlib # :nodoc:
58
152
 
59
153
  public
60
154
 
155
+ # Returns the checksum computed over the data written to this stream so far.
156
+ #
157
+ # <b>NOTE:</b> Refer to the documentation of #new concerning _window_bits_
158
+ # to learn what kind of checksum will be returned.
159
+ #
160
+ # <b>NOTE:</b> Anything still in the internal write buffer has not been
161
+ # processed, so calling #flush prior to calling this method may be necessary
162
+ # for an accurate checksum.
163
+ def checksum
164
+ @deflater.closed? ? @checksum : @deflater.adler
165
+ end
166
+
61
167
  # Closes the writer by finishing the compressed data and flushing it to the
62
168
  # delegate.
63
169
  #
64
170
  # Raises IOError if called more than once.
65
171
  def close
172
+ flush()
173
+ @deflate_buffer << @deflater.finish unless @deflater.finished?
174
+ begin
175
+ until @deflate_buffer.empty? do
176
+ @deflate_buffer.slice!(0, delegate.write(@deflate_buffer))
177
+ end
178
+ rescue Errno::EAGAIN, Errno::EINTR
179
+ retry if write_ready?
180
+ end
181
+ @checksum = @deflater.adler
182
+ @compressed_size = @deflater.total_out
183
+ @uncompressed_size = @deflater.total_in
184
+ @deflater.close
66
185
  super()
67
- delegate.write(@deflater.finish)
68
186
  nil
69
187
  end
70
188
 
71
189
  # Returns the number of bytes of compressed data produced so far.
72
190
  #
73
- # <b>NOTE:</b> Anything still in the internal write buffer has not been
74
- # processed, so calling #flush prior to calling this method may be necessary
75
- # for an accurate count.
191
+ # <b>NOTE:</b> This value is only updated when both the internal write
192
+ # buffer is flushed and there is enough data to produce a compressed block.
193
+ # It does not necessarily reflect the amount of data written to the
194
+ # delegate until this stream is closed however. Until then the only
195
+ # guarantee is that the value will be greater than or equal to <tt>0</tt>.
76
196
  def compressed_size
77
- @deflater.total_out
197
+ @deflater.closed? ? @compressed_size : @deflater.total_out
78
198
  end
79
199
 
80
200
  # Returns the number of bytes sent to be compressed so far.
81
201
  #
82
- # <b>NOTE:</b> Anything still in the internal write buffer has not been
83
- # processed, so calling #flush prior to calling this method may be necessary
84
- # for an accurate count.
202
+ # <b>NOTE:</b> This value is only updated when the internal write buffer is
203
+ # flushed.
85
204
  def uncompressed_size
86
- @deflater.total_in
205
+ @deflater.closed? ? @uncompressed_size : @deflater.total_in
87
206
  end
88
207
 
89
208
  private
90
209
 
210
+ # Allows resetting this object and the delegate object back to the beginning
211
+ # of the stream or reporting the current position in the stream.
212
+ #
213
+ # Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is either
214
+ # IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ is
215
+ # IO::SEEK_SEK and the delegate object does not respond to the _rewind_
216
+ # method.
217
+ def unbuffered_seek(offset, whence = IO::SEEK_SET)
218
+ unless offset == 0 &&
219
+ ((whence == IO::SEEK_SET && delegate.respond_to?(:rewind)) ||
220
+ whence == IO::SEEK_CUR) then
221
+ raise Errno::EINVAL
222
+ end
223
+
224
+ case whence
225
+ when IO::SEEK_SET
226
+ delegate.rewind
227
+ @deflater.finish
228
+ @deflater.close
229
+ @deflater = Zlib::Deflate.new(
230
+ @level, @window_bits, @mem_level, @strategy
231
+ )
232
+ @deflate_buffer = ''
233
+ 0
234
+ when IO::SEEK_CUR
235
+ @deflater.total_in
236
+ end
237
+ end
238
+
91
239
  def unbuffered_write(string)
240
+ # First try to write out the contents of the deflate buffer because if
241
+ # that raises a failure we can let that pass up the call stack without
242
+ # having polluted the deflater instance.
92
243
  until @deflate_buffer.empty? do
93
244
  @deflate_buffer.slice!(0, delegate.write(@deflate_buffer))
94
245
  end
246
+ # At this point we can deflate the given string into a new buffer and
247
+ # behave as if it was written.
95
248
  @deflate_buffer = @deflater.deflate(string)
96
-
97
- begin
98
- @deflate_buffer.slice!(0, delegate.write(@deflate_buffer))
99
- rescue Errno::EINTR, Errno::EAGAIN
100
- # Ignore this because everything is in the deflate buffer and will be
101
- # attempted again the next time this method is called.
102
- end
103
- @crc32 = Zlib.crc32(string, @crc32)
104
249
  string.length
105
250
  end
106
251
  end
@@ -120,8 +265,8 @@ module Zlib # :nodoc:
120
265
  # guaranteed to be called after the block completes.
121
266
  #
122
267
  # Equivalent to #new if no block is given.
123
- def self.open(io, window_bits = nil)
124
- zr = new(io, window_bits)
268
+ def self.open(delegate, window_bits = nil)
269
+ zr = new(delegate, window_bits)
125
270
  return zr unless block_given?
126
271
 
127
272
  begin
@@ -131,31 +276,82 @@ module Zlib # :nodoc:
131
276
  end
132
277
  end
133
278
 
134
- # Creates a new instance of this class. _io_ must respond to the _read_
135
- # method as an IO instance would. _window_bits_ is passed directly to
136
- # Zlib::Inflate.new(). See the documentation of that method for its
137
- # meaning. If _io_ also responds to _rewind_, then the _rewind_ method of
138
- # this class can be used to reset the whole stream back to the beginning.
279
+ # Creates a new instance of this class. _delegate_ must respond to the
280
+ # _read_ method as an IO instance would. _window_bits_ is passed directly
281
+ # to Zlib::Inflate.new().
282
+ #
283
+ # <b>
284
+ # The following description of _window_bits_ is based on the description
285
+ # found in zlib.h version 1.2.3. Some of the statements concerning default
286
+ # settings or value ranges may not be accurate depending on the version of
287
+ # the zlib library used by a given Ruby interpreter.
288
+ # </b>
289
+ #
290
+ # The _window_bits_ parameter specifies the size of the history buffer, the
291
+ # format of the compressed stream, and the kind of checksum returned by the
292
+ # checksum method. The size of the history buffer is specified by setting
293
+ # the value of _window_bits_ in the range of <tt>8</tt>..<tt>15</tt>,
294
+ # inclusive. It must be at least as large as the setting used to create the
295
+ # stream or a Zlib::DataError will be raised. Modification of this base
296
+ # value for _window_bits_ as noted below dictates what kind of compressed
297
+ # stream is expected and what kind of checksum will be produced <b>while
298
+ # preserving the setting for the history buffer</b>.
299
+ #
300
+ # If nothing else is done to the base value of _window_bits_, a zlib stream
301
+ # is expected with an appropriate header and trailer. In this case the
302
+ # checksum method of this object will be an adler32.
303
+ #
304
+ # Adding <tt>16</tt> to the base value of _window_bits_ indicates that a
305
+ # gzip stream is expected with an appropriate header and trailer. In this
306
+ # case the checksum method of this object will be a crc32.
307
+ #
308
+ # Adding <tt>32</tt> to the base value of _window_bits_ indicates that an
309
+ # automatic detection of the stream format should be made based on the
310
+ # header in the stream. In this case the checksum method of this object
311
+ # will depend on whether a zlib or a gzip stream is detected.
312
+ #
313
+ # Finally, negating the base value of _window_bits_ indicates that a raw
314
+ # zlib stream is expected without any header or trailer. In this case the
315
+ # checksum method of this object will always return <tt>1</tt>. This is for
316
+ # use with other formats that use the deflate compressed data format such as
317
+ # zip. Such formats should provide their own check values.
318
+ #
319
+ # If unspecified or +nil+, _window_bits_ defaults to <tt>15</tt>.
320
+ #
321
+ # In all cases, Zlib::DataError is raised if the wrong stream format is
322
+ # found <b>when reading</b>.
323
+ #
324
+ # This class has extremely limited seek capabilities. It is possible to
325
+ # seek with an offset of <tt>0</tt> and a whence of <tt>IO::SEEK_CUR</tt>.
326
+ # As a result, the _pos_ and _tell_ methods also work as expected.
327
+ #
328
+ # Due to certain optimizations within IO::Like#seek and if there is data in
329
+ # the read buffer, the _seek_ method can be used to seek forward from the
330
+ # current stream position up to the end of the buffer. Unless it is known
331
+ # definitively how much data is in the buffer, it is best to avoid relying
332
+ # on this behavior.
333
+ #
334
+ # If _delegate_ also responds to _rewind_, then the _rewind_ method of this
335
+ # class can be used to reset the whole stream back to the beginning. Using
336
+ # _seek_ of this class to seek directly to offset <tt>0</tt> using
337
+ # <tt>IO::SEEK_SET</tt> for whence will also work in this case.
338
+ #
339
+ # Any other seeking attempts, will raise Errno::EINVAL exceptions.
139
340
  #
140
341
  # <b>NOTE:</b> Due to limitations in Ruby's finalization capabilities, the
141
342
  # #close method is _not_ automatically called when this object is garbage
142
343
  # collected. Make sure to call #close when finished with this object.
143
- def initialize(io, window_bits = nil)
144
- @delegate = io
344
+ def initialize(delegate, window_bits = nil)
345
+ @delegate = delegate
145
346
  @delegate_read_size = DEFAULT_DELEGATE_READ_SIZE
146
347
  @window_bits = window_bits
147
348
  @inflater = Zlib::Inflate.new(@window_bits)
148
- @crc32 = 0
149
- @decompress_buffer = ''
349
+ @inflate_buffer = ''
350
+ @checksum = nil
351
+ @compressed_size = nil
352
+ @uncompressed_size = nil
150
353
  end
151
354
 
152
- # The CRC32 checksum of the uncompressed data read using this object.
153
- #
154
- # <b>NOTE:</b> The contents of the internal read buffer are immediately
155
- # processed any time the buffer is filled, so this count is only accurate if
156
- # all data has been read out of this object.
157
- attr_reader :crc32
158
-
159
355
  # The number of bytes to read from the delegate object each time the
160
356
  # internal read buffer is filled.
161
357
  attr_accessor :delegate_read_size
@@ -167,57 +363,88 @@ module Zlib # :nodoc:
167
363
 
168
364
  public
169
365
 
366
+ # Returns the checksum computed over the data read from this stream.
367
+ #
368
+ # <b>NOTE:</b> Refer to the documentation of #new concerning _window_bits_
369
+ # to learn what kind of checksum will be returned.
370
+ #
371
+ # <b>NOTE:</b> The contents of the internal read buffer are immediately
372
+ # processed any time the internal buffer is filled, so this checksum is only
373
+ # accurate if all data has been read out of this object.
374
+ def checksum
375
+ @inflater.closed? ? @checksum : @inflater.adler
376
+ end
377
+
170
378
  # Closes the reader.
171
379
  #
172
380
  # Raises IOError if called more than once.
173
381
  def close
174
382
  super()
383
+ @checksum = @inflater.adler
384
+ @compressed_size = @inflater.total_in
385
+ @uncompressed_size = @inflater.total_out
175
386
  @inflater.close
176
387
  nil
177
388
  end
178
389
 
179
- # Returns the number of bytes sent to be decompressed so far.
390
+ # Returns the number of bytes sent to be compressed so far.
391
+ #
392
+ # <b>NOTE:</b> This value is updated whenever the internal read buffer needs
393
+ # to be filled, not when data is read out of this stream.
180
394
  def compressed_size
181
- @inflater.total_in
395
+ @inflater.closed? ? @compressed_size : @inflater.total_in
182
396
  end
183
397
 
184
398
  # Returns the number of bytes of decompressed data produced so far.
399
+ #
400
+ # <b>NOTE:</b> This value is updated whenever the internal read buffer needs
401
+ # to be filled, not when data is read out of this stream.
185
402
  def uncompressed_size
186
- @inflater.total_out
403
+ @inflater.closed? ? @uncompressed_size : @inflater.total_out
187
404
  end
188
405
 
189
406
  private
190
407
 
191
408
  def unbuffered_read(length)
192
- if @decompress_buffer.empty? && @inflater.finished? then
409
+ if @inflate_buffer.empty? && @inflater.finished? then
193
410
  raise EOFError, 'end of file reached'
194
411
  end
195
412
 
196
413
  begin
197
- while @decompress_buffer.length < length && ! @inflater.finished? do
198
- @decompress_buffer <<
414
+ while @inflate_buffer.length < length && ! @inflater.finished? do
415
+ @inflate_buffer <<
199
416
  @inflater.inflate(delegate.read(@delegate_read_size))
200
417
  end
201
418
  rescue Errno::EINTR, Errno::EAGAIN
202
- raise if @decompress_buffer.empty?
419
+ raise if @inflate_buffer.empty?
203
420
  end
204
- buffer = @decompress_buffer.slice!(0, length)
205
- @crc32 = Zlib.crc32(buffer, @crc32)
206
- buffer
421
+ @inflate_buffer.slice!(0, length)
207
422
  end
208
423
 
424
+ # Allows resetting this object and the delegate object back to the beginning
425
+ # of the stream or reporting the current position in the stream.
426
+ #
427
+ # Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is either
428
+ # IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ is
429
+ # IO::SEEK_SEK and the delegate object does not respond to the _rewind_
430
+ # method.
209
431
  def unbuffered_seek(offset, whence = IO::SEEK_SET)
210
- unless offset == 0 && whence == IO::SEEK_SET then
211
- raise Errno::EINVAL, 'Invalid argument'
432
+ unless offset == 0 &&
433
+ ((whence == IO::SEEK_SET && delegate.respond_to?(:rewind)) ||
434
+ whence == IO::SEEK_CUR) then
435
+ raise Errno::EINVAL
212
436
  end
213
- unless delegate.respond_to?(:rewind) then
214
- raise Errno::ESPIPE, 'Illegal seek'
437
+
438
+ case whence
439
+ when IO::SEEK_SET
440
+ delegate.rewind
441
+ @inflater.close
442
+ @inflater = Zlib::Inflate.new(@window_bits)
443
+ @inflate_buffer = ''
444
+ 0
445
+ when IO::SEEK_CUR
446
+ @inflater.total_out - @inflate_buffer.length
215
447
  end
216
- delegate.rewind
217
- @inflater.close
218
- @inflater = Zlib::Inflate.new(@window_bits)
219
- @crc32 = 0
220
- @decompress_buffer = ''
221
448
  end
222
449
  end
223
450
  end