home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / PASCAL / TBTREE16.ZIP / BTREE3.INC < prev    next >
Text File  |  1989-04-01  |  23KB  |  544 lines

  1. (******************************************************************************)
  2. (*                                                                           *)
  3. (*               B T R E E   C U R S O R   R O U T I N E S                   *)
  4. (*                                                                           *)
  5. (*****************************************************************************)
  6.  
  7.  
  8. (* The following routines are provided as an alternate method to retrieve
  9.    logical record numbers from an index.  Originally, TBTREE was developed
  10.    with very powerful retrieval capabilities.  However, all retrievals
  11.    required the creation of a logical record list.  This lists provide
  12.    excellent flexibility and power.  In some cases, they are overkill and an
  13.    alternate method is now provided.
  14.  
  15.    As of version 1.4, all indexes have one internal cursor associated with it.
  16.    This cursor can be used to perform several types of retrievals.  Their use
  17.    parallels the use of the retrieval routines found in the LrList unit
  18.    although there are several important distinctions.
  19.  
  20.    One prime distinction is that you do not create nor destroy cursors as you
  21.    would a logical record list.  The cursor always exists, although it may not
  22.    be valid.  It will not be valid until you use one of these retrieval
  23.    routines.  These routines set the cursor to a location (loaction depends on
  24.    which routine you use) thus making it valid.  It will continue to be valid
  25.    until you either delete the entry at the cursor or you use the routine
  26.    provided to make the cursor invalid.  It is important to note that the
  27.    cursor will actually live after the program terminates. This is because the
  28.    cursor is stored as part of the parameter record for the index.  Since the
  29.    parameter record always exists, the cursor always exists.  This does not
  30.    cause any great problems, but you should be aware of it.
  31.  
  32.    Another distinction is that the cursor is dynamic rather than static (which
  33.    the logical record lists are).  In other words, once a logical record list
  34.    is created, there is no longer a relationship between the list and the
  35.    index. The list can be sorted, manipulated, etc without affecting the
  36.    index. Likewise, if the index is changed, the logical record list is
  37.    unaware of it. On the other hand, the cursor remembers where it is and
  38.    keeps up with changes to the index.  Even if you add or delete index
  39.    entries, the cursor continues to point to the same entry.  The only
  40.    exception is if you delete the entry at which the cursor is precsently
  41.    pointing.  In this case, the cursor will be set to invalid. This precludes
  42.    it from pointing off to never-never land.  The capability to remeber where
  43.    it is gives the cursor some unique capabilities which the logical record
  44.    list does not have.  Specifically, you can walk through an index, add
  45.    something, and contiue walking through the index, etc.
  46.  
  47.    One use of these routines follows.  Assume the following declarations and
  48.    also assume that myIndexFile is an index which is on field1 of myDataFile.
  49.    MyRecord corresponds to myDataFile.  To perform a retrieval for the first
  50.    record which has field1 = 20 follows:
  51.  
  52.          type
  53.              MyRecord = record
  54.                  field1 : Byte;
  55.                  field2 : Word;
  56.                  field3 : String[50];
  57.                  end;
  58.  
  59.              myDataFile,
  60.              myIndexFile : FnString;
  61.              key : Byte;
  62.  
  63.              begin
  64.              .
  65.              .
  66.              .
  67.              key := 20;
  68.              lrNum := UsingCursorAndValueGetLr(myIndexFile,key);
  69.              if lrNum = 0 then
  70.                  begin
  71.                  { no matching record found }
  72.                  end
  73.              else
  74.                  begin
  75.                  {  process record as desired
  76.                     probably retrieve the record using GetALogicalRecord  }
  77.                  end;
  78.  
  79.    You could also put the above in a loop and move the cursor along
  80.    retrieving logical record numbers until you wanted to quit.  For example
  81.  
  82.              key := 20;
  83.              lrNum := UsingCursorAndValueGetLr(myIndexFile,key);
  84.              while lrNum <> 0 do
  85.                  begin
  86.                  {  process record as desired
  87.                     probably retrieve the record using GetALogicalRecord  }
  88.                  lrNum := UsingCursorGetNextLr(iFName : FnString);
  89.                  end;
  90.  
  91.    These routines are really well suited for either quick and dirty retrievals
  92.    or retrievals that don't work well using logical record lists (for whatever
  93.    reason).  For folks more familiar with other products, this method may feel
  94.    more comfortable than using logical record lists.                         *)
  95.  
  96. (*\*)
  97. (* This routine will return the logical record associated with the cursor.
  98.    If the cursor in not valid, 0 will be returned.                           *)
  99.  
  100. function LrNumToReturn(var pg : SinglePage;            (* var for speed only *)
  101.                        var pRec : ParameterRecord      (* var for speed only *)
  102.                        ) : LrNumber;
  103.  
  104. var
  105.     lrNum : LrNumber;
  106.  
  107.     begin
  108.     if pRec.cursor.valid then
  109.         begin
  110.         Move(pg[((pRec.cursor.entryNum - 1) * (pRec.vSize + RNSIZE)) + 1],
  111.              lrNum,
  112.              RNSIZE);
  113.         end
  114.     else
  115.         begin
  116.         lrNum := 0;
  117.         end;
  118.     LrNumToReturn := lrNum;
  119.     end;                                     (* end of LrNumToReturn routine *)
  120.  
  121. (*\*)
  122. (* This routine will set the tree cursor to the front of the index.  In
  123.    other words, it will point to the first entry in the index.  Remember, the
  124.    index is ordered by the value of each entry.  It will also return the
  125.    logical record associated with the first entry in the index.  It will
  126.    return 0 only if there is no first entry (the index is empty).  This
  127.    routine should be called if you want to start at the beginning of an index
  128.    and want to retrieve logical record numbers in order of entry.            *)
  129.  
  130. function UsingCursorGetFirstLr(iFName : FnString) : LrNumber;
  131.  
  132. var
  133.     pRec : ParameterRecord;
  134.     pg : SinglePage;
  135.  
  136.     begin
  137.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  138.     FetchPage(iFName,pRec.fSNode,pg);
  139.     if pg[VCNTLOC] > 0 then
  140.         begin
  141.         pRec.cursor.prNum := pRec.fSNode;
  142.         pRec.cursor.entryNum := 1;
  143.         pRec.cursor.valid := TRUE;
  144.         end
  145.     else
  146.         begin
  147.         pRec.cursor.valid := FALSE;
  148.         end;
  149.     SaveFileParameters(iFName,pRec,SizeOf(pRec));
  150.     UsingCursorGetFirstLr := LrNumToReturn(pg,pRec);
  151.     end;                             (* end of UsingCursorGetFirstLr routine *)
  152.  
  153. (*\*)
  154. (* This routine will set the tree cursor to the end of the index.  In other
  155.    words, it will point to the last entry in the index.  Remember, the index
  156.    is ordered by the value of each entry.  It will also return the logical
  157.    record associated with the last entry in the index.  It will return 0 only
  158.    if there is no first entry (the index is empty).  This routine should be
  159.    called if you want to start at the end of an index and want to retrieve
  160.    logical record numbers in order of entry.                                 *)
  161.  
  162. function UsingCursorGetLastLr(iFName : FnString) : LrNumber;
  163.  
  164. var
  165.     pRec : ParameterRecord;
  166.     pg : SinglePage;
  167.  
  168.     begin
  169.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  170.     FetchPage(iFName,pRec.lSNode,pg);
  171.     if pg[VCNTLOC] > 0 then
  172.         begin
  173.         pRec.cursor.prNum := pRec.lSNode;
  174.         pRec.cursor.entryNum := pg[VCNTLOC];
  175.         pRec.cursor.valid := TRUE;
  176.         end
  177.     else
  178.         begin
  179.         pRec.cursor.valid := FALSE;
  180.         end;
  181.     SaveFileParameters(iFName,pRec,SizeOf(pRec));
  182.     UsingCursorGetLastLr := LrNumToReturn(pg,pRec);
  183.     end;                              (* end of UsingCursorGetLastLr routine *)
  184.  
  185. (*\*)
  186. (* This routine will set the tree cursor to the location in the index where
  187.    the first occurence of the desired value (paramValue) is located.  It will
  188.    also return the logical record associated with this entry. It will return 0
  189.    if there is no entry associated with this value.  This routine should be
  190.    called if you want to start at a certain location (at a certain value)
  191.    within the index and want to retrieve logical record numbers in forward or
  192.    reverse order.                                                            *)
  193.  
  194. function UsingCursorAndValueGetLr(iFName : FnString;
  195.                                   var paramValue) : LrNumber;
  196.  
  197. var
  198.     pRec : ParameterRecord;
  199.     pg : SinglePage;
  200.     cnt : Byte;               (* used to count number of values *)
  201.     bytePtr : PageRange;      (* used to keep track of current byte *)
  202.     thisNode : NodePtrType;
  203.  
  204.     begin
  205.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  206.     thisNode := FindSNode(iFName,pRec.rNode,paramValue,pRec);
  207.     FetchPage(iFName,thisNode,pg);
  208.     cnt := BinarySearchEntry(pg,paramValue,pRec);
  209.     if (cnt <> 0) and (cnt <= pg[VCNTLOC]) then
  210.         begin
  211.         bytePtr := BytePointerPosition(cnt,pRec.vsize);
  212.         if CompareValues(paramValue,pg[bytePtr + RNSIZE],pRec.vType) =
  213.            EQUALTO then
  214.             begin
  215.             pRec.cursor.prNum := thisNode;
  216.             pRec.cursor.entryNum := cnt;
  217.             pRec.cursor.valid := TRUE;
  218.             end
  219.         else
  220.             begin
  221.             pRec.cursor.valid := FALSE;
  222.             end;
  223.         end
  224.     else
  225.         begin
  226.         pRec.cursor.valid := FALSE;
  227.         end;
  228.     SaveFileParameters(iFName,pRec,SizeOf(pRec));
  229.     UsingCursorAndValueGetLr := LrNumToReturn(pg,pRec);
  230.     end;                          (* end of UsingCursorAndValueGetLr routine *)
  231.  
  232. (*\*)
  233. (* This routine is the same as UsingCursorAndValueGetLr except that this
  234.    routine will set the tree cursor to the location of the first value in the
  235.    index which is greater than or equal to paramValue.  It will also return
  236.    the logical record associated with this entry.  It will return 0 if there
  237.    is no entry which is greater than or equal to this value.                 *)
  238.  
  239. function UsingCursorAndGEValueGetLr(iFName : FnString;
  240.                                     var paramValue) : LrNumber;
  241.  
  242. var
  243.     pRec : ParameterRecord;
  244.     pg : SinglePage;
  245.     cnt : Byte;               (* used to count number of values *)
  246.     bytePtr : PageRange;      (* used to keep track of current byte *)
  247.     thisNode : NodePtrType;
  248.  
  249.     begin
  250.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  251.     thisNode := FindSNode(iFName,pRec.rNode,paramValue,pRec);
  252.     FetchPage(iFName,thisNode,pg);
  253.     cnt := BinarySearchEntry(pg,paramValue,pRec);
  254.     if (cnt <> 0) and (cnt <= pg[VCNTLOC]) then
  255.         begin
  256.         bytePtr := BytePointerPosition(cnt,pRec.vsize);
  257.         pRec.cursor.prNum := thisNode;
  258.         pRec.cursor.entryNum := cnt;
  259.         pRec.cursor.valid := TRUE;
  260.         end
  261.     else
  262.         begin
  263.         pRec.cursor.valid := FALSE;
  264.         end;
  265.     SaveFileParameters(iFName,pRec,SizeOf(pRec));
  266.     UsingCursorAndGEValueGetLr := LrNumToReturn(pg,pRec);
  267.     end;                         (* end of UsingCursorAndGEValueGetLr routine *)
  268.  
  269. (*\*)
  270. (* This routine will move the cursor to the right one entry and return the
  271.    value associated with this entry.  It will return 0 if the cursor was not
  272.    valid (not pointing to an entry) or if there is no next entry (you are at
  273.    end of index).  This routine should be called if you want to move the
  274.    cursor to the next larger entry from the present cursor position and
  275.    retrieve the associated logical record number.  This routine should not
  276.    normally be used until the cursor has been positioned using one of the
  277.    three previous positioning routines.                                      *)
  278.  
  279. function UsingCursorGetNextLr(iFName : FnString) : LrNumber;
  280.  
  281. var
  282.     pRec : ParameterRecord;
  283.     pg : SinglePage;
  284.  
  285.     begin
  286.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  287.     if pRec.cursor.valid then
  288.         begin
  289.         FetchPage(iFName,pRec.cursor.prNum,pg);
  290.         Inc(pRec.cursor.entryNum);
  291.         if pRec.cursor.entryNum > pg[VCNTLOC] then
  292.             begin
  293.             Move(pg[NEXTLOC],pRec.cursor.prNum,RNSIZE);
  294.             if pRec.cursor.prNum = NULL then
  295.                 begin
  296.                 pRec.cursor.valid := FALSE;
  297.                 end
  298.             else
  299.                 begin
  300.                 FetchPage(iFName,pRec.cursor.prNum,pg);
  301.                 if pg[VCNTLOC] = 0 then
  302.                     begin
  303.                     pRec.cursor.valid := FALSE;
  304.                     end
  305.                 else
  306.                     begin
  307.                     pRec.cursor.entryNum := 1;
  308.                     end;
  309.                 end;
  310.             end;
  311.         SaveFileParameters(iFName,pRec,SizeOf(pRec));
  312.         end;
  313.     UsingCursorGetNextLr := LrNumToReturn(pg,pRec);
  314.     end;                              (* end of UsingCursorGetNextLr routine *)
  315.  
  316. (*\*)
  317. (* This routine will move the cursor to the left one entry and return the
  318.    value associated with this entry.  It will return 0 if the cursor was not
  319.    valid (not pointing to an entry) or if there is no previous entry (you are
  320.    at beginning of the index).  This routine should be called if you want to
  321.    move the cursor to the next smaller entry from the present cursor position
  322.    and retrieve the associated logical record number.  This routine should not
  323.    normally be used until the cursor has been positioned using one of the
  324.    three previous positioning routines.                                      *)
  325.  
  326. function UsingCursorGetPrevLr(iFName : FnString) : LrNumber;
  327.  
  328. var
  329.     pRec : ParameterRecord;
  330.     pg : SinglePage;
  331.  
  332.     begin
  333.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  334.     if pRec.cursor.valid then
  335.         begin
  336.         FetchPage(iFName,pRec.cursor.prNum,pg);
  337.         Dec(pRec.cursor.entryNum);
  338.         if pRec.cursor.entryNum = 0 then
  339.             begin
  340.             Move(pg[PREVLOC],pRec.cursor.prNum,RNSIZE);
  341.             if pRec.cursor.prNum = NULL then
  342.                 begin
  343.                 pRec.cursor.valid := FALSE;
  344.                 end
  345.             else
  346.                 begin
  347.                 FetchPage(iFName,pRec.cursor.prNum,pg);
  348.                 pRec.cursor.entryNum := pg[VCNTLOC];
  349.                 end;
  350.             end;
  351.         SaveFileParameters(iFName,pRec,SizeOf(pRec));
  352.         end;
  353.     UsingCursorGetPrevLr := LrNumToReturn(pg,pRec);
  354.     end;                              (* end of UsingCursorGetPrevLr routine *)
  355.  
  356. (*\*)
  357. (* This routine will move the cursor to the right.  It will move the cursor
  358.    to the next entry in which the value is not equal to the current entry and
  359.    return the associated logical record number.  In other words, it will skip
  360.    the cursor over all matching values.  It will return 0 if the cursor was
  361.    not valid (not pointing to an entry) or if there is no next entry (you are
  362.    at beginning of the index).  This routine should be used if you only want
  363.    to process the first entry of a given value etc.  This routine should not
  364.    normally be used until the cursor has been positioned using one of the
  365.    three previous positioning routines.                                      *)
  366.  
  367. function UsingCursorSkipAndGetNextLr(iFName : FnString) : LrNumber;
  368.  
  369. var
  370.     pRec : ParameterRecord;
  371.     pg1,
  372.     pg2 : SinglePage;
  373.     done : boolean;
  374.     oldNode : NodePtrType;
  375.  
  376.     begin
  377.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  378.     if pRec.cursor.valid then
  379.         begin
  380.         FetchPage(iFName,pRec.cursor.prNum,pg1);
  381.         done := FALSE;
  382.         while not done do
  383.             begin
  384.             Inc(pRec.cursor.entryNum);
  385.             if pRec.cursor.entryNum > pg1[VCNTLOC] then
  386.                 begin
  387.                 oldNode := pRec.cursor.prNum;
  388.                 Move(pg1[NEXTLOC],pRec.cursor.prNum,RNSIZE);
  389.                 if pRec.cursor.prNum = NULL then
  390.                     begin
  391.                     pRec.cursor.valid := FALSE;
  392.                     done := TRUE;
  393.                     end
  394.                 else
  395.                     begin
  396.                     pg2 := pg1;
  397.                     FetchPage(iFName,pRec.cursor.prNum,pg1);
  398.                     if pg1[VCNTLOC] = 0  then
  399.                         begin
  400.                         pRec.cursor.valid := FALSE;
  401.                         end
  402.                     else
  403.                         begin
  404.                         pRec.cursor.entryNum := 1;
  405.                         if CompareValues(pg1[1 + RNSIZE],
  406.                                          pg2[((pg2[VCNTLOC] - 1) *
  407.                                               (pRec.vSize + RNSIZE)) +
  408.                                              1 + RNSIZE],
  409.                                          pRec.vType) <> EQUALTO then
  410.                             begin
  411.                             done := TRUE;
  412.                             end;
  413.                         end;
  414.                     end;
  415.                 end
  416.             else
  417.                 begin
  418.                 if CompareValues(pg1[((pRec.cursor.entryNum - 1) *
  419.                                       (pRec.vSize + RNSIZE)) + 1 + RNSIZE],
  420.                                  pg1[((pRec.cursor.entryNum - 2) *
  421.                                       (pRec.vSize + RNSIZE)) + 1 + RNSIZE],
  422.                                  pRec.vType) <> EQUALTO then
  423.                     begin
  424.                     done := TRUE;
  425.                     end;
  426.                 end;
  427.             end;
  428.         SaveFileParameters(iFName,pRec,SizeOf(pRec));
  429.         end;
  430.     UsingCursorSkipAndGetNextLr := LrNumToReturn(pg1,pRec);
  431.     end;                       (* end of UsingCursorSkipAndGetNextLr routine *)
  432.  
  433. (*\*)
  434. (* This routine will move the cursor to the left.  It will move the cursor to
  435.    the previous entry in which the value is not equal to the current entry and
  436.    return the associated logical record number.  In other words, it will skip
  437.    the cursor over all matching values.  It will return 0 if the cursor was
  438.    not valid (not pointing to an entry) or if there is no previous entry (you
  439.    are at beginning of the index).  This routine should be used if you only
  440.    want to process the first entry of a given value etc.  This routine should
  441.    not normally be used until the cursor has been positioned using one of the
  442.    three previous positioning routines.                                      *)
  443.  
  444. function UsingCursorSkipAndGetPrevLr(iFName : FnString) : LrNumber;
  445.  
  446. var
  447.     pRec : ParameterRecord;
  448.     pg1,
  449.     pg2 : SinglePage;
  450.     done : boolean;
  451.     oldNode : NodePtrType;
  452.  
  453.     begin
  454.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  455.     if pRec.cursor.valid then
  456.         begin
  457.         FetchPage(iFName,pRec.cursor.prNum,pg1);
  458.         done := FALSE;
  459.         while not done do
  460.             begin
  461.             Dec(pRec.cursor.entryNum);
  462.             if pRec.cursor.entryNum = 0 then
  463.                 begin
  464.                 oldNode := pRec.cursor.prNum;
  465.                 Move(pg1[PREVLOC],pRec.cursor.prNum,RNSIZE);
  466.                 if pRec.cursor.prNum = NULL then
  467.                     begin
  468.                     pRec.cursor.valid := FALSE;
  469.                     done := TRUE;
  470.                     end
  471.                 else
  472.                     begin
  473.                     pg2 := pg1;
  474.                     FetchPage(iFName,pRec.cursor.prNum,pg1);
  475.                     pRec.cursor.entryNum := pg1[VCNTLOC];
  476.                     if CompareValues(pg2[1 + RNSIZE],
  477.                                      pg1[((pg1[VCNTLOC] - 1) *
  478.                                           (pRec.vSize + RNSIZE)) +
  479.                                          1 + RNSIZE],
  480.                                      pRec.vType) <> EQUALTO then
  481.                         begin
  482.                         done := TRUE;
  483.                         end;
  484.                     end;
  485.                 end
  486.             else
  487.                 begin
  488.                 if CompareValues(pg1[((pRec.cursor.entryNum - 1) *
  489.                                       (pRec.vSize + RNSIZE)) + 1 + RNSIZE],
  490.                                  pg1[(pRec.cursor.entryNum  *
  491.                                      (pRec.vSize + RNSIZE)) + 1 + RNSIZE],
  492.                                  pRec.vType) <> EQUALTO then
  493.                     begin
  494.                     done := TRUE;
  495.                     end;
  496.                 end;
  497.             end;
  498.         SaveFileParameters(iFName,pRec,SizeOf(pRec));
  499.         end;
  500.     UsingCursorSkipAndGetPrevLr := LrNumToReturn(pg1,pRec);
  501.     end;                       (* end of UsingCursorSkipAndGetPrevLr routine *)
  502.  
  503. (*\*)
  504. (* This routine will not move the cursor.  It will return the logical record
  505.    number asociated with the current cursor position.  It will return 0 only
  506.    if the current cursor position is not valid.                              *)
  507.  
  508. function UsingCursorGetCurrLr(iFName : FnString) : LrNumber;
  509.  
  510. var
  511.     pRec : ParameterRecord;
  512.     pg : SinglePage;
  513.     lrNum : LrNumber;
  514.  
  515.     begin
  516.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  517.     if pRec.cursor.valid then
  518.         begin
  519.         FetchPage(iFName,pRec.cursor.prNum,pg);
  520.         end;
  521.     UsingCursorGetCurrLr := LrNumToReturn(pg,pRec);
  522.     end;                              (* end of UsingCursorGetCurrLr routine *)
  523.  
  524.  
  525. (* This routine will set the cursor to invalid.  This is never required,
  526.    but can be used once the cursor use is completed and the cursor won't be
  527.    used until it is repositioned using one of the three positioning
  528.    routines. Using this routine will slightly speed up inserts and deletes.
  529.    This is because, on an insert or delete, the cursor position must be
  530.    kept correct if the cursor is valid.  This requires a small amount of
  531.    extra processing.  This processing is extraneous if you don't care about
  532.    the cursor position.                                                      *)
  533.  
  534. procedure UsingCursorMakeCursorInvalid(iFName : FnString);
  535.  
  536. var
  537.     pRec : ParameterRecord;
  538.  
  539.     begin
  540.     FetchFileParameters(iFName,pRec,SizeOf(pRec));
  541.     pRec.cursor.valid := FALSE;
  542.     SaveFileParameters(iFName,pRec,SizeOf(pRec));
  543.     end;                      (* end of UsingCursorMakeCursorInvalid routine *)
  544.