Newer
Older
ubixfs-2 / Bptree.pas
@flameshadow flameshadow on 10 Jun 2005 22 KB UbixFS
unit BPTree;

interface

uses UbixFS, strings;

const B_NODE_LEAF  = $4641454C;
const B_NODE_INDEX = $58444E49;
const B_NODE_OPEN  = $4E45504F;

const B_MAX_NAME_LENGTH = 254;

type
  PbNode = ^TbNode;
  PbNodeData = ^TbNodeData;

  uPtr = packed record
   case integer of
     0: (iAddr  : inodeAddr);
     1: (bPtr   : PbNode);
     2: (offset : int64);
{     3: (iPtr   : PubixfsInode);}
  end;

  TbNode = packed record
    magic    : uInt32;
    tag      : uInt32;
    numKeys  : uInt32;
    size     : uInt32;
    left     : uPtr;
    right    : uPtr;
    parent   : uPtr;
    reserved : uPtr;
    data     : array[0..0] of byte; 
    {data     : array[0..64-1] of byte;}
    {data     : array[0..464-1] of byte;}
    {data     : array[0..464+512-1] of byte;}
    {data     : array[0..4048-1] of byte;}
  end;

  TbNodeData = packed record
    link     : uPtr;
    key      : string[1];
  end; {TbNode}

type
  PbTree = ^bTree;
  bTree = object
   private
    root         : uPtr;
    curTag       : uInt32;
    treeHeight   : uInt32;
    treeLeafKeys : uInt32;
    firstDeleted : uPtr;
    dataFile     : file;
    memoryTree   : boolean;
    f:text;
    bNodeSize : uInt32;
{    fs        : PUbixFS;
    header    : PbTreeHeader;
    fd        : PfileDescriptor;
    tag       : uInt32;}
    function    align(keyLength:integer):integer;    
    function    allocEmptyNode:uPtr;
    procedure   deAllocNode(node:PbNode);
    function    deleteEntry(u:uPtr; key :PChar; value:uPtr):uPtr;
    procedure   discardPage(node:PbNode);
    function    findLeafNode(u:uPtr; key:PChar):uPtr;
    procedure   freeAll;
    procedure   freePage(u:uPtr);
    function    getAddress(node:PbNode):uPtr;
    function    insertEntry(u:uPtr; key:PChar; value:uPtr):boolean;    
    function    isNull(u:uPtr):boolean;
    function    loadPage(u:uPtr):PbNode;
    procedure	savePage(node:PbNode);
    procedure   setLeft(u:uPtr; newPtr:uPtr);
    procedure   setRight(u:uPtr; newPtr:uPtr);
    procedure   setParent(u:uPtr; newPtr:uPtr);
   public
    constructor init(const filename:string);
    function    Delete(key:PChar; value:uPtr):uPtr;
    function    Insert(key:PChar; value:uPtr):boolean;
    function    Find(key:PChar):uPtr;
    procedure   Info(u:uPtr);
    procedure   Print(u:uPtr);
    procedure   PrintList;    
    procedure   PrintWholeTree;
    destructor  done;
  end; {bTree}

var nulluPtr:uPtr;

implementation

uses strings, dos, crt;

function compareUPtrs(u1, u2:uPtr):boolean;
begin
{  if (memoryTree) then
    result := (u1.bPtr = u2.bPtr)
  else}
    result := (u1.offset = u2.offset);
end; {compareUPtrs}

overload = = compareUPtrs;


constructor bTree.init;
var node:PbNode;
begin
  root   := nulluptr;
  curTag := 0;
  treeHeight := 0;
  treeLeafKeys := 0;
  bNodeSize := 1024;
  firstDeleted := nullUPtr;

  memoryTree := fileName = '';

  if not (memoryTree) then
    begin

      assign(dataFile, filename);
      if (fsearch(filename, '')<>'') then 
        reset(dataFile, bNodeSize)
      else
        rewrite(dataFile, bNodeSize);

      getmem(node, bNodeSize);
      fillchar(node^, bNodeSize, 0);
      curTag := fileSize(dataFile);
      if (filesize(dataFile) <> 0) then 
        begin
          blockread(dataFile, node^, 1);
          while not (isNull(node^.parent)) do
            begin
              seek(dataFile, dword(node^.parent.offset));
              blockread(dataFile, node^, 1);
            end;
          root := getAddress(node);
        end;
      freemem(node, bNodeSize);
    end;
  assign(f, 'output.txt');
  {assign(f, '');}
  rewrite(f);
end; {bTree.init}

function bTree.align;
begin
  result := ((sizeof(TbNodeData) + keyLength + 7) shr 3) shl 3;
end;

function bTree.allocEmptyNode;
var newNode:PbNode;
begin
  result := nulluptr;
  if (not isNull(firstDeleted)) then
    begin
      newNode := loadPage(firstDeleted);
      if (newNode = NIL) then exit;
      firstDeleted := newNode^.parent;
    end
  else
    begin
      getmem(newNode, bNodeSize);
      fillchar(newNode^, bNodeSize, 0);
      newNode^.tag := curTag;
      inc(curTag);
    end;
  with newNode^ do
    begin
      magic          := B_NODE_LEAF;
      numKeys        := 1;
      size           := dword(@newNode^.data) - dword(newNode);
      left           := nulluPtr;
      right          := nulluptr;
      parent         := nulluptr;
      reserved       := nulluptr; 
    end; {with}
  result := getAddress(newNode);
  savePage(newNode);
  discardPage(newNode);

end; {bTree.allocEmptyNode}

procedure bTree.deAllocNode;
begin
  if (node = NIL) then exit;
  with node^ do
    begin
      magic := B_NODE_OPEN;
      left := nulluPtr;
      right := nulluPtr;
      parent := firstDeleted;
      reserved := nulluPtr;
      if (numKeys <> 0) then writeln('Warning! Node isn''t completely empty!');
      {numKeys := 1;  no keys, but get it ready for the next use}
      size := dword(@node^.data) - dword(node);
      fillchar(data, bNodeSize - size, 0);
    end;  
  firstDeleted := getAddress(node);
end; {btree.deAllocNode}

function bTree.deleteEntry;
var 
  bnode : PbNode;
  dataNode, nextDataNode:PbNodeData;
  nodeSize:uInt32;
  curNode, res: integer;
  found: boolean;
begin
  result := nulluPtr;

  if (isNull(u)) then exit;
  bnode := loadPage(u);
  
  if (bnode^.numKeys = 0) then halt;  {this should never happen}
  
  if (bnode^.magic = B_NODE_LEAF) then
    begin
      
      dataNode := @bnode^.data;
      curNode := 0;
      repeat
        res := strcomp(@dataNode^.key[1], key);
        if (res >= 0) then break;
        inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
        inc(curNode);
      until (curNode = bnode^.numKeys);
      
      if (res <> 0) then exit;
      
      result := dataNode^.link;      
            
      nodeSize := align(length(dataNode^.key));
      nextDataNode := dataNode;
      inc(uInt8ArrayPtr(nextDataNode), nodeSize);
      move(nextDataNode^, dataNode^, bnode^.size - (dword(dataNode) - dword(bnode) - nodeSize));
      dec(bnode^.numKeys);
      dec(bnode^.size, nodeSize);
      dataNode := pointer(dword(bnode) + bnode^.size);
      fillchar(dataNode^, nodeSize, 0);
      dec(treeLeafKeys);
      
      if (bnode^.numKeys = 0) then 
        begin
          savePage(bnode);
      	
      	  if not(isNull(bnode^.left)) then setRight(bnode^.left, bnode^.right);
      	  {bnode^.left.bPtr^.right.bPtr := bnode^.right.bPtr;}
          if not(isNull(bnode^.right)) then setLeft(bnode^.right, bnode^.left);
          {bnode^.right.bPtr^.left.bPtr := bnode^.left.bPtr;}

          if (getAddress(bnode) = root) then 
            root := nulluptr
          else
            deleteEntry(bnode^.parent, NIL, getAddress(bnode));
            
          deAllocNode(bnode);
        end;
    end
  else {not a leaf}
    begin
    	
      dataNode := @bnode^.data;
      found := FALSE;
      curNode := 0;
      for curNode := 0 to bnode^.numKeys do
        begin
          if (curNode <> bnode^.numKeys) then nodeSize := align(length(dataNode^.key));
          found := (dataNode^.link.bPtr = value.bPtr);
          if (found) then break;
          inc(uInt8ArrayPtr(dataNode), nodeSize);
        end;
      
      if (not found) then 
        begin
          discardPage(bnode);
          exit;
        end;

      nextDataNode := dataNode;
      inc(uInt8ArrayPtr(nextDataNode), nodeSize);
        
      if (curNode = bnode^.numKeys) then nextDataNode^.link := dataNode^.link;

      fillchar(dataNode^, nodeSize, 0);
      move(nextDataNode^, dataNode^, bnode^.size - (dword(dataNode) - dword(bnode) - nodeSize));
      
      dec(bnode^.numKeys);
      dec(bnode^.size, nodeSize);
      
      if (bnode^.numKeys = 0) then
        begin
          (* 
           * technically we don't have to set left/right pointers in an index node
           *)
      	  if not(isNull(bnode^.left)) then setRight(bnode^.left, bnode^.right);
      	  {bnode^.left.bPtr^.right.bPtr := bnode^.right.bPtr;}
          if not(isNull(bnode^.right)) then setLeft(bnode^.right, bnode^.left);
          {orig: bnode^.right.bPtr^.left.bPtr := bnode^.left.bPtr;}

          savePage(bnode);          
          if (getAddress(bnode) = root) then
            begin
              dataNode := @bnode^.data;
              root := dataNode^.link;
              dec(treeHeight);
            end
          else  {not the root}
            begin
              deleteEntry(bnode^.parent, NIL, getAddress(bnode));
            end;
          deAllocNode(bnode);
        end; {if bnode^.numKeys = 0}
    end;
  savePage(bnode);
  discardPage(bnode); 
end; {bTree.deleteEntry}

procedure bTree.discardPage;
begin
  if not (memoryTree) then if (node <> NIL) then freemem(node, bNodeSize);  
end; {bTree.discardPage}

function bTree.insertEntry;
var
  midKey:array[byte] of char;
  dataNode, nextDataNode:PbNodeData;
  nodeSize:uInt32;
  keyLength:integer;
  curNode : integer;
  dataSize:integer;
  tmpUPtr:uPtr;
  bnode, newNode, rootNode:PbNode;

begin
{  writeln(f, 'Inserting: ', key);}
  result := FALSE;
  keyLength := strlen(key);
  // keep in mind that we use length + string + null, so the actual maxlength is 254 instead of 255
  if (keyLength = 0) or (keyLength > B_MAX_NAME_LENGTH) then exit;

  nodeSize := align(keyLength);
  
{  writeln('nodeSize: ', nodeSize);
  nodeSize := sizeof(TbNodeData) + keyLength;
  inc(nodeSize, 8 - (nodeSize and not -8));}

  if (isNull(root)) then  
    begin

      (* 
       * This case should only occur once when a directory is first created.
       *)
      root := allocEmptyNode;
      if (isNull(root)) then halt;

      rootNode := loadPage(root);

      inc(treeLeafKeys);
      inc(treeHeight);
      rootNode^.magic := B_NODE_LEAF;
      dataNode := @rootNode^.data;
       
      fillchar(dataNode^, nodeSize, 0);

      dataNode^.link := value;
      dataNode^.key[0] := char(keyLength);
      move(key^, dataNode^.key[1], keyLength);
      
      inc(rootNode^.size, nodeSize);

      savePage(rootNode);
      discardPage(rootNode);
      result := TRUE;
      exit
    end;

  bnode := loadPage(u);

  if (bnode^.size + nodeSize <= bNodeSize) then
    begin

      dataNode := @bnode^.data;
      
      curNode := 0;
      repeat
        if (strcomp(key, @dataNode^.key[1]) <= 0) then break;
        inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
        inc(curNode);
      until curNode = bnode^.numKeys;

      nextDataNode := dataNode;
      inc(uInt8ArrayPtr(nextDataNode), nodeSize); {cast to byte size records}
      
      move(dataNode^, nextDataNode^, bnode^.size - (dword(dataNode) - dword(bNode)));
        
      fillchar(dataNode^, nodeSize, 0);
      
      dataNode^.key[0] := char(keyLength);
      move(key^, dataNode^.key[1], keyLength);

      dataNode^.link := value;
      
      if (bnode^.magic = B_NODE_INDEX) then        
        begin
          if (not isNull(dataNode^.link)) then setParent(dataNode^.link, getAddress(bnode));
          {if (dataNode^.link.bPtr <> NIL) then
              dataNode^.link.bPtr^.parent.bPtr := bnode;}
 
          tmpUPtr := dataNode^.link;
          dataNode^.link := nextDataNode^.link;
          nextDataNode^.link := tmpUPtr;
        end;
      inc(bnode^.numKeys);
      
      if (bnode^.magic = B_NODE_LEAF) then inc(treeLeafKeys);
      inc(bNode^.size, nodeSize);
      savePage(bnode);
    end
  else
    begin

      newNode := loadPage(allocEmptyNode);
      if (newNode = NIL) then halt;
      
      newNode^.magic := bnode^.magic;

      newNode^.left := getAddress(bnode);
      newNode^.right := bnode^.right;
      newNode^.parent := bnode^.parent;
      
      if not(isNull(newNode^.right)) then setLeft(newNode^.right, getAddress(newNode));
      bnode^.right := getAddress(newNode);
      
      dataNode := @bnode^.data;
      
      for curNode:=0 to (bnode^.numKeys div 2)-1 do
        inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
        
      fillchar(midKey, sizeof(midKey), 0);
      move(dataNode^.key[1], midKey, length(dataNode^.key)); 

      if (bnode^.magic = B_NODE_LEAF) then
        begin  

  {        newNode^.left := getAddress(bnode);
          newNode^.right := bnode^.right;
          if not(isNull(newNode^.right)) then setLeft(newNode^.right, getAddress(newNode));
          bnode^.right := getAddress(newNode);}
          
          inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
          
          dataSize := bnode^.size - (dword(dataNode) - dword(bnode));
          move(dataNode^, newNode^.data, dataSize);
          fillchar(dataNode^, dataSize, 0);
  
          dec(bnode^.size, dataSize);
          newNode^.size := dataSize + (dword(@newNode^.data) - dword(newNode));

          newNode^.numKeys := bnode^.numKeys - (bnode^.numKeys div 2)-1;

          dec(bnode^.numKeys, newNode^.numKeys);

          savePage(bnode);
          savePage(newNode);
          if (strcomp(key, midKey) <= 0) then
            insertEntry(getAddress(bnode), key, value)
          else
            insertEntry(getAddress(newNode), key, value);

        end
      else {not a leaf}
        begin

          inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
          
          dataSize := bnode^.size - (dword(dataNode) - dword(bnode));
          move(dataNode^, newNode^.data, dataSize);
          
          fillchar(dataNode^, dataSize, 0);
                    
          dec(bnode^.size, dataSize);
          newNode^.size := dataSize + (dword(@newNode^.data) - dword(newNode));
	  newNode^.numKeys := bnode^.numKeys - (bnode^.numKeys div 2)-1;
	  dec(bnode^.numKeys, newNode^.numKeys);
	  dec(bnode^.numKeys);
          
          savePage(newNode);	  

	  dataNode := @newNode^.data;
	  for curNode := 0 to newNode^.numKeys do
	    begin
	      setParent(dataNode^.link, getAddress(newNode));
	      {dataNode^.link.bPtr^.parent.bPtr := newNode;}
	      inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
	    end;
	  savePage(bnode);

	  if (strcomp(key, midKey) <= 0) then
            insertEntry(getAddress(bnode), key, value)
          else
            insertEntry(getAddress(newNode), key, value);
  
        end;
            
      (*
       * If we were the root node, then create a new one
       *)
      
      if (getAddress(bnode) = root) then
        begin

          root := allocEmptyNode;
          rootNode := loadPage(root);
          setParent(getAddress(bnode), root);
          setParent(getAddress(newNode), root);
{          bnode^.parent := root;
          newNode^.parent := root;}
          inc(treeHeight);
          {dataNode := @newNode^.data;     }
          dataNode := @rootNode^.data;
          with rootNode^ do
            begin
              magic := B_NODE_INDEX;
              {numKeys := 1;}
              dataNode^.link := getAddress(bnode);
              keyLength := strlen(midKey);
              
              dataNode^.key[0] := char(keyLength);
              move(midKey, dataNode^.key[1], keyLength);
              
              inc(size, align(keyLength));
              inc(size, sizeof(uPtr));
              inc(uInt8ArrayPtr(dataNode), align(keyLength));
              dataNode^.link := getAddress(newNode);
              
            end;  {with}
          savePage(rootNode);
          discardPage(rootNode); 
        end {if oldNode = root}
      else
        begin
          insertEntry(bnode^.parent, midKey, getAddress(newNode));
        end;
      discardPage(newNode);
    end; {if out of space in this node}
  discardPage(bnode);
  result := TRUE;
end; {bTree.insertEntry}

function bTree.isNull;
begin
  if (memoryTree) then
    result := (u.bPtr = nulluPtr.bPtr)
  else
    result := (u.offset = nulluPtr.offset)
end; {bTree.isNull}

function bTree.loadPage;
var node:PbNode;
begin
  result := NIL;
  if isNull(u) then exit;

  if (memoryTree) then
    node := u.bPtr
  else
    begin
      seek(dataFile, dword(u.offset));
      getmem(node, bNodeSize);
      blockread(dataFile, node^, 1);
    end;
  
  result := node
end; {bTree.loadPage}

procedure bTree.savePage;
begin
  if (memoryTree) then exit;

  if (node = NIL) then exit;
  seek(dataFile, dword(node^.tag));
  blockwrite(dataFile, node^, 1);

end; {btree.savePage}

procedure bTree.setLeft;
var node:PbNode;
begin
  node := loadPage(u);
  if (node = NIL) then exit;
  node^.left := newPtr;
  savePage(node);
  discardPage(node);
end; {bTree.setLeft}

procedure bTree.setParent;
var node:PbNode;
begin
  node := loadPage(u);
  if (node = NIL) then exit;
  node^.parent := newPtr;
  savePage(node);
  discardPage(node);
end; {bTree.setParent}

procedure bTree.setRight;
var node:PbNode;
begin
  node := loadPage(u);
  if (node = NIL) then exit;
  node^.right := newPtr;
  savePage(node);
  discardPage(node);
end; {bTree.setRight}

procedure bTree.findLeafNode;
var 
  bNode:PbNode;
  dataNode:PbNodeData;
  i:integer;
  link:uPtr;
begin
  result := nullUPtr;
  
  if (isNull(u)) then exit;
  
  bnode := loadPage(u);

  if (bnode^.magic = B_NODE_LEAF) then 
    begin
      result := u;
      discardPage(bnode);
      exit;
    end
  else
    begin
      dataNode := @bnode^.data;
      for i:=0 to bnode^.numKeys -1 do
        begin
          if (strcomp(key, @dataNode^.key[1]) <= 0) then
            begin
              link := dataNode^.link;
              discardPage(bnode);
              result := findLeafNode(link, key);
              exit;
            end;
          inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
        end; {for}
      link := dataNode^.link;
      discardPage(bnode);
      result := findLeafNode(link, key);
    end;

end; {bTree.findLeafNode}

procedure bTree.freeAll;
begin
  if not(memoryTree) then exit;
  freePage(root);
  root := nulluptr;
end; {bTree.freeAll}

procedure bTree.freePage;
var 
  curNode:integer;
  dataNode:PbNodeData;
  node : PbNode;
begin
 
  if isNull(u) then exit;

  node := u.bPtr;
  curNode := 0;
  if (node^.magic = B_NODE_INDEX) then
    begin
      dataNode := @node^.data;
      for curNode := 0 to node^.numKeys-1 do
        begin
          freePage(dataNode^.link);
          inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
        end;
      freePage(dataNode^.link);
    end;
  freemem(node, bNodeSize);
end; {bTree.freeAll}

function bTree.getAddress;
var u:uPtr;
begin
  u := nulluptr;
  if (memoryTree) then
    u.bPtr := node
  else
    u.offset := node^.tag; { * bNodeSize;}
  
  result := u;	
end; {bTree.getAddress}

function bTree.Delete;
begin
  result := find(key);
{  result := deleteEntry(findLeafNode(root, key), key, value);}
end; {bTree.Delete}

function bTree.Find;
var
  bnode:PbNode;
  dataNode:PbNodeData;
  i, res:integer;

begin
  result := nulluPtr;
  
  bnode := loadPage(findLeafNode(root, key));
  if (bnode = NIL) then exit; {oops! serious problem}
  
  if (bnode^.magic <> B_NODE_LEAF) then writeln('Error! not in leaf nodes');
  
  dataNode := @bnode^.data;

  for i:=0 to bnode^.numKeys-1 do      
    begin
      res := strcomp(@dataNode^.key[1], key);
      if (res >= 0) then break;
      inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
    end;
   if (res = 0) then result := dataNode^.link;
  discardPage(bnode);
end; {bTree.Find}

procedure bTree.Info;
const leafStr:array[boolean] of string[5] = ('INDEX', 'LEAF');
var
  dataNode:PbNodeData;
  curNode:integer;
  node : PbNode;
begin
  node := loadPage(u);
  if (node = NIL) then exit;
  write(f, '             ', hex(dword(node^.parent.offset)));
  writeln(f, '              ', leafStr[node^.magic = B_NODE_LEAF]); 
  write(f, hex(dword(node^.left.bPtr)), ' <-> ',  hex(dword(node)), ' <-> ', hex(dword(node^.right.bPtr)));
  writeln(f, '   |   capacity ',      node^.size,'(', bNodeSize, ')', '   |   keys:', node^.numKeys);
  if (node^.numKeys = 0) then exit;
  dataNode := @node^.data;
  
  curNode := 0;
  for curNode := 0 to node^.numKeys-(ord(node^.magic = B_NODE_LEAF)) do
    begin
      
      write(f, '| ', curNode:2, '| ');
      write(f, '[');

      if (node^.magic = B_NODE_LEAF) then
        begin
{    	  if (dataNode^.link.iPtr <> NIL) then
      	    write(f, dataNode^.link.iPtr^.name)
          else
          write(f, '/');}
        end
      else
        write(f, hex(longint(dword(dataNode^.link.offset))));
      write(f, ']');
  
      if (curNode <> node^.numKeys) then    
        writeln(f, ' {', dataNode^.key, '} ');
    
      inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
    end;
  discardPage(node);
  writeln(f);
end; {bTree.Info}

function bTree.Insert;
begin
  result := insertEntry(findLeafNode(root, key), key, value);
end; {bTree.Insert}

procedure bTree.Print;
var 
  curNode:integer;
  dataNode:PbNodeData;
  node : PbNode;
  link : uPtr;
begin
  if isNull(u) then exit;
  Info(u);
  node := loadPage(u);
  curNode := 0;
  if (node^.magic = B_NODE_INDEX) then
    begin
      dataNode := @node^.data;
      for curNode := 0 to node^.numKeys-1 do
        begin
          Print(dataNode^.link);
          inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
        end;
      Print(dataNode^.link);
    end;
  discardPage(node);  
end; {bTree.Print}

procedure bTree.PrintList;
var 
  dataNode:PbNodeData;
  node:PbNode;
  link:uPtr;
  i : integer;
begin
  
  if (isNull(root)) then exit;
  
  node := loadPage(root);
  while (node^.magic = B_NODE_INDEX) do
    begin
      dataNode := @node^.data;
      link := dataNode^.link;
      discardPage(node);
      node := loadPage(link);
    end;
  
  while (not isNull(link)) do
    begin
      dataNode := @node^.data;
      for i := 0 to node^.numKeys -1 do
        begin
          writeln(f, dataNode^.key);
          inc(uInt8ArrayPtr(dataNode), align(length(dataNode^.key)));
        end; {for i}
      link := node^.right;
      discardPage(node);
      node := loadPage(link);
    end; {while}
  
end; {bTree.PrintList}

procedure bTree.PrintWholeTree;
begin
  Print(root);  
  writeln(f, '---------------------------------------------------------------------------');  
end; {bTree.PrintWholeTree}

destructor bTree.done;
begin
  if not(memoryTree) then close(dataFile);
  
  if (not isNull(root)) then
    begin
      writeln('tree height: ', treeHeight);
      writeln('tree leaf key count: ', treeLeafKeys);
      writeln('Total number of nodes: ', curTag);
      freeAll;
      close(f);

    end;
 
end; {bTree.done}

begin
  {$ifdef MEMORY_TREE}
    fillchar(nulluptr, sizeof(nulluptr), 0);
  {$else}
    nulluptr.offset := -1;
  {$endif}
end.