home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / btree / tree / logical.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-07-13  |  31.4 KB  |  747 lines

  1. (* TBTree16             Copyright (c)  1988,1989       Dean H. Farwell II    *)
  2.  
  3. unit Logical;
  4.  
  5. (*****************************************************************************)
  6. (*                                                                           *)
  7. (*              L O G I C A L  R E C O R D  R O U T I N E S                  *)
  8. (*                                                                           *)
  9. (*****************************************************************************)
  10.  
  11.  
  12. (* This unit is used to manipulate data files and the logical records (data
  13.    records) which comprise the data files.  The terms logical record and data
  14.    record are used interchangeably.  A logical record is made of a fixed
  15.    number of bytes which is set at file creation time.  The minimum logical
  16.    record size is one byte.  The maximum size is MAXDATASIZE (65520 bytes).
  17.    Depending on the size of logical and physical records, many logical records
  18.    could reside in a single physical record or a logical record could use many
  19.    physical records. In the later case contiguous physical records are used.
  20.    Since fixed logical and physical record sizes are used the physical records
  21.    used for a logical record can easily be calculated.
  22.  
  23.    In version 1.4 this unit was totaly redesigned and took on the added task
  24.    of dealing with the data files.  This unit is used to create and delete
  25.    data files and is also used to put data into the data files and retrieve
  26.    data from the data files.  Deleting data from the data files is also
  27.    accomplished using this routine.
  28.  
  29.    Each data file maintains its own bitmap records.  These bitmap records are
  30.    used to determine which records are currentlyu in use and which records are
  31.    available for reuse.  You should never need to deal with the bitmap records
  32.    because everything is handled automatically.
  33.  
  34.    Note - You should never use any of these routines for any files other than
  35.    data files.                                                               *)
  36.  
  37.  
  38. (*\*)
  39. (* Version Information
  40.  
  41.    Version 1.1 - Added GetValidLogicalRecords routine
  42.  
  43.    Version 1.2 - Removed several types which were not used
  44.  
  45.                - Removed CalculateRecordSize routine
  46.                  This routine was not used and was for future enhancements
  47.  
  48.    Version 1.3 - Changed MAXDATASIZE to 65520 which is the maximum
  49.                  number of bytes for a structued type in Turbo Pascal 4.0
  50.  
  51.                - Added StoreNewLogicalRecord routine
  52.  
  53.    Version 1.4 - Fixed a bug in GetALogicalRecord routine.  Bug affected
  54.                  retrievals for logical records with size > 512 bytes
  55.  
  56.                - Changed (expanded) definition of DataSizeRange.  See note in
  57.                  interface section for details
  58.  
  59.                - Made many internal changes to the routine.  The most
  60.                  important is that the bitmap records are now maintained as
  61.                  part of the data files.  Individual bitmap files are no
  62.                  longer needed.
  63.  
  64.                - Added CreateDataFile routine which must be used to create
  65.                  data files
  66.  
  67.                - Added DeleteDataFile routine which must be used to delete
  68.                  data files
  69.  
  70.                - Moved the RecordUsed routine from the FILES unit and renamed
  71.                  it DataRecordUsed
  72.  
  73.                - The logical record size is not needed when data is stored and
  74.                  retrieved.  It is now needed only when the data file is
  75.                  created.
  76.  
  77.                - Added DeleteDataFile routine.  This routine should always be
  78.                  used when deleting a DATA file.
  79.  
  80.                - Added DeleteDataRecord routine which must be used for
  81.                  deleting data records.
  82.  
  83.    Version 1.5 - Fixed a couple of problems which caused fatal errors when
  84.                  several of the routines were called with lrNum = 0.  Remember,
  85.                  zero is not a valid logical record number.  The first valid
  86.                  logical record number is 1.   See the comments associated
  87.                  with  the specific routines DataRecordUsed, DeleteDataRecord,
  88.                  GetALogicalRecord and StoreALogicalRecord.
  89.  
  90.                - Changed code internally to use Inc and Dec where practical
  91.  
  92.                - Changed code internally to use newly added FastMove unit
  93.  
  94.                - Added LastDataRecord routine
  95.  
  96.                - Changed the way records are added as the index grows on
  97.                  inserts.  As the file becomes larger,  the number of records
  98.                  added at one time increases.  This will speed up the insert
  99.                  process.
  100.  
  101.    Version 1.6 - No Changes                                                  *)
  102.  
  103.  
  104. (*\*)
  105. (*////////////////////////// I N T E R F A C E //////////////////////////////*)
  106.  
  107. interface
  108.  
  109. uses
  110.     Compare,
  111.     FastMove,
  112.     FileDecs,
  113.     Files,
  114.     LRecList,
  115.     Math,
  116.     Numbers,
  117.     Page;
  118.  
  119. const
  120.     MAXDATASIZE = 65520;       (* max number of bytes for a logical record   *)
  121.  
  122. type
  123.     DataSizeRange = 0 .. MAXDATASIZE;     (* range for size of a field.
  124.                                              Also maximum number of
  125.                                              bytes in a logical record       *)
  126.  
  127.                                           (* Note : in version 1.4 the
  128.                                              definition has changed to include
  129.                                              0 in the valid range.  This is
  130.                                              done to allow a variable of this
  131.                                              type to be initialized to 0 before
  132.                                              a size is actually assigned.  This
  133.                                              adds flexibility with no loss of
  134.                                              functionality                   *)
  135.  
  136. (*\*)
  137. (* This routine will create a data file with the file name as specified
  138.    by dFName.  The lrSize parameter specifies the size of the data
  139.    record.  The easiest way to determine this is to use the SizeOf
  140.    function.                                                                 *)
  141.  
  142. procedure CreateDataFile(dFName : FnString;
  143.                          lrSize : DataSizeRange);
  144.  
  145.  
  146. (* This routine will delete a data file.                                     *)
  147.  
  148. procedure DeleteDataFile(dFName : FnString);
  149.  
  150.  
  151. (* This routine will check for the existence of a particular data record.  If
  152.    the data record is in use, TRUE will be returned.  Otherwise, FALSE will be
  153.    returned.  If this routine is called with lrNum = 0 then FALSE will be
  154.    returned since the zeroth logical record is never a valid logical record. *)
  155.  
  156. function DataRecordUsed(dFName : FnString;
  157.                         lrNum : LrNumber) : Boolean;
  158.  
  159.  
  160. (* This routine will delete a logical record from the data file by setting the
  161.    appropriate bitmap bit to zero.  If the data record (lrNum) is not in use,
  162.    then nothing will happen.  No error will occur.                           *)
  163.  
  164. procedure DeleteDataRecord(dFName : FnString;
  165.                            lrNum : lrNumber);
  166.  
  167.  
  168. (* This routine will get a logical record from a given data file and will put
  169.    the record into a memory location.  The location will be destination.  The
  170.    number of bytes retrieved is equal to the size of the logical record which
  171.    was set when the file was created.  The BITMAP will be checked to ensure
  172.    that the record is in use (that it exists). If it is in use then it is
  173.    fetched.  Otherwise, nothing will be returned in destination.  Before
  174.    calling this routine, you can check to see if the logical record exists.
  175.    If it was retrieved from an index then it exists (unless the record was
  176.    deleted and it wasn't deleted from the index).  Also, record numbers which
  177.    are stored in a logical record list as a result of GetValidLogicalRecords
  178.    also exist as long as records were not deleted after the list was created.
  179.    If you are not sure whether a logical record exists you can use
  180.    DataRecordUsed(dFName,lrNum) to check for the existence of the record
  181.    before calling this routine.
  182.  
  183.    Warning : If this routine is called with lrNum = 0 no error will occur, but
  184.    nothing will be passed back in destination (destination will remain
  185.    unchanged)  You should ensure that lrNum is not equal to zero prior to
  186.    calling this routine.                                                     *)
  187.  
  188. procedure GetALogicalRecord(dFName : FnString;
  189.                             lrNum : LrNumber;
  190.                             var destination);
  191.  
  192. (*\*)
  193. (* This routine will store a logical record for a given DATA file.  The
  194.    routine will set the logical record to used and will create the appropriate
  195.    physical record(s) if required.  The data must reside in source.  This
  196.    routine is only used if the logical record number is known.  If a new
  197.    record is to be stored use StoreNewLogicalRecord rather than this routine
  198.  
  199.    Warning : If this routine is called with lrNum = 0 no error will occur, but
  200.    nothing will be saved.  You should ensure that lrNum is not equal to zero
  201.    prior to calling this routine.                                            *)
  202.  
  203. procedure StoreALogicalRecord(dFName : FnString;
  204.                               lrNum : LrNumber;
  205.                               var source);
  206.  
  207.  
  208. (* This routine will store a new logical record for a given logical (data)
  209.    file.  The routine will set the logical record to used and will create the
  210.    appropriate physical record(s) if required.  Normally, when inserting new
  211.    records, you will not know the next unused logical record number.  This
  212.    routine will assign the appropriate logical record number so that you won't
  213.    have to worry about it. The routine will return the logical record number
  214.    which will be associated with this record upon return.  You will need this
  215.    returned logical record number if there are any indexes associated with
  216.    this data file.                                                           *)
  217.  
  218. function StoreNewLogicalRecord(dFName : FnString;
  219.                                var source) : LrNumber;
  220.  
  221.  
  222. (* This routine will return a list of logical records which are currently in
  223.    use (contain valid data) for a given data file.  This routine is necessary
  224.    to be able to process all records which have not been deleted without
  225.    using an index.                                                           *)
  226.  
  227. procedure GetValidLogicalRecords(dFName : FnString;
  228.                                  var lrLst : LrList);
  229.  
  230.  
  231. (* This routine will return the data record size for the given data file     *)
  232.  
  233. function GetDataRecordSize(dFName : FnString) : DataSizeRange;
  234.  
  235.  
  236. (* This routine will return the logical record number for the last logical
  237.    record in use in the file (logical record with the highest logical record
  238.    number).                                                                  *)
  239.  
  240. function LastDataRecord(dFName : FnString) : LrNumber;
  241.  
  242. (*!*)
  243. (*\*)
  244. (*///////////////////// I M P L E M E N T A T I O N /////////////////////////*)
  245.  
  246. implementation
  247.  
  248.  
  249. (* These parameters are contained in the first record (0) in the data file
  250.  
  251.      variable        parameter               type         range
  252.      --------        ---------               ----         -----
  253.       userData       user data array      UserDataArray   N/A
  254.       version        version info         VersionString   N/A
  255.       nextAvail      next available lr    LrNumber        0 - MAXLONGINT
  256.       firstBMRec     first bitmap record  PrNumber        0 - MAXLONGINT
  257.       lastBMRec      last bitmap record   PrNumber        0 - MAXLONGINT
  258.       fType          file type            FileTypes       INDEX,DATA,
  259.                                                           LLIST,VLRDATA
  260.       lrSize         logical record size  DataSizeRange   0 - 65520
  261.       bf             blocking factor      DataSizeRange   0 - 65520
  262.       prReqd         Pr's per Lr          DataSizeRange   0 - 65520
  263.       lastInUse      last lr in use       LrNumber        0 - MAXLONGINT     *)
  264.  
  265.  
  266. type
  267.     ParameterRecord = record
  268.          userData   : UserDataArray;                     (* for use by users *)
  269.          version    : VersionString;               (* version of TBTREE used
  270.                                                          to create data file *)
  271.          nextAvail  : LrNumber;            (* next logical record available  *)
  272.          firstBMRec : PrNumber;           (* first record used for bitmap    *)
  273.          lastBMRec  : PrNumber;           (* last record used for bitmap     *)
  274.          fType      : FileTypes;          (* type of file                    *)
  275.          lrSize     : DataSizeRange;              (* size of logical records *)
  276.          bf         : DataSizeRange;                      (* blocking factor *)
  277.          prReqd     : DataSizeRange;      (* Pr's per Lr                     *)
  278.          lastInUse  : LrNumber;           (* Last data record in use (not
  279.                                              last record in file             *)
  280.          end;
  281.  
  282. (*\*)
  283. (* This routine will create a data file with the file name as specified
  284.    by dFName.  The lrSize parameter specifies the size of the data
  285.    record.  The easiest way to determine this is to use the SizeOf
  286.    function.                                                                 *)
  287.  
  288. procedure CreateDataFile(dFName : FnString;
  289.                          lrSize : DataSizeRange);
  290.  
  291. var
  292.     pRec : ParameterRecord;
  293.     page : SinglePage;
  294.  
  295.     begin
  296.     CreateGenericFile(dFName);
  297.     FillChar(page,PAGESIZE,0);
  298.     StorePage(dFName,0,page);                            (* parameter record *)
  299.     StorePage(dFName,1,page);                               (* bitmap record *)
  300.     pRec.version := CURRENTVERSION;
  301.     pRec.nextAvail  := 1;
  302.     pRec.firstBMRec := 1;
  303.     pRec.lastBMRec  := 1;
  304.     pRec.fType := DATA;
  305.     pRec.lrSize := lrSize;
  306.     if lrSize < PAGESIZE then
  307.         begin
  308.         pRec.bf := PAGESIZE Div lrSize;
  309.         pRec.prReqd := 1;
  310.         end
  311.     else
  312.         begin
  313.         pRec.bf := ((lrSize - 1) Div PAGESIZE) + 1;
  314.         pRec.prReqd := pRec.bf;
  315.         end;
  316.     pRec.lastInUse  := 0;
  317.     SaveFileParameters(dFName,pRec,SizeOf(pRec));      (* write parameters back to buffer *)
  318.     end;                                    (* end of CreateDataFile routine *)
  319.  
  320.  
  321. (* This routine will delete a data file.                                     *)
  322.  
  323. procedure DeleteDataFile(dFName : FnString);
  324.  
  325.     begin
  326.     DeleteGenericFile(dFName);
  327.     end;                                    (* end of DeleteDataFile routine *)
  328.  
  329. (*\*)
  330. (* This routine will calculate the physical record number and the byte location
  331.    within the physical record for a given logical record.  The logical record
  332.    number is passed as a parameter.                                          *)
  333.  
  334. procedure ConvertLogicalToPhysical(lrNum : LrNumber;
  335.                                    var pRec : ParameterRecord;
  336.                                                        (* var for speed only *)
  337.                                    var prNum : PrNumber;
  338.                                    var firstByte : PageRange);
  339.  
  340.     begin
  341.     if pRec.lrSize < PAGESIZE then
  342.         begin
  343.         prNum := ((lrNum - 1) Div pRec.bf) + 1;
  344.         firstByte := ((lrNum - ((pRec.bf * (prNum - 1)) + 1)) * pRec.lrSize)
  345.                      + 1;
  346.         end
  347.     else
  348.         begin
  349.         prNum := ((lrNum - 1) * pRec.bf) + 1;
  350.         firstByte := 1;
  351.         end;
  352.     end;                          (* end of ConvertLogicalToPhysical routine *)
  353.  
  354. (*\*)
  355. (* This routine will return the record number for the first unused data record
  356.    (logical record).  The routine checks to see if the bitmap records must be
  357.    moved to make room.  The bitmap records will moved down to free up disk
  358.    space.  The number of physical pages freed up depends on the size of the
  359.    data file.  It also resets the firstBMRec field in the parameter record to
  360.    the next available record.                                                *)
  361.  
  362. function FirstUnusedDataRecord(var dFName : FnString;
  363.                                                        (* var for speed only *)
  364.                                var pRec : ParameterRecord) : LrNumber;
  365.  
  366. var
  367.     prNum : PrNumber;
  368.     dummy : PageRange;                (* needed as dummy parameter to a call *)
  369.     newRecord : LrNumber;                    (* record number to be returned *)
  370.     recsToMove : PrNumber;
  371.  
  372.     begin
  373.     newRecord := pRec.nextAvail;                  (* record number to return *)
  374.     pRec.nextAvail := FindNextAvailInBitmap(dFName,pRec.firstBMRec,
  375.                                             pRec.lastBMRec,newRecord);
  376.     ConvertLogicalToPhysical(newRecord,pRec,prNum,dummy);
  377.                                (* above call sets prNum to first pr for the
  378.                                   new record                                 *)
  379.     if (prNum + (pRec.prReqd - 1)) >= pRec.firstBMRec then
  380.         begin                                 (* need to move bitmap records *)
  381.         if pRec.firstBMRec <= 4 then
  382.             begin
  383.             recsToMove := pRec.prReqd;
  384.             end
  385.         else
  386.             begin
  387.             if pRec.firstBMRec <= 10 then
  388.                 begin
  389.                 recsToMove := pRec.prReqd * 3;
  390.                 end
  391.             else
  392.                 begin
  393.                 recsToMove := pRec.prReqd * 5;
  394.                 end;
  395.             end;
  396.         MoveRecords(dFName,pRec.firstBMRec,pRec.lastBMRec,recsToMove);
  397.         end;
  398.     FirstUnUsedDataRecord := newRecord;           (* record number to return *)
  399.     end;                             (* end of FirstUnusedDataRecord routine *)
  400.  
  401. (*\*)
  402. (* This routine will check for the existence of a particular data record.  If
  403.    the data record is in use, TRUE will be returned.  Otherwise, FALSE will be
  404.    returned.  This routine checks to ensure that lrNum is not zero.  If this
  405.    is the case, FALSE will be returned, since the zeroth logical record never
  406.    exists.                                                                   *)
  407.  
  408. function DataRecordUsedInternal(var dFName : FnString; (* var for speed only *)
  409.                                 lrNum : LrNumber;
  410.                                 var pRec : ParameterRecord) : Boolean;
  411.                                                        (* var for speed only *)
  412.  
  413.     begin
  414.     if (lrNum = 0) or (lrNum > pRec.lastInUse) then
  415.         begin
  416.         DataRecordUsedInternal := FALSE;
  417.         end
  418.     else
  419.         begin
  420.         DataRecordUsedInternal := CheckBitInBitmap(dFName,
  421.                                                    pRec.firstBMRec,
  422.                                                    lrNum);
  423.         end;
  424.     end;                            (* end of DataRecordUsedInternal routine *)
  425.  
  426.  
  427. (* This routine will check for the existence of a particular data record.  If
  428.    the data record is in use, TRUE will be returned.  Otherwise, FALSE will be
  429.    returned.  If this routine is called with lrNum = 0 then FALSE will be
  430.    returned since the zeroth logical record is never a valid logical record. *)
  431.  
  432. function DataRecordUsed(dFName : FnString;
  433.                         lrNum : LrNumber) : Boolean;
  434.  
  435. var
  436.     pRec : ParameterRecord;
  437.  
  438.     begin
  439.     FetchFileParameters(dFName,pRec,SizeOf(pRec));
  440.     DataRecordUsed := DataRecordUsedInternal(dFName,lrNum,pRec);
  441.     end;                            (* end of DataRecordUsedInternal routine *)
  442.  
  443. (*\*)
  444. (* This routine will mark the specified record as used.                      *)
  445.  
  446. procedure SetDataRecordUsed(var dFName : FnString;     (* var for speed only *)
  447.                             lrNum : LrNumber;
  448.                             var pRec : ParameterRecord (* var for speed only *)
  449.                             );
  450.  
  451. var
  452.     page : SinglePage;
  453.     recCnt,
  454.     prNum : PrNumber;
  455.     byteNumDummy : PageRange;         (* needed as dummy parameter to a call *)
  456.     bitNumDummy : BytePosition;       (* needed as dummy parameter to a call *)
  457.  
  458.     begin
  459.     prNum := CalculateBitmapRecord(pRec.firstBMRec,lrNum);
  460.     if prNum > pRec.lastBMRec then
  461.         begin                                (* bitmap record does not exist *)
  462.         FillChar(page,PAGESIZE,0);
  463.         for recCnt := (pRec.lastBMRec + 1) to prNum do
  464.             begin
  465.             StorePage(dFName,prNum,page);       (* store empty bitmap record *)
  466.             end;
  467.         pRec.lastBMRec := prNum;
  468.         end;
  469.     SetBitInBitmap(dFName,pRec.firstBMRec,lrNum,1);
  470.     end;                                 (* end of SetDataRecordUsed routine *)
  471.  
  472. (*\*)
  473. (* This routine will delete a logical record from the data file by setting the
  474.    appropriate bitmap bit to zero.  If the data record (lrNum) is not in use,
  475.    then nothing will happen.  No error will occur.                           *)
  476.  
  477. procedure DeleteDataRecord(dFName : FnString;
  478.                            lrNum : lrNumber);
  479.  
  480. var
  481.     pRec : ParameterRecord;
  482.     page : SinglePage;
  483.  
  484.     begin
  485.     FetchFileParameters(dFName,pRec,SizeOf(pRec));
  486.     if DataRecordUsedInternal(dFName,lrNum,pRec) then
  487.         begin
  488.         SetBitInBitmap(dFName,pRec.firstBMRec,lrNum,0);    (* mark as unused *)
  489.         if lrNum < pRec.nextAvail then
  490.             begin
  491.             pRec.nextAvail := lrNum;
  492.             end;
  493.         if lrNum = pRec.lastInUse then
  494.             begin
  495. {$B-}                            (* next statement depends on short circuit
  496.                                               boolean expression evaluation *)
  497.             while (pRec.lastInUse <> 0) and
  498.                   (not DataRecordUsedInternal(dFName,pRec.lastInUse,pRec)) do
  499.                 begin
  500.                 Dec(pRec.lastInUse);
  501.                 end;
  502.             end;
  503.         SaveFileParameters(dFName,pRec,SizeOf(pRec));
  504.         end;
  505.     end;                                  (* end of DeleteDataRecord routine *)
  506.  
  507. (*\*)
  508. (* This routine will get a logical record from a given data file and will put
  509.    the record into a memory location.  The location will be destination.  The
  510.    number of bytes retrieved is equal to the size of the logical record which
  511.    was set when the file was created.  The BITMAP will be checked to ensure
  512.    that the record is in use (that it exists). If it is in use then it is
  513.    fetched.  Otherwise, nothing will be returned in destination.  Before
  514.    calling this routine, you can check to see if the logical record exists.
  515.    If it was retrieved from an index then it exists (unless the record was
  516.    deleted and it wasn't deleted from the index).  Also, record numbers which
  517.    are stored in a logical record list as a result of GetValidLogicalRecords
  518.    also exist as long as records were not deleted after the list was created.
  519.    If you are not sure whether a logical record exists you can use
  520.    DataRecordUsed(dFName,lrNum) to check for the existence of the record
  521.    before calling this routine.
  522.  
  523.    Warning : If this routine is called with lrNum = 0 no error will occur, but
  524.    nothing will be passed back in destination (destination will remain
  525.    unchanged)  You should ensure that lrNum is not equal to zero prior to
  526.    calling this routine.                                                     *)
  527.  
  528. procedure GetALogicalRecord(dFName : FnString;
  529.                             lrNum : LrNumber;
  530.                             var destination);
  531.  
  532. type
  533.     MemoryArray = Array [1 .. MAXDATASIZE] of Byte;
  534.  
  535. var
  536.     pRec : ParameterRecord;
  537.     prNum : PrNumber;
  538.     firstByte : PageRange;
  539.     page : SinglePage;
  540.     byteCnt : DataSizeRange;
  541.     memory : MemoryArray absolute destination;
  542.     done : Boolean;
  543.     size : DataSizeRange;
  544.  
  545.     begin
  546.     FetchFileParameters(dFName,pRec,SizeOf(pRec));
  547.     if DataRecordUsedInternal(dFName,lrNum,pRec) then     (* check to ensure
  548.                                                              record is in
  549.                                                              use and that
  550.                                                              lrNum <> 0      *)
  551.         begin
  552.         ConvertLogicalToPhysical(lrNum,pRec,prNum,firstByte);
  553.         size := pRec.lrSize;
  554.         if size <= PAGESIZE then
  555.             begin
  556.             FetchPage(dFName,prNum,page);
  557.             FastMover(page[firstByte],destination,size);
  558.             end
  559.         else
  560.             begin
  561.             byteCnt := 1;
  562.             done := FALSE;
  563.             while not done do
  564.                 begin
  565.                 FetchPage(dFName,prNum,page);
  566.                 if size > PAGESIZE then
  567.                     begin
  568.                     FastMover(page[firstByte],memory[byteCnt],PAGESIZE);
  569.                     Inc(prNum);
  570.                     Inc(byteCnt,PAGESIZE);
  571.                     Dec(size,PAGESIZE);
  572.                     end
  573.                 else
  574.                     begin                             (* last time thru loop *)
  575.                     FastMover(page[firstByte],memory[byteCnt],size);
  576.                     done := TRUE;
  577.                     end;
  578.                 end;
  579.             end;
  580.         end;
  581.     end;                                 (* end of GetALogicalRecord routine *)
  582.  
  583. (*\*)
  584. (* This routine will store a logical record for a given DATA file.  The
  585.    routine will set the logical record to used and will create the appropriate
  586.    physical record(s) if required.  The data must reside in source.  This
  587.    routine is only used if the logical record number is known.  If a new
  588.    record is to be stored use StoreNewLogicalRecord rather than this routine
  589.  
  590.    Warning : If this routine is called with lrNum = 0 no error will occur, but
  591.    nothing will be saved.  You should ensure that lrNum is not equal to zero
  592.    prior to calling this routine.                                            *)
  593.  
  594. procedure StoreALogicalRecord(dFName : FnString;
  595.                               lrNum : LrNumber;
  596.                               var source);
  597.  
  598. type
  599.     MemoryArray = Array [1 .. MAXDATASIZE] of Byte;
  600.  
  601. var
  602.     pRec : ParameterRecord;
  603.     prNum : PrNumber;
  604.     firstByte : PageRange;
  605.     page : SinglePage;
  606.     byteCnt : DataSizeRange;
  607.     memory : MemoryArray absolute source;
  608.     done : Boolean;
  609.     size : DataSizeRange;
  610.  
  611.     begin
  612.     if lrNum <> 0 then          (* make sure that lrNum <> 0 else do nothing *)
  613.         begin
  614.         FetchFileParameters(dFName,pRec,SizeOf(pRec));
  615.         ConvertLogicalToPhysical(lrNum,pRec,prNum,firstByte);
  616.         SetDataRecordUsed(dFName,lrNum,pRec);     (* It may already be used.
  617.                                                      This doesn't matter and
  618.                                                     its faster not to check  *)
  619.         size := pRec.lrSize;
  620.         if size <= PAGESIZE then
  621.             begin
  622.             if PageExists(dFName,prNum) then          (* if it exists get it *)
  623.                 begin
  624.                 FetchPage(dFName,prNum,page);
  625.                 end
  626.             else                       (* if it doesn't exist make a new one *)
  627.                 begin
  628.                 FillChar(page,PAGESIZE,0);
  629.                 end;
  630.             FastMover(source,page[firstByte],size);
  631.             StorePage(dFName,prNum,page);
  632.             end
  633.         else
  634.             begin
  635.             byteCnt := 1;
  636.             done := FALSE;
  637.             while not done do
  638.                 begin
  639.                 if size > PAGESIZE then
  640.                     begin
  641.                     FastMover(memory[byteCnt],page[firstByte],PAGESIZE);
  642.                     StorePage(dFName,prNum,page);
  643.                     Inc(prNum);
  644.                     Inc(byteCnt,PAGESIZE);
  645.                     Dec(size,PAGESIZE);
  646.                     end
  647.                 else
  648.                     begin                             (* last time thru loop *)
  649.                     FillChar(page,PAGESIZE,0);
  650.                     FastMover(memory[byteCnt],page[firstByte],size);
  651.                     StorePage(dFName,prNum,page);
  652.                     done := TRUE;
  653.                     end;
  654.                 end;
  655.             end;
  656.         if pRec.lastInUse < lrNum then
  657.             begin
  658.             pRec.lastInUse := lrNum;
  659.             SaveFileParameters(dFName,pRec,SizeOf(pRec));
  660.             end;
  661.         end;
  662.     end;                               (* end of StoreALogicalRecord routine *)
  663.  
  664. (*\*)
  665. (* This routine will store a new logical record for a given logical (data)
  666.    file.  The routine will set the logical record to used and will create the
  667.    appropriate physical record(s) if required.  Normally, when inserting new
  668.    records, you will not know the next unused logical record number.  This
  669.    routine will assign the appropriate logical record number so that you won't
  670.    have to worry about it. The routine will return the logical record number
  671.    which will be associated with this record upon return.  You will need this
  672.    returned logical record number if there are any indexes associated with
  673.    this data file.                                                           *)
  674.  
  675. function StoreNewLogicalRecord(dFName : FnString;
  676.                                var source) : LrNumber;
  677.  
  678. var
  679.     pRec : ParameterRecord;
  680.     lrNum : LrNumber;
  681.  
  682.     begin
  683.     FetchFileParameters(dFName,pRec,SizeOf(pRec));
  684.     lrNum := FirstUnUsedDataRecord(dFName,pRec);
  685.     SaveFileParameters(dFName,pRec,SizeOf(pRec));
  686.     StoreALogicalRecord(dFName,lrNum,source);
  687.     StoreNewLogicalRecord := lrNum;
  688.     end;                             (* end of StoreNewLogicalRecord routine *)
  689.  
  690.  
  691. (* This routine will return a list of logical records which are currently in
  692.    use (contain valid data) for a given data file.  This routine is necessary
  693.    to be able to process all records which have not been deleted without
  694.    using an index.                                                           *)
  695.  
  696. procedure GetValidLogicalRecords(dFName : FnString;
  697.                                  var lrLst : LrList);
  698.  
  699. var
  700.     pRec : ParameterRecord;
  701.     lrNum : LrNumber;
  702.  
  703.     begin
  704.     FetchFileParameters(dFName,pRec,SizeOf(pRec));
  705.     CreateLrList(lrLst);
  706.     for lrNum := 1 to pRec.lastInUse do   (* will do nothing if file empty
  707.                                              because if file is empty, then
  708.                                              pRec.lastInUse = 0              *)
  709.         begin
  710.         if DataRecordUsedInternal(dFName,lrNum,pRec) then
  711.             begin
  712.             AddToLrList(lrNum,lrLst);
  713.             end;
  714.         end;
  715.     end;                            (* end of GetValidLogicalRecords routine *)
  716.  
  717. (*\*)
  718. (* This routine will return the data record size for the given data file     *)
  719.  
  720. function GetDataRecordSize(dFName : FnString) : DataSizeRange;
  721.  
  722. var
  723.     pRec : ParameterRecord;
  724.  
  725.     begin
  726.     FetchFileParameters(dFName,pRec,SizeOf(pRec));
  727.     GetDataRecordSize := pRec.lrSize;
  728.     end;                                 (* end of GetDataRecordSize routine *)
  729.  
  730.  
  731. (* This routine will return the logical record number for the last logical
  732.    record in use in the file (logical record with the highest logical record
  733.    number).                                                                  *)
  734.  
  735. function LastDataRecord(dFName : FnString) : LrNumber;
  736.  
  737. var
  738.     pRec : ParameterRecord;
  739.  
  740.     begin
  741.     FetchFileParameters(dFName,pRec,SizeOf(pRec));
  742.     LastDataRecord := pRec.lastInUse;
  743.     end;                                    (* end of LastDataRecord routine *)
  744.  
  745.  
  746. end.                                                  (* end of Logical unit *)
  747.