Class: Omnizip::Formats::Ole::AllocationTable
- Inherits:
-
Object
- Object
- Omnizip::Formats::Ole::AllocationTable
- Includes:
- Constants
- Defined in:
- lib/omnizip/formats/ole/allocation_table.rb
Overview
OLE allocation table
Manages block chains for files stored in OLE containers. There are two types: Big (BBAT) and Small (SBAT).
Defined Under Namespace
Constant Summary
Constants included from Constants
Constants::AVAIL, Constants::BAT, Constants::BYTE_ORDER_LE, Constants::DEFAULT_BIG_BLOCK_SHIFT, Constants::DEFAULT_SMALL_BLOCK_SHIFT, Constants::DEFAULT_THRESHOLD, Constants::DIRENT_COLORS, Constants::DIRENT_SIZE, Constants::DIRENT_TYPES, Constants::EOC, Constants::EOT, Constants::HEADER_BLOCK_SIZE, Constants::HEADER_SIZE, Constants::MAGIC, Constants::MAX_NAME_LENGTH, Constants::META_BAT
Instance Attribute Summary collapse
-
#block_size ⇒ Integer
readonly
Block size in bytes.
-
#entries ⇒ Array<Integer>
readonly
Table entries.
-
#io ⇒ IO
readonly
Underlying IO.
-
#ole ⇒ Object
readonly
Parent OLE storage.
Instance Method Summary collapse
-
#[](idx) ⇒ Integer
Get entry at index.
-
#[]=(idx, val) ⇒ Object
Set entry at index.
-
#blocks_to_ranges(blocks, size = nil) ⇒ Array<Array<Integer, Integer>>
Convert block chain to byte ranges.
-
#chain(idx) ⇒ Array<Integer>
Follow chain from starting index.
-
#free ⇒ void
Free resources to prevent memory leaks.
-
#free_block ⇒ Integer
Find a free block.
-
#initialize(ole) ⇒ AllocationTable
constructor
Initialize allocation table.
-
#length ⇒ Integer
Get number of entries.
-
#load(data) ⇒ Object
Load allocation table from binary data.
-
#pack ⇒ String
Pack table to binary data.
-
#ranges(chain_or_idx, size = nil) ⇒ Array<Array<Integer, Integer>>
Get ranges for a chain.
-
#read(chain_or_idx, size = nil) ⇒ String
Read data from block chain.
-
#resize_chain(blocks, size) ⇒ Array<Integer>
Resize a block chain.
-
#truncate ⇒ Array<Integer>
Truncate table to remove trailing AVAIL entries.
-
#truncate_entries ⇒ Object
Truncate entries in place.
Constructor Details
#initialize(ole) ⇒ AllocationTable
Initialize allocation table
30 31 32 33 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 30 def initialize(ole) @ole = ole @entries = [] end |
Instance Attribute Details
#block_size ⇒ Integer (readonly)
Returns Block size in bytes.
25 26 27 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 25 def block_size @block_size end |
#entries ⇒ Array<Integer> (readonly)
Returns Table entries.
16 17 18 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 16 def entries @entries end |
#io ⇒ IO (readonly)
Returns Underlying IO.
22 23 24 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 22 def io @io end |
#ole ⇒ Object (readonly)
Returns Parent OLE storage.
19 20 21 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 19 def ole @ole end |
Instance Method Details
#[](idx) ⇒ Integer
Get entry at index
46 47 48 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 46 def [](idx) @entries[idx] end |
#[]=(idx, val) ⇒ Object
Set entry at index
54 55 56 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 54 def []=(idx, val) @entries[idx] = val end |
#blocks_to_ranges(blocks, size = nil) ⇒ Array<Array<Integer, Integer>>
Convert block chain to byte ranges
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 106 def blocks_to_ranges(blocks, size = nil) return [] if blocks.empty? # Truncate chain if size specified blocks = blocks[0, (size.to_f / block_size).ceil] if size # Convert to ranges ranges = blocks.map { |i| [block_size * i, block_size] } # Truncate final range if needed if ranges.last && size ranges.last[1] -= ((ranges.length * block_size) - size) end ranges end |
#chain(idx) ⇒ Array<Integer>
Follow chain from starting index
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 80 def chain(idx) result = [] visited = Set.new until idx >= META_BAT if idx.negative? || idx > length raise ArgumentError, "Broken allocation chain at index #{idx}" end if visited.include?(idx) raise ArgumentError, "Circular chain detected at index #{idx}" end visited << idx result << idx idx = @entries[idx] end result end |
#free ⇒ void
This method returns an undefined value.
Free resources to prevent memory leaks
Call this when done with the allocation table to release memory.
69 70 71 72 73 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 69 def free @entries = nil @ole = nil @io = nil end |
#free_block ⇒ Integer
Find a free block
153 154 155 156 157 158 159 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 153 def free_block idx = @entries.index(AVAIL) return idx if idx @entries << AVAIL @entries.length - 1 end |
#length ⇒ Integer
Get number of entries
61 62 63 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 61 def length @entries.length end |
#load(data) ⇒ Object
Load allocation table from binary data
38 39 40 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 38 def load(data) @entries = data.unpack("V*") end |
#pack ⇒ String
Pack table to binary data
213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 213 def pack table = truncate # Pad to block boundary num = @ole.bbat.block_size / 4 if (table.length % num) != 0 table += [AVAIL] * (num - (table.length % num)) end table.pack("V*") end |
#ranges(chain_or_idx, size = nil) ⇒ Array<Array<Integer, Integer>>
Get ranges for a chain
128 129 130 131 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 128 def ranges(chain_or_idx, size = nil) blocks = chain_or_idx.is_a?(Array) ? chain_or_idx : chain(chain_or_idx) blocks_to_ranges(blocks, size) end |
#read(chain_or_idx, size = nil) ⇒ String
Read data from block chain
138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 138 def read(chain_or_idx, size = nil) ranges = self.ranges(chain_or_idx, size) data = "".b ranges.each do |offset, len| @io.seek(offset) data << @io.read(len).to_s end data end |
#resize_chain(blocks, size) ⇒ Array<Integer>
Resize a block chain
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 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 166 def resize_chain(blocks, size) new_num_blocks = (size / block_size.to_f).ceil old_num_blocks = blocks.length if new_num_blocks < old_num_blocks # De-allocate excess blocks (new_num_blocks...old_num_blocks).each do |i| @entries[blocks[i]] = AVAIL end if new_num_blocks.positive? @entries[blocks[new_num_blocks - 1]] = EOC end blocks.slice!(new_num_blocks..-1) elsif new_num_blocks > old_num_blocks # Allocate more blocks last_block = blocks.last (new_num_blocks - old_num_blocks).times do block = free_block @entries[last_block] = block if last_block blocks << block last_block = block @entries[last_block] = EOC end end blocks end |
#truncate ⇒ Array<Integer>
Truncate table to remove trailing AVAIL entries
198 199 200 201 202 203 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 198 def truncate temp = @entries.reverse first_non_avail = temp.find { |b| b != AVAIL } temp = temp[temp.index(first_non_avail)..] if first_non_avail temp.reverse end |
#truncate_entries ⇒ Object
Truncate entries in place
206 207 208 |
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 206 def truncate_entries @entries.replace(truncate) end |