1#!/usr/bin/perl 2# 3# $Header: /Users/claude/fuzz/lib/Genezzo/BufCa/RCS/BCFile.pm,v 7.10 2006/08/02 05:40:10 claude Exp claude $ 4# 5# copyright (c) 2003-2006 Jeffrey I Cohen, all rights reserved, worldwide 6# 7# 8use strict; 9use warnings; 10 11package Genezzo::BufCa::BCFile; 12 13use IO::File; 14use IO::Handle; 15use Genezzo::BufCa::BufCa; 16use Genezzo::Block::Util; 17use Genezzo::Util; 18use Carp; 19use File::Spec; 20use warnings::register; 21 22our @ISA = qw(Genezzo::BufCa::BufCa) ; 23 24# non-exported package globals go here 25 26our $USE_MAX_FILES = 1; # true if fixed number of open files 27 28# initialize package globals, first exported ones 29#my $Var1 = ''; 30#my %Hashit = (); 31 32# then the others (which are still accessible as $Some::Module::stuff) 33#$stuff = ''; 34#@more = (); 35 36# all file-scoped lexicals must be created before 37# the functions below that use them. 38 39# file-private lexicals go here 40#my $priv_var = ''; 41#my %secret_hash = (); 42# here's a file-private function as a closure, 43# callable as &$priv_func; it cannot be prototyped. 44#my $priv_func = sub { 45 # stuff goes here. 46#}; 47 48# make all your functions, whether exported or not; 49# remember to put something interesting in the {} stubs 50#sub func1 {print "hi";} # no prototype 51#sub func2() {} # proto'd void 52#sub func3($$) {} # proto'd to 2 scalars 53#sub func5 {print "ho";} # no prototype 54 55sub _init 56{ 57 #whoami; 58 #greet @_; 59 my $self = shift; 60 61 $self->{ __PACKAGE__ . ":FN_ARRAY" } = []; 62 $self->{ __PACKAGE__ . ":FN_HASH" } = {}; 63 $self->{ __PACKAGE__ . ":HITLIST" } = {}; 64 $self->{bc} = Genezzo::BufCa::BufCa->new(@_); 65 $self->{cache_hits} = 0; 66 $self->{cache_misses} = 0; 67 $self->{read_only} = 0; # TODO: set for read-only database support 68 69 $self->{open_list} = []; 70 71 return 1; 72} 73 74sub new 75{ 76 my $invocant = shift; 77 my $class = ref($invocant) || $invocant ; 78 my $self = {}; 79 80# whoami; 81 82 my %args = (@_); 83 84 return undef 85 unless (_init($self,%args)); 86 87 if (exists($args{tsname})) 88 { 89 $self->{tsname} = $args{tsname}; 90 } 91 92 return bless $self, $class; 93 94} # end new 95 96sub _get_fn_array 97{ 98 my $self = shift; 99 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 100 my $fn_hsh = $self->{ __PACKAGE__ . ":FN_HASH" }; 101 return $fn_arr; 102} 103sub _get_fn_hash 104{ 105 my $self = shift; 106 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 107 my $fn_hsh = $self->{ __PACKAGE__ . ":FN_HASH" }; 108 return $fn_hsh; 109} 110 111sub Dump 112{ 113 whoami; 114 my $self = shift; 115 my $hitlist = $self->{ __PACKAGE__ . ":HITLIST" }; 116 117 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 118 my $fn_hsh = $self->{ __PACKAGE__ . ":FN_HASH" }; 119 120 my %hashi = (bc => $self->{bc}->Dump(), 121 cache_hits => $self->{cache_hits}, 122 cache_misses => $self->{cache_misses}, 123 hitlist => scalar keys %{$hitlist}, 124# fileinfo => $fn_arr 125 open_list => $self->{open_list} 126 ); 127 128 if (exists($self->{tsname})) 129 { 130 $hashi{tsname} = $self->{tsname} 131 } 132 133 return \%hashi; 134} 135 136 137sub Resize 138{ 139# whoami; 140 my $self = shift; 141 return 0 142 unless ($self->Flush()); 143 my $stat = $self->{bc}->Resize(@_); 144# greet $stat; 145 return $stat; 146} 147 148sub FileReg 149{ 150 my $self = shift; 151 152 local $Genezzo::Util::QUIETWHISPER = 1; # XXX: quiet the whispering 153 154 whoami @_; 155 156 my %required = ( 157 FileName => "no FileName !", 158 FileNumber => "no FileNumber !" 159 ); 160 161 my %args = ( 162 @_); 163 164 return undef 165 unless (Validate(\%args, \%required)); 166 167 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 168 my $fn_hsh = $self->{ __PACKAGE__ . ":FN_HASH" }; 169 170 my $filename = File::Spec->rel2abs($args{FileName}); 171 172 # XXX: need a lock here for multithread 173 unless (exists($fn_hsh->{$filename})) 174 { 175 # array of hashes of file info 176 my %th; 177 my @headerinfo; 178 $th{name} = $filename; 179 180 # XXX: open all handles for now 181 $th{fh} = new IO::File "+<$th{name}" 182 or die "Could not open $th{name} for writing : $! \n"; 183 184 @headerinfo = 185 Genezzo::Util::FileGetHeaderInfo(filehandle => $th{fh}, 186 filename => $th{name}); 187 188# greet @headerinfo; 189 return undef 190 unless (scalar(@headerinfo)); 191 $th{hdrsize} = $headerinfo[0]; 192 193 my $fileno = $args{FileNumber}; 194 195 if ($USE_MAX_FILES) 196 { 197 # close everything 198 $th{fh}->close; 199 delete $th{fh}; 200 } 201 else 202 { 203 push @{$self->{open_list}}, $fileno; # open file list 204 } 205 206# greet $fileno; 207 208 # XXX: NOTE: treat filename array as 1 based, vs 0 based 209 # -- use fn_arr[n-1]->name to get filename. 210 211 $fn_hsh->{$filename} = $fileno; 212 $fileno--; 213 $fn_arr->[$fileno] = \%th; 214 } 215 216 return ($fn_hsh->{$filename}) 217} 218 219sub _getOpenFileHandle 220{ 221 my $self = shift; 222 my %required = ( 223 filenum => "no filenum !" 224 ); 225 226 my %optional = (getscalar => 0); # return fname, fh, fhdrsize by default 227 228 my %args = (%optional, 229 @_); 230 231 return undef 232 unless (Validate(\%args, \%required)); 233 234 my $fnum = $args{filenum}; 235 236 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 237 238 # XXX: NOTE: treat filename array as 1 based, vs 0 based 239 # -- use fn_arr[n-1]->name to get filename. 240 241 my $entry = $fn_arr->[$fnum-1]; 242 243 my $fname = $entry->{name}; 244 245 unless (exists($entry->{fh}) 246 && (defined($entry->{fh}))) 247 { 248 249 whisper "re-open $fname\n"; 250 251 while ($USE_MAX_FILES) 252 { 253 my $num_open_files = scalar(@{$self->{open_list}}); 254 255 last 256 if ($num_open_files < $Genezzo::Util::MAXOPENFILES); 257# if ($num_open_files < 2); 258 259 # randomly close one of the open files -- remove it from 260 # the open list 261 my $close_victim = int(rand($num_open_files)); 262 263 my @foo = splice(@{$self->{open_list}}, $close_victim, 1); 264 265 last 266 unless (scalar(@foo)); 267 268 my $close_fnum = $foo[0]; 269 270 my $close_entry = $fn_arr->[$close_fnum-1]; 271 272 whisper "close $close_entry->{name}\n"; 273 274 next 275 unless (exists($close_entry->{fh}) 276 && defined($close_entry->{fh})); 277 my $close_fh = $close_entry->{fh}; 278 279 if ($Genezzo::Util::USE_FSYNC) 280 { 281 whisper "failed to sync $fname" 282 unless ($close_fh->sync); # should be "0 but true" 283 } 284 $close_fh->close; 285 delete $close_entry->{fh}; 286 287 last; 288 } # end while 289 290 $entry->{fh} = new IO::File "+<$fname" 291 or die "Could not open $fname for writing : $! \n"; 292 293 push @{$self->{open_list}}, $fnum; # open file list 294 } 295 my $fh = $entry->{fh}; 296 my $fhdrsz = $entry->{hdrsize}; 297 298 return $entry 299 if $args{getscalar}; 300 301 return ($fname, $fh, $fhdrsz); 302} 303 304sub BCFileInfoByName 305{ 306 my $self = shift; 307 308 whoami @_; 309 310 my %required = ( 311 FileName => "no FileName !" 312 ); 313 314 my %args = ( 315 @_); 316 317 return undef 318 unless (Validate(\%args, \%required)); 319 320 my $fn_hsh = $self->{ __PACKAGE__ . ":FN_HASH" }; 321 322 my $filename = File::Spec->rel2abs($args{FileName}); 323 324 return undef 325 unless (exists($fn_hsh->{$filename})); 326 327 my $fileno = $fn_hsh->{$filename}; 328 329 return ($self->_getOpenFileHandle(filenum => $fileno, getscalar => 1)); 330} 331 332sub FileSetHeaderInfoByName 333{ 334 my $self = shift; 335 336 whoami @_; 337 338 my %required = ( 339 FileName => "no FileName !", 340 newkey => "no key!", 341 newval => "no val!" 342 ); 343 344 my %args = ( 345 @_); 346 347 return undef 348 unless (Validate(\%args, \%required)); 349 350 my $filename = $args{FileName}; 351 my $newkey = $args{newkey}; 352 my $newval = $args{newval}; 353 354 my $file_info = $self->BCFileInfoByName(FileName => $filename); 355 356 return undef 357 unless (defined($file_info)); 358 359 my $fh = $file_info->{fh}; 360 361 return Genezzo::Util::FileSetHeaderInfo( 362 filehandle => $fh, 363 filename => $filename, 364 newkey => $newkey, 365 newval => $newval 366 ); 367 368} 369 370sub _filereadblock 371{ 372# whoami; 373 my ($self, $fname, $fnum, $fh, $bnum, $refbuf, $hdrsize) = @_; 374 375# greet $fname, $fnum, $fh, $bnum, $hdrsize; 376 377 my $blocksize = $self->{bc}->{blocksize}; 378 379 $fh->sysseek (($hdrsize+($bnum * $blocksize)), 0 ) 380 or die "bad seek - file $fname : $fnum, block $bnum : $! \n"; 381 382 # HOOK: PRE SYSREAD BLOCK 383 384 Genezzo::Util::gnz_read ($fh, $$refbuf, $blocksize) 385 == $blocksize 386 or die "bad read - file $fname : $fnum, block $bnum : $! \n"; 387 388 # HOOK: POST SYSREAD BLOCK 389 390 if (1) 391 { 392 my @cksums = Genezzo::Block::Util::GetChecksums($refbuf, $blocksize); 393 # test if the calculated checksum matches the stored checksum 394 unless ((scalar(@cksums) == 2) && 395 ($cksums[0] == $cksums[1])) 396 { 397 # XXX XXX: need failure or repair procedure - warn about 398 # problem but ignore for now 399 my $w1 = "bad read - invalid checksum for file $fname : " 400 . "$fnum, block $bnum : $! \n"; 401 warn $w1; 402 } 403 404 } 405 406 # HOOK: post filereadblock 407 408 return (1); 409 410} 411 412#sub _init_filewriteblock 413#{ 414# my ($self, $fname, $fnum, $fh, $bnum, $refbuf, $hdrsize, $bce) = @_; 415# 416# return 1 417# unless (defined($bce)); 418# 419# whoami; 420# 421# if (1) 422# { 423# my $foo = $bce->GetContrib(); 424# 425# return 1 426# unless (defined($foo)); 427# 428# if (exists($foo->{mailbox}) 429# && exists($foo->{mailbox}->{'Genezzo::Block::RDBlock'})) 430# { 431# my $rdblock = $foo->{mailbox}->{'Genezzo::Block::RDBlock'}; 432# greet $rdblock->_set_meta_row("BCE", ["BCE","howdy"]); 433# } 434# } 435# return 1; 436#} 437 438sub _filewriteblock 439{ 440 my $self = shift; 441 my ($fname, $fnum, $fh, $bnum, $refbuf, $hdrsize, $bce) = @_; 442 443 return 0 444 if ($self->{read_only}); 445 446 my $blocksize = $self->{bc}->{blocksize}; 447 448 $fh->sysseek (($hdrsize+($bnum * $blocksize)), 0 ) 449 or die "bad seek - file $fname : $fnum, block $bnum : $! \n"; 450 451 # HOOK: init filewriteblock 452 # use sys_hook to define 453 if (defined(&_init_filewriteblock)) 454 { 455 return 0 456 unless (_init_filewriteblock($self, @_)); 457 } 458 459 # update the block header with filenum, blocknum and 460 # set the footer checksum 461 Genezzo::Block::Util::UpdateBlockHeader($fnum, $bnum, $refbuf, $blocksize); 462 463 if (1) 464 { 465 Genezzo::Block::Util::UpdateBlockFooter($refbuf, $blocksize); 466 } 467 468 # HOOK: PRE SYSWRITE BLOCK 469 470 gnz_write ($fh, $$refbuf, $blocksize) 471 == $blocksize 472 or die "bad write - file $fname : $fnum, block $bnum : $! \n"; 473 474 # HOOK: POST SYSWRITE BLOCK 475 476 return (1); 477} 478 479sub ReadBlock 480{ 481 my $self = shift; 482 483# whoami @_; 484 485 my %required = ( 486 filenum => "no filenum !", 487 blocknum => "no blocknum !" 488 ); 489 490# my %optional ;# XXX XXX XXX: dbh_ctx 491 492 my %args = ( 493 @_); 494 495 return undef 496 unless (Validate(\%args, \%required)); 497 498 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 499 my $fnum = $args{filenum}; 500 501 return undef 502 unless (NumVal( 503 verbose => warnings::enabled(), 504 name => "filenum", 505 val => $fnum, 506 MIN => 0, 507 MAX => (scalar(@{$fn_arr}) + 1))) ; 508 509 my $hitlist = $self->{ __PACKAGE__ . ":HITLIST" }; 510 my $bnum = $args{blocknum}; 511 512 # cache hit 513 if (exists($hitlist->{"FILE:" . "$fnum" . ":". "$bnum"})) 514 { 515# whisper "hit!"; 516 $self->{cache_hits} += 1; 517 518 my $bcblocknum = $hitlist->{"FILE:" . "$fnum" . ":". "$bnum"}; 519 return $self->{bc}->ReadBlock(blocknum => $bcblocknum); 520 } 521 522 # miss 523# whisper "miss!"; 524 $self->{cache_misses} += 1; 525 526 my $thing = $self->{bc}->GetFree(); 527 528 unless (2 == scalar(@{$thing})) 529 { 530 whisper "no free blocks!"; 531 532 greet $hitlist; 533 return undef; 534 } 535 536 my $bceref = pop (@{$thing}); 537 my $bcblocknum = pop (@{$thing}); 538 539 my $bce = $$bceref; 540 541 if (1) # need to clean the hitlist even if not dirty 542 { 543# greet $hitlist; 544 545 if (exists($hitlist->{"BC:" . "$bcblocknum"})) 546 { 547 my $fileinfo = $hitlist->{"BC:" . "$bcblocknum"}; 548 549 my ($ofnum, $obnum) = ($fileinfo =~ m/FILE:(\d.*):(\d.*)/); 550# greet $fileinfo, $ofnum, $obnum; 551 delete $hitlist->{$fileinfo}; 552# greet $hitlist; 553 if ($bce->_dirty()) 554 { 555 my ($ofname, $ofh, $ofhdrsz) = 556 $self->_getOpenFileHandle(filenum => $ofnum); 557 558 return (undef) 559 unless ( 560 $self->_filewriteblock( 561 $ofname, 562 $ofnum, 563 $ofh, 564 $obnum, 565 $bce->{bigbuf}, 566 $ofhdrsz, 567 $bce 568 ) 569 ); 570 } 571 } 572 } 573 574 my $fileinfo = "FILE:" . "$fnum" . ":". "$bnum"; 575 $hitlist->{$fileinfo} = $bcblocknum; 576 $hitlist->{"BC:" . "$bcblocknum"} = $fileinfo; 577 578 # get the hash of bce information and update with filenum, blocknum 579 my $infoh = $bce->GetContrib(); 580 581 # update the GetContrib *before* the fileread so locking code has some 582 # place to look up the information 583 $infoh->{filenum} = $fnum; 584 $infoh->{blocknum} = $bnum; 585 586 $bce->_fileread(1); 587 588 my ($fname, $fh, $fhdrsz) = $self->_getOpenFileHandle(filenum => $fnum); 589 my $readstat = $self->_filereadblock($fname, $fnum, $fh, $bnum, 590 $bce->{bigbuf}, $fhdrsz); 591 $bce->_fileread(0); 592 # new block is not dirty 593 $bce->_dirty(0); 594 595 # XXX XXX XXX: error -- need to clean the hitlist!! 596 return (undef) 597 unless ($readstat); 598 599# greet $hitlist; 600 return $self->{bc}->ReadBlock(blocknum => $bcblocknum); 601} # end ReadBlock 602 603 604sub WriteBlock 605{ 606 my $self = shift; 607 608# whoami @_; 609 610 my %required = ( 611 filenum => "no filenum !", 612 blocknum => "no blocknum !" 613 ); 614 615# my %optional ;# XXX XXX XXX: dbh_ctx 616 617 my %args = ( 618 @_); 619 620 return undef 621 unless (Validate(\%args, \%required)); 622 623 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 624 my $fnum = $args{filenum}; 625 626 return undef 627 unless (NumVal( 628 verbose => warnings::enabled(), 629 name => "filenum", 630 val => $fnum, 631 MIN => 0, 632 MAX => (scalar(@{$fn_arr}) + 1))) ; 633 634 my $hitlist = $self->{ __PACKAGE__ . ":HITLIST" }; 635 my $bnum = $args{blocknum}; 636 637 return 1 638 unless (exists($hitlist->{"FILE:" . "$fnum" . ":". "$bnum"})); 639 # cache hit 640 641 my $bcblocknum = $hitlist->{"FILE:" . "$fnum" . ":". "$bnum"}; 642 my $bceref = $self->{bc}->ReadBlock(blocknum => $bcblocknum); 643 my $bce = $$bceref; 644 645 if ($bce->_dirty()) 646 { 647 my ($fname, $fh, $fhdrsz) = 648 $self->_getOpenFileHandle(filenum => $fnum); 649 650 return (0) 651 unless ( 652 $self->_filewriteblock($fname, $fnum, $fh, $bnum, 653 $bce->{bigbuf}, $fhdrsz, $bce) 654 ); 655 } 656 $bce->_dirty(0); 657 658 return 1; 659 660} # end WriteBlock 661 662sub Flush 663{ 664 my $self = shift; 665 666 whoami; 667 668 my $hitlist = $self->{ __PACKAGE__ . ":HITLIST" }; 669 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 670 671 unless ($Genezzo::Util::USE_FSYNC) 672 { 673 # Win32 problem: 674 # no fsync, so have to autoflush everything, which may be much 675 # more inefficient. 676 for my $th (@{$fn_arr}) 677 { 678 if (exists($th->{fh}) 679 && defined($th->{fh})) 680 { 681 $th->{fh}->autoflush(1); 682 } 683 } 684 } 685 686 my %sync_list; 687 688 # HOOK: PRE FLUSH BCFILE 689 690 while (my ($kk, $vv) = each (%{$hitlist})) 691 { 692 next if ($kk !~ /^FILE/); 693 694 my ($fnum, $bnum) = ($kk =~ m/FILE:(\d.*):(\d.*)/); 695 696 my $bceref = $self->{bc}->ReadBlock(blocknum => $vv); 697 my $bce = $$bceref; 698 699 if ($bce->_dirty()) 700 { 701 my ($fname, $fh, $fhdrsz) = 702 $self->_getOpenFileHandle(filenum => $fnum); 703 704 $sync_list{$fnum} = 1; 705 706 whisper "write dirty block : $fname - $fnum : $bnum"; 707 708 return (0) 709 unless ( 710 $self->_filewriteblock($fname, $fnum, $fh, $bnum, 711 $bce->{bigbuf}, $fhdrsz, $bce) 712 ); 713 } 714 $bce->_dirty(0); 715 } 716 717 718 if ($Genezzo::Util::USE_FSYNC) 719 { 720## print "\nsync here!\n"; 721 722 for my $fnum (keys (%sync_list)) 723 { 724 # sync the file handles - normally, can bcfile can buffer 725 # writes, but in this case we want to assure they get written 726 # before commit 727 # 728 # Note: sync is an IO::Handle method inherited by IO::File 729 my ($fname, $fh, $fhdrsz) = 730 $self->_getOpenFileHandle(filenum => $fnum); 731 732 whisper "failed to sync $fname" 733 unless ($fh->sync); # should be "0 but true" 734 } 735 } 736 else 737 { 738## print "\nno sync here!\n"; 739 # Win32 problem: 740 # cleanup the autoflush 741 for my $th (@{$fn_arr}) 742 { 743 if (exists($th->{fh}) 744 && defined($th->{fh})) 745 { 746 $th->{fh}->autoflush(0); 747 } 748 } 749 750 751 } 752 753 # HOOK: POST FLUSH BCFILE 754 755 return 1; 756# greet $hitlist; 757 758} # end flush 759 760sub Rollback 761{ 762 my $self = shift; 763 764 whoami; 765 766 my $hitlist = $self->{ __PACKAGE__ . ":HITLIST" }; 767 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 768 769 # HOOK: PRE ROLLBACK BCFILE 770 771 while (my ($kk, $vv) = each (%{$hitlist})) 772 { 773 next if ($kk !~ /^FILE/); 774 775 my ($fnum, $bnum) = ($kk =~ m/FILE:(\d.*):(\d.*)/); 776 777 my $bceref = $self->{bc}->ReadBlock(blocknum => $vv); 778 my $bce = $$bceref; 779 780 if ($bce->_dirty()) 781 { 782 my ($fname, $fh, $fhdrsz) = 783 $self->_getOpenFileHandle(filenum => $fnum); 784 785 whisper "replace dirty block : $fname - $fnum : $bnum"; 786 787 $bce->_dirty(0); 788 789 return (0) 790 unless ( 791 $self->_filereadblock($fname, $fnum, $fh, $bnum, 792 $bce->{bigbuf}, $fhdrsz) 793 ); 794 } 795 } 796 797 # HOOK: POST ROLLBACK BCFILE 798 799 return 1; 800# greet $hitlist; 801 802} 803 804sub BCGrowFile 805{ 806 whoami; 807 my ($self, $filenumber, $startblock, $numblocks) = @_; 808 809 my $fnum = $filenumber; 810 my $fn_arr = $self->{ __PACKAGE__ . ":FN_ARRAY" }; 811 my $blocksize = $self->{bc}->{blocksize}; 812 813 my ($fname, $fh, $fhdrsz) = 814 $self->_getOpenFileHandle(filenum => $fnum); 815 816 my $packstr = "\0" x $blocksize ; # fill with nulls 817 818 my @outi; 819 820 push @outi, $startblock; 821 822 for my $ii (0..($numblocks - 1)) 823 { 824 my $bnum = $startblock + $ii; 825# greet "new block $bnum"; 826 return @outi 827 unless ( 828 $self->_filewriteblock($fname, $fnum, $fh, $bnum, 829 \$packstr, $fhdrsz) 830 ); 831 $outi[1] = $ii + 1; # number of blocks added 832 } 833 return @outi; # starting block number, number of new blocks 834} 835 836sub DESTROY 837{ 838 my $self = shift; 839# whoami; 840 841 if (exists($self->{bc})) 842 { 843 $self->{bc} = (); 844 } 845 846} 847 848END { } # module clean-up code here (global destructor) 849 850## YOUR CODE GOES HERE 851 8521; # don't forget to return a true value from the file 853 854=head1 NAME 855 856 Genezzo::BufCa::BCFile.pm - A simple in-memory buffer cache for 857 multiple files for a single process, without locking. 858 859=head1 SYNOPSIS 860 861 use Genezzo::BufCa::BCFile; 862 863 # get a buffer cache 864 my $bc = Genezzo::BufCa::BCFile->new(blocksize => 10, numblocks => 5); 865 866 # register a file 867 my $fileno = Genezzo::BufCa::BCFile->FileReg(FileName => 'file.dat'); 868 869 # get back some block 870 $bceref = $bc->ReadBlock(filenum => $fileno, 871 blocknum => $blocknum); 872 $bce = $$bceref; 873 874=head1 DESCRIPTION 875 876 The file buffer cache is a simple module designed to form the 877 basis of a more complicated multi-process buffer cache 878 with locking. The buffer cache contains a number of Buffer Cache 879 Elements (BCEs), a special wrapper class for simple byte buffers 880 (blocks). See L<Genezzo::BufCa::BufCa>. 881 882 Note that this module does not perform space management or allocation 883 within the files -- it only reads and writes the blocks. The caller 884 is responsible for managing the contents of the file. 885 886=head1 FUNCTIONS 887 888=over 4 889 890=item new 891 892 Takes arguments blocksize (required, in bytes), numblocks (10 by 893 default). Returns a new buffer cache of the specified number of 894 blocks of size blocksize. 895 896=item BCFileInfoByName 897 Return the file state information. 898 899=item FileSetHeaderInfoByName 900 Update the datafile header. 901 902=item FileReg 903 904 Register a file with the cache -- returns a file number. Reregistering 905 a file should return the same number. 906 907=item ReadBlock 908 909 Takes argument blocknum, which must be a valid block number, and 910 the argument filenum, which must be a valid file number. If the 911 block is in memory it returns the bceref. If the block is not in 912 the cache it fetches it from disk into an unused block. If the 913 unused block is dirty, then ReadBlock writes it out first. 914 Fails if all blocks are in use. 915 916=item WriteBlock 917 918 Write a block to disk. Not really necessary -- ReadBlock will 919 flush some dirty blocks to disk automatically, and Flush 920 will write all dirty blocks to disk. 921 922=item Flush 923 924 Write all dirty blocks to disk. 925 926=item Rollback 927 928 Discard all dirty blocks and replace with blocks from disk.. 929 930=back 931 932=head2 EXPORT 933 934 None by default. 935 936=head1 LIMITATIONS 937 938Currently requires 2 blocks per open file. 939 940=head1 TODO 941 942=over 4 943 944=item note that _fileread could just be part of GetContrib 945 946=item need to move TSExtendFile functionality here if want to overload 947 syswrite with encryption 948 949=item read_only database support 950 951=item buffer cache block zero should contain description of buffer cache 952 layout 953 954=item need a way to free blocks associated with a file that is not 955 currently in use 956 957=back 958 959 960=head1 AUTHOR 961 962 Jeffrey I. Cohen, jcohen@genezzo.com 963 964=head1 SEE ALSO 965 966perl(1). 967 968Copyright (c) 2003-2006 Jeffrey I Cohen. All rights reserved. 969 970 This program is free software; you can redistribute it and/or modify 971 it under the terms of the GNU General Public License as published by 972 the Free Software Foundation; either version 2 of the License, or 973 any later version. 974 975 This program is distributed in the hope that it will be useful, 976 but WITHOUT ANY WARRANTY; without even the implied warranty of 977 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 978 GNU General Public License for more details. 979 980 You should have received a copy of the GNU General Public License 981 along with this program; if not, write to the Free Software 982 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 983 984Address bug reports and comments to: jcohen@genezzo.com 985 986For more information, please visit the Genezzo homepage 987at L<http://www.genezzo.com> 988 989=cut 990