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