home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / TBTREE.ZIP / PAGE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-07-25  |  36.6 KB  |  977 lines

  1. (* TBTree13             Copyright (c)  1988            Dean H. Farwell II    *)
  2.  
  3. unit Page;
  4.  
  5. (*****************************************************************************)
  6. (*                                                                           *)
  7. (*          P A G E  B U F F E R  H A N D L I N G  R O U T I N E S           *)
  8. (*                                                                           *)
  9. (*****************************************************************************)
  10.  
  11. (*  This unit handles the page buffer.  This buffer is used for keeping
  12.     disk pages in memory.  The pages can be data, indexes, or bitmaps.
  13.     The buffer uses a demand paging scheme in which the least recently used
  14.     page is swapped out when a page is needed and the buffer is full.        *)
  15.  
  16. (* Version Information
  17.  
  18.    Version 1.1 - No Changes
  19.  
  20.    Version 1.2 - No Changes
  21.  
  22.    Version 1.3 - No Changes                                                  *)
  23.  
  24.  
  25. (*////////////////////////// I N T E R F A C E //////////////////////////////*)
  26.  
  27. interface
  28.  
  29. uses
  30.     Compare,
  31.     FileBuff,
  32.     FileDecs,
  33.     Hex,
  34.     MyPrint,
  35.     Numbers,
  36.     Printer,
  37.     Strings,
  38.     Time;
  39.  
  40. const
  41.     PAGESIZE = 512;                  (* Number of bytes in a Physical Record *)
  42.  
  43. type
  44.     BufferSizeType = 0 .. MAXWORD; (* used for number of pages in the buffer *)
  45.  
  46.     PageRange  = 1 .. PAGESIZE;    (* type used primarily for indexing a page
  47.                                       byte by byte.                          *)
  48.  
  49.     SinglePage = Array [PageRange] of Byte;    (* type used to hold one page *)
  50.  
  51. (*\*)
  52. (* This routine will check to see if a given physical record for a given file
  53.    actually exists either on disk or in the buffer.  It first checks the
  54.    buffer.  If its not in the buffer, it checks to see if it is past the
  55.    end of the file.  It essentially replaces EOF.  EOF will not work properly
  56.    if the pages reside in the buffer but have not been written to disk yet.
  57.  
  58.    Note - This routine is quite different than routines found in Files.Pas.
  59.    Thes files.pas routines use bitmap files to see if a record is actively
  60.    being used as opposed to existing and containing garbage.  PageExists
  61.    only checks the physical existence of a physical record (BITMAP, INDEX,
  62.    or DATA).  It does not check bitmaps like the others do.                  *)
  63.  
  64. function PageExists(fName : FnString;
  65.                     prNum : PrNumber) : Boolean;
  66.  
  67.  
  68. (* This function will fetch a page and return a copy of the page to the caller.
  69.    It accomplishes this by first looking in the buffer itself.  If it can't
  70.    locate it in the buffer, it checks to see if there is room in the buffer.
  71.    If there is no available room, the least recently used page is written to
  72.    disk.  That frees up that page for use.                                   *)
  73.  
  74. procedure FetchPage(fName : FnString;
  75.                     prNum : PrNumber;
  76.                     var pg : SinglePage);
  77.  
  78.  
  79. (* This routine will store a page in the buffer.  It accomplishes this by
  80.    seeing if an old version is in the buffer.  If it is not it creates a new
  81.    page.  The page is stored, the dirty flag is set, and the timeUsed is
  82.    set.
  83.  
  84.    This can be used to store a page even if the corresponding page does not yet
  85.    exist.  It this case, the record will be created and stored in the buffer.
  86.    It will be physically created in the file when the page is written to
  87.    disk.
  88.  
  89.    note - This routine will immediately write this page to disk if the user
  90.    has called SetImmediateDiskWrite with a value of true.  Using this feature
  91.    ensure that current info is always on the disk but will greatly reduce
  92.    efficiency                                                                *)
  93.  
  94. procedure StorePage(fName : FnString;
  95.                     prNum : PrNumber;
  96.                     pg : SinglePage);
  97.  
  98. (*\*)
  99. (* Routine to write all dirty pages in buffer to disk.                       *)
  100.  
  101. procedure WriteEntireBufferToDisk;
  102.  
  103.  
  104. (* This routine will all pages corresponding to the given file out to disk.
  105.    The routine does not release the pages from the buffer.                   *)
  106.  
  107. procedure WriteBufferToDisk(fName : FnString);
  108.  
  109.  
  110. (* This routine will release the page in the buffer for a given physical
  111.    record in a given file.  Of course, the routine first checks to see
  112.    if the record is in fact in the buffer.                                   *)
  113.  
  114.  
  115. procedure ReleasePage(fName : FnString;
  116.                       prNum : PrNumber);
  117.  
  118.  
  119. (* This routine will release all pages in the buffer for the given file (fName)
  120.    It is extremely important to realize that this DOES NOT write the buffer
  121.    pages to disk prior to releasing them.  This must be done explicitly or they
  122.    will be lost.  This routine is handy for deleting a file to ensure that no
  123.    pages are left roaming around in the buffer.                              *)
  124.  
  125. procedure ReleaseAllPages(fName : FnString);
  126.  
  127.  
  128. (* This routine will return the number of buffer pages currently in use.     *)
  129.  
  130. function CheckPagesInUse : BufferSizeType;
  131.  
  132.  
  133. (* This routine will return the number of maximum buffer pages allowed       *)
  134.  
  135. function CheckMaxBufferPages : BufferSizeType;
  136.  
  137. (*\*)
  138. (* This routine will allow the user to set the maximum number of buffer pages
  139.    to be in use at one time.  This routine allows the user to change this while
  140.    the program is running.  The program will check to ensure that the user is
  141.    not setting the maximum number of pages in use to an illegal value.  An
  142.    illegal value is zero or less.  The buffer must contain at least one
  143.    page to function properly.  If the caller has specified a new setting which
  144.    is below the number of pages in use, the routine will release pages
  145.    randomly until the count of pages in use is reduced to n.  There is
  146.    nothing fancy about the algorithm to chose pages to release.  The
  147.    user can alleviate having the wrong pages swapped out by specifying
  148.    certain pages to be swapped out prior to calling this.  For example,
  149.    the user could save and release all pages for a file which won't be used for
  150.    awhile.  Remember, swapping out the wrong pages will not cause errors, but
  151.    it may temporarily affect performance as the pages will have to be read
  152.    back in upon their next use.  As an aside, I did not swap out least
  153.    recently used pages since a large number might be swapped out.  Each
  154.    swap would entail going through the entire buffer to find the least
  155.    recently used page.  This would cause too much overhead.
  156.  
  157.    note - notice use of Exit for exiting the routine.  The routine will not
  158.    normally fall out the bottom.                                             *)
  159.  
  160. procedure SetMaxBufferPages(n : BufferSizeType);
  161.  
  162.  
  163. (* This routine will allow the user to set whether stored pages will be
  164.    immediately  written to disk during StorePage operations.  It will
  165.    set an internal variable which keeps track of whether pages should
  166.    be immediately written to disk or only stored in the buffer.  The
  167.    variable cannot be accessed by the user.  The only way is to set
  168.    it is to call this routine.  The routine is called at initialization
  169.    time with x = FALSE which means that pages will not be written immediately
  170.    to disk.  The user can change this at any time.  Once this is set to
  171.    TRUE all dirty pages in the buffer (ones that have not yet been written
  172.    to disk) will still be dirty (will not be automatically written).  All
  173.    newly stored pages will be immediately written.  To ensure that all
  174.    pages have been written to disk, use WriteEntireBufferToDisk.  Regardless
  175.    of whether this is set to TRUE or FALSE, the page will still be in the
  176.    buffer and will be available for FetchPage operations without going to
  177.    disk.                                                                     *)
  178.  
  179. procedure SetImmediateDiskWrite(x : Boolean);
  180.  
  181. (*\*)
  182. (* This routine will return TRUE if the parameter has been set forcing pages
  183.    to be written to disk immediately on calling StorePage.  This is the only
  184.    to check the variable since it is hidden in the implementation.           *)
  185.  
  186. function CheckImmediateDiskWrite : Boolean;
  187.  
  188. (* This routine will print the entire page buffer to the printer *)
  189.  
  190. procedure PrintPageBuffer;
  191.  
  192.  
  193. (* This routine will print the buffer statistics                             *)
  194.  
  195. procedure PrintBufferStats;
  196.  
  197. (*\*)
  198. (*///////////////////// I M P L E M E N T A T I O N /////////////////////////*)
  199.  
  200. (* the following declarations are for defining and storing the buffer *)
  201.  
  202. implementation
  203.  
  204. const
  205.     POINTERARRAYSIZE = 199;          (* used to set up array of linked lists
  206.                                          this number needs to be prime       *)
  207.  
  208. type
  209.     PagePtr = ^PageEntry;
  210.  
  211.     PageEntry  = record
  212.                  fName     : FnString;
  213.                  prNum     : PrNumber;
  214.                  dirty     : boolean;
  215.                  timeUsed  : TimeArr;
  216.                  page      : SinglePage;
  217.                  nextPage  : PagePtr;
  218.                  end;
  219.  
  220.     PointerArrayRange = 0 .. POINTERARRAYSIZE;
  221.  
  222.  
  223. var
  224.     pagesInUse : BufferSizeType;     (* value should never exceed the current
  225.                                                      value of maxBufferPages *)
  226.  
  227.     pointerArray : Array [PointerArrayRange] of PagePtr;  (* Type of Array
  228.                                                          holding the pointers
  229.                                                          to the linked list of
  230.                                                          pages in the
  231.                                                          page buffer *)
  232.  
  233.     immediateDiskWrite : Boolean;         (* used to keep track of whether to
  234.                                              store pages immediately to disk
  235.                                              on all StorePage calls          *)
  236.  
  237. (*\*)
  238. (* the following declarations are for keeping and printing statistics on
  239.    buffer usage                                                              *)
  240.  
  241. type
  242.     StatsRange = 0 .. MAXLONGINT;  (* used as type for many buffer stat vars *)
  243.  
  244.     BufferStats = record                   (* used to hold buffer statistics *)
  245.                   pagesInUse : StatsRange;
  246.                   maxPages : StatsRange;
  247.                   attempts : StatsRange;
  248.                   hits : StatsRange;
  249.                   end;
  250.  
  251.  
  252. var
  253.     maxBufferPages : BufferSizeType;  (* Number of buffer pages in buffer.
  254.                                          This can be set by the user to
  255.                                          allow a flexible buffer size        *)
  256.  
  257.  
  258.     bufferAttempts: StatsRange;     (* total attempts to fetch a page from the
  259.                                        buffer                                *)
  260.  
  261.     bufferHits : StatsRange;        (* used for to keep track of attempts to
  262.                                        fetch a physical record from the buffer
  263.                                        in which the record was there         *)
  264.  
  265. (*\*)
  266. (* This routine will initialize the pointer array to all NILS and will set
  267.    the pages in the pagesInUse counter to zero.  This last item will reflect
  268.    the fact that there are no pages active in the buffer.                    *)
  269.  
  270. procedure InitializePointerArray;
  271.  
  272. var
  273.     cnt : PointerArrayRange;
  274.  
  275.     begin
  276.     for cnt := 0 to POINTERARRAYSIZE do
  277.         begin
  278.         pointerArray[cnt] := NIL;
  279.         end;
  280.     pagesInUse := 0;
  281.     end;                            (* end of InitializePointerArray routine *)
  282.  
  283.  
  284. (* This routine will write a specified page to disk.  It will also change the
  285.    Dirty flag to FALSE showing that the page is not dirty.                   *)
  286.  
  287. procedure WriteToDisk(pgPtr : PagePtr);
  288.  
  289. var
  290.     tempFile : File;
  291.  
  292.     begin
  293.     OpenUntypedFile(pgPtr^.fName,tempFile,PAGESIZE);
  294.     Seek(tempFile,pgPtr^.prNum);
  295.     BlockWrite(tempFile,pgPtr^.page,1);
  296.     pgPtr^.dirty := FALSE;
  297.     end;                                     (* end of WriteToDisk procedure *)
  298.  
  299. (*\*)
  300. (* This routine will read in a specified page from disk.  It will change the
  301.    Dirty flag to false showing that the page is not dirty.  It will also
  302.    set the file name and set the physical record number.  It does not set the
  303.    the time.  This will be done by the procedure which actually decides to
  304.    fetch this record.                                                        *)
  305.  
  306. procedure ReadFromDisk(fName : FnString;
  307.                        prNum : PrNumber;
  308.                        pgPtr : PagePtr);
  309.  
  310. var
  311.     tempFile : file;
  312.  
  313.     begin
  314.     OpenUntypedFile(fName,tempFile,PAGESIZE);
  315.     Seek(tempFile,prNum);
  316.     BlockRead(tempFile,pgPtr^.page,1);
  317.     pgPtr^.fName := fName;
  318.     pgPtr^.prNum := prNum;
  319.     pgPtr^.dirty := FALSE;
  320.     end;                                    (* end of ReadFromDisk procedure *)
  321.  
  322.  
  323. (* This routine will return the index to the pointerArray corresponding to the
  324.    given file and physical record.                                           *)
  325.  
  326. function Hash(fName : FnString;
  327.               prNum : PrNumber) : PointerArrayRange;
  328.  
  329. {$V-}
  330.     begin
  331.     Hash := (prNum + TotalString(fName)) Mod POINTERARRAYSIZE;
  332.     end;                                              (* end of Hash routine *)
  333. {$V+}
  334.  
  335. (*\*)
  336. (* This routine will return a pointer pointing to the page corresponding to a
  337.    given file and physical record number.  It will return NIL if the page is
  338.    not in the buffer.                                                        *)
  339.  
  340. function GetPagePtr(fName : FnString;
  341.                     prNum : PrNumber) : PagePtr;
  342.  
  343. var
  344.     tempPtr : PagePtr;
  345.     found : boolean;
  346.  
  347.     begin
  348.     tempPtr := pointerArray[Hash(fName,prNum)];
  349.     found := FALSE;
  350.     while (not found) and (tempPtr <> NIL) do
  351.         begin
  352.         if (tempPtr^.fName = fName) and (tempPtr^.prNum = prNum) then
  353.            begin
  354.            found := TRUE;
  355.            end
  356.        else
  357.            begin
  358.            tempPtr := tempPtr^.nextPage;
  359.            end;
  360.        end;
  361.    GetPagePtr := tempPtr;
  362.    end;                                           (* end of FindPage routine *)
  363.  
  364.  
  365. (* This routine will pull a page out of a page list.  It does not Dispose of
  366.    the page.  This allows the page to be immediately reused.  The calling
  367.    routine should either reuse it or Dispose it.                             *)
  368.  
  369. procedure DeletePgFromList(pgPtr : PagePtr);
  370.  
  371. var
  372.     tempPtr : PagePtr;
  373.  
  374.     begin
  375.     tempPtr := pointerArray[Hash(pgPtr^.fName,pgPtr^.prNum)];
  376.     if tempPtr = pgPtr then
  377.         begin                             (* page to delete is first in list *)
  378.         pointerArray[Hash(pgPtr^.fName,pgPtr^.prNum)] := pgPtr^.nextPage;
  379.         end
  380.     else
  381.         begin
  382.         while tempPtr^.nextPage <> pgPtr do
  383.             begin
  384.             tempPtr := tempPtr^.nextPage;
  385.             end;
  386.         tempPtr^.nextPage := pgPtr^.nextPage;
  387.         end;
  388.     end;                                  (* end of DeletePgFromList routine *)
  389.  
  390. (*\*)
  391. (* This routine will take a page and insert it into the proper place in the
  392.    buffer.                                                                   *)
  393.  
  394. procedure InsertPgInList(fName : FnString;
  395.                          prNum : PrNumber;
  396.                          pgPtr : PagePtr);
  397.  
  398. var
  399.     arrayIndex : PointerArrayRange;
  400.  
  401.     begin
  402.     arrayIndex := Hash(fName,prNum);
  403.     pgPtr^.nextPage := pointerArray[arrayIndex];  (* insert page as first    *)
  404.     pointerArray[arrayIndex] := pgPtr;            (* page in page list       *)
  405.     end;                                    (* end of InsertPgInList routine *)
  406.  
  407.  
  408. (* This routine creates a new page and sets the file name and record number
  409.    fields.  It then inserts the new page in the front of the appropriate
  410.    page list.  It does not set the time nor dirty fields.  This routine does
  411.    not check to see if there is a page available.   This is the responsibility
  412.    of the caller.                                                            *)
  413.  
  414. procedure CreateNewPage(fName : FnString;
  415.                         prNum : PrNumber;
  416.                         var pgPtr : PagePtr);
  417.  
  418.     begin
  419.     New(pgPtr);
  420.     pagesInUse := pagesInUse + 1;                   (* one more page used up *)
  421.     InsertPgInList(fName,prNum,pgPtr);               (* put page into proper
  422.                                                              place in buffer *)
  423.     end;                                     (* end of CreateNewPage routine *)
  424.  
  425. (*\*)
  426. (* This routine will find the least recently used page, delete it from the
  427.    page list and write it to disk.  The pointer to the page is then returned *)
  428.  
  429. function LRUPage : PagePtr;
  430.  
  431. var
  432.     cnt : PointerArrayRange;
  433.     tempPgPtr,
  434.     leastPgPtr : PagePtr;
  435.     minTime : TimeArr;
  436.  
  437.     begin
  438.     SetMaxTime(minTime);
  439.     for cnt := 0 to POINTERARRAYSIZE do
  440.         begin
  441.         tempPgPtr := pointerArray[cnt];
  442.         while tempPgPtr <> NIL do
  443.             begin
  444.             if CompareTime(tempPgPtr^.timeUsed,mintime) = LESSTHAN then
  445.                 begin
  446.                 minTime := tempPgPtr^.timeUsed;
  447.                 leastPgPtr := tempPgPtr;
  448.                 end;
  449.             tempPgPtr := tempPgPtr^.nextPage;
  450.             end;
  451.         end;
  452.     WriteToDisk(leastPgPtr);                       (* write page out to disk *)
  453.     DeletePgFromList(leastPgPtr);              (* pull page out of page list *)
  454.     LRUPage := leastPgPtr;               (* return pointer to page to caller *)
  455.     end;                                           (* end of LRUPage routine *)
  456.  
  457. (*\*)
  458. (* This routine will check to see if a given physical record for a given file
  459.    actually exists either on disk or in the buffer.  It first checks the
  460.    buffer.  If its not in the buffer, it checks to see if it is past the
  461.    end of the file.  It essentially replaces EOF.  EOF will not work properly
  462.    if the pages reside in the buffer but have not been written to disk yet.
  463.  
  464.    Note - This routine is quite different than routines found in Files.Pas.
  465.    Thes files.pas routines use bitmap files to see if a record is actively
  466.    being used as opposed to existing and containing garbage.  PageExists
  467.    only checks the physical existence of a physical record (BITMAP, INDEX,
  468.    or DATA).  It does not check bitmaps like the others do.                  *)
  469.  
  470. function PageExists(fName : FnString;
  471.                     prNum : PrNumber) : Boolean;
  472.  
  473. var
  474.     tempFile : File;
  475.  
  476.     begin
  477.     if GetPagePtr(fName,prNum) = NIL then            (* check to see if record
  478.                                                                 is in buffer *)
  479.         begin
  480.         OpenUntypedFile(fName,tempFile,PAGESIZE);
  481.         if prNum <= FileSize(tempFile) - 1 then
  482.             begin                             (* record not past end of file *)
  483.             PageExists := TRUE;
  484.             end
  485.         else
  486.             begin               (* record not in buffer and past end of file *)
  487.             PageExists := FALSE;
  488.             end;
  489.         end
  490.     else
  491.         begin                    (* page is in buffer .. therefore it exists *)
  492.         PageExists := TRUE;
  493.         end;
  494.     end;                                        (* end of PageExists routine *)
  495.  
  496. (*\*)
  497. (* This function will fetch a page and return a copy of the page to the caller.
  498.    It accomplishes this by first looking in the buffer itself.  If it can't
  499.    locate it in the buffer, it checks to see if there is room in the buffer.
  500.    If there is no available room, the least recently used page is written to
  501.    disk.  That frees up that page for use.                                   *)
  502.  
  503. procedure FetchPage(fName : FnString;
  504.                     prNum : PrNumber;
  505.                     var pg : SinglePage);
  506.  
  507. var
  508.     pgPtr : PagePtr;
  509.  
  510.     begin
  511.     pgPtr := GetPagePtr(fName,prNum);          (* try to find page in buffer *)
  512.     if pgPtr = NIL then                    (* check to see if page was found *)
  513.         begin                                    (* page not found in buffer *)
  514.         if (pagesInUse <> maxBufferPages) and      (* check for unused pages *)
  515.            (MaxAvail >= SizeOf(PageEntry)) then      (* check for heap space *)
  516.             begin                             (* there is room in the buffer *)
  517.             CreateNewPage(fName,prNum,pgPtr);    (* make new page and use it *)
  518.             end
  519.         else
  520.             begin                                         (* no unused pages *)
  521.             pgPtr := LRUPage;                (* get least recently used page *)
  522.                                                      (* and write it to disk *)
  523.             InsertPgInList(fName,prNum,pgPtr);      (* reuse page and put in
  524.                                                       proper place in buffer *)
  525.             end;
  526.         ReadFromDisk(fName,prNum,pgPtr);             (* read in desired page *)
  527.         end
  528.     else
  529.         begin                                           (* page is in buffer *)
  530.         bufferHits := bufferHits + 1;                 (* update hits counter *)
  531.         end;
  532.     GetTime(pgPtr^.timeUsed);                 (* set time page was requested *)
  533.     pg := pgPtr^.page;       (* return copy of the actual page to the caller *)
  534.     bufferAttempts := bufferAttempts + 1;
  535.     end;                                         (* end of FetchPage routine *)
  536.  
  537. (*\*)
  538. (* This routine will store a page in the buffer.  It accomplishes this by
  539.    seeing if an old version is in the buffer.  If it is not it creates a new
  540.    page.  The page is stored, the dirty flag is set, and the timeUsed is
  541.    set.
  542.  
  543.    This can be used to store a page even if the corresponding page does not yet
  544.    exist.  It this case, the record will be created and stored in the buffer.
  545.    It will be physically created in the file when the page is written to
  546.    disk.
  547.  
  548.    note - This routine will immediately write this page to disk if the user
  549.    has called SetImmediateDiskWrite with a value of true.  Using this feature
  550.    ensure that current info is always on the disk but will greatly reduce
  551.    efficiency                                                                *)
  552.  
  553. procedure StorePage(fName : FnString;
  554.                     prNum : PrNumber;
  555.                     pg : SinglePage);
  556.  
  557. var
  558.     pgPtr : PagePtr;
  559.  
  560.     begin
  561.     pgPtr := GetPagePtr(fName,prNum);
  562.     if pgPtr = NIL then
  563.         begin
  564.         if (pagesInUse <> maxBufferPages) and      (* check for unused pages *)
  565.            (MaxAvail >= SizeOf(PageEntry)) then      (* check for heap space *)
  566.             begin
  567.             CreateNewPage(fName,prNum,pgPtr);
  568.             end
  569.         else
  570.             begin
  571.             pgPtr := LRUPage;
  572.             InsertPgInList(fName,prNum,pgPtr);
  573.             end;
  574.         pgPtr^.fName := fName;
  575.         pgPtr^.prNum := prNum;
  576.         end;
  577.     pgPtr^.page := pg;
  578.     pgPtr^.dirty := TRUE;
  579.     GetTime(pgPtr^.timeUsed);
  580.     if immediateDiskWrite then
  581.         begin
  582.         WriteToDisk(pgPtr);
  583.         end;
  584.     end;                                         (* end of StorePage routine *)
  585.  
  586. (*\*)
  587. (* Routine to write all dirty pages in buffer to disk.                       *)
  588.  
  589. procedure WriteEntireBufferToDisk;
  590.  
  591. var
  592.     pgPtr : PagePtr;
  593.     cnt : PointerArrayRange;
  594.  
  595.     begin
  596.     for cnt:= 0 to POINTERARRAYSIZE do
  597.         begin
  598.         pgPtr := PointerArray[cnt];
  599.         while pgPtr <> NIL do
  600.             begin
  601.             if pgPtr^.dirty then
  602.                 begin
  603.                 WriteBufferToDisk(pgPtr^.fName);
  604.                 end;
  605.             pgPtr := pgPtr^.nextPage;
  606.             end;
  607.         end;
  608.     end;                           (* end of WriteEntireBufferToDisk routine *)
  609.  
  610.  
  611. (* This routine will all pages corresponding to the given file out to disk.
  612.    The routine does not release the pages from the buffer.                   *)
  613.  
  614. procedure WriteBufferToDisk(fName : FnString);
  615.  
  616. var
  617.     pgPtr : PagePtr;
  618.     cnt : PointerArrayRange;
  619.  
  620.     begin
  621.     for cnt := 0 to POINTERARRAYSIZE do
  622.         begin
  623.         pgPtr := PointerArray[cnt];
  624.         while pgPtr <> NIL do
  625.             begin
  626.             if pgPtr^.fName = fName then
  627.                 begin
  628.                 WriteTodisk(pgPtr);
  629.                 end;
  630.             pgPtr := pgPtr^.nextPage;
  631.             end;
  632.         end;;
  633.     end;                                 (* end of WriteBufferToDisk routine *)
  634.  
  635. (*\*)
  636. (* This routine will release the page in the buffer for a given physical
  637.    record in a given file.  Of course, the routine first checks to see
  638.    if the record is in fact in the buffer.  It is important to realize that
  639.    this page will not be written to disk, but will be lost.                  *)
  640.  
  641. procedure ReleasePage(fName : FnString;
  642.                       prNum : PrNumber);
  643.  
  644. var
  645.     pgPtr : PagePtr;
  646.  
  647.     begin
  648.     pgPtr := GetPagePtr(fName,prNum);
  649.     if pgPtr <> NIL then
  650.         begin
  651.         DeletePgFromList(pgPtr);
  652.         Dispose(pgPtr);
  653.         pagesInUse := pagesInUse - 1;
  654.         end;
  655.     end;                                       (* end of ReleasePage routine *)
  656.  
  657. (*\*)
  658. (* This routine will release all pages in the buffer for the given file (fName)
  659.    It is extremely important to realize that this DOES NOT write the buffer
  660.    pages to disk prior to releasing them.  This must be done explicitly or they
  661.    will be lost.  This routine is handy for deleting a file to ensure that no
  662.    pages are left roaming around in the buffer.                              *)
  663.  
  664. procedure ReleaseAllPages(fName : FnString);
  665.  
  666. var
  667.     pgPtr : PagePtr;
  668.     cnt : PointerArrayRange;
  669.  
  670.     begin
  671.     for cnt := 0 to POINTERARRAYSIZE do
  672.         begin
  673.         pgPtr := pointerArray[cnt];
  674.         while pgPtr <> NIL do
  675.             begin
  676.             if pgPtr^.fName = fName then
  677.                 begin
  678.                 ReleasePage(fName,pgPtr^.prNum);
  679.                 pgPtr := PointerArray[cnt];     (* reset to a valid location *)
  680.                 end
  681.             else
  682.                 begin
  683.                 pgPtr := pgPtr^.nextPage;
  684.                 end;
  685.             end;
  686.         end;
  687.     end;                                   (* end of ReleaseAllPages routine *)
  688.  
  689.  
  690. (* This routine will return the number of buffer pages currently in use.     *)
  691.  
  692. function CheckPagesInUse : BufferSizeType;
  693.  
  694.     begin
  695.     CheckPagesInUse := pagesInUse;
  696.     end;                                   (* end of CheckPagesInUse routine *)
  697.  
  698. (* This routine will return the number of maximum buffer pages allowed       *)
  699.  
  700. function CheckMaxBufferPages : BufferSizeType;
  701.  
  702.     begin
  703.     CheckMaxBufferPages := maxBufferPages;
  704.     end;                               (* end of CheckMaxBufferPages routine *)
  705.  
  706. (*\*)
  707. (* This routine will allow the user to set the maximum number of buffer pages
  708.    to be in use at one time.  This routine allows the user to change this while
  709.    the program is running.  The program will check to ensure that the user is
  710.    not setting the maximum number of pages in use to an illegal value.  An
  711.    illegal value is zero or less.  The buffer must contain at least one
  712.    page to function properly.  If the caller has specified a new setting which
  713.    is below the number of pages in use, the routine will release pages
  714.    randomly until the count of pages in use is reduced to n.  There is
  715.    nothing fancy about the algorithm to chose pages to release.  The
  716.    user can alleviate having the wrong pages swapped out by specifying
  717.    certain pages to be swapped out prior to calling this.  For example,
  718.    the user could save and release all pages for a file which won't be used for
  719.    awhile.  Remember, swapping out the wrong pages will not cause errors, but
  720.    it may temporarily affect performance as the pages will have to be read
  721.    back in upon their next use.  As an aside, I did not swap out least
  722.    recently used pages since a large number might be swapped out.  Each
  723.    swap would entail going through the entire buffer to find the least
  724.    recently used page.  This would cause too much overhead.
  725.  
  726.    note - notice use of Exit for exiting the routine.  The routine will not
  727.    normally fall out the bottom.                                             *)
  728.  
  729. procedure SetMaxBufferPages(n : BufferSizeType);
  730.  
  731. var
  732.     pgPtr : PagePtr;
  733.     cnt : PointerArrayRange;
  734.  
  735.     begin
  736.     if n > 0 then      (* make sure that value is not 0! if it is do nothing *)
  737.         begin
  738.         if pagesInUse <= n then
  739.             begin
  740.             maxBufferPages := n;
  741.             end
  742.         else
  743.             begin
  744.             for cnt := 0 to POINTERARRAYSIZE do
  745.                 begin
  746.                 while pgPtr <> NIL do
  747.                     begin
  748.                     if pgPtr^.dirty then
  749.                         begin
  750.                         WriteToDisk(pgPtr);
  751.                         end;
  752.                     ReleasePage(pgPtr^.fName,pgPtr^.prNum);
  753.                     if pagesInUse = n then
  754.                         begin
  755.                         Exit;  (* done .. leave the routine *)
  756.                         end
  757.                     else
  758.                         begin
  759.                         pgPtr := pointerArray[cnt];        (* reset pgPtr to
  760.                                                             a valid location *)
  761.                         end;
  762.                     end;
  763.                 end;
  764.             end;
  765.         end;
  766.     end;                                 (* end of SetMaxBufferPages routine *)
  767.  
  768. (*\*)
  769. (* This routine will allow the user to set whether stored pages will be
  770.    immediately  written to disk during StorePage operations.  It will
  771.    set an internal variable which keeps track of whether pages should
  772.    be immediately written to disk or only stored in the buffer.  The
  773.    variable cannot be accessed by the user.  The only way is to set
  774.    it is to call this routine.  The routine is called at initialization
  775.    time with x = FALSE which means that pages will not be written immediately
  776.    to disk.  The user can change this at any time.  Once this is set to
  777.    TRUE all dirty pages in the buffer (ones that have not yet been written
  778.    to disk) will still be dirty (will not be automatically written).  All
  779.    newly stored pages will be immediately written.  To ensure that all
  780.    pages have been written to disk, use WriteEntireBufferToDisk.  Regardless
  781.    of whether this is set to TRUE or FALSE, the page will still be in the
  782.    buffer and will be available for FetchPage operations without going to
  783.    disk.                                                                     *)
  784.  
  785. procedure SetImmediateDiskWrite(x : Boolean);
  786.  
  787.     begin
  788.     ImmediateDiskWrite := x;
  789.     end;                             (* end of SetImmediateDiskWrite routine *)
  790.  
  791.  
  792. (* This routine will return TRUE if the parameter has been set forcing pages
  793.    to be written to disk immediately on calling StorePage.  This is the only
  794.    to check the variable since it is hidden in the implementation.           *)
  795.  
  796. function CheckImmediateDiskWrite : Boolean;
  797.  
  798.     begin
  799.     CheckImmediateDiskWrite := immediateDiskWrite;
  800.     end;                           (* end of CheckImmediateDiskWrite routine *)
  801.  
  802. (*\*)
  803. (* These routines support debugging of the page buffer routines              *)
  804.  
  805. procedure PrintPageInfo(pgPtr : PagePtr);
  806.  
  807.     (* Prints out string equivalent of boolean value *)
  808.     procedure PrintBoolean(x : boolean);
  809.  
  810.     begin
  811.     case x of
  812.         FALSE : Write(lst,'FALSE');
  813.         TRUE  : Write(lst,'TRUE');
  814.         end;                                        (* end of case statement *)
  815.     end;                                   (* end of PrintPageBuffer routine *)
  816.  
  817.     (* determines if x is a screen printable non control character *)
  818.     function PrintableChar(x : Char) : boolean;
  819.  
  820.     begin
  821.     PrintableChar := Integer(x) in [32 .. 127];
  822.     end;                                     (* end of PrintableChar routine *)
  823.  
  824. const
  825.     LINESIZE = 32;          (* number of bytes output on one line of printer *)
  826.  
  827. var
  828.     loopByteCnt,            (* used in inner loop to point to bytes *)
  829.     maxLoopByteCnt,         (* used in inner loop to keep from going past
  830.                                end of buffer page  *)
  831.     byteCnt : PageRange;    (* current byte in buffer page *)
  832.     done : boolean;         (* used for inner loop termination *)
  833.  
  834.     begin
  835.     Writeln(lst,'     fName = ',pgPtr^.fName);
  836.     Writeln(lst,'     prNum = ',pgPtr^.prNum);
  837.     Write(lst,'     dirty = ');
  838.     PrintBoolean(pgPtr^.dirty);
  839.     Writeln(lst);
  840.     Write(lst,'     timeUsed = ');
  841.     Write(lst,pgPtr^.timeUsed[1],'     ');
  842.     Write(lst,pgPtr^.timeUsed[2],'     ');
  843.     Write(lst,pgPtr^.timeUsed[3],'     ');
  844.     Write(lst,pgPtr^.timeUsed[4],'     ');
  845.     Writeln(lst); Writeln(lst);
  846.     byteCnt := 1;
  847.     done := FALSE;
  848.     repeat
  849.         begin
  850.         if ((byteCnt + LINESIZE) - 1) <= PAGESIZE then
  851.             begin
  852.             maxLoopByteCnt := byteCnt + LINESIZE - 1;
  853.             end
  854.         else
  855.             begin
  856.             maxLoopByteCnt := PAGESIZE;
  857.             end;
  858.         (* print column position *)
  859.         for loopByteCnt := byteCnt to maxLoopByteCnt do
  860.             begin
  861.             Write(lst,loopByteCnt : 3,' ');
  862.             end;
  863.         Writeln(lst);
  864.         (* Print HEX value *)
  865.         for loopByteCnt := byteCnt to maxLoopByteCnt do
  866.             begin
  867.             Write(lst,'$',ByteToHex(pgPtr^.page[loopByteCnt]),' ');
  868.             end;
  869.         Writeln(lst);
  870.         (* print integer equivalent *)
  871.         for loopByteCnt := byteCnt to maxLoopByteCnt do
  872.             begin
  873.             Write(lst,pgPtr^.page[loopByteCnt] :3,' ');
  874.             end;
  875.         Writeln(lst);
  876.         (* character equivalent or print '*' if char not printable *)
  877.         for loopByteCnt := byteCnt to maxLoopByteCnt do
  878.             begin
  879.             if PrintableChar(Chr(pgPtr^.page[loopByteCnt])) then
  880.                 begin
  881.                 Write(lst,' ',Chr(pgPtr^.page[loopByteCnt]),'  ');
  882.                 end
  883.             else
  884.                 begin
  885.                 Write(lst,' *  ');
  886.                 end;
  887.             end;
  888.         Writeln(lst); Writeln(lst);
  889.         if byteCnt + LINESIZE > PAGESIZE then
  890.             begin
  891.             done := TRUE;
  892.             end
  893.         else
  894.             begin
  895.             byteCnt := byteCnt + LINESIZE;
  896.             end;
  897.         end;
  898.     until done;
  899.     Writeln(lst); Writeln(lst);
  900.     end;                                     (* end of PrintPageInfo routine *)
  901.  
  902. (*\*)
  903. (* This routine will print the entire page buffer to the printer *)
  904.  
  905. procedure PrintPageBuffer;
  906.  
  907. var
  908.     pgPtr : PagePtr;
  909.     cnt : PointerArrayRange;
  910.  
  911.     begin
  912.     SetCompressedMode;                 (* sets printer to 132 character mode *)
  913.     for cnt := 0 to POINTERARRAYSIZE do
  914.         begin
  915.         pgPtr := PointerArray[cnt];
  916.         while pgPtr <> NIL do
  917.             begin
  918.             PrintPageInfo(pgPtr);
  919.             pgPtr := pgPtr^.nextPage;
  920.             end;
  921.         end;
  922.     CancelCompressedMode;
  923.     end;                                   (* end of PrintPageBuffer routine *)
  924.  
  925.  
  926. (* This routine will initialize the variables used to keep track of buffer
  927.    use statistics.                                                           *)
  928.  
  929. procedure InitializeBufferStats;
  930.  
  931.     begin
  932.     bufferAttempts := 0;
  933.     bufferHits := 0;
  934.     end;                             (* end of InitializeBufferStats routine *)
  935.  
  936.  
  937. (* This routine will return buffer statistics.  The statistic will be returned
  938.    in a a record of type BufferStats.                                        *)
  939.  
  940. procedure CreateBufferStats(var stats : BufferStats);
  941.  
  942.     begin
  943.     stats.pagesInUse := pagesInUse;
  944.     stats.maxPages := maxBufferPages;
  945.     stats.attempts := bufferAttempts;
  946.     stats.hits := bufferHits;
  947.     end;                                 (* end of CreateBufferStats routine *)
  948.  
  949. (*\*)
  950. (* This routine will print the buffer statistics                             *)
  951.  
  952. procedure PrintBufferStats;
  953.  
  954. var
  955.     stats : BufferStats;
  956.  
  957.     begin
  958.     CreateBufferStats(stats);
  959.     Writeln(lst);
  960.     Writeln(lst,'** Buffer Statistics Follow: **');
  961.     Writeln(lst);
  962.     Writeln(lst,'Buffer Pages In Use = ',stats.pagesInUse);
  963.     Writeln(lst,'Maximum buffer pages available =  ',stats.maxPages);
  964.     Writeln(lst,'Attempts to Fetch Data = ',stats.attempts);
  965.     Writeln(lst,'Number of Hits = ',stats.hits);
  966.     Writeln(lst,'Hit percentage = ',Trunc((stats.hits/stats.attempts)*100),'%');
  967.     end;                                       (* end of PrintBuffer routine *)
  968.  
  969.  
  970.  
  971. begin
  972. InitializePointerArray;
  973. InitializeBufferStats;
  974. SetMaxBufferPages(256);                           (* initially a 128K buffer *)
  975. SetImmediateDiskWrite(FALSE);
  976. end.                                                     (* end of Page unit *)
  977.