Class: Ole::Storage::AllocationTable
- Inherits:
-
Array
- Object
- Array
- Ole::Storage::AllocationTable
- Defined in:
- lib/ole/storage.rb
Overview
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.
Defined Under Namespace
Constant Summary collapse
- AVAIL =
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.
0xffffffff
- EOC =
end of a chain
0xfffffffe
- BAT =
these blocks are used for storing the allocation table chains
0xfffffffd
- META_BAT =
0xfffffffc
Instance Attribute Summary collapse
-
#block_size ⇒ Object
readonly
Returns the value of attribute block_size.
-
#io ⇒ Object
readonly
Returns the value of attribute io.
-
#ole ⇒ Object
readonly
Returns the value of attribute ole.
Instance Method Summary collapse
- #[]=(idx, val) ⇒ Object
-
#blocks_to_ranges(chain, size = nil) ⇒ Object
Turn a chain (an array given by
chain
) of blocks (optionally truncated tosize
) into an array of arrays describing the stretches of bytes in the file that it belongs to. -
#chain(idx) ⇒ Object
rewrote this to be non-recursive as it broke on a large attachment chain with a stack error.
- #free_block ⇒ Object
-
#initialize(ole) ⇒ AllocationTable
constructor
A new instance of AllocationTable.
- #load(data) ⇒ Object
-
#open(chain, size = nil, &block) ⇒ Object
quick shortcut.
- #ranges(chain, size = nil) ⇒ Object
- #read(chain, size = nil) ⇒ Object
-
#resize_chain(blocks, size) ⇒ Object
must return first_block.
- #to_s ⇒ Object
- #truncate ⇒ Object
- #truncate! ⇒ Object
Constructor Details
#initialize(ole) ⇒ AllocationTable
Returns a new instance of AllocationTable.
461 462 463 464 465 |
# File 'lib/ole/storage.rb', line 461 def initialize ole @ole = ole @sparse = true super() end |
Instance Attribute Details
#block_size ⇒ Object (readonly)
Returns the value of attribute block_size.
460 461 462 |
# File 'lib/ole/storage.rb', line 460 def block_size @block_size end |
#io ⇒ Object (readonly)
Returns the value of attribute io.
460 461 462 |
# File 'lib/ole/storage.rb', line 460 def io @io end |
#ole ⇒ Object (readonly)
Returns the value of attribute ole.
460 461 462 |
# File 'lib/ole/storage.rb', line 460 def ole @ole end |
Instance Method Details
#[]=(idx, val) ⇒ Object
551 552 553 554 |
# File 'lib/ole/storage.rb', line 551 def []= idx, val @sparse = true if val == AVAIL super end |
#blocks_to_ranges(chain, size = nil) ⇒ Object
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.
513 514 515 516 517 518 519 520 521 |
# File 'lib/ole/storage.rb', line 513 def blocks_to_ranges chain, size=nil # truncate the chain if required chain = chain[0...(size.to_f / block_size).ceil] if size # convert chain to ranges of the block size ranges = chain.map { |i| [block_size * i, block_size] } # truncate final range if required ranges.last[1] -= (ranges.length * block_size - size) if ranges.last and size ranges end |
#chain(idx) ⇒ Object
rewrote this to be non-recursive as it broke on a large attachment chain with a stack error
497 498 499 500 501 502 503 504 505 506 |
# File 'lib/ole/storage.rb', line 497 def chain idx a = [] until idx >= META_BAT raise FormatError, "broken allocationtable chain" if idx < 0 || idx > length a << idx idx = self[idx] end Log.warn "invalid chain terminator #{idx}" unless idx == EOC a end |
#free_block ⇒ Object
556 557 558 559 560 561 562 563 |
# File 'lib/ole/storage.rb', line 556 def free_block if @sparse i = index(AVAIL) and return i end @sparse = false push AVAIL length - 1 end |
#load(data) ⇒ Object
467 468 469 |
# File 'lib/ole/storage.rb', line 467 def load data replace data.unpack('V*') end |
#open(chain, size = nil, &block) ⇒ Object
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.
530 531 532 |
# File 'lib/ole/storage.rb', line 530 def open chain, size=nil, &block RangesIO.open @io, :ranges => ranges(chain, size), &block end |
#ranges(chain, size = nil) ⇒ Object
523 524 525 526 |
# File 'lib/ole/storage.rb', line 523 def ranges chain, size=nil chain = self.chain(chain) unless Array === chain blocks_to_ranges chain, size end |
#read(chain, size = nil) ⇒ Object
534 535 536 |
# File 'lib/ole/storage.rb', line 534 def read chain, size=nil open chain, size, &:read end |
#resize_chain(blocks, size) ⇒ Object
must return first_block
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
# File 'lib/ole/storage.rb', line 566 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 some of our old blocks. TODO maybe zero them out in the file??? (new_num_blocks...old_num_blocks).each { |i| self[blocks[i]] = AVAIL } self[blocks[new_num_blocks-1]] = EOC if new_num_blocks > 0 blocks.slice! new_num_blocks..-1 elsif new_num_blocks > old_num_blocks # need some more blocks. last_block = blocks.last (new_num_blocks - old_num_blocks).times do block = free_block # connect the chain. handle corner case of blocks being [] initially self[last_block] = block if last_block blocks << block last_block = block self[last_block] = EOC end end # update ranges, and return that also now blocks end |
#to_s ⇒ Object
485 486 487 488 489 490 491 492 493 |
# File 'lib/ole/storage.rb', line 485 def to_s table = truncate # pad it out some num = @ole.bbat.block_size / 4 # do you really use AVAIL? they probably extend past end of file, and may shortly # be used for the bat. not really good. table += [AVAIL] * (num - (table.length % num)) if (table.length % num) != 0 table.pack 'V*' end |
#truncate ⇒ Object
471 472 473 474 475 476 477 478 479 |
# File 'lib/ole/storage.rb', line 471 def truncate # this strips trailing AVAILs. come to think of it, this has the potential to break # bogus ole. if you terminate using AVAIL instead of EOC, like I did before. but that is # very broken. however, if a chain ends with AVAIL, it should probably be fixed to EOC # at load time. temp = reverse not_avail = temp.find { |b| b != AVAIL } and temp = temp[temp.index(not_avail)..-1] temp.reverse end |
#truncate! ⇒ Object
481 482 483 |
# File 'lib/ole/storage.rb', line 481 def truncate! replace truncate end |