omnizip 0.3.8 → 0.3.10

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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +51 -50
  3. data/Gemfile +2 -0
  4. data/Rakefile +3 -0
  5. data/TODO.refactor/00-overview.md +67 -0
  6. data/TODO.refactor/01-registry-base-class.md +81 -0
  7. data/TODO.refactor/02-error-hierarchy.md +57 -0
  8. data/TODO.refactor/03-autoload-entry-point.md +69 -0
  9. data/TODO.refactor/04-filter-base-consolidation.md +49 -0
  10. data/TODO.refactor/05-send-private-methods.md +64 -0
  11. data/TODO.refactor/06-instance-variable-access.md +69 -0
  12. data/TODO.refactor/07-respond-to-replacement.md +91 -0
  13. data/TODO.refactor/08-spec-doubles.md +27 -0
  14. data/TODO.refactor/09-cli-shared-module.md +35 -0
  15. data/TODO.refactor/10-format-detector-ocp.md +34 -0
  16. data/TODO.refactor/11-convenience-decoupling.md +38 -0
  17. data/TODO.refactor/12-thread-safety.md +8 -0
  18. data/TODO.refactor/13-lutaml-model-migration.md +82 -0
  19. data/TODO.refactor/14-add-missing-specs.md +25 -0
  20. data/lib/omnizip/algorithm.rb +0 -2
  21. data/lib/omnizip/algorithm_registry.rb +29 -57
  22. data/lib/omnizip/algorithms/lzma/bit_model.rb +1 -1
  23. data/lib/omnizip/algorithms/lzma/dictionary.rb +4 -3
  24. data/lib/omnizip/algorithms/lzma/distance_coder.rb +5 -102
  25. data/lib/omnizip/algorithms/lzma/length_coder.rb +4 -92
  26. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +4 -119
  27. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +2 -0
  28. data/lib/omnizip/algorithms/lzma/match_finder.rb +59 -29
  29. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +20 -12
  30. data/lib/omnizip/algorithms/lzma/range_decoder.rb +77 -269
  31. data/lib/omnizip/algorithms/lzma/range_encoder.rb +0 -9
  32. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +0 -15
  33. data/lib/omnizip/algorithms/lzma/xz_state.rb +0 -8
  34. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +189 -968
  35. data/lib/omnizip/algorithms/lzma2/encoder.rb +0 -2
  36. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +0 -2
  37. data/lib/omnizip/algorithms/lzma2/properties.rb +0 -2
  38. data/lib/omnizip/algorithms/ppmd7/model.rb +1 -1
  39. data/lib/omnizip/algorithms/ppmd8/context.rb +0 -2
  40. data/lib/omnizip/algorithms/ppmd8/model.rb +1 -1
  41. data/lib/omnizip/algorithms/ppmd8.rb +0 -2
  42. data/lib/omnizip/algorithms/ppmd_base.rb +0 -2
  43. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +0 -5
  44. data/lib/omnizip/algorithms/xz_lzma2.rb +0 -5
  45. data/lib/omnizip/algorithms/zstandard/frame/header.rb +3 -5
  46. data/lib/omnizip/algorithms.rb +1 -0
  47. data/lib/omnizip/archive_handler.rb +75 -0
  48. data/lib/omnizip/archive_handlers/tar_handler.rb +51 -0
  49. data/lib/omnizip/archive_handlers/zip_handler.rb +73 -0
  50. data/lib/omnizip/archive_handlers.rb +11 -0
  51. data/lib/omnizip/checksum_registry.rb +23 -96
  52. data/lib/omnizip/cli/shared.rb +31 -0
  53. data/lib/omnizip/cli.rb +6 -43
  54. data/lib/omnizip/commands/archive_create_command.rb +0 -6
  55. data/lib/omnizip/commands/archive_extract_command.rb +0 -4
  56. data/lib/omnizip/commands/archive_list_command.rb +0 -4
  57. data/lib/omnizip/commands/archive_repair_command.rb +0 -2
  58. data/lib/omnizip/commands/archive_verify_command.rb +0 -2
  59. data/lib/omnizip/commands/compress_command.rb +0 -2
  60. data/lib/omnizip/commands/decompress_command.rb +0 -2
  61. data/lib/omnizip/commands/list_command.rb +0 -2
  62. data/lib/omnizip/commands/metadata_command.rb +0 -5
  63. data/lib/omnizip/commands/parity_create_command.rb +0 -2
  64. data/lib/omnizip/commands/parity_repair_command.rb +0 -2
  65. data/lib/omnizip/commands/parity_verify_command.rb +0 -2
  66. data/lib/omnizip/commands/profile_list_command.rb +0 -2
  67. data/lib/omnizip/commands/profile_show_command.rb +0 -2
  68. data/lib/omnizip/convenience.rb +111 -234
  69. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +1 -1
  70. data/lib/omnizip/crypto/aes256/cipher.rb +6 -1
  71. data/lib/omnizip/error.rb +22 -18
  72. data/lib/omnizip/filter.rb +5 -3
  73. data/lib/omnizip/filter_registry.rb +74 -98
  74. data/lib/omnizip/filters/bcj2/decoder.rb +196 -130
  75. data/lib/omnizip/filters/bcj2.rb +0 -2
  76. data/lib/omnizip/filters/bcj_x86.rb +0 -2
  77. data/lib/omnizip/filters/filter_base.rb +4 -70
  78. data/lib/omnizip/filters/registry.rb +0 -2
  79. data/lib/omnizip/format_detector.rb +21 -12
  80. data/lib/omnizip/format_registry.rb +79 -38
  81. data/lib/omnizip/formats/bzip2_file.rb +0 -2
  82. data/lib/omnizip/formats/cpio/entry.rb +0 -2
  83. data/lib/omnizip/formats/cpio/reader.rb +0 -1
  84. data/lib/omnizip/formats/cpio/writer.rb +0 -1
  85. data/lib/omnizip/formats/cpio.rb +0 -1
  86. data/lib/omnizip/formats/format_spec_loader.rb +1 -1
  87. data/lib/omnizip/formats/gzip.rb +0 -2
  88. data/lib/omnizip/formats/iso/directory_record.rb +6 -3
  89. data/lib/omnizip/formats/iso/reader.rb +2 -2
  90. data/lib/omnizip/formats/iso.rb +0 -1
  91. data/lib/omnizip/formats/lzip.rb +5 -25
  92. data/lib/omnizip/formats/lzma_alone.rb +7 -25
  93. data/lib/omnizip/formats/msi/cab_extractor.rb +181 -0
  94. data/lib/omnizip/formats/msi/constants.rb +157 -0
  95. data/lib/omnizip/formats/msi/directory_resolver.rb +188 -0
  96. data/lib/omnizip/formats/msi/entry.rb +128 -0
  97. data/lib/omnizip/formats/msi/reader.rb +301 -0
  98. data/lib/omnizip/formats/msi/string_pool.rb +246 -0
  99. data/lib/omnizip/formats/msi/table_parser.rb +244 -0
  100. data/lib/omnizip/formats/msi.rb +84 -0
  101. data/lib/omnizip/formats/ole/allocation_table.rb +10 -1
  102. data/lib/omnizip/formats/ole/dirent.rb +20 -3
  103. data/lib/omnizip/formats/ole/header.rb +0 -1
  104. data/lib/omnizip/formats/ole/ranges_io.rb +21 -3
  105. data/lib/omnizip/formats/ole/storage.rb +19 -4
  106. data/lib/omnizip/formats/ole.rb +0 -2
  107. data/lib/omnizip/formats/rar/block_parser.rb +0 -1
  108. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +1 -1
  109. data/lib/omnizip/formats/rar/decompressor.rb +38 -1
  110. data/lib/omnizip/formats/rar/header.rb +0 -1
  111. data/lib/omnizip/formats/rar/rar5/writer.rb +0 -2
  112. data/lib/omnizip/formats/rar/reader.rb +2 -3
  113. data/lib/omnizip/formats/rar/writer.rb +0 -1
  114. data/lib/omnizip/formats/rar.rb +2 -2
  115. data/lib/omnizip/formats/rpm/entry.rb +0 -1
  116. data/lib/omnizip/formats/rpm/header.rb +3 -8
  117. data/lib/omnizip/formats/rpm/lead.rb +37 -26
  118. data/lib/omnizip/formats/rpm/tag.rb +0 -1
  119. data/lib/omnizip/formats/rpm/writer.rb +0 -1
  120. data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +98 -61
  121. data/lib/omnizip/formats/seven_zip/coder_chain.rb +1 -2
  122. data/lib/omnizip/formats/seven_zip/encoded_header.rb +0 -1
  123. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +0 -1
  124. data/lib/omnizip/formats/seven_zip/header.rb +0 -1
  125. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +0 -1
  126. data/lib/omnizip/formats/seven_zip/header_writer.rb +22 -61
  127. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +1 -1
  128. data/lib/omnizip/formats/seven_zip/parser.rb +14 -13
  129. data/lib/omnizip/formats/seven_zip/reader.rb +82 -19
  130. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +0 -15
  131. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +0 -1
  132. data/lib/omnizip/formats/seven_zip/writer.rb +22 -68
  133. data/lib/omnizip/formats/seven_zip.rb +63 -5
  134. data/lib/omnizip/formats/tar/entry.rb +0 -2
  135. data/lib/omnizip/formats/tar/header.rb +0 -2
  136. data/lib/omnizip/formats/tar/reader.rb +0 -2
  137. data/lib/omnizip/formats/tar/writer.rb +0 -2
  138. data/lib/omnizip/formats/tar.rb +0 -1
  139. data/lib/omnizip/formats/xar/entry.rb +0 -1
  140. data/lib/omnizip/formats/xar/header.rb +0 -1
  141. data/lib/omnizip/formats/xar/reader.rb +0 -1
  142. data/lib/omnizip/formats/xar/toc.rb +3 -3
  143. data/lib/omnizip/formats/xar/writer.rb +11 -3
  144. data/lib/omnizip/formats/xar.rb +0 -1
  145. data/lib/omnizip/formats/xz/reader.rb +7 -3
  146. data/lib/omnizip/formats/xz.rb +3 -40
  147. data/lib/omnizip/formats/xz_impl/block_decoder.rb +4 -10
  148. data/lib/omnizip/formats/xz_impl/block_encoder.rb +0 -2
  149. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +0 -2
  150. data/lib/omnizip/formats/xz_impl/index_decoder.rb +0 -1
  151. data/lib/omnizip/formats/xz_impl/index_encoder.rb +0 -1
  152. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +1 -1
  153. data/lib/omnizip/formats/xz_impl/stream_footer.rb +0 -1
  154. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +0 -1
  155. data/lib/omnizip/formats/xz_impl/stream_header.rb +0 -1
  156. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +0 -2
  157. data/lib/omnizip/formats/zip/central_directory_header.rb +2 -3
  158. data/lib/omnizip/formats/zip/end_of_central_directory.rb +0 -2
  159. data/lib/omnizip/formats/zip/local_file_header.rb +0 -2
  160. data/lib/omnizip/formats/zip/reader.rb +12 -2
  161. data/lib/omnizip/formats/zip/writer.rb +73 -1
  162. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +0 -2
  163. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +0 -2
  164. data/lib/omnizip/formats/zip/zip64_extra_field.rb +0 -2
  165. data/lib/omnizip/formats/zip.rb +0 -1
  166. data/lib/omnizip/formats.rb +9 -22
  167. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +2 -2
  168. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +1 -1
  169. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +0 -6
  170. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +18 -185
  171. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +3 -3
  172. data/lib/omnizip/io/source.rb +136 -0
  173. data/lib/omnizip/io.rb +2 -0
  174. data/lib/omnizip/link_handler.rb +7 -3
  175. data/lib/omnizip/metadata/metadata_editor.rb +1 -1
  176. data/lib/omnizip/models/algorithm_metadata.rb +33 -60
  177. data/lib/omnizip/models/compression_options.rb +40 -53
  178. data/lib/omnizip/models/conversion_options.rb +9 -18
  179. data/lib/omnizip/models/parallel_options.rb +25 -0
  180. data/lib/omnizip/optimization_registry.rb +8 -44
  181. data/lib/omnizip/parallel/job_scheduler.rb +26 -8
  182. data/lib/omnizip/parallel/parallel_compressor.rb +10 -20
  183. data/lib/omnizip/parallel/parallel_extractor.rb +20 -11
  184. data/lib/omnizip/parity/par2_repairer.rb +5 -5
  185. data/lib/omnizip/parity/par2_verifier.rb +27 -22
  186. data/lib/omnizip/parity.rb +4 -4
  187. data/lib/omnizip/password/encryption_registry.rb +11 -50
  188. data/lib/omnizip/password/zip_crypto_strategy.rb +1 -4
  189. data/lib/omnizip/pipe/stream_decompressor.rb +2 -3
  190. data/lib/omnizip/profile/archive_profile.rb +0 -2
  191. data/lib/omnizip/profile/binary_profile.rb +0 -2
  192. data/lib/omnizip/profile/compression_profile.rb +0 -1
  193. data/lib/omnizip/profile/profile_detector.rb +0 -2
  194. data/lib/omnizip/profile/text_profile.rb +0 -2
  195. data/lib/omnizip/profile.rb +0 -2
  196. data/lib/omnizip/registry.rb +112 -0
  197. data/lib/omnizip/version.rb +1 -1
  198. data/lib/omnizip/zip/file.rb +20 -9
  199. data/lib/omnizip.rb +26 -55
  200. data/lib/tasks/msi_test_files.rake +66 -0
  201. metadata +57 -6
@@ -26,15 +26,24 @@ module Omnizip
26
26
  # Match Finder using hash chain algorithm for LZ77 compression
27
27
  # Ported from XZ Utils lz_encoder.c
28
28
  class MatchFinder
29
- HASH_SIZE = 4096
29
+ # Larger hash table for better distribution
30
+ # 16384 slots with 4-byte keys gives better collision resistance
31
+ HASH_SIZE = 16384
32
+ HASH_MASK = HASH_SIZE - 1
30
33
  MAX_MATCHES = 274
34
+ # Maximum hash chain depth to prevent O(n) lookups
35
+ # XZ Utils uses different values based on compression level (4-256)
36
+ # Ruby needs much lower values due to slower string operations
37
+ # Testing shows depth 16 gives same compression ratio as 32-64 but 2-3x faster
38
+ DEFAULT_CHAIN_DEPTH = 16
31
39
 
32
40
  attr_reader :dictionary, :buffer, :position
33
41
 
34
- def initialize(dictionary)
42
+ def initialize(dictionary, chain_depth: DEFAULT_CHAIN_DEPTH)
35
43
  @dictionary = dictionary
36
44
  @buffer = String.new(encoding: Encoding::BINARY)
37
45
  @position = 0
46
+ @chain_depth = chain_depth
38
47
  # Use nil as empty marker (not 0) to distinguish from position 0
39
48
  @hash_table = Array.new(HASH_SIZE, nil)
40
49
  @hash_chain = Array.new(0)
@@ -84,38 +93,32 @@ module Omnizip
84
93
  # @return [Array<Match>] Array of matches sorted by length (descending)
85
94
  def find_matches(current_pos = @buffer.bytesize - 273)
86
95
  # Calculate hash for current position
96
+ # Note: Using 4-byte hash now (calc_hash checks pos + 4)
87
97
  hash = nil
88
- if current_pos >= 0 && current_pos + 3 <= @buffer.bytesize
98
+ if current_pos >= 0 && current_pos + 4 <= @buffer.bytesize
89
99
  hash = calc_hash(@buffer, current_pos)
90
100
  end
91
101
 
92
- # Update hash table for current position (even for early positions)
93
- # This ensures positions 0-3 are available for later matches
94
- # XZ Utils calls this "skip" - update hash without finding matches
95
- # CRITICAL: Only update if this position hasn't been processed yet
96
- # (i.e., @hash_table[hash] != current_pos)
97
- # This prevents overwriting the hash chain when find_matches is called
98
- # after skip() has already initialized the hash table
99
- if hash && @hash_table[hash] != current_pos
100
- @hash_chain[current_pos] = @hash_table[hash]
101
- @hash_table[hash] = current_pos
102
- end
102
+ # Skip hash table update here - skip() already built the table for all positions
103
+ # Adding current_pos to the chain is redundant since skip processed all positions
103
104
 
104
105
  # Can't find matches if no hash or insufficient data
105
106
  # Note: We CAN find matches at early positions (e.g., position 2 can match position 0)
106
- # The only requirement is that there's enough data for hash calculation (current_pos + 3 <= buffer size)
107
+ # The only requirement is that there's enough data for hash calculation (current_pos + 4 <= buffer size)
107
108
  # and that there's at least 2 bytes of history (for MIN_MATCH_LENGTH=2)
108
109
  # CRITICAL: Don't produce matches until position >= 2 to ensure decoder has enough dict_full
109
110
  # The decoder validates: dict_full > distance, where dict_full starts at 0 after 1st byte
110
111
  # For distance=1 match to be valid, decoder needs dict_full >= 2 (at least 2 bytes decoded)
111
112
  # This happens after processing position 1 (first byte was literal at position 0)
112
113
  # So we can only produce matches starting at position 2
113
- return [] if hash.nil? || @buffer.bytesize < 4 || current_pos + 3 > @buffer.bytesize || current_pos < 2
114
+ return [] if hash.nil? || @buffer.bytesize < 4 || current_pos + 4 > @buffer.bytesize || current_pos < 2
114
115
 
115
116
  @matches_count = 0
116
117
  chain_pos = @hash_chain[current_pos]
118
+ chain_depth = 0
117
119
 
118
- while chain_pos && @matches_count < MAX_MATCHES
120
+ while chain_pos && @matches_count < MAX_MATCHES && chain_depth < @chain_depth
121
+ chain_depth += 1
119
122
  # CRITICAL: Skip invalid chain_pos values (beyond buffer or negative)
120
123
  next if chain_pos >= @buffer.bytesize || chain_pos.negative?
121
124
 
@@ -168,34 +171,61 @@ module Omnizip
168
171
 
169
172
  private
170
173
 
171
- # Calculate hash for position (first 3 bytes)
174
+ # Calculate hash for position using 4-byte key with mixing
175
+ # Uses FNV-1a inspired mixing for better distribution
172
176
  #
173
177
  # @param data [String] Buffer data
174
178
  # @param pos [Integer] Position to hash
175
179
  # @return [Integer] Hash value
176
180
  def calc_hash(data, pos)
177
- return 0 if pos + 3 > data.bytesize
178
-
179
- (data.getbyte(pos) |
180
- (data.getbyte(pos + 1) << 8) |
181
- (data.getbyte(pos + 2) << 16)) % HASH_SIZE
181
+ return 0 if pos + 4 > data.bytesize
182
+
183
+ # Read 4 bytes as 32-bit integer (little-endian)
184
+ v = data.getbyte(pos) |
185
+ (data.getbyte(pos + 1) << 8) |
186
+ (data.getbyte(pos + 2) << 16) |
187
+ (data.getbyte(pos + 3) << 24)
188
+
189
+ # FNV-1a style mixing for better distribution
190
+ # Hash avalanche: each input bit affects all output bits
191
+ v = (v ^ (v >> 16)) * 0x45D9F3B
192
+ v = (v ^ (v >> 16)) * 0x45D9F3B
193
+ (v ^ (v >> 16)) & HASH_MASK
182
194
  end
183
195
 
184
196
  # Verify match length between two positions
197
+ # Uses 4-byte quick check then binary search with integer comparison
185
198
  #
186
199
  # @param pos1 [Integer] First position
187
200
  # @param pos2 [Integer] Second position
188
201
  # @return [Integer] Match length
189
202
  def verify_match(pos1, pos2)
190
- max_len = [273, @buffer.bytesize - pos1, @buffer.bytesize - pos2].min
191
- length = 0
203
+ buf = @buffer
204
+ max_len = [273, buf.bytesize - pos1, buf.bytesize - pos2].min
205
+ return 0 if max_len <= 0
206
+
207
+ # Quick rejection: check first 4 bytes as integer
208
+ if max_len >= 4
209
+ v1 = buf.byteslice(pos1, 4).unpack1("L<")
210
+ v2 = buf.byteslice(pos2, 4).unpack1("L<")
211
+ return 0 if v1 != v2
212
+ end
192
213
 
193
- while length < max_len &&
194
- @buffer.getbyte(pos1 + length) == @buffer.getbyte(pos2 + length)
195
- length += 1
214
+ # Binary search for exact match length
215
+ # Creates O(log n) strings but does efficient integer comparison
216
+ low = 0
217
+ high = max_len
218
+
219
+ while low < high
220
+ mid = (low + high + 1) / 2
221
+ if buf.byteslice(pos1, mid) == buf.byteslice(pos2, mid)
222
+ low = mid
223
+ else
224
+ high = mid - 1
225
+ end
196
226
  end
197
227
 
198
- length
228
+ low
199
229
  end
200
230
  end
201
231
  end
@@ -31,8 +31,6 @@ module Omnizip
31
31
  buf_pos = position
32
32
  buf_avail = [buf.bytesize - buf_pos, MATCH_LEN_MAX].min
33
33
 
34
- # puts "[OPTIMUM] position=#{position} buf_avail=#{buf_avail} buf.bytesize=#{buf.bytesize}" if ENV['DEBUG']
35
-
36
34
  # Not enough input for a match
37
35
  return [UINT32_MAX, 1] if buf_avail < 2
38
36
 
@@ -51,26 +49,36 @@ module Omnizip
51
49
  # In Ruby: buf_back_index = buf_pos - rep_dist - 1
52
50
  buf_back_index = buf_pos - rep_dist - 1
53
51
 
54
- # DEBUG: Trace repeated match check
55
- # puts "[OPTIMAL] Checking rep#{i}: rep_dist=#{rep_dist} buf_pos=#{buf_pos} buf_back_index=#{buf_back_index}" if ENV['DEBUG']
56
-
57
52
  # Skip if out of bounds
58
53
  next if buf_back_index.negative? || buf_back_index >= buf.bytesize
59
54
 
60
- # puts "[OPTIMAL] buf[#{buf_pos}]=#{'%02x' % buf.getbyte(buf_pos)} buf[#{buf_back_index}]=#{'%02x' % buf.getbyte(buf_back_index)}" if ENV['DEBUG']
61
-
62
55
  # If the first two bytes (2 == MATCH_LEN_MIN) do not match,
63
56
  # this rep is not useful.
64
57
  next if buf.getbyte(buf_pos) != buf.getbyte(buf_back_index) ||
65
58
  buf.getbyte(buf_pos + 1) != buf.getbyte(buf_back_index + 1)
66
59
 
67
60
  # The first two bytes matched. Calculate the length of the match.
61
+ # Use 64-bit integer comparison for speed (no string allocation)
62
+ max_match_len = [
63
+ buf.bytesize - buf_pos,
64
+ buf.bytesize - buf_back_index,
65
+ MATCH_LEN_MAX,
66
+ buf_avail,
67
+ ].min
68
+
68
69
  len = 2
69
- while (buf_pos + len < buf.bytesize) &&
70
- (buf_back_index + len < buf.bytesize) &&
71
- (buf.getbyte(buf_pos + len) == buf.getbyte(buf_back_index + len)) &&
72
- (len < MATCH_LEN_MAX) &&
73
- (len < buf_avail)
70
+ # Compare 8 bytes at a time using 64-bit integers
71
+ while len + 8 <= max_match_len
72
+ # Read 8 bytes as little-endian 64-bit integers
73
+ v1 = buf.byteslice(buf_pos + len, 8).unpack1("Q<")
74
+ v2 = buf.byteslice(buf_back_index + len, 8).unpack1("Q<")
75
+ break if v1 != v2
76
+
77
+ len += 8
78
+ end
79
+ # Finalize with byte-by-byte for remaining bytes
80
+ while len < max_match_len &&
81
+ buf.getbyte(buf_pos + len) == buf.getbyte(buf_back_index + len)
74
82
  len += 1
75
83
  end
76
84