Class Ole::Storage::AllocationTable
In: lib/ole/storage/base.rb
Parent: Array
String Data Lpstr Clsid Lpwstr Section Enumerable DateTime FileTime Constants Variant::Constants Storage\n[lib/ole/storage/base.rb\nlib/ole/storage/file_system.rb\nlib/ole/storage/meta_data.rb] PropertySet lib/ole/storage/file_system.rb lib/ole/types/property_set.rb lib/ole/types/base.rb Constants Constants Variant Types Ole dot/m_9_0.png

AllocationTable‘s hold the chains corresponding to files. Given an initial index, AllocationTable#chain follows the chain, returning the blocks that make up that file.

There are 2 allocation tables, the bbat, and sbat, for big and small blocks respectively. The block chain should be loaded using either Storage#read_big_blocks or Storage#read_small_blocks as appropriate.

Whether or not big or small blocks are used for a file depends on whether its size is over the Header#threshold level.

An Ole::Storage document is serialized as a series of directory objects, which are stored in blocks throughout the file. The blocks are either big or small, and are accessed using the AllocationTable.

The bbat allocation table‘s data is stored in the spare room in the header block, and in extra blocks throughout the file as referenced by the meta bat. That chain is linear, as there is no higher level table.

AllocationTable.new is used to create an empty table. It can parse a string with the load method. Serialization is accomplished with the to_s method.

Methods

[]=   blocks_to_ranges   chain   free_block   load   new   open   ranges   read   resize_chain   to_s   truncate   truncate!  

Classes and Modules

Class Ole::Storage::AllocationTable::Big
Class Ole::Storage::AllocationTable::Small

Constants

AVAIL = 0xffffffff   a free block (I don‘t currently leave any blocks free), although I do pad out the allocation table with AVAIL to the block size.
EOC = 0xfffffffe
BAT = 0xfffffffd   these blocks are used for storing the allocation table chains
META_BAT = 0xfffffffc

Attributes

block_size  [R] 
io  [R] 
ole  [R] 

Public Class methods

[Source]

     # File lib/ole/storage/base.rb, line 431
431:                         def initialize ole
432:                                 @ole = ole
433:                                 @sparse = true
434:                                 super()
435:                         end

Public Instance methods

[Source]

     # File lib/ole/storage/base.rb, line 522
522:                         def []= idx, val
523:                                 @sparse = true if val == AVAIL
524:                                 super
525:                         end

Turn a chain (an array given by chain) of blocks (optionally truncated to size) into an array of arrays describing the stretches of bytes in the file that it belongs to.

The blocks are Big or Small blocks depending on the table type.

[Source]

     # File lib/ole/storage/base.rb, line 483
483:                         def blocks_to_ranges chain, size=nil
484:                                 # truncate the chain if required
485:                                 chain = chain[0...(size.to_f / block_size).ceil] if size
486:                                 # convert chain to ranges of the block size
487:                                 ranges = chain.map { |i| [block_size * i, block_size] }
488:                                 # truncate final range if required
489:                                 ranges.last[1] -= (ranges.length * block_size - size) if ranges.last and size
490:                                 ranges
491:                         end

rewrote this to be non-recursive as it broke on a large attachment chain with a stack error

[Source]

     # File lib/ole/storage/base.rb, line 467
467:                         def chain idx
468:                                 a = []
469:                                 until idx >= META_BAT
470:                                         raise FormatError, "broken allocationtable chain" if idx < 0 || idx > length
471:                                         a << idx
472:                                         idx = self[idx]
473:                                 end
474:                                 Log.warn "invalid chain terminator #{idx}" unless idx == EOC
475:                                 a
476:                         end

[Source]

     # File lib/ole/storage/base.rb, line 527
527:                         def free_block
528:                                 if @sparse
529:                                         i = index(AVAIL) and return i
530:                                 end
531:                                 @sparse = false
532:                                 push AVAIL
533:                                 length - 1
534:                         end

[Source]

     # File lib/ole/storage/base.rb, line 437
437:                         def load data
438:                                 replace data.unpack('V*')
439:                         end

quick shortcut. chain can be either a head (in which case the table is used to turn it into a chain), or a chain. it is converted to ranges, then to rangesio.

[Source]

     # File lib/ole/storage/base.rb, line 500
500:                         def open chain, size=nil, &block
501:                                 RangesIO.open @io, :ranges => ranges(chain, size), &block
502:                         end

[Source]

     # File lib/ole/storage/base.rb, line 493
493:                         def ranges chain, size=nil
494:                                 chain = self.chain(chain) unless Array === chain
495:                                 blocks_to_ranges chain, size
496:                         end

[Source]

     # File lib/ole/storage/base.rb, line 504
504:                         def read chain, size=nil
505:                                 open chain, size, &:read
506:                         end

must return first_block. modifies blocks in place

[Source]

     # File lib/ole/storage/base.rb, line 537
537:                         def resize_chain blocks, size
538:                                 new_num_blocks = (size / block_size.to_f).ceil
539:                                 old_num_blocks = blocks.length
540:                                 if new_num_blocks < old_num_blocks
541:                                         # de-allocate some of our old blocks. TODO maybe zero them out in the file???
542:                                         (new_num_blocks...old_num_blocks).each { |i| self[blocks[i]] = AVAIL }
543:                                         self[blocks[new_num_blocks-1]] = EOC if new_num_blocks > 0
544:                                         blocks.slice! new_num_blocks..-1
545:                                 elsif new_num_blocks > old_num_blocks
546:                                         # need some more blocks.
547:                                         last_block = blocks.last
548:                                         (new_num_blocks - old_num_blocks).times do
549:                                                 block = free_block
550:                                                 # connect the chain. handle corner case of blocks being [] initially
551:                                                 self[last_block] = block if last_block
552:                                                 blocks << block
553:                                                 last_block = block
554:                                                 self[last_block] = EOC
555:                                         end
556:                                 end
557:                                 # update ranges, and return that also now
558:                                 blocks
559:                         end

[Source]

     # File lib/ole/storage/base.rb, line 455
455:                         def to_s
456:                                 table = truncate
457:                                 # pad it out some
458:                                 num = @ole.bbat.block_size / 4
459:                                 # do you really use AVAIL? they probably extend past end of file, and may shortly
460:                                 # be used for the bat. not really good.
461:                                 table += [AVAIL] * (num - (table.length % num)) if (table.length % num) != 0
462:                                 table.pack 'V*'
463:                         end

[Source]

     # File lib/ole/storage/base.rb, line 441
441:                         def truncate
442:                                 # this strips trailing AVAILs. come to think of it, this has the potential to break
443:                                 # bogus ole. if you terminate using AVAIL instead of EOC, like I did before. but that is
444:                                 # very broken. however, if a chain ends with AVAIL, it should probably be fixed to EOC
445:                                 # at load time.
446:                                 temp = reverse
447:                                 not_avail = temp.find { |b| b != AVAIL } and temp = temp[temp.index(not_avail)..-1]
448:                                 temp.reverse
449:                         end

[Source]

     # File lib/ole/storage/base.rb, line 451
451:                         def truncate!
452:                                 replace truncate
453:                         end

[Validate]