Class: Omnizip::Formats::Ole::AllocationTable

Inherits:
Object
  • Object
show all
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

Classes: Big, Small

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

Instance Method Summary collapse

Constructor Details

#initialize(ole) ⇒ AllocationTable

Initialize allocation table

Parameters:

  • ole (Object)

    Parent OLE storage



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_sizeInteger (readonly)

Returns Block size in bytes.

Returns:

  • (Integer)

    Block size in bytes



25
26
27
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 25

def block_size
  @block_size
end

#entriesArray<Integer> (readonly)

Returns Table entries.

Returns:

  • (Array<Integer>)

    Table entries



16
17
18
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 16

def entries
  @entries
end

#ioIO (readonly)

Returns Underlying IO.

Returns:

  • (IO)

    Underlying IO



22
23
24
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 22

def io
  @io
end

#oleObject (readonly)

Returns Parent OLE storage.

Returns:

  • (Object)

    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

Parameters:

  • idx (Integer)

    Entry index

Returns:

  • (Integer)

    Entry value



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

Parameters:

  • idx (Integer)

    Entry index

  • val (Integer)

    Entry value



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

Parameters:

  • blocks (Array<Integer>)

    Block indices

  • size (Integer, nil) (defaults to: nil)

    Optional size to truncate to

Returns:

  • (Array<Array<Integer, Integer>>)

    Array of [offset, length] pairs



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

Parameters:

  • idx (Integer)

    Starting block index

Returns:

  • (Array<Integer>)

    Chain of block indices

Raises:

  • (ArgumentError)

    If chain is broken



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

#freevoid

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_blockInteger

Find a free block

Returns:

  • (Integer)

    Free block index



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

#lengthInteger

Get number of entries

Returns:

  • (Integer)

    Entry count



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

Parameters:

  • data (String)

    Binary data containing table entries



38
39
40
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 38

def load(data)
  @entries = data.unpack("V*")
end

#packString

Pack table to binary data

Returns:

  • (String)

    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

Parameters:

  • chain_or_idx (Array<Integer>, Integer)

    Block chain or head index

  • size (Integer, nil) (defaults to: nil)

    Optional size

Returns:

  • (Array<Array<Integer, Integer>>)

    Byte ranges



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

Parameters:

  • chain_or_idx (Array<Integer>, Integer)

    Block chain or head index

  • size (Integer, nil) (defaults to: nil)

    Optional size

Returns:

  • (String)

    Data from 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

Parameters:

  • blocks (Array<Integer>)

    Current blocks (modified in place)

  • size (Integer)

    New size in bytes

Returns:

  • (Array<Integer>)

    Updated blocks



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

#truncateArray<Integer>

Truncate table to remove trailing AVAIL entries

Returns:

  • (Array<Integer>)

    Truncated 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_entriesObject

Truncate entries in place



206
207
208
# File 'lib/omnizip/formats/ole/allocation_table.rb', line 206

def truncate_entries
  @entries.replace(truncate)
end