Class: Cabriolet::OAB::Compressor
- Inherits:
-
Object
- Object
- Cabriolet::OAB::Compressor
- Defined in:
- lib/cabriolet/oab/compressor.rb
Overview
Compressor for OAB (Outlook Offline Address Book) files
OAB files use LZX compression. This compressor can create:
-
Full files (version 3.1): Complete address book data
-
Incremental patches (version 3.2): Binary patches (simplified)
NOTE: This implementation is based on the OAB format specification derived from libmspack’s decompressor. The original libmspack does not implement OAB compression. This is a best-effort implementation that may not produce files identical to Microsoft’s OAB generator.
Constant Summary collapse
- DEFAULT_BUFFER_SIZE =
Default buffer size for I/O operations
4096- DEFAULT_BLOCK_SIZE =
Default block size (use 32KB like LZX frames)
32_768- VERSION_HI =
OAB version numbers
3- VERSION_LO_FULL =
1- VERSION_LO_PATCH =
2
Instance Attribute Summary collapse
-
#block_size ⇒ Object
Returns the value of attribute block_size.
-
#buffer_size ⇒ Object
Returns the value of attribute buffer_size.
-
#io_system ⇒ Object
readonly
Returns the value of attribute io_system.
Instance Method Summary collapse
-
#compress(input_file, output_file, **options) ⇒ Integer
Compress a full OAB file.
-
#compress_data(data, output_file, **options) ⇒ Integer
Compress data from memory to OAB format.
-
#compress_incremental(input_file, base_file, output_file, **options) ⇒ Integer
Create an incremental patch (simplified implementation).
-
#initialize(io_system = nil) ⇒ Compressor
constructor
Initialize OAB compressor.
Constructor Details
#initialize(io_system = nil) ⇒ Compressor
Initialize OAB compressor
35 36 37 38 39 |
# File 'lib/cabriolet/oab/compressor.rb', line 35 def initialize(io_system = nil) @io_system = io_system || System::IOSystem.new @buffer_size = DEFAULT_BUFFER_SIZE @block_size = DEFAULT_BLOCK_SIZE end |
Instance Attribute Details
#block_size ⇒ Object
Returns the value of attribute block_size.
19 20 21 |
# File 'lib/cabriolet/oab/compressor.rb', line 19 def block_size @block_size end |
#buffer_size ⇒ Object
Returns the value of attribute buffer_size.
19 20 21 |
# File 'lib/cabriolet/oab/compressor.rb', line 19 def buffer_size @buffer_size end |
#io_system ⇒ Object (readonly)
Returns the value of attribute io_system.
18 19 20 |
# File 'lib/cabriolet/oab/compressor.rb', line 18 def io_system @io_system end |
Instance Method Details
#compress(input_file, output_file, **options) ⇒ Integer
Compress a full OAB file
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/cabriolet/oab/compressor.rb', line 49 def compress(input_file, output_file, **) block_size = .fetch(:block_size, @block_size) input_handle = @io_system.open(input_file, Constants::MODE_READ) output_handle = @io_system.open(output_file, Constants::MODE_WRITE) begin # Get input size input_size = @io_system.seek(input_handle, 0, Constants::SEEK_END) @io_system.seek(input_handle, 0, Constants::SEEK_START) # Write header header = Binary::OABStructures::FullHeader.new header.version_hi = VERSION_HI header.version_lo = VERSION_LO_FULL header.block_max = block_size header.target_size = input_size header_data = header.to_binary_s bytes_written = @io_system.write(output_handle, header_data) # Compress data in blocks remaining = input_size while remaining.positive? block_bytes = compress_block( input_handle, output_handle, block_size, remaining ) bytes_written += block_bytes remaining -= [block_size, remaining].min end bytes_written ensure @io_system.close(input_handle) if input_handle @io_system.close(output_handle) if output_handle end end |
#compress_data(data, output_file, **options) ⇒ Integer
Compress data from memory to OAB format
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/cabriolet/oab/compressor.rb', line 95 def compress_data(data, output_file, **) block_size = .fetch(:block_size, @block_size) input_handle = System::MemoryHandle.new(data, Constants::MODE_READ) output_handle = @io_system.open(output_file, Constants::MODE_WRITE) begin # Write header header = Binary::OABStructures::FullHeader.new header.version_hi = VERSION_HI header.version_lo = VERSION_LO_FULL header.block_max = block_size header.target_size = data.bytesize header_data = header.to_binary_s bytes_written = @io_system.write(output_handle, header_data) # Compress data in blocks remaining = data.bytesize while remaining.positive? block_bytes = compress_block( input_handle, output_handle, block_size, remaining ) bytes_written += block_bytes remaining -= [block_size, remaining].min end bytes_written ensure @io_system.close(output_handle) if output_handle end end |
#compress_incremental(input_file, base_file, output_file, **options) ⇒ Integer
Create an incremental patch (simplified implementation)
This is a simplified patch format that just stores the new data compressed. A full implementation would generate binary diffs.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/cabriolet/oab/compressor.rb', line 140 def compress_incremental(input_file, base_file, output_file, **) block_size = .fetch(:block_size, @block_size) # For now, just compress the new file with patch header # A full implementation would generate binary diffs input_handle = @io_system.open(input_file, Constants::MODE_READ) base_handle = @io_system.open(base_file, Constants::MODE_READ) output_handle = @io_system.open(output_file, Constants::MODE_WRITE) begin # Get file sizes input_size = @io_system.seek(input_handle, 0, Constants::SEEK_END) @io_system.seek(input_handle, 0, Constants::SEEK_START) base_size = @io_system.seek(base_handle, 0, Constants::SEEK_END) @io_system.seek(base_handle, 0, Constants::SEEK_START) # Read base data for CRC base_data = @io_system.read(base_handle, base_size) base_crc = Zlib.crc32(base_data) # Read target data for CRC target_data = @io_system.read(input_handle, input_size) @io_system.seek(input_handle, 0, Constants::SEEK_START) target_crc = Zlib.crc32(target_data) # Write patch header header = Binary::OABStructures::PatchHeader.new header.version_hi = VERSION_HI header.version_lo = VERSION_LO_PATCH header.block_max = [block_size, 16].max header.source_size = base_size header.target_size = input_size header.source_crc = base_crc header.target_crc = target_crc header_data = header.to_binary_s bytes_written = @io_system.write(output_handle, header_data) # Compress data in blocks (simplified - not true patches) remaining = input_size while remaining.positive? block_bytes = compress_patch_block( input_handle, output_handle, block_size, remaining, 0 ) bytes_written += block_bytes remaining -= [block_size, remaining].min end bytes_written ensure @io_system.close(input_handle) if input_handle @io_system.close(base_handle) if base_handle @io_system.close(output_handle) if output_handle end end |