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.
- checksums.yaml +4 -4
- data/lib/omnizip/algorithm.rb +36 -1
- data/lib/omnizip/algorithms/bzip2.rb +1 -3
- data/lib/omnizip/algorithms/deflate.rb +1 -3
- data/lib/omnizip/algorithms/deflate64.rb +2 -2
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +7 -8
- data/lib/omnizip/algorithms/lzma.rb +1 -1
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +5 -1
- data/lib/omnizip/algorithms/lzma2.rb +4 -12
- data/lib/omnizip/algorithms/zstandard/constants.rb +38 -25
- data/lib/omnizip/algorithms/zstandard/decoder.rb +101 -136
- data/lib/omnizip/algorithms/zstandard/encoder.rb +81 -138
- data/lib/omnizip/algorithms/zstandard/frame/block.rb +17 -0
- data/lib/omnizip/algorithms/zstandard/frame/header.rb +42 -29
- data/lib/omnizip/algorithms/zstandard/fse/bitstream.rb +114 -117
- data/lib/omnizip/algorithms/zstandard/fse/encoder.rb +434 -222
- data/lib/omnizip/algorithms/zstandard/fse/interleaved.rb +92 -0
- data/lib/omnizip/algorithms/zstandard/fse/table.rb +98 -176
- data/lib/omnizip/algorithms/zstandard/fse/table_description.rb +203 -0
- data/lib/omnizip/algorithms/zstandard/fse.rb +21 -2
- data/lib/omnizip/algorithms/zstandard/huffman.rb +195 -188
- data/lib/omnizip/algorithms/zstandard/huffman_encoder.rb +232 -255
- data/lib/omnizip/algorithms/zstandard/literals.rb +170 -103
- data/lib/omnizip/algorithms/zstandard/literals_encoder.rb +47 -199
- data/lib/omnizip/algorithms/zstandard/sequences.rb +260 -252
- data/lib/omnizip/algorithms/zstandard/xxhash.rb +159 -0
- data/lib/omnizip/algorithms/zstandard.rb +20 -24
- data/lib/omnizip/algorithms.rb +14 -1
- data/lib/omnizip/buffer.rb +1 -2
- data/lib/omnizip/commands/archive_list_command.rb +9 -7
- data/lib/omnizip/convenience.rb +1 -2
- data/lib/omnizip/entry.rb +44 -0
- data/lib/omnizip/extraction/selective_extractor.rb +2 -10
- data/lib/omnizip/filter_pipeline.rb +1 -1
- data/lib/omnizip/filter_registry.rb +18 -16
- data/lib/omnizip/filters/bcj2.rb +1 -1
- data/lib/omnizip/filters/bcj_arm.rb +1 -1
- data/lib/omnizip/filters/bcj_arm64.rb +1 -1
- data/lib/omnizip/filters/bcj_ia64.rb +1 -1
- data/lib/omnizip/filters/bcj_ppc.rb +1 -1
- data/lib/omnizip/filters/bcj_sparc.rb +1 -1
- data/lib/omnizip/filters/bcj_x86.rb +1 -1
- data/lib/omnizip/filters.rb +0 -2
- data/lib/omnizip/formats/bzip2_file.rb +0 -7
- data/lib/omnizip/formats/cpio/entry.rb +6 -0
- data/lib/omnizip/formats/cpio.rb +0 -6
- data/lib/omnizip/formats/gzip.rb +0 -7
- data/lib/omnizip/formats/iso/directory_record.rb +7 -0
- data/lib/omnizip/formats/iso.rb +0 -5
- data/lib/omnizip/formats/lzip.rb +0 -7
- data/lib/omnizip/formats/lzma_alone.rb +0 -6
- data/lib/omnizip/formats/msi/entry.rb +6 -0
- data/lib/omnizip/formats/msi.rb +0 -9
- data/lib/omnizip/formats/ole/dirent.rb +6 -0
- data/lib/omnizip/formats/ole.rb +0 -9
- data/lib/omnizip/formats/rar.rb +0 -6
- data/lib/omnizip/formats/rar3/reader.rb +7 -0
- data/lib/omnizip/formats/rar5/reader.rb +7 -0
- data/lib/omnizip/formats/rpm/entry.rb +6 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +7 -0
- data/lib/omnizip/formats/seven_zip.rb +0 -6
- data/lib/omnizip/formats/tar/entry.rb +6 -0
- data/lib/omnizip/formats/tar.rb +2 -10
- data/lib/omnizip/formats/xar/entry.rb +6 -0
- data/lib/omnizip/formats/xar.rb +0 -6
- data/lib/omnizip/formats/zip.rb +0 -6
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +33 -31
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +177 -163
- data/lib/omnizip/implementations.rb +18 -4
- data/lib/omnizip/io/source.rb +3 -0
- data/lib/omnizip/metadata/entry_metadata.rb +7 -0
- data/lib/omnizip/parallel/engine.rb +48 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +5 -16
- data/lib/omnizip/parallel/parallel_extractor.rb +5 -16
- data/lib/omnizip/parallel.rb +1 -0
- data/lib/omnizip/profile/profile_registry.rb +1 -2
- data/lib/omnizip/registry.rb +0 -1
- data/lib/omnizip/rubyzip_compat.rb +0 -1
- data/lib/omnizip/temp/temp_file.rb +1 -2
- data/lib/omnizip/version.rb +1 -1
- data/lib/omnizip/zip/entry.rb +7 -0
- data/lib/omnizip/zip/file.rb +4 -8
- data/lib/omnizip.rb +1 -1
- metadata +7 -6
- data/lib/omnizip/filters/filter_base.rb +0 -6
- data/lib/omnizip/filters/registration.rb +0 -22
- data/lib/omnizip/filters/registry.rb +0 -111
- data/lib/omnizip/format_registry.rb +0 -100
|
@@ -23,318 +23,326 @@
|
|
|
23
23
|
module Omnizip
|
|
24
24
|
module Algorithms
|
|
25
25
|
class Zstandard
|
|
26
|
-
# Sequences section decoder (RFC 8878
|
|
26
|
+
# Sequences section decoder (RFC 8878 §3.1.1.3.2) and the LZ77
|
|
27
|
+
# executor (§3.1.2).
|
|
27
28
|
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
29
|
+
# Decoding follows the C reference ZSTD_decodeSequence:
|
|
30
|
+
#
|
|
31
|
+
# - State initialization order: LL, OF, ML.
|
|
32
|
+
# - Per sequence: resolve the offset first (reading its extra
|
|
33
|
+
# bits and updating the repeat-offset slots), then read the ML
|
|
34
|
+
# extra bits, then the LL extra bits.
|
|
35
|
+
# - State updates (non-last sequences) in order LL, ML, OF.
|
|
30
36
|
class SequencesDecoder
|
|
31
37
|
include Constants
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
Sequence = Struct.new(:literal_length, :match_length, :offset)
|
|
40
|
+
|
|
41
|
+
# @return [Array<Sequence>]
|
|
34
42
|
attr_reader :sequences
|
|
35
43
|
|
|
36
|
-
#
|
|
44
|
+
# @return [Hash] FSE tables for the next block's Repeat mode
|
|
45
|
+
attr_reader :fse_tables
|
|
46
|
+
|
|
47
|
+
# Decode the sequences section at the head of `input`.
|
|
37
48
|
#
|
|
38
|
-
# @param input [
|
|
39
|
-
# @param
|
|
40
|
-
#
|
|
41
|
-
# @
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
# @param input [String]
|
|
50
|
+
# @param previous_tables [Hash] tables carried from the previous
|
|
51
|
+
# compressed block (:ll, :ml, :of entries)
|
|
52
|
+
# @param executor [SequenceExecutor] repeat-offset state,
|
|
53
|
+
# updated in place while decoding
|
|
54
|
+
# @return [SequencesDecoder]
|
|
55
|
+
def self.decode(input, previous_tables = {}, executor = nil)
|
|
56
|
+
new(input, previous_tables, executor || SequenceExecutor.new).decode_section
|
|
46
57
|
end
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
#
|
|
50
|
-
# @param input [IO] Input stream
|
|
51
|
-
# @param literals_size [Integer] Size of decoded literals
|
|
52
|
-
# @param previous_tables [Hash] Previous FSE tables
|
|
53
|
-
def initialize(input, literals_size, previous_tables = {})
|
|
59
|
+
def initialize(input, previous_tables, executor)
|
|
54
60
|
@input = input
|
|
55
|
-
@literals_size = literals_size
|
|
56
61
|
@previous_tables = previous_tables
|
|
62
|
+
@executor = executor
|
|
57
63
|
@sequences = []
|
|
58
64
|
@fse_tables = {}
|
|
59
65
|
end
|
|
60
66
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# @return [void]
|
|
67
|
+
# rubocop:disable Metrics/MethodLength
|
|
68
|
+
# rubocop:disable Metrics/AbcSize
|
|
64
69
|
def decode_section
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
num_sequences, offset = read_sequence_count
|
|
71
|
+
return self if num_sequences.zero?
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
if @input.bytesize <= offset
|
|
74
|
+
raise Omnizip::DecompressionError,
|
|
75
|
+
"truncated sequences section: missing modes byte"
|
|
76
|
+
end
|
|
77
|
+
# rubocop:enable Metrics/AbcSize
|
|
78
|
+
|
|
79
|
+
modes = @input.getbyte(offset)
|
|
80
|
+
ll_mode = (modes >> 6) & 0x03
|
|
81
|
+
of_mode = (modes >> 4) & 0x03
|
|
82
|
+
ml_mode = (modes >> 2) & 0x03
|
|
83
|
+
|
|
84
|
+
cursor = offset + 1
|
|
85
|
+
@ll_table, cursor = build_table(ll_mode, :ll,
|
|
86
|
+
PREDEFINED_LL_DISTRIBUTION,
|
|
87
|
+
LITERALS_LENGTH_ACCURACY_LOG, cursor)
|
|
88
|
+
@of_table, cursor = build_table(of_mode, :of,
|
|
89
|
+
PREDEFINED_OFFSET_DISTRIBUTION,
|
|
90
|
+
OFFSET_ACCURACY_LOG, cursor)
|
|
91
|
+
@ml_table, cursor = build_table(ml_mode, :ml,
|
|
92
|
+
PREDEFINED_ML_DISTRIBUTION,
|
|
93
|
+
MATCH_LENGTH_ACCURACY_LOG, cursor)
|
|
94
|
+
|
|
95
|
+
decode_sequences(num_sequences, @input.byteslice(cursor..))
|
|
96
|
+
self
|
|
78
97
|
end
|
|
98
|
+
# rubocop:enable Metrics/AbcSize
|
|
99
|
+
# rubocop:enable Metrics/MethodLength
|
|
79
100
|
|
|
80
101
|
private
|
|
81
102
|
|
|
82
|
-
#
|
|
103
|
+
# Sequence count: 1-3 bytes (RFC 8878 §3.1.1.3.2.1).
|
|
104
|
+
# rubocop:disable Metrics/AbcSize
|
|
83
105
|
def read_sequence_count
|
|
84
|
-
|
|
106
|
+
b0 = @input.getbyte(0)
|
|
107
|
+
if b0 < 0x80
|
|
108
|
+
[b0, 1]
|
|
109
|
+
elsif b0 == 0xFF
|
|
110
|
+
if @input.bytesize < 3
|
|
111
|
+
raise Omnizip::DecompressionError,
|
|
112
|
+
"truncated 3-byte sequence count"
|
|
113
|
+
end
|
|
114
|
+
# rubocop:enable Metrics/AbcSize
|
|
85
115
|
|
|
86
|
-
|
|
87
|
-
0
|
|
88
|
-
elsif byte1 < 128
|
|
89
|
-
byte1
|
|
116
|
+
[@input.byteslice(1, 2).unpack1("v") + 0x7F00, 3]
|
|
90
117
|
else
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
# Read symbol compression modes for LL, ML, OF
|
|
97
|
-
def read_symbol_modes
|
|
98
|
-
modes_byte = @input.read(1).ord
|
|
99
|
-
|
|
100
|
-
{
|
|
101
|
-
ll: (modes_byte >> 6) & 0x03, # Literals length mode
|
|
102
|
-
of: (modes_byte >> 4) & 0x03, # Offset mode
|
|
103
|
-
ml: (modes_byte >> 2) & 0x03, # Match length mode
|
|
104
|
-
}
|
|
105
|
-
end
|
|
118
|
+
if @input.bytesize < 2
|
|
119
|
+
raise Omnizip::DecompressionError,
|
|
120
|
+
"truncated 2-byte sequence count"
|
|
121
|
+
end
|
|
106
122
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
@fse_tables[:ll] = build_fse_table(modes[:ll], :ll)
|
|
110
|
-
@fse_tables[:ml] = build_fse_table(modes[:ml], :ml)
|
|
111
|
-
@fse_tables[:of] = build_fse_table(modes[:of], :of)
|
|
123
|
+
[((b0 - 0x80) << 8) + @input.getbyte(1), 2]
|
|
124
|
+
end
|
|
112
125
|
end
|
|
113
126
|
|
|
114
|
-
# Build
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
# Build the table for one symbol type. Returns [table, new_cursor].
|
|
128
|
+
# rubocop:disable Metrics/AbcSize
|
|
129
|
+
def build_table(mode, type, predef_distribution, predef_log, cursor)
|
|
130
|
+
table = case mode
|
|
131
|
+
when MODE_PREDEFINED
|
|
132
|
+
FSE::Table.build_predefined(predef_distribution.to_a,
|
|
133
|
+
predef_log)
|
|
134
|
+
when MODE_RLE
|
|
135
|
+
if cursor >= @input.bytesize
|
|
136
|
+
raise Omnizip::DecompressionError,
|
|
137
|
+
"RLE mode: missing symbol byte"
|
|
138
|
+
end
|
|
139
|
+
# rubocop:enable Metrics/AbcSize
|
|
140
|
+
|
|
141
|
+
symbol = @input.getbyte(cursor)
|
|
142
|
+
limit = { ll: LITERAL_LENGTH_TABLE.length,
|
|
143
|
+
ml: MATCH_LENGTH_TABLE.length,
|
|
144
|
+
of: OF_BASE.length }[type]
|
|
145
|
+
if symbol >= limit
|
|
146
|
+
raise Omnizip::DecompressionError,
|
|
147
|
+
"RLE symbol #{symbol} out of range for #{type}"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
FSE::Table.build_rle(symbol, 0)
|
|
151
|
+
when MODE_FSE
|
|
152
|
+
tbl, consumed = FSE.read_table(@input.byteslice(cursor..))
|
|
153
|
+
attach_code_values(tbl, type)
|
|
154
|
+
@fse_tables[type] = tbl
|
|
155
|
+
return [tbl, cursor + consumed]
|
|
156
|
+
when MODE_REPEAT
|
|
157
|
+
prev = @previous_tables[type]
|
|
158
|
+
if prev.nil?
|
|
159
|
+
raise Omnizip::DecompressionError,
|
|
160
|
+
"repeat mode for #{type} without a previous table"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
return [prev, cursor]
|
|
164
|
+
else
|
|
165
|
+
raise Omnizip::DecompressionError,
|
|
166
|
+
"invalid sequence mode #{mode}"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
attach_code_values(table, type)
|
|
170
|
+
@fse_tables[type] = table
|
|
171
|
+
cursor2 = mode == MODE_RLE ? cursor + 1 : cursor
|
|
172
|
+
[table, cursor2]
|
|
126
173
|
end
|
|
127
174
|
|
|
128
|
-
#
|
|
129
|
-
|
|
175
|
+
# Annotate each FSE state with the (base, extra_bits) of the LL /
|
|
176
|
+
# ML / OF code its symbol maps to, following the C reference's
|
|
177
|
+
# precomputed seqSymbol layout.
|
|
178
|
+
# rubocop:disable Metrics/AbcSize
|
|
179
|
+
def attach_code_values(table, type)
|
|
130
180
|
case type
|
|
131
181
|
when :ll
|
|
132
|
-
|
|
133
|
-
|
|
182
|
+
table.states.each do |st|
|
|
183
|
+
base, bits = LITERAL_LENGTH_TABLE[st.symbol] || [0, 0]
|
|
184
|
+
st.base_val = base
|
|
185
|
+
st.nb_add_bits = bits
|
|
186
|
+
end
|
|
187
|
+
# rubocop:enable Metrics/AbcSize
|
|
134
188
|
when :ml
|
|
135
|
-
|
|
136
|
-
|
|
189
|
+
table.states.each do |st|
|
|
190
|
+
base, bits = MATCH_LENGTH_TABLE[st.symbol] || [0, 0]
|
|
191
|
+
st.base_val = base
|
|
192
|
+
st.nb_add_bits = bits
|
|
193
|
+
end
|
|
137
194
|
when :of
|
|
138
|
-
|
|
139
|
-
|
|
195
|
+
table.states.each do |st|
|
|
196
|
+
st.base_val = OF_BASE[st.symbol] || 0
|
|
197
|
+
st.nb_add_bits = OF_BITS[st.symbol] || 0
|
|
198
|
+
end
|
|
140
199
|
end
|
|
141
200
|
end
|
|
142
201
|
|
|
143
|
-
#
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
202
|
+
# rubocop:disable Metrics/MethodLength
|
|
203
|
+
# rubocop:disable Metrics/AbcSize
|
|
204
|
+
def decode_sequences(count, bitstream_bytes)
|
|
205
|
+
bs = FSE::BitStream.new(bitstream_bytes)
|
|
206
|
+
|
|
207
|
+
ll_tbl = @ll_table
|
|
208
|
+
of_tbl = @of_table
|
|
209
|
+
ml_tbl = @ml_table
|
|
210
|
+
ll_state = bs.read_bits(ll_tbl.accuracy_log)
|
|
211
|
+
bs.reload
|
|
212
|
+
of_state = bs.read_bits(of_tbl.accuracy_log)
|
|
213
|
+
bs.reload
|
|
214
|
+
ml_state = bs.read_bits(ml_tbl.accuracy_log)
|
|
215
|
+
bs.reload
|
|
216
|
+
|
|
217
|
+
count.times do |seq_idx|
|
|
218
|
+
is_last = seq_idx == count - 1
|
|
219
|
+
|
|
220
|
+
ll_e = ll_tbl[ll_state]
|
|
221
|
+
of_e = of_tbl[of_state]
|
|
222
|
+
ml_e = ml_tbl[ml_state]
|
|
223
|
+
|
|
224
|
+
ll0 = ll_e.base_val.zero?
|
|
225
|
+
offset = @executor.resolve_offset(of_e, ll0, bs)
|
|
226
|
+
match_length = ml_e.base_val + bs.read_bits(ml_e.nb_add_bits)
|
|
227
|
+
literal_length = ll_e.base_val + bs.read_bits(ll_e.nb_add_bits)
|
|
228
|
+
|
|
229
|
+
unless is_last
|
|
230
|
+
ll_state = ll_e.baseline + bs.read_bits(ll_e.num_bits)
|
|
231
|
+
ml_state = ml_e.baseline + bs.read_bits(ml_e.num_bits)
|
|
232
|
+
of_state = of_e.baseline + bs.read_bits(of_e.num_bits)
|
|
233
|
+
bs.reload
|
|
234
|
+
end
|
|
235
|
+
# rubocop:enable Metrics/AbcSize
|
|
164
236
|
|
|
165
|
-
|
|
166
|
-
def decode_sequences_internal(count)
|
|
167
|
-
return if count.zero?
|
|
168
|
-
|
|
169
|
-
# Read the bitstream (remaining data in block)
|
|
170
|
-
bitstream_data = @input.read # Read remaining data
|
|
171
|
-
bitstream = FSE::BitStream.new(bitstream_data)
|
|
172
|
-
|
|
173
|
-
# Initialize FSE decoders
|
|
174
|
-
ll_decoder = FSE::Decoder.new(@fse_tables[:ll]) if @fse_tables[:ll]
|
|
175
|
-
ml_decoder = FSE::Decoder.new(@fse_tables[:ml]) if @fse_tables[:ml]
|
|
176
|
-
of_decoder = FSE::Decoder.new(@fse_tables[:of]) if @fse_tables[:of]
|
|
177
|
-
|
|
178
|
-
# Initialize states
|
|
179
|
-
ll_decoder&.init_state(bitstream)
|
|
180
|
-
ml_decoder&.init_state(bitstream)
|
|
181
|
-
of_decoder&.init_state(bitstream)
|
|
182
|
-
|
|
183
|
-
# Decode each sequence
|
|
184
|
-
count.times do
|
|
185
|
-
ll_symbol = ll_decoder ? ll_decoder.decode(bitstream) : 0
|
|
186
|
-
ml_symbol = ml_decoder ? ml_decoder.decode(bitstream) : 0
|
|
187
|
-
of_symbol = of_decoder ? of_decoder.decode(bitstream) : 0
|
|
188
|
-
|
|
189
|
-
# Convert symbols to actual values
|
|
190
|
-
ll_value = decode_literal_length(ll_symbol, bitstream)
|
|
191
|
-
ml_value = decode_match_length(ml_symbol, bitstream)
|
|
192
|
-
of_value = decode_offset(of_symbol, bitstream)
|
|
193
|
-
|
|
194
|
-
@sequences << {
|
|
195
|
-
literals_length: ll_value,
|
|
196
|
-
match_length: ml_value,
|
|
197
|
-
offset: of_value,
|
|
198
|
-
}
|
|
237
|
+
@sequences << Sequence.new(literal_length, match_length, offset)
|
|
199
238
|
end
|
|
200
239
|
end
|
|
240
|
+
# rubocop:enable Metrics/MethodLength
|
|
241
|
+
end
|
|
201
242
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return baseline if extra_bits.zero?
|
|
208
|
-
|
|
209
|
-
extra = bitstream.read_bits(extra_bits)
|
|
210
|
-
baseline + extra
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# Decode match length value from symbol
|
|
214
|
-
def decode_match_length(symbol, bitstream)
|
|
215
|
-
return 3 if symbol.nil? || symbol.negative? || symbol >= MATCH_LENGTH_TABLE.length
|
|
216
|
-
|
|
217
|
-
baseline, extra_bits = MATCH_LENGTH_TABLE[symbol]
|
|
218
|
-
return baseline if extra_bits.zero?
|
|
219
|
-
|
|
220
|
-
extra = bitstream.read_bits(extra_bits)
|
|
221
|
-
baseline + extra
|
|
222
|
-
end
|
|
243
|
+
# Stateful LZ77 sequence executor (RFC 8878 §3.1.2). Tracks the
|
|
244
|
+
# three repeat-offset slots across the frame; the slots are
|
|
245
|
+
# updated while decoding each sequence's offset.
|
|
246
|
+
class SequenceExecutor
|
|
247
|
+
include Constants
|
|
223
248
|
|
|
224
|
-
#
|
|
225
|
-
|
|
226
|
-
# Offsets 1-3 are repeat offsets
|
|
227
|
-
return symbol if symbol <= 3
|
|
249
|
+
# @return [Array<Integer>] repeat offsets, most recent first
|
|
250
|
+
attr_reader :repeat_offsets
|
|
228
251
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
symbol - 3
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
# Get symbol count for type
|
|
235
|
-
def symbol_count_for_type(type)
|
|
236
|
-
case type
|
|
237
|
-
when :ll then LITERAL_LENGTH_TABLE.length
|
|
238
|
-
when :ml then MATCH_LENGTH_TABLE.length
|
|
239
|
-
when :of then 32
|
|
240
|
-
end
|
|
252
|
+
def initialize
|
|
253
|
+
@repeat_offsets = DEFAULT_REPEAT_OFFSETS.dup
|
|
241
254
|
end
|
|
242
255
|
|
|
243
|
-
#
|
|
244
|
-
def
|
|
245
|
-
|
|
246
|
-
when :ll then LITERALS_LENGTH_ACCURACY_LOG
|
|
247
|
-
when :ml then MATCH_LENGTH_ACCURACY_LOG
|
|
248
|
-
when :of then OFFSET_ACCURACY_LOG
|
|
249
|
-
end
|
|
256
|
+
# Reset to the frame defaults [1, 4, 8].
|
|
257
|
+
def reset
|
|
258
|
+
@repeat_offsets = DEFAULT_REPEAT_OFFSETS.dup
|
|
250
259
|
end
|
|
251
|
-
end
|
|
252
260
|
|
|
253
|
-
|
|
254
|
-
#
|
|
255
|
-
# Executes decoded sequences to produce output.
|
|
256
|
-
class SequenceExecutor
|
|
257
|
-
include Constants
|
|
258
|
-
|
|
259
|
-
# Execute sequences to produce decompressed output
|
|
261
|
+
# Resolve a sequence offset (C reference semantics).
|
|
260
262
|
#
|
|
261
|
-
#
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
263
|
+
# Offset codes 0 and 1 address the repeat slots; the exact
|
|
264
|
+
# behaviour depends on ll0 (whether the sequence emits no
|
|
265
|
+
# literals). Codes >= 2 carry a real distance:
|
|
266
|
+
# distance = OF_BASE[code] + read(code bits).
|
|
267
|
+
#
|
|
268
|
+
# @param of_e [FSE::State] offset table entry for this sequence
|
|
269
|
+
# @param ll0 [Boolean] literal length is zero
|
|
270
|
+
# @param bs [FSE::BitStream]
|
|
271
|
+
# @return [Integer] resolved byte distance
|
|
272
|
+
# rubocop:disable Metrics/AbcSize
|
|
273
|
+
def resolve_offset(of_e, ll0, bs)
|
|
274
|
+
of_bits = of_e.nb_add_bits
|
|
275
|
+
prev = @repeat_offsets
|
|
276
|
+
|
|
277
|
+
if of_bits > 1
|
|
278
|
+
offset = of_e.base_val + bs.read_bits(of_bits)
|
|
279
|
+
prev[2] = prev[1]
|
|
280
|
+
prev[1] = prev[0]
|
|
281
|
+
prev[0] = offset
|
|
282
|
+
offset
|
|
283
|
+
elsif of_bits.zero?
|
|
284
|
+
offset = prev[ll0 ? 1 : 0]
|
|
285
|
+
if ll0
|
|
286
|
+
prev[1] = prev[0]
|
|
287
|
+
prev[0] = offset
|
|
288
|
+
end
|
|
289
|
+
# rubocop:enable Metrics/AbcSize
|
|
290
|
+
offset
|
|
291
|
+
else
|
|
292
|
+
# of_bits == 1: the "rep3-1" special family.
|
|
293
|
+
selector = of_e.base_val + (ll0 ? 1 : 0) + bs.read_bits(1)
|
|
294
|
+
temp = case selector
|
|
295
|
+
when 1 then prev[1]
|
|
296
|
+
when 3 then prev[0] - 1
|
|
297
|
+
else prev[2]
|
|
298
|
+
end
|
|
299
|
+
if temp.zero?
|
|
300
|
+
raise Omnizip::DecompressionError,
|
|
301
|
+
"repeat offset 1 - 1 evaluated to 0 (corrupt stream)"
|
|
302
|
+
end
|
|
268
303
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
304
|
+
prev[2] = prev[1] unless selector == 1
|
|
305
|
+
prev[1] = prev[0]
|
|
306
|
+
prev[0] = temp
|
|
307
|
+
temp
|
|
308
|
+
end
|
|
272
309
|
end
|
|
273
310
|
|
|
274
|
-
# Execute sequences
|
|
311
|
+
# Execute decoded sequences against the literal buffer,
|
|
312
|
+
# appending to output and returning the produced bytes.
|
|
275
313
|
#
|
|
276
|
-
# @param literals [String]
|
|
277
|
-
# @param sequences [Array<
|
|
278
|
-
# @
|
|
279
|
-
|
|
280
|
-
|
|
314
|
+
# @param literals [String]
|
|
315
|
+
# @param sequences [Array<SequencesDecoder::Sequence>]
|
|
316
|
+
# @param output [String] accumulator (frame output so far)
|
|
317
|
+
# @return [String] the block's output
|
|
318
|
+
# rubocop:disable Metrics/AbcSize
|
|
319
|
+
def execute(literals, sequences, output)
|
|
281
320
|
lit_pos = 0
|
|
321
|
+
block_start = output.bytesize
|
|
282
322
|
|
|
283
323
|
sequences.each do |seq|
|
|
284
|
-
ll = seq
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if ll.positive? && lit_pos < literals.length
|
|
290
|
-
copy_len = [ll, literals.length - lit_pos].min
|
|
291
|
-
output << literals.slice(lit_pos, copy_len)
|
|
292
|
-
lit_pos += copy_len
|
|
324
|
+
ll = seq.literal_length
|
|
325
|
+
if ll.positive?
|
|
326
|
+
take = [ll, literals.bytesize - lit_pos].min
|
|
327
|
+
output << literals.byteslice(lit_pos, take)
|
|
328
|
+
lit_pos += take
|
|
293
329
|
end
|
|
330
|
+
# rubocop:enable Metrics/AbcSize
|
|
294
331
|
|
|
295
|
-
|
|
296
|
-
if
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
# Copy match from output history
|
|
301
|
-
match_str = output.slice(-offset, [ml, offset].min) || ""
|
|
302
|
-
# If match is longer than offset, we need to copy byte by byte
|
|
303
|
-
while match_str.length < ml && output.length.positive?
|
|
304
|
-
match_str << match_str[-offset] if offset <= match_str.length
|
|
305
|
-
end
|
|
306
|
-
output << match_str.slice(0, ml)
|
|
307
|
-
end
|
|
332
|
+
distance = seq.offset
|
|
333
|
+
if distance.zero? || distance > output.bytesize
|
|
334
|
+
raise Omnizip::DecompressionError,
|
|
335
|
+
"match distance #{distance} exceeds decoded output " \
|
|
336
|
+
"length #{output.bytesize}"
|
|
308
337
|
end
|
|
309
|
-
end
|
|
310
338
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
output <<
|
|
339
|
+
ml = seq.match_length
|
|
340
|
+
src_start = output.bytesize - distance
|
|
341
|
+
ml.times { |i| output << output.getbyte(src_start + (i % distance)) }
|
|
314
342
|
end
|
|
315
343
|
|
|
316
|
-
output
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
private
|
|
320
|
-
|
|
321
|
-
# Resolve offset code to actual offset
|
|
322
|
-
def resolve_offset(code)
|
|
323
|
-
case code
|
|
324
|
-
when 1
|
|
325
|
-
@repeat_offsets[0]
|
|
326
|
-
when 2
|
|
327
|
-
@repeat_offsets[1]
|
|
328
|
-
when 3
|
|
329
|
-
@repeat_offsets[2]
|
|
330
|
-
else
|
|
331
|
-
# New offset - update repeat offsets
|
|
332
|
-
actual_offset = code - 3
|
|
333
|
-
@repeat_offsets[2] = @repeat_offsets[1]
|
|
334
|
-
@repeat_offsets[1] = @repeat_offsets[0]
|
|
335
|
-
@repeat_offsets[0] = actual_offset
|
|
336
|
-
actual_offset
|
|
337
|
-
end
|
|
344
|
+
output << literals.byteslice(lit_pos..) if lit_pos < literals.bytesize
|
|
345
|
+
output.byteslice(block_start..)
|
|
338
346
|
end
|
|
339
347
|
end
|
|
340
348
|
end
|