omnizip 0.3.13 → 0.3.15

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/lib/omnizip/algorithm.rb +36 -1
  3. data/lib/omnizip/algorithms/bzip2.rb +1 -3
  4. data/lib/omnizip/algorithms/deflate.rb +1 -3
  5. data/lib/omnizip/algorithms/deflate64.rb +2 -2
  6. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +7 -8
  7. data/lib/omnizip/algorithms/lzma.rb +1 -1
  8. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +5 -1
  9. data/lib/omnizip/algorithms/lzma2.rb +4 -12
  10. data/lib/omnizip/algorithms/zstandard/constants.rb +38 -25
  11. data/lib/omnizip/algorithms/zstandard/decoder.rb +101 -136
  12. data/lib/omnizip/algorithms/zstandard/encoder.rb +81 -138
  13. data/lib/omnizip/algorithms/zstandard/frame/block.rb +17 -0
  14. data/lib/omnizip/algorithms/zstandard/frame/header.rb +42 -29
  15. data/lib/omnizip/algorithms/zstandard/fse/bitstream.rb +114 -117
  16. data/lib/omnizip/algorithms/zstandard/fse/encoder.rb +434 -222
  17. data/lib/omnizip/algorithms/zstandard/fse/interleaved.rb +92 -0
  18. data/lib/omnizip/algorithms/zstandard/fse/table.rb +98 -176
  19. data/lib/omnizip/algorithms/zstandard/fse/table_description.rb +203 -0
  20. data/lib/omnizip/algorithms/zstandard/fse.rb +21 -2
  21. data/lib/omnizip/algorithms/zstandard/huffman.rb +195 -188
  22. data/lib/omnizip/algorithms/zstandard/huffman_encoder.rb +232 -255
  23. data/lib/omnizip/algorithms/zstandard/literals.rb +170 -103
  24. data/lib/omnizip/algorithms/zstandard/literals_encoder.rb +47 -199
  25. data/lib/omnizip/algorithms/zstandard/sequences.rb +260 -252
  26. data/lib/omnizip/algorithms/zstandard/xxhash.rb +159 -0
  27. data/lib/omnizip/algorithms/zstandard.rb +20 -24
  28. data/lib/omnizip/algorithms.rb +14 -1
  29. data/lib/omnizip/buffer.rb +1 -2
  30. data/lib/omnizip/commands/archive_list_command.rb +9 -7
  31. data/lib/omnizip/convenience.rb +1 -2
  32. data/lib/omnizip/entry.rb +44 -0
  33. data/lib/omnizip/extraction/selective_extractor.rb +2 -10
  34. data/lib/omnizip/filter_pipeline.rb +1 -1
  35. data/lib/omnizip/filter_registry.rb +18 -16
  36. data/lib/omnizip/filters/bcj2.rb +1 -1
  37. data/lib/omnizip/filters/bcj_arm.rb +1 -1
  38. data/lib/omnizip/filters/bcj_arm64.rb +1 -1
  39. data/lib/omnizip/filters/bcj_ia64.rb +1 -1
  40. data/lib/omnizip/filters/bcj_ppc.rb +1 -1
  41. data/lib/omnizip/filters/bcj_sparc.rb +1 -1
  42. data/lib/omnizip/filters/bcj_x86.rb +1 -1
  43. data/lib/omnizip/filters.rb +0 -2
  44. data/lib/omnizip/formats/bzip2_file.rb +0 -7
  45. data/lib/omnizip/formats/cpio/entry.rb +6 -0
  46. data/lib/omnizip/formats/cpio.rb +0 -6
  47. data/lib/omnizip/formats/gzip.rb +0 -7
  48. data/lib/omnizip/formats/iso/directory_record.rb +7 -0
  49. data/lib/omnizip/formats/iso.rb +0 -5
  50. data/lib/omnizip/formats/lzip.rb +0 -7
  51. data/lib/omnizip/formats/lzma_alone.rb +0 -6
  52. data/lib/omnizip/formats/msi/entry.rb +6 -0
  53. data/lib/omnizip/formats/msi.rb +0 -9
  54. data/lib/omnizip/formats/ole/dirent.rb +6 -0
  55. data/lib/omnizip/formats/ole.rb +0 -9
  56. data/lib/omnizip/formats/rar.rb +0 -6
  57. data/lib/omnizip/formats/rar3/reader.rb +7 -0
  58. data/lib/omnizip/formats/rar5/reader.rb +7 -0
  59. data/lib/omnizip/formats/rpm/entry.rb +6 -0
  60. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +7 -0
  61. data/lib/omnizip/formats/seven_zip.rb +0 -6
  62. data/lib/omnizip/formats/tar/entry.rb +6 -0
  63. data/lib/omnizip/formats/tar.rb +2 -10
  64. data/lib/omnizip/formats/xar/entry.rb +6 -0
  65. data/lib/omnizip/formats/xar.rb +0 -6
  66. data/lib/omnizip/formats/zip.rb +0 -6
  67. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +33 -31
  68. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +177 -163
  69. data/lib/omnizip/implementations.rb +18 -4
  70. data/lib/omnizip/io/source.rb +3 -0
  71. data/lib/omnizip/metadata/entry_metadata.rb +7 -0
  72. data/lib/omnizip/parallel/engine.rb +48 -0
  73. data/lib/omnizip/parallel/parallel_compressor.rb +5 -16
  74. data/lib/omnizip/parallel/parallel_extractor.rb +5 -16
  75. data/lib/omnizip/parallel.rb +1 -0
  76. data/lib/omnizip/profile/profile_registry.rb +1 -2
  77. data/lib/omnizip/registry.rb +0 -1
  78. data/lib/omnizip/rubyzip_compat.rb +0 -1
  79. data/lib/omnizip/temp/temp_file.rb +1 -2
  80. data/lib/omnizip/version.rb +1 -1
  81. data/lib/omnizip/zip/entry.rb +7 -0
  82. data/lib/omnizip/zip/file.rb +4 -8
  83. data/lib/omnizip.rb +1 -1
  84. metadata +7 -6
  85. data/lib/omnizip/filters/filter_base.rb +0 -6
  86. data/lib/omnizip/filters/registration.rb +0 -22
  87. data/lib/omnizip/filters/registry.rb +0 -111
  88. data/lib/omnizip/format_registry.rb +0 -100
@@ -24,160 +24,157 @@ module Omnizip
24
24
  module Algorithms
25
25
  class Zstandard
26
26
  module FSE
27
- # FSE bitstream reader (RFC 8878 Section 4.1)
27
+ # Reverse-direction bit reader matching the zstd C reference
28
+ # `BIT_DStream` (lib/common/bitstream.h).
28
29
  #
29
- # Reads FSE-encoded bitstreams which are read in reverse order
30
- # (from end to beginning) according to RFC 8878.
30
+ # The bitstream is written forward by the encoder but read
31
+ # BACKWARDS: bytes are consumed from the last toward the first,
32
+ # and within each byte bits are consumed MSB-first. The reader
33
+ # keeps a 64-bit container (little-endian window onto 8 bytes)
34
+ # and takes bits from the high end after `bits_consumed`.
31
35
  #
32
- # The bitstream is consumed from the end toward the beginning,
33
- # with bits read from LSB to MSB within each byte.
36
+ # The last byte holds a 1 end-mark bit with 0-7 padding zero
37
+ # bits above it; the last byte can therefore never be 0x00.
34
38
  class BitStream
35
- # @return [String] The compressed data
36
- attr_reader :data
39
+ MASK64 = 0xFFFFFFFFFFFFFFFF
37
40
 
38
- # @return [Integer] Current bit position (from end)
39
- attr_reader :bit_position
41
+ # Reload statuses mirroring C's BIT_DStream_status.
42
+ UNFINISHED = :unfinished
43
+ END_OF_BUFFER = :end_of_buffer
44
+ COMPLETED = :completed
45
+ OVERFLOW = :overflow
40
46
 
41
- # Initialize bitstream with data
42
- #
43
- # @param data [String] The compressed bitstream data
47
+ attr_reader :data
48
+
49
+ # @param data [String] the full bitstream bytes (binary)
44
50
  def initialize(data)
45
- @data = data.dup.force_encoding(Encoding::BINARY)
46
- @bit_position = data.bytesize * 8
51
+ @data = data
52
+ n = data.bytesize
53
+ last = n.positive? ? data.getbyte(n - 1) : 0
54
+ end_mark = last.positive? ? 8 - Constants.highbit32(last) : 0
55
+
56
+ if n >= 8
57
+ @ptr = n - 8
58
+ @container = read_le64(@ptr)
59
+ @bits_consumed = end_mark
60
+ else
61
+ @ptr = 0
62
+ @container = 0
63
+ n.times { |i| @container |= data.getbyte(i) << (i * 8) }
64
+ @bits_consumed = end_mark + ((8 - n) * 8)
65
+ end
47
66
  end
48
67
 
49
- # Read bits from the stream (in reverse order)
50
- #
51
- # Bits are read from LSB to MSB, starting from the end of the stream.
68
+ # Read `count` bits, first-read bit becoming the value's MSB
69
+ # (little-endian value semantics per RFC 8878 §4.1).
52
70
  #
53
- # @param count [Integer] Number of bits to read
54
- # @return [Integer] The read bits
71
+ # @param count [Integer] 0..32
72
+ # @return [Integer]
55
73
  def read_bits(count)
56
74
  return 0 if count.zero?
57
75
 
58
- result = 0
59
- count.times do |i|
60
- bit = read_single_bit
61
- result |= (bit << i)
62
- end
63
- result
76
+ shift_left = @bits_consumed & 63
77
+ shift_right = (64 - count) & 63
78
+ result = ((@container << shift_left) & MASK64) >> shift_right
79
+ @bits_consumed += count
80
+ result & ((1 << count) - 1)
64
81
  end
65
82
 
66
- # Peek at bits without consuming them
67
- #
68
- # @param count [Integer] Number of bits to peek
69
- # @return [Integer] The peeked bits
83
+ # Peek `count` bits without consuming.
70
84
  def peek_bits(count)
71
- saved_position = @bit_position
85
+ saved_ptr = @ptr
86
+ saved_container = @container
87
+ saved_consumed = @bits_consumed
72
88
  result = read_bits(count)
73
- @bit_position = saved_position
89
+ @ptr = saved_ptr
90
+ @container = saved_container
91
+ @bits_consumed = saved_consumed
74
92
  result
75
93
  end
76
94
 
77
- # Check if bitstream is exhausted
78
- #
79
- # @return [Boolean]
80
- def exhausted?
81
- @bit_position <= 0
95
+ # Reload the container after enough bits have been consumed,
96
+ # mirroring C's BIT_reloadDStream.
97
+ def reload
98
+ return if @bits_consumed < 8
99
+
100
+ if @ptr >= 8
101
+ bytes_consumed = @bits_consumed >> 3
102
+ @ptr -= bytes_consumed
103
+ @ptr = 0 if @ptr.negative?
104
+ @bits_consumed &= 7
105
+ load_container
106
+ elsif @ptr.zero?
107
+ # No earlier bytes to load; leave the container as is.
108
+ else
109
+ nb_bytes = @bits_consumed >> 3
110
+ actual_bytes = [nb_bytes, @ptr].min
111
+ @ptr -= actual_bytes
112
+ @bits_consumed -= actual_bytes * 8
113
+ load_container
114
+ end
82
115
  end
83
116
 
84
- # Get remaining bits
117
+ # Full reload that also reports the C reload status.
85
118
  #
86
- # @return [Integer]
87
- def remaining_bits
88
- @bit_position
119
+ # @return [Symbol] one of UNFINISHED, END_OF_BUFFER, COMPLETED,
120
+ # OVERFLOW
121
+ def reload_status
122
+ return OVERFLOW if @bits_consumed > 64
123
+
124
+ if @ptr >= 8
125
+ bytes_consumed = @bits_consumed >> 3
126
+ @ptr -= bytes_consumed
127
+ @ptr = 0 if @ptr.negative?
128
+ @bits_consumed &= 7
129
+ load_container
130
+ return UNFINISHED
131
+ end
132
+ return END_OF_BUFFER if @ptr.zero?
133
+
134
+ nb_bytes = @bits_consumed >> 3
135
+ actual_bytes = [nb_bytes, @ptr].min
136
+ @ptr -= actual_bytes
137
+ @bits_consumed -= actual_bytes * 8
138
+ load_container
139
+ actual_bytes < nb_bytes ? END_OF_BUFFER : UNFINISHED
89
140
  end
90
141
 
91
- # Align to byte boundary (skip remaining bits in current byte)
142
+ # Skip to the next byte boundary (toward the stream start).
92
143
  def align_to_byte
93
- @bit_position = ((@bit_position + 7) / 8) * 8
94
- end
95
-
96
- private
144
+ remainder = @bits_consumed % 8
145
+ return if remainder.zero?
97
146
 
98
- # Read a single bit from the stream
99
- #
100
- # @return [Integer] 0 or 1
101
- def read_single_bit
102
- return 0 if @bit_position <= 0
103
-
104
- @bit_position -= 1
105
- byte_index = @bit_position / 8
106
- bit_index = @bit_position % 8
107
-
108
- return 0 if byte_index.negative? || byte_index >= @data.bytesize
109
-
110
- byte = @data.getbyte(byte_index)
111
- (byte >> bit_index) & 1
147
+ @bits_consumed += 8 - remainder
148
+ reload if @bits_consumed >= 8
112
149
  end
113
- end
114
150
 
115
- # Forward bitstream reader (for Huffman decoding)
116
- #
117
- # Reads bits in normal forward order from a starting position.
118
- class ForwardBitStream
119
- # @return [String] The compressed data
120
- attr_reader :data
121
-
122
- # @return [Integer] Current bit position
123
- attr_reader :bit_position
124
-
125
- # Initialize bitstream with data
126
- #
127
- # @param data [String] The compressed bitstream data
128
- # @param start_byte [Integer] Starting byte position
129
- def initialize(data, start_byte = 0)
130
- @data = data.dup.force_encoding(Encoding::BINARY)
131
- @bit_position = start_byte * 8
132
- end
133
-
134
- # Read bits from the stream (in forward order)
135
- #
136
- # Bits are read from MSB to LSB within each byte.
137
- #
138
- # @param count [Integer] Number of bits to read
139
- # @return [Integer] The read bits
140
- def read_bits(count)
141
- return 0 if count.zero?
142
-
143
- result = 0
144
- count.times do
145
- result = (result << 1) | read_single_bit
146
- end
147
- result
151
+ # Bits not yet consumed, counting from the end of the data.
152
+ def remaining_bits
153
+ total = @data.bytesize * 8
154
+ consumed = bits_consumed_so_far
155
+ total >= consumed ? total - consumed : 0
148
156
  end
149
157
 
150
- # Check if bitstream is exhausted
151
- #
152
- # @return [Boolean]
153
158
  def exhausted?
154
- @bit_position >= @data.bytesize * 8
155
- end
156
-
157
- # Get current byte position
158
- #
159
- # @return [Integer]
160
- def byte_position
161
- @bit_position / 8
159
+ remaining_bits.zero?
162
160
  end
163
161
 
164
162
  private
165
163
 
166
- # Read a single bit from the stream
167
- #
168
- # @return [Integer] 0 or 1
169
- def read_single_bit
170
- return 0 if exhausted?
171
-
172
- byte_index = @bit_position / 8
173
- bit_index = 7 - (@bit_position % 8) # MSB first
174
-
175
- @bit_position += 1
164
+ def bits_consumed_so_far
165
+ bytes_after_window = @data.bytesize - (@ptr + 8)
166
+ bytes_after_window = 0 if bytes_after_window.negative?
167
+ (bytes_after_window * 8) + @bits_consumed
168
+ end
176
169
 
177
- return 0 if byte_index >= @data.bytesize
170
+ def read_le64(offset)
171
+ v = 0
172
+ 8.times { |i| v |= @data.getbyte(offset + i) << (i * 8) }
173
+ v
174
+ end
178
175
 
179
- byte = @data.getbyte(byte_index)
180
- (byte >> bit_index) & 1
176
+ def load_container
177
+ @container = read_le64(@ptr)
181
178
  end
182
179
  end
183
180
  end