dalli 5.3.1 → 5.3.2

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
  SHA256:
3
- metadata.gz: ecf53cb1683b72cd89df9074f442a040de892003a6f39254e4958654a9f430a7
4
- data.tar.gz: 46b400e8a5cb63261af72340de184bf7f1188a7b5fc2454a052af13e8ff62167
3
+ metadata.gz: 01e6a1f1483a28b5913847f35280e7c4cf2f490f01ddda3607ba1cf7687e3081
4
+ data.tar.gz: 0a4c8e31094f2d0fabc9a17062765d7e5293ec29ed338fabca6c93076d4ea224
5
5
  SHA512:
6
- metadata.gz: 518451bbab10db4e47140cd9b716c1845ca1b1be08f95e5e5996d4f6dc655f7a9bfd2a40e51c968cdaa45eaa3355518d41a0e7c36a37367d2c387bc9dcb18bed
7
- data.tar.gz: 84c9fa710a82fb7f444f50b84820e43c7697050fd70376117d6d9daa30f905d159bd1639e29762c027391b8521361888075b47df476f6cfb59ddcb0e76496f78
6
+ metadata.gz: 1fa29fe92cc42e648f5752c79a185466e24614283d0ebed7f8a12a5b3ab2f98806065c1989c403144b10809ad33baaa4033c66f140e80500c4ecb9c3a6b6a41b
7
+ data.tar.gz: be9a46942962a98dc2d14d9058f48c0b78c22c245b447e7578b030aa941e286a67941516e0d8a9d377167fd9fcbb3960865c3c118e2f3e8ba88ba1aece683914
data/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ Dalli Changelog
4
4
  Unreleased
5
5
  ==========
6
6
 
7
+ 5.3.2
8
+ ==========
9
+
10
+ Bug fixes:
11
+
12
+ - A signal arriving while `Dalli::Compressor` or `Dalli::GzipCompressor` was compressing or decompressing could make Ruby's zlib return before it had finished (ruby/zlib#49). Depending on the Ruby and zlib gem versions, that raised `Zlib::BufError` or `Zlib::DataError`, or stored a truncated value that later failed to decompress. Both compressors now keep finishing the stream until zlib reports its end. `GzipCompressor` no longer goes through `GzipWriter` and `GzipReader`, so it now always returns decompressed values as binary strings, as `Compressor` does (#1299)
13
+
7
14
  5.3.1
8
15
  ==========
9
16
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'zlib'
4
- require 'stringio'
5
4
 
6
5
  module Dalli
7
6
  ##
@@ -10,7 +9,7 @@ module Dalli
10
9
  ##
11
10
  class Compressor
12
11
  def self.compress(data)
13
- Zlib::Deflate.deflate(data)
12
+ deflate_fully(Zlib::Deflate.new, data)
14
13
  end
15
14
 
16
15
  # DEFLATE can't expand data by more than about 1032:1
@@ -24,11 +23,51 @@ module Dalli
24
23
  # as the output passes it, so a small compressed value can't expand into
25
24
  # gigabytes of memory.
26
25
  def self.decompress(data, max_bytes: nil)
27
- return Zlib::Inflate.inflate(data) unless may_exceed?(data, max_bytes)
26
+ return inflate_fully(Zlib::Inflate.new, data) unless may_exceed?(data, max_bytes)
28
27
 
29
28
  inflate_within_limit(Zlib::Inflate.new, data, max_bytes)
30
29
  end
31
30
 
31
+ # Ruby's zlib extension can return from a call before zlib has finished
32
+ # when a signal interrupts it (ruby/zlib#49). One-shot calls like
33
+ # Zlib::Deflate.deflate then raise or, with zlib 3.2.4, return truncated
34
+ # output, so these call finish until the stream has ended.
35
+ def self.deflate_fully(deflater, data)
36
+ finish_stream(deflater, deflater.deflate(data, Zlib::FINISH))
37
+ ensure
38
+ deflater.close
39
+ end
40
+
41
+ def self.inflate_fully(inflater, data)
42
+ finish_stream(inflater, feed(inflater, data) || String.new(encoding: Encoding::BINARY))
43
+ ensure
44
+ inflater.close
45
+ end
46
+
47
+ # zlib gem 3.2.3 and earlier retry an interrupted inflate even when it has
48
+ # used up its input, and raise the Z_BUF_ERROR zlib returns for that. The
49
+ # output so far stays in the stream, and finish raises for input that is
50
+ # truly truncated.
51
+ def self.feed(inflater, data, &)
52
+ inflater.inflate(data, &)
53
+ rescue Zlib::BufError
54
+ nil
55
+ end
56
+
57
+ def self.finish_stream(stream, out)
58
+ out << next_chunk(stream) until stream.finished?
59
+ out
60
+ end
61
+
62
+ # Each finish makes progress, so one that returns nothing means the
63
+ # stream can't end: the compressed input is truncated
64
+ def self.next_chunk(stream)
65
+ chunk = stream.finish
66
+ raise Zlib::BufError, 'buffer error' if chunk.empty?
67
+
68
+ chunk
69
+ end
70
+
32
71
  # False when even the largest possible expansion stays within max_bytes,
33
72
  # so the value can be inflated in one step
34
73
  def self.may_exceed?(data, max_bytes)
@@ -43,16 +82,15 @@ module Dalli
43
82
  # with Zlib::Inflate.inflate; finish would return it as output
44
83
  break if inflater.finished?
45
84
  end
46
- return out if inflater.finished?
47
-
48
- append_within_limit(out, inflater.finish, max_bytes)
85
+ append_within_limit(out, next_chunk(inflater), max_bytes) until inflater.finished?
86
+ out
49
87
  ensure
50
88
  inflater.close
51
89
  end
52
90
 
53
91
  # Both yielded and returned output count toward the limit
54
92
  def self.inflate_slice(inflater, slice, out, max_bytes)
55
- rest = inflater.inflate(slice) { |chunk| append_within_limit(out, chunk, max_bytes) }
93
+ rest = feed(inflater, slice) { |chunk| append_within_limit(out, chunk, max_bytes) }
56
94
  append_within_limit(out, rest, max_bytes)
57
95
  end
58
96
 
@@ -70,19 +108,18 @@ module Dalli
70
108
  # entry.
71
109
  ##
72
110
  class GzipCompressor
111
+ # MAX_WBITS + 16 makes zlib write and read the gzip format (and check its CRC)
112
+ WINDOW_BITS = Zlib::MAX_WBITS + 16
113
+
73
114
  def self.compress(data)
74
- io = StringIO.new(+'', 'w')
75
- gz = Zlib::GzipWriter.new(io)
76
- gz.write(data)
77
- gz.close
78
- io.string
115
+ Compressor.deflate_fully(Zlib::Deflate.new(Zlib::DEFAULT_COMPRESSION, WINDOW_BITS), data)
79
116
  end
80
117
 
81
118
  def self.decompress(data, max_bytes: nil)
82
- return Zlib::GzipReader.new(StringIO.new(data, 'rb')).read unless Compressor.may_exceed?(data, max_bytes)
119
+ inflater = Zlib::Inflate.new(WINDOW_BITS)
120
+ return Compressor.inflate_fully(inflater, data) unless Compressor.may_exceed?(data, max_bytes)
83
121
 
84
- # MAX_WBITS + 16 makes zlib read the gzip format (and check its CRC)
85
- Compressor.inflate_within_limit(Zlib::Inflate.new(Zlib::MAX_WBITS + 16), data, max_bytes)
122
+ Compressor.inflate_within_limit(inflater, data, max_bytes)
86
123
  end
87
124
  end
88
125
  end
data/lib/dalli/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dalli
4
- VERSION = '5.3.1'
4
+ VERSION = '5.3.2'
5
5
 
6
6
  MIN_SUPPORTED_MEMCACHED_VERSION = '1.6.27'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dalli
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.1
4
+ version: 5.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
@@ -90,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
90
  - !ruby/object:Gem::Version
91
91
  version: '0'
92
92
  requirements: []
93
- rubygems_version: 4.0.23
93
+ rubygems_version: 4.0.24
94
94
  specification_version: 4
95
95
  summary: High performance memcached client for Ruby
96
96
  test_files: []