zip_kit 6.3.2 → 6.3.3
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/Rakefile +1 -1
- data/lib/zip_kit/streamer/deflated_writer.rb +5 -0
- data/lib/zip_kit/streamer/heuristic.rb +7 -0
- data/lib/zip_kit/streamer/stored_writer.rb +4 -0
- data/lib/zip_kit/streamer/writable.rb +9 -3
- data/lib/zip_kit/streamer.rb +1 -1
- data/lib/zip_kit/version.rb +1 -1
- data/lib/zip_kit/write_buffer.rb +6 -6
- data/lib/zip_kit/zlib_cleanup.rb +25 -0
- data/lib/zip_kit.rb +1 -0
- data/rbi/zip_kit.rbi +73 -9
- data/rbi/zip_kit.rbs +2000 -0
- data/zip_kit.gemspec +7 -1
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e037e0584d0dbee2e4ab4d934567d04564a26e4f30348f538bda6f7697f8d700
|
4
|
+
data.tar.gz: e069300aae55e54c3d88ed1a6c1e23803bdffbc92a7764de3f58a38a892f2b82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d35e87fc08da2212b9238e4f39df44091454ff77ef41e9810785aaa6855b46c017b285672b1fd0c0bb36df3255a24219331185ce1fc5f7dcf49584c46cce92f0
|
7
|
+
data.tar.gz: b50c429db7b8bbfe7b62d6b14be6cd0827cf1937f6065078b28a6d19b5e016e90e3ac2af0a3567b645ce14f44e4767ac42a5f5c20bb0e0d65ba207b310c02fc0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 6.3.3
|
2
|
+
|
3
|
+
* Make sure `Writable#<<` converts the strings it is given into binary if they are not already in binary. This fixes an issue where `Heuristic` would suddenly start forwarding strings as-is to downstream callees. There is a lot of spots where the string-to-write gets forwarded and converting in every single one will be quite wasteful, but it can be handy to do in a few key places.
|
4
|
+
* Make sure `WritableBuffer#<<` converts the strings it is given into binary if they are not already in binary. This helps prevent an issue where the receiving object the buffer flushes to is in a different encoding than binary (and all of our use cases assume bytes anyway, except for filenames).
|
5
|
+
* When rescuing a failed `write_file`, differentiate between `#close`
|
6
|
+
and `#release_resources_on_failure!`. Closing a Writable can still try
|
7
|
+
to do things to the Streamer output, it can try to write to the destination
|
8
|
+
IO which is no longer accepting writes and so on. What we do want is to
|
9
|
+
safely destroy the zlib deflaters.
|
10
|
+
|
1
11
|
## 6.3.2
|
2
12
|
|
3
13
|
* Make sure `rollback!` correctly works with `write_file` and the original exception gets re-raised from `write_file` if
|
data/Rakefile
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "rspec/core/rake_task"
|
5
5
|
require "yard"
|
6
|
-
require "rubocop/rake_task"
|
7
6
|
require "standard/rake"
|
8
7
|
|
9
8
|
task :format do
|
@@ -16,6 +15,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
16
15
|
|
17
16
|
task :generate_typedefs do
|
18
17
|
`bundle exec sord rbi/zip_kit.rbi`
|
18
|
+
`bundle exec sord rbi/zip_kit.rbs`
|
19
19
|
end
|
20
20
|
|
21
21
|
task default: [:spec, :standard, :generate_typedefs]
|
@@ -5,6 +5,7 @@
|
|
5
5
|
# interchangeable with the StoredWriter in terms of interface.
|
6
6
|
class ZipKit::Streamer::DeflatedWriter
|
7
7
|
include ZipKit::WriteShovel
|
8
|
+
include ZipKit::ZlibCleanup
|
8
9
|
|
9
10
|
# The amount of bytes we will buffer before computing the intermediate
|
10
11
|
# CRC32 checksums. Benchmarks show that the optimum is 64KB (see
|
@@ -42,4 +43,8 @@ class ZipKit::Streamer::DeflatedWriter
|
|
42
43
|
ensure
|
43
44
|
@deflater.close
|
44
45
|
end
|
46
|
+
|
47
|
+
def release_resources_on_failure!
|
48
|
+
safely_dispose_of_incomplete_deflater(@deflater)
|
49
|
+
end
|
45
50
|
end
|
@@ -13,6 +13,8 @@ require "zlib"
|
|
13
13
|
# on the Streamer passed into it once it knows which compression
|
14
14
|
# method should be applied
|
15
15
|
class ZipKit::Streamer::Heuristic < ZipKit::Streamer::Writable
|
16
|
+
include ZipKit::ZlibCleanup
|
17
|
+
|
16
18
|
BYTES_WRITTEN_THRESHOLD = 128 * 1024
|
17
19
|
MINIMUM_VIABLE_COMPRESSION = 0.75
|
18
20
|
|
@@ -48,6 +50,11 @@ class ZipKit::Streamer::Heuristic < ZipKit::Streamer::Writable
|
|
48
50
|
@winner.close
|
49
51
|
end
|
50
52
|
|
53
|
+
def release_resources_on_failure!
|
54
|
+
safely_dispose_of_incomplete_deflater(@deflater)
|
55
|
+
@winner&.release_resources_on_failure!
|
56
|
+
end
|
57
|
+
|
51
58
|
private def decide
|
52
59
|
# Finish and then close the deflater - it has likely buffered some data
|
53
60
|
@bytes_deflated += @deflater.finish.bytesize until @deflater.finished?
|
@@ -18,11 +18,11 @@ class ZipKit::Streamer::Writable
|
|
18
18
|
|
19
19
|
# Writes the given data to the output stream
|
20
20
|
#
|
21
|
-
# @param
|
21
|
+
# @param string[String] the string to write (part of the uncompressed file)
|
22
22
|
# @return [self]
|
23
|
-
def <<(
|
23
|
+
def <<(string)
|
24
24
|
raise "Trying to write to a closed Writable" if @closed
|
25
|
-
@writer <<
|
25
|
+
@writer << string.b
|
26
26
|
self
|
27
27
|
end
|
28
28
|
|
@@ -33,4 +33,10 @@ class ZipKit::Streamer::Writable
|
|
33
33
|
@streamer.update_last_entry_and_write_data_descriptor(**@writer.finish)
|
34
34
|
@closed = true
|
35
35
|
end
|
36
|
+
|
37
|
+
def release_resources_on_failure!
|
38
|
+
return if @closed
|
39
|
+
@closed = true
|
40
|
+
@writer.release_resources_on_failure!
|
41
|
+
end
|
36
42
|
end
|
data/lib/zip_kit/streamer.rb
CHANGED
data/lib/zip_kit/version.rb
CHANGED
data/lib/zip_kit/write_buffer.rb
CHANGED
@@ -42,14 +42,14 @@ class ZipKit::WriteBuffer
|
|
42
42
|
# Appends the given data to the write buffer, and flushes the buffer into the
|
43
43
|
# writable if the buffer size exceeds the `buffer_size` given at initialization
|
44
44
|
#
|
45
|
-
# @param
|
45
|
+
# @param string[String] data to be written
|
46
46
|
# @return self
|
47
|
-
def <<(
|
48
|
-
if
|
49
|
-
flush
|
50
|
-
@writable <<
|
47
|
+
def <<(string)
|
48
|
+
if string.bytesize >= @buffer_size
|
49
|
+
flush # <- this is were we can output less than @buffer_size
|
50
|
+
@writable << string.b
|
51
51
|
else
|
52
|
-
@buf <<
|
52
|
+
@buf << string.b
|
53
53
|
flush if @buf.bytesize >= @buffer_size
|
54
54
|
end
|
55
55
|
self
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ZipKit::ZlibCleanup
|
4
|
+
# This method is used to flush and close the native zlib handles
|
5
|
+
# should an archiving routine encounter an error. This is necessary,
|
6
|
+
# since otherwise unclosed deflaters may hang around in memory
|
7
|
+
# indefinitely, creating leaks.
|
8
|
+
#
|
9
|
+
# @param [Zlib::Deflater?]deflater the deflater to safely finish and close
|
10
|
+
# @return void
|
11
|
+
def safely_dispose_of_incomplete_deflater(deflater)
|
12
|
+
return unless deflater
|
13
|
+
|
14
|
+
# It can be a bit tricky to close and dealloc the deflater correctly.
|
15
|
+
# We want to do the right things for it to be GCd, including the
|
16
|
+
# native zlib handle. Also, leaving zlib handles dangling around
|
17
|
+
# creates warnings with "...with N bytes remaining to read", which are an
|
18
|
+
# eyesore. But they are there for a reason - so that we don't forget to do
|
19
|
+
# exactly this.
|
20
|
+
if !deflater.closed? && !deflater.finished?
|
21
|
+
deflater.finish until deflater.finished?
|
22
|
+
end
|
23
|
+
deflater.close unless deflater.closed?
|
24
|
+
end
|
25
|
+
end
|
data/lib/zip_kit.rb
CHANGED
@@ -24,6 +24,7 @@ module ZipKit
|
|
24
24
|
autoload :WriteShovel, File.dirname(__FILE__) + "/zip_kit/write_shovel.rb"
|
25
25
|
autoload :RackChunkedBody, File.dirname(__FILE__) + "/zip_kit/rack_chunked_body.rb"
|
26
26
|
autoload :RackTempfileBody, File.dirname(__FILE__) + "/zip_kit/rack_tempfile_body.rb"
|
27
|
+
autoload :ZlibCleanup, File.dirname(__FILE__) + "/zip_kit/zlib_cleanup.rb"
|
27
28
|
|
28
29
|
require_relative "zip_kit/railtie" if defined?(::Rails)
|
29
30
|
end
|
data/rbi/zip_kit.rbi
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# typed: strong
|
2
2
|
module ZipKit
|
3
|
-
VERSION = T.let("6.3.
|
3
|
+
VERSION = T.let("6.3.3", T.untyped)
|
4
4
|
|
5
5
|
class Railtie < Rails::Railtie
|
6
6
|
end
|
@@ -106,8 +106,12 @@ module ZipKit
|
|
106
106
|
# Is used to write ZIP archives without having to read them back or to overwrite
|
107
107
|
# data. It outputs into any object that supports `<<` or `write`, namely:
|
108
108
|
#
|
109
|
-
#
|
110
|
-
#
|
109
|
+
# * `Array` - will contain binary strings
|
110
|
+
# * `File` - data will be written to it as it gets generated
|
111
|
+
# * `IO` (`Socket`, `StringIO`) - data gets written into it
|
112
|
+
# * `String` - in binary encoding and unfrozen - also makes a decent output target
|
113
|
+
#
|
114
|
+
# or anything else that responds to `#<<` or `#write`.
|
111
115
|
#
|
112
116
|
# You can also combine output through the `Streamer` with direct output to the destination,
|
113
117
|
# all while preserving the correct offsets in the ZIP file structures. This allows usage
|
@@ -514,6 +518,10 @@ module ZipKit
|
|
514
518
|
# is likely already on the wire. However, excluding the entry from the central directory of the ZIP
|
515
519
|
# file will allow better-behaved ZIP unarchivers to extract the entries which did store correctly,
|
516
520
|
# provided they read the ZIP from the central directory and not straight-ahead.
|
521
|
+
# Rolling back does not perform any writes.
|
522
|
+
#
|
523
|
+
# `rollback!` gets called for you if an exception is raised inside the block of `write_file`,
|
524
|
+
# `write_deflated_file` and `write_stored_file`.
|
517
525
|
#
|
518
526
|
# _@return_ — position in the output stream / ZIP archive
|
519
527
|
#
|
@@ -670,9 +678,9 @@ module ZipKit
|
|
670
678
|
|
671
679
|
# Writes the given data to the output stream
|
672
680
|
#
|
673
|
-
# _@param_ `
|
674
|
-
sig { params(
|
675
|
-
def <<(
|
681
|
+
# _@param_ `string` — the string to write (part of the uncompressed file)
|
682
|
+
sig { params(string: String).returns(T.self_type) }
|
683
|
+
def <<(string); end
|
676
684
|
|
677
685
|
# sord omit - no YARD return type given, using untyped
|
678
686
|
# Flushes the writer and recovers the CRC32/size values. It then calls
|
@@ -680,6 +688,10 @@ module ZipKit
|
|
680
688
|
sig { returns(T.untyped) }
|
681
689
|
def close; end
|
682
690
|
|
691
|
+
# sord omit - no YARD return type given, using untyped
|
692
|
+
sig { returns(T.untyped) }
|
693
|
+
def release_resources_on_failure!; end
|
694
|
+
|
683
695
|
# Writes the given data to the output stream. Allows the object to be used as
|
684
696
|
# a target for `IO.copy_stream(from, to)`
|
685
697
|
#
|
@@ -701,6 +713,7 @@ module ZipKit
|
|
701
713
|
# on the Streamer passed into it once it knows which compression
|
702
714
|
# method should be applied
|
703
715
|
class Heuristic < ZipKit::Streamer::Writable
|
716
|
+
include ZipKit::ZlibCleanup
|
704
717
|
BYTES_WRITTEN_THRESHOLD = T.let(128 * 1024, T.untyped)
|
705
718
|
MINIMUM_VIABLE_COMPRESSION = T.let(0.75, T.untyped)
|
706
719
|
|
@@ -718,9 +731,25 @@ module ZipKit
|
|
718
731
|
sig { returns(T.untyped) }
|
719
732
|
def close; end
|
720
733
|
|
734
|
+
# sord omit - no YARD return type given, using untyped
|
735
|
+
sig { returns(T.untyped) }
|
736
|
+
def release_resources_on_failure!; end
|
737
|
+
|
721
738
|
# sord omit - no YARD return type given, using untyped
|
722
739
|
sig { returns(T.untyped) }
|
723
740
|
def decide; end
|
741
|
+
|
742
|
+
# sord warn - "Zlib::Deflater?" does not appear to be a type
|
743
|
+
# This method is used to flush and close the native zlib handles
|
744
|
+
# should an archiving routine encounter an error. This is necessary,
|
745
|
+
# since otherwise unclosed deflaters may hang around in memory
|
746
|
+
# indefinitely, creating leaks.
|
747
|
+
#
|
748
|
+
# _@param_ `deflater` — the deflater to safely finish and close
|
749
|
+
#
|
750
|
+
# _@return_ — void
|
751
|
+
sig { params(deflater: SORD_ERROR_ZlibDeflater).returns(T.untyped) }
|
752
|
+
def safely_dispose_of_incomplete_deflater(deflater); end
|
724
753
|
end
|
725
754
|
|
726
755
|
# Sends writes to the given `io`, and also registers all the data passing
|
@@ -749,6 +778,10 @@ module ZipKit
|
|
749
778
|
sig { returns(T::Hash[T.untyped, T.untyped]) }
|
750
779
|
def finish; end
|
751
780
|
|
781
|
+
# sord omit - no YARD return type given, using untyped
|
782
|
+
sig { returns(T.untyped) }
|
783
|
+
def release_resources_on_failure!; end
|
784
|
+
|
752
785
|
# Writes the given data to the output stream. Allows the object to be used as
|
753
786
|
# a target for `IO.copy_stream(from, to)`
|
754
787
|
#
|
@@ -764,6 +797,7 @@ module ZipKit
|
|
764
797
|
# interchangeable with the StoredWriter in terms of interface.
|
765
798
|
class DeflatedWriter
|
766
799
|
include ZipKit::WriteShovel
|
800
|
+
include ZipKit::ZlibCleanup
|
767
801
|
CRC32_BUFFER_SIZE = T.let(64 * 1024, T.untyped)
|
768
802
|
|
769
803
|
# sord omit - no YARD type given for "io", using untyped
|
@@ -787,6 +821,22 @@ module ZipKit
|
|
787
821
|
sig { returns(T::Hash[T.untyped, T.untyped]) }
|
788
822
|
def finish; end
|
789
823
|
|
824
|
+
# sord omit - no YARD return type given, using untyped
|
825
|
+
sig { returns(T.untyped) }
|
826
|
+
def release_resources_on_failure!; end
|
827
|
+
|
828
|
+
# sord warn - "Zlib::Deflater?" does not appear to be a type
|
829
|
+
# This method is used to flush and close the native zlib handles
|
830
|
+
# should an archiving routine encounter an error. This is necessary,
|
831
|
+
# since otherwise unclosed deflaters may hang around in memory
|
832
|
+
# indefinitely, creating leaks.
|
833
|
+
#
|
834
|
+
# _@param_ `deflater` — the deflater to safely finish and close
|
835
|
+
#
|
836
|
+
# _@return_ — void
|
837
|
+
sig { params(deflater: SORD_ERROR_ZlibDeflater).returns(T.untyped) }
|
838
|
+
def safely_dispose_of_incomplete_deflater(deflater); end
|
839
|
+
|
790
840
|
# Writes the given data to the output stream. Allows the object to be used as
|
791
841
|
# a target for `IO.copy_stream(from, to)`
|
792
842
|
#
|
@@ -1712,11 +1762,11 @@ end, T.untyped)
|
|
1712
1762
|
# Appends the given data to the write buffer, and flushes the buffer into the
|
1713
1763
|
# writable if the buffer size exceeds the `buffer_size` given at initialization
|
1714
1764
|
#
|
1715
|
-
# _@param_ `
|
1765
|
+
# _@param_ `string` — data to be written
|
1716
1766
|
#
|
1717
1767
|
# _@return_ — self
|
1718
|
-
sig { params(
|
1719
|
-
def <<(
|
1768
|
+
sig { params(string: String).returns(T.untyped) }
|
1769
|
+
def <<(string); end
|
1720
1770
|
|
1721
1771
|
# Explicitly flushes the buffer if it contains anything
|
1722
1772
|
#
|
@@ -1745,6 +1795,20 @@ end, T.untyped)
|
|
1745
1795
|
def write(bytes); end
|
1746
1796
|
end
|
1747
1797
|
|
1798
|
+
module ZlibCleanup
|
1799
|
+
# sord warn - "Zlib::Deflater?" does not appear to be a type
|
1800
|
+
# This method is used to flush and close the native zlib handles
|
1801
|
+
# should an archiving routine encounter an error. This is necessary,
|
1802
|
+
# since otherwise unclosed deflaters may hang around in memory
|
1803
|
+
# indefinitely, creating leaks.
|
1804
|
+
#
|
1805
|
+
# _@param_ `deflater` — the deflater to safely finish and close
|
1806
|
+
#
|
1807
|
+
# _@return_ — void
|
1808
|
+
sig { params(deflater: SORD_ERROR_ZlibDeflater).returns(T.untyped) }
|
1809
|
+
def safely_dispose_of_incomplete_deflater(deflater); end
|
1810
|
+
end
|
1811
|
+
|
1748
1812
|
# Permits Deflate compression in independent blocks. The workflow is as follows:
|
1749
1813
|
#
|
1750
1814
|
# * Run every block to compress through deflate_chunk, remove the header,
|