module RubySMB::Compression::LZNT1
Public Class Methods
compress(buf, chunk_size: 0x1000)
click to toggle source
# File lib/ruby_smb/compression/lznt1.rb, line 4 def self.compress(buf, chunk_size: 0x1000) out = '' until buf.empty? chunk = buf[0...chunk_size] compressed = compress_chunk(chunk) # chunk is compressed if compressed.length < chunk.length out << [0xb000 | (compressed.length - 1)].pack('v') out << compressed else out << [0x3000 | (chunk.length - 1)].pack('v') out << chunk end buf = buf[chunk_size..-1] break if buf.nil? end out end
compress_chunk(chunk)
click to toggle source
# File lib/ruby_smb/compression/lznt1.rb, line 24 def self.compress_chunk(chunk) blob = chunk out = '' pow2 = 0x10 l_mask3 = 0x1002 o_shift = 12 until blob.empty? bits = 0 tmp = '' i = -1 loop do i += 1 bits >>= 1 while pow2 < (chunk.length - blob.length) pow2 <<= 1 l_mask3 = (l_mask3 >> 1) + 1 o_shift -= 1 end max_len = [blob.length, l_mask3].min offset, length = find(chunk[0...(chunk.length - blob.length)], blob, max_len) # try to find more compressed pattern _offset2, length2 = find(chunk[0...chunk.length - blob.length + 1], blob[1..-1], max_len) length = 0 if length < length2 if length > 0 symbol = ((offset - 1) << o_shift) | (length - 3) tmp << [symbol].pack('v') # set the highest bit bits |= 0x80 blob = blob[length..-1] else tmp += blob[0] blob = blob[1..-1] end break if blob.empty? || i == 7 end out << [bits >> (7 - i)].pack('C') out << tmp end out end
decompress(buf, length_check: true)
click to toggle source
# File lib/ruby_smb/compression/lznt1.rb, line 72 def self.decompress(buf, length_check: true) out = '' until buf.empty? header = buf.unpack1('v') length = (header & 0xfff) + 1 raise EncodingError, 'invalid chunk length' if length_check && length > (buf.length - 2) chunk = buf[2...length + 2] out << if header & 0x8000 == 0 chunk else decompress_chunk(chunk) end buf = buf[length + 2..-1] end out end
decompress_chunk(chunk)
click to toggle source
# File lib/ruby_smb/compression/lznt1.rb, line 91 def self.decompress_chunk(chunk) out = '' until chunk.empty? flags = chunk[0].unpack1('C') chunk = chunk[1..-1] 8.times do |i| if (flags >> i & 1) == 0 out << chunk[0] chunk = chunk[1..-1] else flag = chunk.unpack1('v') pos = out.length - 1 l_mask = 0xfff o_shift = 12 while pos >= 0x10 l_mask >>= 1 o_shift -= 1 pos >>= 1 end length = (flag & l_mask) + 3 offset = (flag >> o_shift) + 1 if length >= offset out_offset = out[-offset..-1] tmp = out_offset * (0xfff / out_offset.length + 1) out << tmp[0...length] else out << out[-offset..-offset + length - 1] end chunk = chunk[2..-1] end break if chunk.empty? end end out end
Private Class Methods
find(src, target, max_len)
click to toggle source
# File lib/ruby_smb/compression/lznt1.rb, line 133 def find(src, target, max_len) result_offset = 0 result_length = 0 1.upto(max_len - 1) do |i| offset = src.rindex(target[0...i]) next if offset.nil? tmp_offset = src.length - offset tmp_length = i if tmp_offset == tmp_length src_offset = src[offset..-1] tmp = src_offset * (0xfff / src_offset.length + 1) i.upto(max_len) do |j| offset = tmp.rindex(target[0...j]) break if offset.nil? tmp_length = j end end if tmp_length > result_length result_offset = tmp_offset result_length = tmp_length end end result_length < 3 ? [0, 0] : [result_offset, result_length] end