home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / TBTREE.ZIP / BTREE1.INC < prev    next >
Encoding:
Text File  |  1988-07-26  |  48.6 KB  |  1,147 lines

  1.  
  2. const
  3.     NULL     : RecordNumber = 0;                  (* used to dilineate null
  4.                                                               record number *)
  5.  
  6. type
  7.     NodeType = (INVALIDNODETYPE,INDEXNODE,SEQUENCENODE);
  8.                              (* INVALIDNODETYPE is not used for anything.
  9.                                 It serves one purpose.  It gives positive
  10.                                 values to the two remaining legal values
  11.                                 for this enumerated type.  I didn't want
  12.                                 to have zero be valid.  This helped in
  13.                                 debugging and there is no reason to
  14.                                 change it.                                   *)
  15.  
  16. (* These parameters are contained in the first record (0) in the working file
  17.  
  18.      variable      parameter       location   size   type    range
  19.      --------      ---------       --------   ----   ----    -----
  20.       vSize        value size           1       1    Byte    1 - 114
  21.       rNode        root node            2       4 Longint    1 - MAXLONGINT
  22.       fSNode       first sequence node  6       4 Longint    1 - MAXLONGINT
  23.       lSNODE       last  sequence node 10       4 LongInt    1 - MAXLONGINT
  24.       vType        value type          14       1    Byte    0 = INVALIDVALUE
  25.                                                              1 = BYTEVALUE
  26.                                                              2 = SHORTINTVALUE
  27.                                                              3 = INTEGERVALUE
  28.                                                              4 = LONGINTVALUE
  29.                                                              5 = WORDVALUE
  30.                                                              6 = STRINGVALUE
  31.                                                              7 = REALVALUE
  32.                                                              8 = SINGLEVALUE
  33.                                                              9 = DOUBLEVALUE
  34.                                                             10 = EXTENDEDVALUE
  35.                                                             11 = COMPVALUE
  36.       dFNameSize   data file name size 15       1 Byte       1 - FNSIZE
  37.       dFName       data file name      16      80 FnString                   *)
  38.  
  39. const
  40.     VSIZELOC      = 1;
  41.     RNODELOC      = 2;
  42.     FSNODELOC     = 6;
  43.     LSNODELOC     = 10;
  44.     VTYPELOC      = 14;
  45.     DFNAMESIZELOC = 15;
  46.     DFNAMELOC     = 16;
  47.  
  48. (*\*)
  49. type
  50.     NodePtrType   = PrNumber;                (* pointer to index records     *)
  51.     FNameSizeType = 0 .. FNSIZE;             (* range for size of a file name
  52.                                                 string                       *)
  53.  
  54.  
  55.     ParameterRecord = record
  56.          vSize      : VSizeType;
  57.          rNode      : NodePtrType;
  58.          fSNode     : NodePtrType;
  59.          lSNode     : NodePtrType;
  60.          dFNameSize : FNameSizeType;
  61.          dFName     : FnString;
  62.          vType      : ValueType;
  63.          end;
  64.  
  65.  
  66.  
  67.  
  68. (* These parameters is found in every index and sequence node in the index
  69.    file.
  70.  
  71.      variable      parameter       location   size   type    range
  72.      --------      ---------       --------   ----   ----    -----
  73.       prev         prev sequence     503        4    int     -1 - MAXINT
  74.                       node
  75.  
  76.       next         next sequence     507        4    int     -1 - MAXINT
  77.                       node
  78.  
  79.       nType        node type         511        1    Byte    0 = INVALIDNODETYPE
  80.                                                              1 = INDEXNODE
  81.                                                              2 = SEQUENCENODE
  82.  
  83.       vCnt         value count       512        1    Byte    1 - MAXBYTE     *)
  84.  
  85.  
  86. const
  87.     PREVLOC  = 503;
  88.     NEXTLOC  = 507;
  89.     NTYPELOC = 511;
  90.     VCNTLOC  = 512;
  91.  
  92. (*\*)
  93. (* This procedure will read the zeroth physical record in the index file.
  94.    The record contains the parameters to be stored in a variable (pRec) of type
  95.    ParameterRecord (see above).                                              *)
  96.  
  97. Procedure FetchIndexParameters(iFName : FnString;
  98.                                var pRec : ParameterRecord);
  99.  
  100. var
  101.     page : SinglePage;
  102.  
  103.     begin
  104.     FetchPage(iFName,0,page);
  105.     pRec.vSize := page[VSIZELOC];
  106.     Move(page[RNODELOC],pRec.rNode,RNSIZE);
  107.     Move(page[FSNODELOC],pRec.fSNode,RNSIZE);
  108.     Move(page[LSNODELOC],pRec.lSNode,RNSIZE);
  109.     pRec.vType := ValueType(page[VTYPELOC]);
  110.     pRec.dFNameSize := page[DFNAMESIZELOC];
  111.     Move(page[DFNAMESIZELOC],pRec.dFName,pRec.dFNameSize + 1);
  112.     end;                            (* End of FetchIndexParameters procedure *)
  113.  
  114.  
  115. (* This procedure will save the zeroth physical record in the index file.
  116.    The record contains the parameters documented above.  The zeroth record will
  117.    be updated according to the values passed in pRec.                        *)
  118.  
  119. Procedure SaveIndexParameters(iFName : FnString;
  120.                               pRec : ParameterRecord);
  121.  
  122. var
  123.     page : SinglePage;
  124.  
  125.     begin
  126.     FetchPage(iFName,0,page);
  127.     page[VSIZELOC] := pRec.vSize;
  128.     Move(pRec.rNode,page[RNODELOC],RNSIZE);
  129.     Move(pRec.fSNode,page[FSNODELOC],RNSIZE);
  130.     Move(pRec.lSNode,page[LSNODELOC],RNSIZE);
  131.     page[VTYPELOC] := Integer(pRec.vType);
  132.     Move(pRec.dFName,page[DFNAMESIZELOC],pRec.dFNameSize + 1);
  133.     StorePage(ifName,0,page);
  134.     end;                             (* End of SaveIndexParameters procedure *)
  135.  
  136. (*\*)
  137. (* This routine will insert a node between prevNode and nextNode in a node list.
  138.    It will accomplish this by setting the prev and next ptrs as necessary
  139.    for a node and its prev and next nodes.  Obviously, the node ptr and the
  140.    next and prev node pointers must be known.  If the node type is
  141.    SEQUENCENODE and this node is the first node in the sequential list, the
  142.    parameter record will be updated to reflect this change (the sNode parameter
  143.    will be set to this node ).                                               *)
  144.  
  145. procedure InsertNodeInList(iFName : FnString;
  146.                            thisNode : NodePtrType;
  147.                            var prevNode;
  148.                            var nextNode);
  149.  
  150. var
  151.     page : SinglePage;
  152.     pRec : ParameterRecord;
  153.     tempPrevNode,
  154.     tempNextNode : NodePtrType;
  155.  
  156.     begin
  157.     Move(prevNode,tempPrevNode,RNSIZE);
  158.     Move(nextNode,tempNextNode,RNSIZE);
  159.     FetchPage(iFName,thisNode,page);
  160.     Move(prevNode,page[PREVLOC],RNSIZE);
  161.     Move(nextNode,page[NEXTLOC],RNSIZE);
  162.     StorePage(iFName,thisNode,page);
  163.     if tempPrevNode <> NULL then
  164.         begin
  165.         FetchPage(iFName,tempPrevNode,page);
  166.         Move(thisNode,page[NEXTLOC],RNSIZE);
  167.         StorePage(iFName,tempPrevNode,page);
  168.         end
  169.     else
  170.         begin                               (* new node is first node *)
  171.         if page[NTYPELOC] = Integer(SEQUENCENODE) then
  172.             begin             (* set first seq node pointer to this new node *)
  173.             FetchIndexParameters(iFName,pRec);
  174.             pRec.fSNode := thisNode;
  175.             SaveIndexParameters(iFName,pRec);
  176.             end;
  177.         end;
  178.     if tempNextNode <> NULL then
  179.         begin
  180.         FetchPage(iFName,tempNextNode,page);
  181.         Move(thisNode,page[PREVLOC],RNSIZE);
  182.         StorePage(iFName,tempNextNode,page);
  183.         end
  184.     else
  185.         begin                                       (* new node is last node *)
  186.         if page[NTYPELOC] = Integer(SEQUENCENODE) then
  187.             begin              (* set last seq node pointer to this new node *)
  188.             FetchIndexParameters(iFName,pRec);
  189.             pRec.lSNode := thisNode;
  190.             SaveIndexParameters(iFName,pRec);
  191.             end;
  192.         end;
  193.     end;                                  (* end of InsertNodeInList routine *)
  194.  
  195. (*\*)
  196. (* This routine will delete a node from a node list and set its neighbors prev
  197.    and next node pointers as appropriate.  It will also delete the record from
  198.    the index file to allow it to be reused.                                  *)
  199.  
  200. procedure DeleteNodeFromList(iFName : FnString;
  201.                              thisNode : NodePtrType);
  202.  
  203. var
  204.     pRec : ParameterRecord;
  205.     page : SinglePage;
  206.     prevNode,
  207.     nextNode : NodePtrType;
  208.  
  209.     begin
  210.     FetchPage(iFName,thisNode,page);
  211.     Move(page[PREVLOC],prevNode,RNSIZE); (* get Prev node ptr *)
  212.     Move(page[NEXTLOC],nextNode,RNSIZE); (* get Next node ptr *)
  213.     if prevNode <> NULL then
  214.         begin
  215.         FetchPage(iFName,prevNode,page);
  216.         Move(nextNode,page[NEXTLOC],RNSIZE);
  217.         StorePage(iFName,prevNode,page);
  218.         end
  219.     else
  220.         begin
  221.         if NodeType(page[NTYPELOC]) = SEQUENCENODE then
  222.             begin
  223.             FetchIndexParameters(iFName,pRec);
  224.             pRec.fSNode := nextNode;
  225.             SaveIndexParameters(iFName,pRec);
  226.             end;
  227.         end;
  228.     if nextNode <> NULL then
  229.         begin
  230.         FetchPage(iFName,nextNode,page);
  231.         Move(prevNode,page[PREVLOC],RNSIZE);
  232.         StorePage(iFName,nextNode,page);
  233.         end
  234.     else
  235.         begin
  236.         if NodeType(page[NTYPELOC]) = SEQUENCENODE then
  237.             begin
  238.             FetchIndexParameters(iFName,pRec);
  239.             pRec.lSNode := nextNode;
  240.             SaveIndexParameters(iFName,pRec);
  241.             end;
  242.         end;
  243.     DeleteRecord(iFName,thisNode);              (* get rid of phys rec *)
  244.     end;                                        (* end of DeleteNodeFromList *)
  245.  
  246. (*\*)
  247. (* This routine will create a new node and set the node type parameter
  248.    and will insert this node between the prev node and the next node
  249.    in the node linked list.  Remember, this level linked list is required to
  250.    facilitate deletions.                                                     *)
  251.  
  252. function CreatedNode(iFName : FnString;
  253.                      var prevNode;
  254.                      var nextNode;
  255.                      nType : NodeType) : NodePtrType;
  256.  
  257. var
  258.     page : SinglePage;
  259.     newNode : NodePtrType;
  260.  
  261.     begin
  262.     newNode := FirstUnUsedRecord(iFName);
  263.     FillChar(page,PAGESIZE,0);
  264.     page[NTYPELOC] := Integer(nType);                   (* set the node type *)
  265.     StorePage(iFName,newNode,page);    (* will create new node automatically *)
  266.     InsertNodeInList(iFName,newNode,prevNode,nextNode);
  267.     CreatedNode := newNode;    (* return the node ptr for this new node *)
  268.     end;                                  (* end of CreatedNode routine *)
  269.  
  270.  
  271. (* This routine will create an index file and set the zeroth record as
  272.    appropriate.  The root node will also be created.  The root node will
  273.    be empty.
  274.  
  275.    note - File name passed as parameter does not have a file extension.  The
  276.    extension will be added and returned.
  277.  
  278.    note - Extremely important - WARNING - for STRINGVALUE indexes only - the
  279.    valSize must be 1 greater than the number of characters of the longest
  280.    string.  This will allow 1 byte for the string length to be stored.
  281.    for example - if 'abc' is the longest string then valSize = 4.            *)
  282.  
  283. procedure CreateIndex(var iFName : FnString;
  284.                       valSize : VSizeType;
  285.                       dataFName : FnString;
  286.                       valType : ValueType);
  287.  
  288. var
  289.     pRec : ParameterRecord;
  290.  
  291.     begin
  292.     CreateFile(iFName,INDEX);
  293.     pRec.vSize := valSize;
  294.     pRec.rNode := CreatedNode(iFName,NULL,NULL,INDEXNODE);    (* create root *)
  295.     pRec.fSNode := NULL;
  296.     pRec.lSNode := NULL;
  297.     pRec.dFNameSize := Length(dataFName);
  298.     pRec.dFName := dataFName;
  299.     pRec.vType := valType;
  300.     SaveIndexParameters(iFName,pRec);     (* write parameters back to buffer *)
  301.     end;                                       (* end of CreateIndex routine *)
  302.  
  303. (*\*)
  304. (* This routine will search an index node and return the record number for the
  305.    next lower node corresponding to the paramValue.  The returned node will
  306.    either be another index node or a sequence node.
  307.  
  308.    Note : this assumes that there are lower nodes.  Prior to calling this
  309.    routine check for an empty root                                           *)
  310.  
  311. function FindNextLevelPtr(page : SinglePage;
  312.                           var paramValue;
  313.                           pRec : ParameterRecord) : NodePtrType;
  314.  
  315.  var
  316.      cnt,
  317.      vCnt : Byte;
  318.      bytePtr : PageRange;
  319.      done : Boolean;
  320.      p : NodePtrType;                 (* temporarily holds pointer to return *)
  321.      result : Comparison;
  322.  
  323.      begin
  324.      vCnt := page[VCNTLOC];
  325.      bytePtr := 1;
  326.      cnt := 1;
  327.      done := FALSE;
  328.      while not done do
  329.          begin                          (* no more values in node *)
  330.          if cnt > vCnt then
  331.              begin
  332.              Move(page[bytePtr],p,RNSIZE);    (* ptr to be returned *)
  333.              done := TRUE;
  334.              end
  335.          else
  336.              begin
  337.              result := CompareValues(paramValue,
  338.                                      page[bytePtr + RNSIZE],
  339.                                      pRec.vType);
  340.              if (result = LESSTHAN) or (result = EQUALTO) then
  341.                  begin
  342.                  Move(page[bytePtr],p,RNSIZE); (* ptr to be returned *)
  343.                  done := TRUE;
  344.                  end
  345.              else
  346.                  begin
  347.                  cnt := cnt + 1;
  348.                  bytePtr := bytePtr + pRec.vSize + RNSIZE;
  349.                  end;
  350.              end;
  351.          end;
  352.      FindNextLevelPtr := p;
  353.      end;                                 (* end of FindNextLevelPtr routine *)
  354.  
  355. (*\*)
  356. (* This recursive routine will start at the specified node (rNum) and work
  357.    down the tree until the correct sequence node is found.  It will return
  358.    the record number of the sequence node.
  359.  
  360.    This routine assumes that as long as an index node is not empty, there
  361.    should be one more pointer than there are values.  In other words, there
  362.    is always a trailing valid pointer which takes care of the case of values
  363.    larger than the largest value in the tree.
  364.  
  365.    This routine also assumes that some sequence node exists.  This will not
  366.    work for an empty root.  This must be checked by caller.                  *)
  367.  
  368. Function FindSNode(iFName : FnString;
  369.                    rNum : NodePtrType;
  370.                    var paramValue;
  371.                    pRec : ParameterRecord) : NodePtrType;
  372.  
  373. var
  374.     page : SinglePage;
  375.  
  376.     begin
  377.     FetchPage(iFName,rNum,page);                                 (* get node *)
  378.     if NodeType(page[NTYPELOC]) = INDEXNODE then
  379.         begin
  380.         FindSNode := FindSNode(iFName,
  381.                                FindNextLevelPtr(page,paramValue,pRec),
  382.                                paramValue,pRec);
  383.         end
  384.     else
  385.         begin
  386.         FindSNode := rNum;
  387.         end;
  388.     end;                                        (* end of FindSNode function *)
  389.  
  390. (*\*)
  391. (* This function locates the value within the index and returns a list of
  392.    logical record numbers for this associated value.  It accomplishes this by
  393.    using FindSNode to locate the sequence node.  It will fetch the sequence
  394.    set node and search until the target key is found or it is determined that
  395.    it does not exist (no entry).  If it is found all lrNumbers with a matching
  396.    target key value will be returned in lrLst.  If the matching key value is
  397.    not found then an empty list will be returned.                            *)
  398.  
  399. procedure GetEqualsFromBTree(iFName : FnString;
  400.                              var paramValue;
  401.                              var lrLst : LrList);
  402.  
  403. var
  404.     pRec : ParameterRecord;   (* holds the index parameters *)
  405.     next : PrNumber;          (* used as pointer to next sequence node *)
  406.     vCnt : Byte;              (* count of values in a node *)
  407.     result : Comparison;
  408.     page : SinglePage;
  409.     done : Boolean;
  410.     cnt : Byte;               (* used to count number of values *)
  411.     lrNum : LrNumber;
  412.     bytePtr : PageRange;      (* used to keep track of current byte *)
  413.  
  414.     begin
  415.     FetchIndexParameters(iFName,pRec);
  416.     CreateLrList(lrLst);
  417.     if pRec.fSNode <> NULL then
  418.         begin
  419.         FetchPage(iFName,
  420.                   FindSNode(iFName,pRec.rnode,paramValue,pRec),
  421.                   page);
  422.         vCnt := page[VCNTLOC];
  423.         Move(page[NEXTLOC],next,RNSIZE);
  424.         cnt := 1;
  425.         bytePtr := 1;
  426.         done := FALSE;
  427.         while not done do
  428.             begin
  429.             result := CompareValues(paramValue,
  430.                                     page[bytePtr+RNSIZE],
  431.                                     pRec.vType);
  432.             if result = EQUALTO then
  433.                 begin
  434.                 Move(page[bytePtr],lrNum,RNSIZE);
  435.                 AddToLrList(lrNum,lrLst);
  436.                 end
  437.             else
  438.                 begin
  439.                 if result = LESSTHAN then
  440.                     begin
  441.                     done := TRUE;               (* this will get us out of this
  442.                                                    loop .. value does not exist
  443.                                                    in this record *)
  444.                     end;
  445.                 end;
  446.             if not done then
  447.                 begin
  448.                 if cnt = vCnt then
  449.                     begin
  450.                     if next <> NULL then (* if next is not null *)
  451.                         begin            (* then get next seq node *)
  452.                         cnt := 1;        (* need to continue in case there is *)
  453.                         bytePtr := 1;    (* more than one occurrence for this *)
  454.                         FetchPage(iFName,next,page);                 (* value *)
  455.                         vCnt := page[VCNTLOC];
  456.                         Move(page[NEXTLOC],next,RNSIZE);
  457.                         end
  458.                     else
  459.                         begin                            (* no more seq nodes *)
  460.                         done := TRUE;
  461.                         end;
  462.                     end
  463.                 else
  464.                     begin
  465.                     bytePtr := bytePtr + pRec.vSize + RNSIZE;
  466.                     cnt := cnt + 1;
  467.                     end;
  468.                 end;
  469.             end;
  470.         end;
  471.     end;                                (* end of GetEqualsFromBTree routine *)
  472.  
  473. (*\*)
  474. (* This routine inserts a new value into a node.  It will locate the
  475.    proper place, move all values and pointers past the spot to allow room for
  476.    the new value and pointer, and insert the new value and pointer.  If there
  477.    is a value equal to this new value, the new value will be inserted in
  478.    front of the old one.  This routine will not work if there is not enough
  479.    room in the node.  This must be checked prior to calling this routine.
  480.    This routine works with both sequence and index nodes.  It assumes that the
  481.    proper page has been read in prior to this routine being called.  This is
  482.    why a page is passed in as a parameter in lieu of physical record number.
  483.  
  484.    This works for both index and sequence nodes                              *)
  485.  
  486. procedure InsertValueIntoNode(var page : SinglePage;
  487.                               var paramValue;
  488.                               rNum : RecordNumber;
  489.                               pRec : ParameterRecord);
  490.  
  491. var
  492.     result : Comparison;
  493.     cnt,
  494.     vCnt : Byte;
  495.     done : Boolean;
  496.     bytePtr : PageRange;
  497.  
  498.     begin
  499.     vCnt := page[VCNTLOC];                               (* get value count *)
  500.     bytePtr := 1;
  501.     cnt := 1;
  502.     done := FALSE;
  503.     while not done do
  504.         begin
  505.         if cnt > vCnt then
  506.             begin
  507.             done := TRUE;
  508.             end
  509.         else
  510.             begin
  511.             result := CompareValues(paramValue,
  512.                                     page[bytePtr + RNSIZE],
  513.                                     pRec.vType);
  514.             if (result = LESSTHAN) or (result = EQUALTO) then
  515.                 begin
  516.                 done := TRUE;
  517.                 end
  518.             else
  519.                 begin
  520.                 cnt := cnt + 1;
  521.                             (* we don't have to worry about range of bytePtr
  522.                                since the value will definitely fit           *)
  523.                 bytePtr := byteptr + pRec.vSize + RNSIZE;
  524.                 end;
  525.             end;
  526.         end;
  527.     Move(page[bytePtr],                                         (* make room *)
  528.          page[bytePtr + pRec.vSize + RNSIZE],
  529.          ((vCnt - cnt) + 1) * (pRec.vSize + RNSIZE) + RNSIZE);
  530.     Move(rNum,page[bytePtr],RNSIZE);                       (* insert pointer *)
  531.     Move(paramValue,page[bytePtr + RNSIZE],pRec.vSize);        (*insert value*)
  532.     page[VCNTLOC] := vCnt + 1;                            (* new value count *)
  533.     end;                               (* end of InsertValueIntoNode routine *)
  534.  
  535. (*\*)
  536. (* This function will check to see if there is room to insert a new value into
  537.    any type node.  If room exists then a routine is called to insert the
  538.    node into the proper place.  The result is returned (True or False)
  539.    This routine assumes that the proper page has been read in prior to this
  540.    routine being called.  This is why page number is used in lieu of physical
  541.    record number.                                                            *)
  542.  
  543. function InsertedInNode(var page : SinglePage;
  544.                         var paramValue;
  545.                         rNum : RecordNumber;
  546.                         pRec : ParameterRecord) : Boolean;
  547.  
  548. var
  549.     maxValues : Byte;
  550.     vCnt : Byte;
  551.  
  552.     begin
  553.     maxValues := (MAXVALSIZE + RNSIZE) DIV (pRec.vSize + RNSIZE);
  554.     vCnt := page[VCNTLOC];
  555.     if vCnt < maxValues then
  556.         begin
  557.         InsertValueIntoNode(page,paramValue,rNum,pRec);
  558.         InsertedInNode := TRUE;
  559.         end
  560.     else
  561.         begin
  562.         InsertedInNode := FALSE;
  563.         end;
  564.     end;                                    (* end of InsertedInNode routine *)
  565.  
  566. (*\*)
  567. (* This routine will move n/2 (rounded down) values from the right node
  568.    (rtNode) to the empty left node (ltNode).                                 *)
  569.  
  570. procedure MoveValues(iFName : FnString;
  571.                      rtNode : NodePtrType;
  572.                      ltNode : NodePtrType;
  573.                      vSize : VSizeType);
  574.  
  575. var
  576.     bytesToMove,                            (* total number of bytes to move *)
  577.     numToMove,                                   (* number of values to move *)
  578.     vCnt : Byte;                            (* count of values in right node *)
  579.     rtPage,
  580.     ltPage : SinglePage;
  581.  
  582.     begin
  583.     FetchPage(ifName,rtNode,rtPage);               (* get right node's page  *)
  584.     FetchPage(ifName,ltNode,ltPage);               (* get left node's page   *)
  585.     vCnt := rtPage[VCNTLOC];                       (* get right node's count *)
  586.     numToMove := vCnt div 2;
  587.     bytesToMove := (RNSIZE + vSize) * numToMove;  (* calc # of bytes to move *)
  588.     Move(rtPage[1],ltPage[1],bytesToMove);
  589.     Move(rtPage[BytesToMove + 1],
  590.          rtPage[1],(MAXVALSIZE + RNSIZE + RNSIZE) - bytesToMove);
  591.     vCnt := vCnt - numToMove;
  592.     if NodeType(rtPage[NTYPELOC]) = INDEXNODE then
  593.         begin
  594.         FillChar(rtPage[(vCnt * (vSize + RNSIZE)) + 1 + RNSIZE],
  595.                  numToMove * (vSize + RNSIZE),
  596.                  0);
  597.         end
  598.     else
  599.         begin
  600.         FillChar(rtPage[(vCnt * (vSize + RNSIZE)) + 1],
  601.                  numToMove * (vSize + RNSIZE),
  602.                  0);
  603.         end;
  604.     rtPage[VCNTLOC] := vCnt;
  605.     ltPage[VCNTLOC] := numToMove;
  606.     StorePage(iFName,rtNode,rtPage);
  607.     StorePage(iFName,ltNode,ltPage);
  608.     end;                                        (* end of MoveValues routine *)
  609.  
  610. (*\*)
  611. (* This recursive routine will start at a given node (usually the root) and
  612.    follow the tree down until the correct sequence node is found.  The new
  613.    value and record number will be inserted into the correct sequence node.
  614.    In the event that the sequence node is full, the node will be split.  The
  615.    value and record number will be put in the proper node if a split occurs.
  616.    The routine will return NULL if no split occurs.  If a split occurs, the
  617.    record number (node pointer) of the newly created node will be returned.
  618.    This new node will be inserted in the parent index node.  If it won't
  619.    fit the index node will be split and the new child record number will
  620.    be inserted in the proper index node.  The value associated with the child
  621.    record number is the largest value in the newly created child node.  This
  622.    process continues until we bubble back to the root in the node.  Once at
  623.    the root the routine will return back to the original caller.  If the root
  624.    was not split then NULL will be returned.  If the root was split, then the
  625.    newly created child record number is returned.  The caller will have to
  626.    create a new root node and insert the new value and the child record
  627.    number.  Be sure that the caller also inserts the newly inserted child's
  628.    right sibling (record number only) since all indexes have one more pointer
  629.    than they do values.
  630.  
  631.    This routine expects at least one pointer in the root.  This needs to be
  632.    checked by the caller.                                                    *)
  633.  
  634. function InsertValue(iFName : FnString;
  635.                      rNum : RecordNumber;    (* record number to be inserted *)
  636.                      var paramValue;         (* value to be inserted *)
  637.                      thisNode : NodePtrType; (* node *)
  638.                      pRec : ParameterRecord) : NodePtrType;
  639.  
  640. var
  641.     newNode,                    (* newly created node if needed (node split) *)
  642.     lowerNode : NodePtrType;
  643.     thisPage,
  644.     newPage,
  645.     lowerPage : SinglePage;
  646.     lastValLoc : PageRange;       (* used to hold buffer position *)
  647.  
  648.     begin
  649.     FetchPage(iFName,thisNode,thisPage);
  650.     case NodeType(thisPage[NTYPELOC]) of
  651.         INDEXNODE:
  652.             begin
  653.             lowerNode := InsertValue(iFName,rNum,paramValue,
  654.                                      FindNextLevelPtr(thisPage,paramValue,
  655.                                                       pRec),pRec);
  656.             if lowerNode <> NULL then
  657.                 begin                     (* lower node must have been split *)
  658.                 FetchPage(iFName,lowerNode,lowerPage);
  659.                 lastValLoc := (((lowerPage[VCNTLOC] - 1)
  660.                               * ( pRec.vSize + RNSIZE)) + RNSIZE) + 1;
  661.                 if InsertedInNode(thisPage,lowerPage[lastValLoc],
  662.                                   lowerNode,pRec) then
  663.                     begin
  664.                     InsertValue := NULL;    (* node not split .. return NULL *)
  665.                     StorePage(iFName,thisNode,thisPage);
  666.                     end
  667.                 else
  668.                     begin
  669.                     newNode := CreatedNode(iFName,
  670.                                            thisPage[PREVLOC],
  671.                                            thisNode,
  672.                                            INDEXNODE);
  673.                     MoveValues(iFName,thisNode,newNode,pRec.vSize);
  674.                     FetchPage(iFName,thisNode,thisPage);
  675.                     if CompareValues(paramValue,thisPage[RNSIZE + 1],
  676.                                      pRec.vType) = GREATERTHAN then
  677.                         begin
  678.                         InsertValueIntoNode(thisPage,
  679.                                             lowerPage[lastValLoc],
  680.                                             lowerNode,pRec);
  681.                         StorePage(iFName,thisNode,thisPage);
  682.                         end
  683.                     else
  684.                         begin
  685.                         FetchPage(iFName,newNode,newPage);
  686.                         InsertValueIntoNode(newPage,
  687.                                             lowerPage[lastValLoc],
  688.                                             lowerNode,pRec);
  689.                         StorePage(iFName,newNode,newPage);
  690.                         end;
  691.                     InsertValue := newNode;      (* newly added node will be
  692.                                                     returned                 *)
  693.                     end;
  694.                 end
  695.             else
  696.                 begin
  697.                 InsertValue := NULL;    (* it fit at lower level therefore
  698.                                            this level is fine .. return NULL *)
  699.                 end;
  700.             end;
  701.         SEQUENCENODE :
  702.             begin
  703.             if InsertedInNode(thisPage,paramValue,rNum,pRec) then
  704.                 begin
  705.                 InsertValue := NULL;   (* it fits .. no split .. return NULL *)
  706.                 StorePage(iFName,thisNode,thisPage);
  707.                 end
  708.             else
  709.                 begin
  710.                 newNode := CreatedNode(iFName,
  711.                                        thisPage[prevLoc],
  712.                                        thisNode,
  713.                                        SEQUENCENODE);
  714.                 MoveValues(iFName,thisNode,newNode,pRec.vSize);
  715.                 Fetchpage(iFName,thisNode,thisPage);
  716.                 if CompareValues(paramValue,thisPage[RNSIZE + 1],
  717.                                  pRec.vType) = GREATERTHAN then
  718.                     begin
  719.                     InsertValueIntoNode(thisPage,paramValue,rNum,pRec);
  720.                     StorePage(iFName,thisNode,thisPage);
  721.                     end
  722.                 else
  723.                     begin
  724.                     FetchPage(iFName,newNode,newPage);
  725.                     InsertValueIntoNode(newPage,paramValue,rNum,pRec);
  726.                     StorePage(iFName,newNode,newPage);
  727.                     end;
  728.                 InsertValue := newNode;
  729.                 end;
  730.             end;
  731.         end;                                   (* end of case statement      *)
  732.     end;                                       (* end of Insertvalue routine *)
  733.  
  734. (*\*)
  735. (* This routine will insert a value and its associated logical record number
  736.    into the given index file.  This routine will guard against duplicate
  737.    entries.  An index should have no more than one occurence of any
  738.    lrNum,paramValue pair (no two entries match on paramValue and lrNum).  This
  739.    routine assures this by calling DeleteValueFromBTree prior to performing the
  740.    insert.  This will get rid of a previous occurence if it exists.          *)
  741.  
  742. procedure InsertValueInBTree(iFName : FnString;
  743.                              lrNum : LRNumber;
  744.                              var paramValue);
  745.  
  746. var
  747.     lowerNode : PrNumber;
  748.     pRec : ParameterRecord;
  749.     lowerPage,
  750.     page : SinglePage;             (* used for root and first seq node pages *)
  751.     lastValLoc : PageRange;                  (* used to hold buffer position *)
  752.  
  753.     begin
  754.     DeleteValueFromBTree(iFName,lrNum,paramValue);   (* ensure no duplicates *)
  755.     FetchIndexParameters(iFName,pRec);
  756.     if pRec.fSNode = NULL then
  757.         begin                                          (* root must be empty *)
  758.         pRec.fSNode := CreatedNode(iFName,NULL,NULL,SEQUENCENODE); (* create seq
  759.                                                                      node *)
  760.         pRec.lSNode := pRec.fSNode;
  761.         FetchPage(iFName,pRec.rNode,page);                  (* get root page *)
  762.         Move(pRec.fSNode,page[1],RNSIZE);             (* put seq node in root *)
  763.         StorePage(iFName,pRec.rNode,page);                 (* store the root *)
  764.         FetchPage(iFName,pRec.fSNode,page);                   (* get seq node *)
  765.         InsertValueIntoNode(page,paramValue,lrNum,pRec);   (* insert value into
  766.                                                               seq node       *)
  767.         StorePage(iFName,pRec.fSNode,page);             (* store the seq node *)
  768.         end
  769.     else
  770.         begin
  771.         lowerNode := InsertValue(iFName,lrNum,paramValue,pRec.rNode,pRec);
  772.         if lowerNode <> NULL then
  773.             begin                            (* we need to create a new root *)
  774.             pRec.rNode := CreatedNode(iFName,NULL,NULL,INDEXNODE); (* root has
  775.                                                                  no siblings *)
  776.             FetchPage(iFName,pRec.rNode,page);              (* get root node *)
  777.             FetchPage(iFName,lowerNode,lowerPage);         (* get child node *)
  778.             lastValLoc := (((lowerPage[VCNTLOC] - 1)
  779.                              * ( pRec.vSize + RNSIZE)) + RNSIZE) + 1;
  780.             InsertValueIntoNode(page,              (* insert child into root *)
  781.                                 lowerPage[LastValLoc],
  782.                                 lowerNode,pRec);
  783.             Move(lowerPage[NEXTLOC],
  784.                  page[RNSIZE + 1 + pRec.vSize],RNSIZE);    (* insert ptr for
  785.                                                                  right child *)
  786.             StorePage(iFName,pRec.rNode,page);
  787.             SaveIndexParameters(iFName,pRec);
  788.            end;
  789.         end;
  790.     end;                 (* end of InsertValueInBTree routine *)
  791.  
  792. (*\*)
  793. (* This routine will locate and delete a value and its associated record
  794.    pointer from within a node/list of nodes.  It will first locate the value.
  795.    The value will be found in this node or in succeeding nodes. The search will
  796.    continue until the value and the correct associated record number are found
  797.    or it is determined that it does not exist.If the correct value and record
  798.    number are not found then the routine will return false indicating that no
  799.    value was deleted.  If the correct value and record number are found they
  800.    will be deleted.  In this case the node where the value was deleted from
  801.    will be returned.  If the value deleted was the last in the node or the
  802.    only one in the node these facts will be returned.  This is important
  803.    because the calling node may have to alter or delete values as a result.
  804.  
  805.    note : if a node is the last node in a level node list the node will not
  806.    be deleted.  In this case the value will be deleted but last will be set
  807.    to FALSE.  This is because the parent needs to make no adjustment.        *)
  808.  
  809. function DeleteValueFromNode(iFName : FnString;
  810.                              rNum : RecordNumber;
  811.                              var ParamValue;
  812.                              var thisNode : NodePtrType;
  813.                              pRec : ParameterRecord;
  814.                              var last : Boolean;
  815.                              var nodeDeleted : Boolean) : Boolean;
  816.  
  817. var
  818.     done : Boolean;
  819.     result : Comparison;
  820.     cnt,
  821.     vCnt : Byte;
  822.     bytePtr : PageRange;
  823.     page : SinglePage;
  824.     nextNode : NodePtrType;
  825.  
  826.     begin
  827.     FetchPage(iFName,thisNode,page);             (* fetch page for this node *)
  828.     vCnt := page[VCNTLOC];                                (* get value count *)
  829.     cnt := 1;
  830.     bytePtr := 1;
  831.     done := FALSE;
  832.     while not done do
  833.         begin
  834.         if cnt <= vCnt then
  835.             begin
  836.             result := CompareValues(paramValue,
  837.                                     page[bytePtr + RNSIZE],
  838.                                     pRec.vType);
  839.             case result of
  840.                 LESSTHAN :
  841.                     begin
  842.                     done := TRUE;
  843.                     DeleteValueFromNode := FALSE;
  844.                     last := FALSE;
  845.                     nodeDeleted := FALSE;
  846.                     end;
  847.                 EQUALTO :
  848.                     begin     (* value found .. look for record number match *)
  849.                     result := CompareValues(rNum,
  850.                                             page[bytePtr],
  851.                                             LONGINTVALUE);
  852.                     if result = EQUALTO then
  853.                         begin
  854.                         done := TRUE;
  855.                         DeleteValueFromNode := TRUE;
  856.                         Move(page[NEXTLOC],nextNode,RNSIZE);
  857.                         if (vCnt = 1) and (nextNode <> NULL) then
  858.                             begin               (* only 1 entry in this node *)
  859.                             last := TRUE;
  860.                             nodeDeleted := TRUE;
  861.                             end
  862.                         else
  863.                             begin
  864.                             page[VCNTLOC] := vCnt - 1;
  865.                             nodeDeleted := FALSE;
  866.                             Move(page[bytePtr + RNSIZE + pRec.vSize],
  867.                                  page[bytePtr],
  868.                                  (RNSIZE + pRec.vSize) * (vCnt - cnt) + RNSIZE);
  869.                             FillChar(page[(((vCnt - 1) *
  870.                                             (pRec.vSize + RNSIZE)) + 1)
  871.                                           + RNSIZE],
  872.                                      pRec.vSize + RNSIZE,
  873.                                      0);
  874.                             StorePage(iFName,thisNode,page);   (* store the
  875.                                                                   page *)
  876.                             if (cnt = vCnt) and (vCnt <> 1)
  877.                                             and (nextNode <> NULL) then
  878.                                             (* the nextNode check is used
  879.                                                since the last node at level
  880.                                                only has a node pointer and not
  881.                                                a corresponding value at the
  882.                                                next higher level.  Therefore,
  883.                                                no value adjustment will be
  884.                                                necessary.                    *)
  885.                                 begin              (* value deleted was last *)
  886.                                 last := TRUE;
  887.                                 end
  888.                             else
  889.                                 begin
  890.                                 last := FALSE;
  891.                                 end;
  892.                             end;
  893.                         end
  894.                     else
  895.                         begin
  896.                         cnt := cnt + 1;
  897.                         if cnt <= vCnt then
  898.                             begin      (* if stmt will keep bytePtr in range *)
  899.                             bytePtr := bytePtr + RNSIZE + pRec.vSize;
  900.                             end;
  901.                         end;
  902.                     end;
  903.                 GREATERTHAN :
  904.                     begin
  905.                     cnt := cnt + 1;
  906.                     if cnt <= vCnt then
  907.                         begin          (* if stmt will keep bytePtr in range *)
  908.                         bytePtr := bytePtr + RNSIZE + pRec.vSize;
  909.                         end;
  910.                     end;
  911.                 end;                                (* end of case statement *)
  912.             end
  913.         else
  914.             begin                           (* no more values .. get brother *)
  915.             Move(page[NEXTLOC],thisNode,RNSIZE);              (* get brother *)
  916.             if thisNode = NULL then
  917.                 begin                                  (* no brother .. quit *)
  918.                 done := TRUE;
  919.                 DeleteValueFromNode := FALSE;
  920.                 last := FALSE;
  921.                 nodeDeleted := FALSE;
  922.                 end
  923.             else
  924.                 begin                                       (* brother found *)
  925.                 FetchPage(iFName,thisNode,page);            (* fetch brother *)
  926.                 bytePtr := 1;
  927.                 cnt := 1;
  928.                 vCnt := page[VCNTLOC];
  929.                 end;
  930.             end;
  931.         end;
  932.     end;                               (* end of DeleteValueFromNode routine *)
  933.  
  934. (*\*)
  935. (* This recursive routine will start at a given node (normally the root) and
  936.    follow the tree down until the correct sequence node is found.  Once it
  937.    is found DeleteValueFromNode is used to delete the value from the node if
  938.    it exists.  The routine returns TRUE if the value (including the correct
  939.    physical record pointer is found and deleted.  Otherwise, FALSE is
  940.    returned.  If this deletion causes an empty node, DeleteValueFromNode will
  941.    delete the node.  This routine will take this into account and delete the
  942.    lowernode and lowernode node pointer.  The variable nodeDeleted will be set
  943.    TRUE by DeleteValueFromNode to denote that the lower node was deleted.  If
  944.    the lower node was not deleted but the value deleted was the last value in
  945.    the node (not the only value but the last) and was not the last node of a
  946.    given level then this routine will change the value pointing to the lower
  947.    node to take this into account.  This will be noted by last being set to
  948.    TRUE by DeleteValueFromNode.                                              *)
  949.  
  950. function DeleteValue(iFName : FnString;
  951.                      rNum : RecordNumber;
  952.                      var paramValue;
  953.                      var thisNode : NodePtrType;
  954.                      pRec : ParameterRecord;
  955.                      var last : Boolean;
  956.                      var nodeDeleted : Boolean) : Boolean;
  957.  
  958. var
  959.     lowerPage,
  960.     thisPage : SinglePage;
  961.     lastValLoc,
  962.     bytePtr : PageRange;
  963.     lowerNode : NodePtrType;
  964.     cnt : Byte;
  965.     done : Boolean;
  966.  
  967.     begin
  968.     FetchPage(iFName,thisNode,thisPage);
  969.     case NodeType(thisPage[NTYPELOC]) of
  970.         INDEXNODE :
  971.             begin
  972.             lowerNode := FindNextLevelPtr(thisPage,paramValue,pRec);
  973.             if DeleteValue(iFName,
  974.                            rNum,
  975.                            paramValue,
  976.                            lowerNode,      (* will become the lower node where
  977.                                                   the value was deleted from *)
  978.                            pRec,
  979.                            last,
  980.                            nodeDeleted) then
  981.                 begin      (* value was successfully deleted from node below *)
  982.                 if nodeDeleted then    (* check to see if lower node deleted *)
  983.                     begin              (* it was - delete corresponding node
  984.                                           pointer from this node             *)
  985.                     DeleteValue := DeleteValueFromNode(iFName,
  986.                                                        lowerNode,  (*lower node
  987.                                                                     pointer *)
  988.                                                        paramValue,
  989.                                                        thisNode,  (* node to
  990.                                                                      delete
  991.                                                                      from *)
  992.                                                        pRec,
  993.                                                        last,
  994.                                                        nodeDeleted);
  995.                     DeleteNodeFromList(iFName,lowerNode);   (*delete lower node
  996.                                                               from ist list *)
  997.                     end
  998.                 else
  999.                     begin
  1000.                     if last then
  1001.                         begin
  1002.                         bytePtr := 1;
  1003.                         cnt := 1;
  1004.                         FetchPage(iFName,lowerNode,lowerPage);
  1005.                         lastValLoc := ((lowerPage[VCNTLOC] - 1)
  1006.                                       * (pRec.vSize + RNSIZE)) + 1;
  1007.                                              (* now find record number match *)
  1008.                         done := FALSE;
  1009.                         while not done do
  1010.                             begin
  1011.                             if CompareValues(lowerNode,
  1012.                                              thisPage[bytePtr],
  1013.                                              LONGINTVALUE) = EQUALTO then
  1014.                                 begin
  1015.                                 done := TRUE;
  1016.                                 if cnt <= thisPage[VCNTLOC] then
  1017.                                     begin       (* the above check takes care
  1018.                                                    of the case where the lower
  1019.                                                    node has a pointer to it in
  1020.                                                    this page but does not have
  1021.                                                    a corresponding value since
  1022.                                                    this is the last index page
  1023.                                                    in a given level sequence *)
  1024.                                     Move(lowerPage[lastValLoc + RNSIZE],
  1025.                                          thisPage[bytePtr + RNSIZE],
  1026.                                          pRec.vSize);
  1027.                                     StorePage(iFName,thisNode,thisPage);
  1028.                                     end;
  1029.                                 end
  1030.                             else
  1031.                                 begin
  1032.                                 if cnt = thisPage[VCNTLOC] then
  1033.                                     begin
  1034.                                     Move(thisPage[NEXTLOC],thisNode,RNSIZE);
  1035.                                     FetchPage(iFName,thisNode,thisPage);
  1036.                                     bytePtr := 1;
  1037.                                     cnt := 1;
  1038.                                     end
  1039.                                 else
  1040.                                     begin
  1041.                                     bytePtr := bytePtr + pRec.vSize + RNSIZE;
  1042.                                     cnt := cnt + 1;
  1043.                                     end;
  1044.                                 end;
  1045.                             end;
  1046.                         end;
  1047.                     DeleteValue := FALSE;
  1048.                     end;
  1049.                 end
  1050.             else
  1051.                 begin
  1052.                 DeleteValue := FALSE;
  1053.                 end;
  1054.             end;
  1055.         SEQUENCENODE :
  1056.             begin
  1057.             DeleteValue := DeleteValueFromNode(iFName,rNum,paramValue,
  1058.                                                thisNode,pRec,last,nodeDeleted);
  1059.             end;
  1060.         end;                                        (* end of case statement *)
  1061.     end;                                       (* end of DeleteValue routine *)
  1062.  
  1063. (*\*)
  1064. (* This routine will delete a value and its associated logical record number
  1065.    from a given index file.  If the logical record number passed in is zero
  1066.    then all records with a matching paramValue will be deleted.  Otherwise,
  1067.    only the entry with the matching paramValue and the matching logical record
  1068.    number will be deleted.                                                   *)
  1069.  
  1070. procedure DeleteValueFromBTree(iFName : FnString;
  1071.                                lrNum : LrNumber;
  1072.                                var paramValue);
  1073.  
  1074. var
  1075.     tempLrNum : LrNumber;
  1076.     lrLst : LrList;
  1077.     pRec : ParameterRecord;
  1078.     last,
  1079.     nodeDeleted : Boolean;
  1080.  
  1081.     begin
  1082.     FetchIndexParameters(iFName,pRec);
  1083.     if pRec.fSNode <> NULL then               (* see if sequence node exists *)
  1084.         begin
  1085.         if lrNum = 0 then
  1086.             begin                     (* delete all occurences of paramValue *)
  1087.             CreateLrList(lrLst);
  1088.             GetEqualsFromBTree(iFName,paramValue,lrLst);
  1089.             tempLrNum := GetFirstLr(lrLst);
  1090.             while tempLrNum <> 0 do
  1091.                 begin
  1092.                 DeleteValueFromBTree(iFName,tempLrNum,paramValue);
  1093.                 tempLrNum := GetNextLr(lrLst);
  1094.                 end;
  1095.             DestroyLrList(lrLst);
  1096.             end
  1097.         else
  1098.             begin
  1099.             if DeleteValue(iFName,lrNum,paramValue,pRec.rNode,
  1100.                            pRec,last,nodeDeleted) then ;
  1101.             end;
  1102.         end;
  1103.     end;                                      (* end of DeleteValueFromBTree *)
  1104.  
  1105.  
  1106. (* This routine will start at the root node and return the number of levels
  1107. that exist in a BTree.  The index file name is the only required input.      *)
  1108.  
  1109. function NumberOfBTreeLevels(iFName : FnString) : Byte;
  1110.  
  1111. var
  1112.     pRec : ParameterRecord;
  1113.     page : SinglePage;
  1114.  
  1115.     function CountLevels(page : SinglePage) : Byte;
  1116.  
  1117.     var
  1118.         lowerNode : PrNumber;
  1119.  
  1120.         begin
  1121.         case NodeType(page[NTYPELOC]) of
  1122.             INDEXNODE :
  1123.                 begin
  1124.                 Move(page,lowerNode,RNSIZE);
  1125.                 FetchPage(iFName,lowerNode,page);
  1126.                 CountLevels := CountLevels(page) + 1;
  1127.                 end;
  1128.             SEQUENCENODE :
  1129.                 begin
  1130.                 CountLevels := 1;
  1131.                 end;
  1132.             end;                                    (* end of case statement *)
  1133.         end;                                   (* end of CountLevels routine *)
  1134.  
  1135.     begin
  1136.     FetchIndexParameters(iFName,pRec);
  1137.     if pRec.fSNode = NULL then
  1138.         begin                                          (* root must be empty *)
  1139.         NumberOfBTreeLevels := 0;
  1140.         end
  1141.     else
  1142.         begin
  1143.         FetchPage(iFName,pRec.rNode,page);                  (* get root node *)
  1144.         NumberOfBTreeLevels := CountLevels(page);
  1145.         end;
  1146.     end;                               (* end of NumberOfBTreeLevels routine *)
  1147.