ziperu 0.1.1-aarch64-linux-musl

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 (105) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +24 -0
  3. data/LICENSE-rubyzip.txt +24 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +276 -0
  6. data/lib/rubyzip.rb +4 -0
  7. data/lib/zip/central_directory.rb +5 -0
  8. data/lib/zip/compressor.rb +5 -0
  9. data/lib/zip/constants.rb +5 -0
  10. data/lib/zip/crypto/aes_encryption.rb +5 -0
  11. data/lib/zip/crypto/decrypted_io.rb +5 -0
  12. data/lib/zip/crypto/encryption.rb +5 -0
  13. data/lib/zip/crypto/null_encryption.rb +5 -0
  14. data/lib/zip/crypto/traditional_encryption.rb +5 -0
  15. data/lib/zip/crypto.rb +5 -0
  16. data/lib/zip/decompressor.rb +5 -0
  17. data/lib/zip/deflater.rb +5 -0
  18. data/lib/zip/dirtyable.rb +5 -0
  19. data/lib/zip/dos_time.rb +5 -0
  20. data/lib/zip/entry.rb +5 -0
  21. data/lib/zip/entry_set.rb +5 -0
  22. data/lib/zip/errors.rb +5 -0
  23. data/lib/zip/extra_field/aes.rb +5 -0
  24. data/lib/zip/extra_field/generic.rb +5 -0
  25. data/lib/zip/extra_field/ntfs.rb +5 -0
  26. data/lib/zip/extra_field/old_unix.rb +5 -0
  27. data/lib/zip/extra_field/universal_time.rb +5 -0
  28. data/lib/zip/extra_field/unix.rb +5 -0
  29. data/lib/zip/extra_field/unknown.rb +5 -0
  30. data/lib/zip/extra_field/zip64.rb +5 -0
  31. data/lib/zip/extra_field.rb +5 -0
  32. data/lib/zip/file.rb +5 -0
  33. data/lib/zip/file_split.rb +5 -0
  34. data/lib/zip/filesystem/dir.rb +5 -0
  35. data/lib/zip/filesystem/directory_iterator.rb +5 -0
  36. data/lib/zip/filesystem/file.rb +5 -0
  37. data/lib/zip/filesystem/file_stat.rb +5 -0
  38. data/lib/zip/filesystem/zip_file_name_mapper.rb +5 -0
  39. data/lib/zip/filesystem.rb +5 -0
  40. data/lib/zip/inflater.rb +5 -0
  41. data/lib/zip/input_stream.rb +5 -0
  42. data/lib/zip/ioextras/abstract_input_stream.rb +5 -0
  43. data/lib/zip/ioextras/abstract_output_stream.rb +5 -0
  44. data/lib/zip/ioextras/fake_io.rb +5 -0
  45. data/lib/zip/ioextras.rb +5 -0
  46. data/lib/zip/null_compressor.rb +5 -0
  47. data/lib/zip/null_decompressor.rb +5 -0
  48. data/lib/zip/null_input_stream.rb +5 -0
  49. data/lib/zip/output_stream.rb +5 -0
  50. data/lib/zip/pass_thru_compressor.rb +5 -0
  51. data/lib/zip/pass_thru_decompressor.rb +5 -0
  52. data/lib/zip/streamable_directory.rb +5 -0
  53. data/lib/zip/streamable_stream.rb +5 -0
  54. data/lib/zip/version.rb +5 -0
  55. data/lib/zip.rb +19 -0
  56. data/lib/ziperu/3.1/ziperu.so +0 -0
  57. data/lib/ziperu/3.2/ziperu.so +0 -0
  58. data/lib/ziperu/3.3/ziperu.so +0 -0
  59. data/lib/ziperu/3.4/ziperu.so +0 -0
  60. data/lib/ziperu/4.0/ziperu.so +0 -0
  61. data/lib/ziperu/central_directory.rb +136 -0
  62. data/lib/ziperu/compressor.rb +14 -0
  63. data/lib/ziperu/constants.rb +148 -0
  64. data/lib/ziperu/crypto.rb +72 -0
  65. data/lib/ziperu/decompressor.rb +35 -0
  66. data/lib/ziperu/deflater.rb +41 -0
  67. data/lib/ziperu/dirtyable.rb +48 -0
  68. data/lib/ziperu/dos_time.rb +87 -0
  69. data/lib/ziperu/entry.rb +760 -0
  70. data/lib/ziperu/entry_set.rb +147 -0
  71. data/lib/ziperu/errors.rb +154 -0
  72. data/lib/ziperu/extra_field/aes.rb +52 -0
  73. data/lib/ziperu/extra_field/generic.rb +45 -0
  74. data/lib/ziperu/extra_field/ntfs.rb +98 -0
  75. data/lib/ziperu/extra_field/old_unix.rb +52 -0
  76. data/lib/ziperu/extra_field/universal_time.rb +81 -0
  77. data/lib/ziperu/extra_field/unix.rb +45 -0
  78. data/lib/ziperu/extra_field/unknown.rb +37 -0
  79. data/lib/ziperu/extra_field/zip64.rb +86 -0
  80. data/lib/ziperu/extra_field.rb +109 -0
  81. data/lib/ziperu/file.rb +614 -0
  82. data/lib/ziperu/file_split.rb +93 -0
  83. data/lib/ziperu/filesystem/dir.rb +88 -0
  84. data/lib/ziperu/filesystem/directory_iterator.rb +50 -0
  85. data/lib/ziperu/filesystem/file.rb +265 -0
  86. data/lib/ziperu/filesystem/file_stat.rb +112 -0
  87. data/lib/ziperu/filesystem/zip_file_name_mapper.rb +83 -0
  88. data/lib/ziperu/filesystem.rb +76 -0
  89. data/lib/ziperu/inflater.rb +60 -0
  90. data/lib/ziperu/input_stream.rb +296 -0
  91. data/lib/ziperu/ioextras/abstract_input_stream.rb +137 -0
  92. data/lib/ziperu/ioextras/abstract_output_stream.rb +57 -0
  93. data/lib/ziperu/ioextras/fake_io.rb +41 -0
  94. data/lib/ziperu/ioextras.rb +42 -0
  95. data/lib/ziperu/null_compressor.rb +20 -0
  96. data/lib/ziperu/null_decompressor.rb +25 -0
  97. data/lib/ziperu/null_input_stream.rb +55 -0
  98. data/lib/ziperu/output_stream.rb +223 -0
  99. data/lib/ziperu/pass_thru_compressor.rb +29 -0
  100. data/lib/ziperu/pass_thru_decompressor.rb +37 -0
  101. data/lib/ziperu/streamable_directory.rb +15 -0
  102. data/lib/ziperu/streamable_stream.rb +59 -0
  103. data/lib/ziperu/version.rb +7 -0
  104. data/lib/ziperu.rb +116 -0
  105. metadata +156 -0
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip path kept for code that requires this file directly.
4
+ require "zip"
5
+ require "ziperu/null_decompressor"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip path kept for code that requires this file directly.
4
+ require "zip"
5
+ require "ziperu/null_input_stream"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip path kept for code that requires this file directly.
4
+ require "zip"
5
+ require "ziperu/output_stream"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip path kept for code that requires this file directly.
4
+ require "zip"
5
+ require "ziperu/pass_thru_compressor"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip path kept for code that requires this file directly.
4
+ require "zip"
5
+ require "ziperu/pass_thru_decompressor"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip path kept for code that requires this file directly.
4
+ require "zip"
5
+ require "ziperu/streamable_directory"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip path kept for code that requires this file directly.
4
+ require "zip"
5
+ require "ziperu/streamable_stream"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip path kept for code that requires this file directly.
4
+ require "zip"
5
+ require "ziperu/version"
data/lib/zip.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip's entry point. Loading ziperu this way makes `Zip` an alias of
4
+ # `Ziperu`, so code written against rubyzip (`Zip::File`, `Zip.setup`, ...)
5
+ # runs unchanged. If another library already defines `Zip` (rubyzip itself,
6
+ # most likely), the two cannot share the name: load ziperu with
7
+ # `require "ziperu"` and use `Ziperu::` directly instead.
8
+ if defined?(::Zip) && !(defined?(::Ziperu) && ::Zip.equal?(::Ziperu))
9
+ raise LoadError, "ziperu cannot alias Zip = Ziperu: a Zip constant is already defined " \
10
+ "(by #{Object.const_source_location(:Zip)&.first || 'another library'}). " \
11
+ "Remove rubyzip, or require \"ziperu\" and use Ziperu:: instead."
12
+ end
13
+
14
+ require "ziperu"
15
+ Zip = Ziperu unless defined?(::Zip)
16
+
17
+ # The `zip/...` paths rubyzip's files live at (`require "zip/filesystem"`
18
+ # is the common one) are kept under lib/zip/ as one-line shims onto the
19
+ # matching lib/ziperu/ file.
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The end-of-central-directory parsing is ported from rubyzip
4
+ # (Copyright (C) 2002-2026 the rubyzip authors).
5
+
6
+ module Ziperu
7
+ # Reads just the end-of-central-directory records of an archive, in Ruby.
8
+ #
9
+ # The native core refuses archives whose EOCD declares more entries than
10
+ # the central directory holds, so `Ziperu::File.open` raises on them
11
+ # where rubyzip warns (or raises `EntryNumberMismatchError` when
12
+ # `Ziperu.validate_declared_number_of_entries` is set). This gives
13
+ # `Ziperu::File.count_entries` rubyzip's behaviour at least.
14
+ class CentralDirectory # :nodoc:all
15
+ END_OF_CD_SIG = 0x06054b50
16
+ ZIP64_END_OF_CD_SIG = 0x06064b50
17
+ ZIP64_EOCD_LOCATOR_SIG = 0x07064b50
18
+
19
+ STATIC_EOCD_SIZE = 22
20
+ ZIP64_STATIC_EOCD_SIZE = 56
21
+ ZIP64_EOCD_LOC_SIZE = 20
22
+ MAX_FILE_COMMENT_SIZE = (1 << 16) - 1
23
+ MAX_END_OF_CD_SIZE =
24
+ MAX_FILE_COMMENT_SIZE + STATIC_EOCD_SIZE + ZIP64_EOCD_LOC_SIZE
25
+
26
+ attr_reader :size, :cdir_offset, :comment
27
+
28
+ # Number of entries declared by the EOCD of `path_or_io`.
29
+ def self.count_entries(path_or_io)
30
+ if path_or_io.respond_to?(:read)
31
+ new.count_entries(path_or_io)
32
+ else
33
+ ::File.open(path_or_io.to_s, 'rb') { |io| new.count_entries(io) }
34
+ end
35
+ end
36
+
37
+ def count_entries(io)
38
+ read_eocds(io)
39
+ @size
40
+ end
41
+
42
+ private
43
+
44
+ def unpack_64_e_o_c_d(buffer)
45
+ _, # ZIP64_END_OF_CD_SIG. We know we have this at this point.
46
+ _size_of_zip64_e_o_c_d,
47
+ _version_made_by,
48
+ _version_needed_for_extract,
49
+ _number_of_this_disk,
50
+ _number_of_disk_with_start_of_cdir,
51
+ _total_number_of_entries_in_cdir_on_this_disk,
52
+ @size,
53
+ size_in_bytes,
54
+ @cdir_offset = buffer.unpack('VQ<vvVVQ<Q<Q<Q<')
55
+
56
+ validate_size!(@size, size_in_bytes)
57
+ end
58
+
59
+ def unpack_64_eocd_locator(buffer)
60
+ _, _, zip64_eocd_offset, = buffer.unpack('VVQ<V')
61
+ zip64_eocd_offset
62
+ end
63
+
64
+ # Returns true when the plain EOCD was complete (no zip64 needed).
65
+ def unpack_e_o_c_d(buffer)
66
+ _, # END_OF_CD_SIG. We know we have this at this point.
67
+ number_of_this_disk,
68
+ number_of_disk_with_start_of_cdir,
69
+ total_number_of_entries_in_cdir_on_this_disk,
70
+ @size,
71
+ size_in_bytes,
72
+ @cdir_offset,
73
+ comment_length = buffer.unpack('VvvvvVVv')
74
+
75
+ complete = !([number_of_this_disk, number_of_disk_with_start_of_cdir,
76
+ total_number_of_entries_in_cdir_on_this_disk, @size].any?(0xFFFF) ||
77
+ size_in_bytes == 0xFFFFFFFF || @cdir_offset == 0xFFFFFFFF)
78
+
79
+ validate_size!(@size, size_in_bytes) if complete
80
+
81
+ @comment = comment_length.positive? ? buffer.slice(STATIC_EOCD_SIZE, comment_length) : ''
82
+ complete
83
+ end
84
+
85
+ def read_eocds(io)
86
+ base_location, data = eocd_data(io)
87
+
88
+ eocd_location = data.rindex([END_OF_CD_SIG].pack('V'))
89
+ raise Error, 'Zip end of central directory signature not found' unless eocd_location
90
+
91
+ return if unpack_e_o_c_d(data.slice(eocd_location..-1))
92
+
93
+ zip64_eocd_locator = data.rindex([ZIP64_EOCD_LOCATOR_SIG].pack('V'), eocd_location)
94
+ unless zip64_eocd_locator
95
+ raise Error, 'Zip64 end of central directory locator signature expected but not found'
96
+ end
97
+
98
+ zip64_eocd_location = data.rindex([ZIP64_END_OF_CD_SIG].pack('V'), zip64_eocd_locator)
99
+
100
+ zip64_eocd_data =
101
+ if zip64_eocd_location
102
+ data.slice(zip64_eocd_location..zip64_eocd_locator)
103
+ else
104
+ zip64_eocd_location = unpack_64_eocd_locator(
105
+ data.slice(zip64_eocd_locator..eocd_location)
106
+ )
107
+ raise Error, 'Zip64 end of central directory signature not found' unless zip64_eocd_location
108
+
109
+ io.seek(zip64_eocd_location, IO::SEEK_SET)
110
+ io.read(base_location + zip64_eocd_locator - zip64_eocd_location)
111
+ end
112
+
113
+ unpack_64_e_o_c_d(zip64_eocd_data)
114
+ end
115
+
116
+ def eocd_data(io)
117
+ io.binmode if io.respond_to?(:binmode)
118
+ begin
119
+ io.seek(-MAX_END_OF_CD_SIZE, IO::SEEK_END)
120
+ rescue Errno::EINVAL
121
+ io.seek(0, IO::SEEK_SET)
122
+ end
123
+
124
+ [io.tell, io.read.to_s.b]
125
+ end
126
+
127
+ def validate_size!(size, size_in_bytes)
128
+ return if size * CDIR_ENTRY_STATIC_HEADER_LENGTH <= size_in_bytes
129
+
130
+ error = EntryNumberMismatchError.new(size, size_in_bytes)
131
+ raise error if Ziperu.validate_declared_number_of_entries
132
+
133
+ warn error.message
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ported verbatim from rubyzip (Copyright (C) 2002-2026 the rubyzip authors).
4
+ # Not on any ziperu code path: the Rust core compresses and decompresses.
5
+ # Kept so that code which references these classes keeps loading.
6
+ module Ziperu
7
+ class Compressor # :nodoc:all
8
+ def finish; end
9
+ end
10
+ end
11
+
12
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
13
+ # rubyzip is free software; you can redistribute it and/or
14
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubyzip's constants, verbatim, plus the newer compression methods the Rust
4
+ # core can read.
5
+ module Ziperu
6
+ # :stopdoc:
7
+
8
+ RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i
9
+
10
+ CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50
11
+ CDIR_ENTRY_STATIC_HEADER_LENGTH = 46
12
+
13
+ LOCAL_ENTRY_SIGNATURE = 0x04034b50
14
+ LOCAL_ENTRY_STATIC_HEADER_LENGTH = 30
15
+ LOCAL_ENTRY_TRAILING_DESCRIPTOR_LENGTH = 4 + 4 + 4
16
+ VERSION_MADE_BY = 52
17
+ VERSION_NEEDED_TO_EXTRACT = 20
18
+ VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
19
+ VERSION_NEEDED_TO_EXTRACT_AES = 51
20
+
21
+ SPLIT_FILE_SIGNATURE = 0x08074b50
22
+
23
+ FILE_TYPE_FILE = 0o10
24
+ FILE_TYPE_DIR = 0o04
25
+ FILE_TYPE_SYMLINK = 0o12
26
+
27
+ FSTYPE_FAT = 0
28
+ FSTYPE_AMIGA = 1
29
+ FSTYPE_VMS = 2
30
+ FSTYPE_UNIX = 3
31
+ FSTYPE_VM_CMS = 4
32
+ FSTYPE_ATARI = 5
33
+ FSTYPE_HPFS = 6
34
+ FSTYPE_MAC = 7
35
+ FSTYPE_Z_SYSTEM = 8
36
+ FSTYPE_CPM = 9
37
+ FSTYPE_TOPS20 = 10
38
+ FSTYPE_NTFS = 11
39
+ FSTYPE_QDOS = 12
40
+ FSTYPE_ACORN = 13
41
+ FSTYPE_VFAT = 14
42
+ FSTYPE_MVS = 15
43
+ FSTYPE_BEOS = 16
44
+ FSTYPE_TANDEM = 17
45
+ FSTYPE_THEOS = 18
46
+ FSTYPE_MAC_OSX = 19
47
+ FSTYPE_ATHEOS = 30
48
+
49
+ FSTYPES = {
50
+ FSTYPE_FAT => 'FAT',
51
+ FSTYPE_AMIGA => 'Amiga',
52
+ FSTYPE_VMS => 'VMS (Vax or Alpha AXP)',
53
+ FSTYPE_UNIX => 'Unix',
54
+ FSTYPE_VM_CMS => 'VM/CMS',
55
+ FSTYPE_ATARI => 'Atari ST',
56
+ FSTYPE_HPFS => 'OS/2 or NT HPFS',
57
+ FSTYPE_MAC => 'Macintosh',
58
+ FSTYPE_Z_SYSTEM => 'Z-System',
59
+ FSTYPE_CPM => 'CP/M',
60
+ FSTYPE_TOPS20 => 'TOPS-20',
61
+ FSTYPE_NTFS => 'NTFS',
62
+ FSTYPE_QDOS => 'SMS/QDOS',
63
+ FSTYPE_ACORN => 'Acorn RISC OS',
64
+ FSTYPE_VFAT => 'Win32 VFAT',
65
+ FSTYPE_MVS => 'MVS',
66
+ FSTYPE_BEOS => 'BeOS',
67
+ FSTYPE_TANDEM => 'Tandem NSK',
68
+ FSTYPE_THEOS => 'Theos',
69
+ FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)',
70
+ FSTYPE_ATHEOS => 'AtheOS'
71
+ }.freeze
72
+
73
+ COMPRESSION_METHOD_STORE = 0
74
+ COMPRESSION_METHOD_SHRINK = 1
75
+ COMPRESSION_METHOD_REDUCE_1 = 2
76
+ COMPRESSION_METHOD_REDUCE_2 = 3
77
+ COMPRESSION_METHOD_REDUCE_3 = 4
78
+ COMPRESSION_METHOD_REDUCE_4 = 5
79
+ COMPRESSION_METHOD_IMPLODE = 6
80
+ COMPRESSION_METHOD_DEFLATE = 8
81
+ COMPRESSION_METHOD_DEFLATE_64 = 9
82
+ COMPRESSION_METHOD_DEFLATE64 = COMPRESSION_METHOD_DEFLATE_64
83
+ COMPRESSION_METHOD_PKWARE_DCLI = 10
84
+ COMPRESSION_METHOD_BZIP2 = 12
85
+ COMPRESSION_METHOD_LZMA = 14
86
+ COMPRESSION_METHOD_IBM_CMPSC = 16
87
+ COMPRESSION_METHOD_IBM_TERSE = 18
88
+ COMPRESSION_METHOD_IBM_LZ77 = 19
89
+ COMPRESSION_METHOD_ZSTD = 93
90
+ COMPRESSION_METHOD_XZ = 95
91
+ COMPRESSION_METHOD_JPEG = 96
92
+ COMPRESSION_METHOD_WAVPACK = 97
93
+ COMPRESSION_METHOD_PPMD = 98
94
+ COMPRESSION_METHOD_AES = 99
95
+
96
+ COMPRESSION_METHODS = {
97
+ COMPRESSION_METHOD_STORE => 'Store (no compression)',
98
+ COMPRESSION_METHOD_SHRINK => 'Shrink',
99
+ COMPRESSION_METHOD_REDUCE_1 => 'Reduce with compression factor 1',
100
+ COMPRESSION_METHOD_REDUCE_2 => 'Reduce with compression factor 2',
101
+ COMPRESSION_METHOD_REDUCE_3 => 'Reduce with compression factor 3',
102
+ COMPRESSION_METHOD_REDUCE_4 => 'Reduce with compression factor 4',
103
+ COMPRESSION_METHOD_IMPLODE => 'Implode',
104
+ COMPRESSION_METHOD_DEFLATE => 'Deflate',
105
+ COMPRESSION_METHOD_DEFLATE_64 => 'Deflate64(tm)',
106
+ COMPRESSION_METHOD_PKWARE_DCLI => 'PKWARE Data Compression Library Imploding (old IBM TERSE)',
107
+ COMPRESSION_METHOD_BZIP2 => 'BZIP2',
108
+ COMPRESSION_METHOD_LZMA => 'LZMA',
109
+ COMPRESSION_METHOD_IBM_CMPSC => 'IBM z/OS CMPSC Compression',
110
+ COMPRESSION_METHOD_IBM_TERSE => 'IBM TERSE (new)',
111
+ COMPRESSION_METHOD_IBM_LZ77 => 'IBM LZ77 z Architecture (PFS)',
112
+ COMPRESSION_METHOD_ZSTD => 'Zstandard',
113
+ COMPRESSION_METHOD_XZ => 'XZ',
114
+ COMPRESSION_METHOD_JPEG => 'JPEG variant',
115
+ COMPRESSION_METHOD_WAVPACK => 'WavPack compressed data',
116
+ COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1',
117
+ COMPRESSION_METHOD_AES => 'AES encryption'
118
+ }.freeze
119
+
120
+ # Methods the Rust core can decompress.
121
+ READABLE_COMPRESSION_METHODS = [
122
+ COMPRESSION_METHOD_STORE,
123
+ COMPRESSION_METHOD_SHRINK,
124
+ COMPRESSION_METHOD_REDUCE_1,
125
+ COMPRESSION_METHOD_REDUCE_2,
126
+ COMPRESSION_METHOD_REDUCE_3,
127
+ COMPRESSION_METHOD_REDUCE_4,
128
+ COMPRESSION_METHOD_IMPLODE,
129
+ COMPRESSION_METHOD_DEFLATE,
130
+ COMPRESSION_METHOD_DEFLATE_64,
131
+ COMPRESSION_METHOD_BZIP2,
132
+ COMPRESSION_METHOD_LZMA,
133
+ COMPRESSION_METHOD_ZSTD,
134
+ COMPRESSION_METHOD_XZ,
135
+ COMPRESSION_METHOD_PPMD,
136
+ COMPRESSION_METHOD_AES
137
+ ].freeze
138
+
139
+ # Methods the Rust core can compress with.
140
+ WRITABLE_COMPRESSION_METHODS = [
141
+ COMPRESSION_METHOD_STORE,
142
+ COMPRESSION_METHOD_DEFLATE,
143
+ COMPRESSION_METHOD_BZIP2,
144
+ COMPRESSION_METHOD_ZSTD
145
+ ].freeze
146
+
147
+ # :startdoc:
148
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ziperu
4
+ # The encrypter/decrypter objects rubyzip's API passes around
5
+ # (`encrypter:` / `decrypter:` keywords). The Rust core does the actual
6
+ # ZipCrypto/AES work, so these are typed password holders.
7
+ class Decrypter # :nodoc:
8
+ attr_reader :password
9
+
10
+ def initialize(password)
11
+ @password = password.to_s
12
+ end
13
+ end
14
+
15
+ class TraditionalDecrypter < Decrypter; end
16
+
17
+ class AESDecrypter < Decrypter
18
+ def initialize(password, _strength = nil)
19
+ super(password)
20
+ end
21
+ end
22
+
23
+ module AESEncryption # :nodoc:
24
+ STRENGTH_128_BIT = 0x01
25
+ STRENGTH_192_BIT = 0x02
26
+ STRENGTH_256_BIT = 0x03
27
+ end
28
+
29
+ class Encrypter # :nodoc:
30
+ attr_reader :password
31
+
32
+ def initialize(password)
33
+ @password = password.to_s
34
+ end
35
+
36
+ # Options for the native writer.
37
+ def native_options # :nodoc:
38
+ {}
39
+ end
40
+ end
41
+
42
+ class NullEncrypter < Encrypter
43
+ def initialize
44
+ super(nil)
45
+ end
46
+ end
47
+
48
+ # ZipCrypto, the weak legacy cipher that Windows Explorer and other
49
+ # AES-less tools can open. Entries are written with general purpose bits
50
+ # 0 and 3 set, as rubyzip writes them; only Stored and Deflate entries
51
+ # can be encrypted this way.
52
+ class TraditionalEncrypter < Encrypter
53
+ def native_options # :nodoc:
54
+ { password: @password.b, zipcrypto: true }
55
+ end
56
+ end
57
+
58
+ class AESEncrypter < Encrypter
59
+ include AESEncryption
60
+
61
+ attr_reader :strength
62
+
63
+ def initialize(password, strength = STRENGTH_256_BIT)
64
+ super(password)
65
+ @strength = strength
66
+ end
67
+
68
+ def native_options # :nodoc:
69
+ { password: @password.b, aes_mode: @strength }
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ported verbatim from rubyzip (Copyright (C) 2002-2026 the rubyzip authors).
4
+ # Not on any ziperu code path: the Rust core compresses and decompresses.
5
+ # Kept so that code which references these classes keeps loading.
6
+ module Ziperu
7
+ class Decompressor # :nodoc:all
8
+ CHUNK_SIZE = 32_768
9
+
10
+ def self.decompressor_classes
11
+ @decompressor_classes ||= {}
12
+ end
13
+
14
+ def self.register(compression_method, decompressor_class)
15
+ decompressor_classes[compression_method] = decompressor_class
16
+ end
17
+
18
+ def self.find_by_compression_method(compression_method)
19
+ decompressor_classes[compression_method]
20
+ end
21
+
22
+ attr_reader :decompressed_size, :input_stream
23
+
24
+ def initialize(input_stream, decompressed_size = nil)
25
+ super()
26
+
27
+ @input_stream = input_stream
28
+ @decompressed_size = decompressed_size
29
+ end
30
+ end
31
+ end
32
+
33
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
34
+ # rubyzip is free software; you can redistribute it and/or
35
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ported verbatim from rubyzip (Copyright (C) 2002-2026 the rubyzip authors).
4
+ # Not on any ziperu code path: the Rust core compresses and decompresses.
5
+ # Kept so that code which references these classes keeps loading.
6
+ module Ziperu
7
+ class Deflater < Compressor # :nodoc:all
8
+ def initialize(output_stream, level = Ziperu.default_compression, encrypter = NullEncrypter.new)
9
+ super()
10
+ @output_stream = output_stream
11
+ @zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
12
+ @size = 0
13
+ @crc = ::Zlib.crc32
14
+ @encrypter = encrypter
15
+ end
16
+
17
+ def <<(data)
18
+ val = data.to_s
19
+ @crc = Zlib.crc32(val, @crc)
20
+ @size += val.bytesize
21
+
22
+ # When JRuby#3962 is fixed, we can remove the flushing parameter here.
23
+ buffer = @zlib_deflater.deflate(data, Ziperu::ZLIB_FLUSHING_STRATEGY)
24
+ return @output_stream if buffer.empty?
25
+
26
+ @output_stream << @encrypter.encrypt(buffer)
27
+ end
28
+
29
+ def finish
30
+ buffer = @zlib_deflater.finish
31
+ @output_stream << @encrypter.encrypt(buffer) unless buffer.empty?
32
+ @zlib_deflater.close
33
+ end
34
+
35
+ attr_reader :size, :crc
36
+ end
37
+ end
38
+
39
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
40
+ # rubyzip is free software; you can redistribute it and/or
41
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ported from rubyzip (Copyright (C) 2002-2026 the rubyzip authors), with
4
+ # the setters that dirtied the object recorded as well as the flag: an
5
+ # entry whose only change the native raw copy can carry (a rename, or a
6
+ # chmod) need not be recompressed on commit.
7
+
8
+ module Ziperu
9
+ module Dirtyable # :nodoc:all
10
+ def initialize(dirty_on_create: true)
11
+ @dirty = dirty_on_create
12
+ @dirty_fields = []
13
+ end
14
+
15
+ def dirty?
16
+ @dirty
17
+ end
18
+
19
+ # The setters called since the object was last clean, as symbols.
20
+ def dirty_fields
21
+ @dirty_fields ||= []
22
+ end
23
+
24
+ def mark_dirty!(field)
25
+ @dirty = true
26
+ dirty_fields << field unless dirty_fields.include?(field)
27
+ end
28
+
29
+ module ClassMethods # :nodoc:
30
+ def mark_dirty(*symbols) # :nodoc:
31
+ # Move the original method and call it after we've set the dirty flag.
32
+ symbols.each do |symbol|
33
+ orig_name = "orig_#{symbol}"
34
+ alias_method orig_name, symbol
35
+
36
+ define_method(symbol) do |param|
37
+ mark_dirty!(symbol)
38
+ send(orig_name, param)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def self.included(base)
45
+ base.extend(ClassMethods)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ported from rubyzip (Copyright (C) 2002-2026 the rubyzip authors).
4
+
5
+ module Ziperu
6
+ # A Time that knows how to round-trip the MS-DOS date/time packed into a
7
+ # zip header. Mirrors rubyzip's class so `entry.time` behaves identically.
8
+ class DOSTime < Time # :nodoc:all
9
+ # MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
10
+ #
11
+ # Register CX, the Time:
12
+ # Bits 0-4 2 second increments (0-29)
13
+ # Bits 5-10 minutes (0-59)
14
+ # bits 11-15 hours (0-24)
15
+ #
16
+ # Register DX, the Date:
17
+ # Bits 0-4 day (1-31)
18
+ # bits 5-8 month (1-12)
19
+ # bits 9-15 year (four digit year minus 1980)
20
+
21
+ # The MS-DOS date field is seven bits of "year minus 1980", so only
22
+ # 1980-01-01 00:00:00 through 2107-12-31 23:59:58 can be represented.
23
+ # Times outside that are clamped rather than allowed to wrap: the real
24
+ # time is still carried in the universal time (UT) extra field, but the
25
+ # DOS fields have to stay in range for readers that only look at those.
26
+ DOS_EPOCH_YEAR = 1980
27
+ DOS_MAX_YEAR = 2107
28
+ DOS_MIN_DATE = 0x0021 # 1980-01-01
29
+ DOS_MAX_DATE = 0xff9f # 2107-12-31
30
+ DOS_MIN_TIME = 0x0000 # 00:00:00
31
+ DOS_MAX_TIME = 0xbf7d # 23:59:58
32
+
33
+ attr_writer :absolute_time # :nodoc:
34
+
35
+ # Times built from a DOS field carry no timezone; everything else does.
36
+ def absolute_time?
37
+ @absolute_time.nil? || @absolute_time
38
+ end
39
+
40
+ def to_binary_dos_time
41
+ return DOS_MIN_TIME if year < DOS_EPOCH_YEAR
42
+ return DOS_MAX_TIME if year > DOS_MAX_YEAR
43
+
44
+ (sec / 2) + (min << 5) + (hour << 11)
45
+ end
46
+
47
+ def to_binary_dos_date
48
+ return DOS_MIN_DATE if year < DOS_EPOCH_YEAR
49
+ return DOS_MAX_DATE if year > DOS_MAX_YEAR
50
+
51
+ day + (month << 5) + ((year - DOS_EPOCH_YEAR) << 9)
52
+ end
53
+
54
+ # Deprecated. Remove for version 4.
55
+ def dos_equals(other)
56
+ warn 'Ziperu::DOSTime#dos_equals is deprecated. Use `==` instead.'
57
+ self == other
58
+ end
59
+
60
+ # DOS time is only stored with two seconds accuracy.
61
+ def <=>(other)
62
+ return unless other.kind_of?(Time)
63
+
64
+ (to_i / 2) <=> (other.to_i / 2)
65
+ end
66
+
67
+ # Create a DOSTime instance from a vanilla Time instance.
68
+ def self.from_time(time)
69
+ local(time.year, time.month, time.day, time.hour, time.min, time.sec)
70
+ end
71
+
72
+ # Raises ArgumentError for fields that don't make a valid date, like
73
+ # rubyzip; callers decide whether to warn.
74
+ def self.parse_binary_dos_format(bin_dos_date, bin_dos_time)
75
+ second = 2 * (0b11111 & bin_dos_time)
76
+ minute = (0b11111100000 & bin_dos_time) >> 5
77
+ hour = (0b1111100000000000 & bin_dos_time) >> 11
78
+ day = (0b11111 & bin_dos_date)
79
+ month = (0b111100000 & bin_dos_date) >> 5
80
+ year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980
81
+
82
+ time = local(year, month, day, hour, minute, second)
83
+ time.absolute_time = false
84
+ time
85
+ end
86
+ end
87
+ end