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

interface

uses UbixFS, strings;

const B_NODE_MAGIC = $DEADBEEF;

const B_MAX_KEYS = 15;
const B_MAX_NAME_LENGTH = 256;

type
  PbNode = ^TbNode;
  PbNodeData = ^TbNodeData;

  uPtr = packed record
   case integer of
     0: (iAddr  : inodeAddr);
     1: (bPtr   : PbNode);
     2: (vPtr   : pointer);
     3: (iPtr   : PubixfsInode);
     4: (nPtr   : PbNodeData);
     5: (offset : int64);
  end;

  TbNode = packed record
    magic1   : uInt32;
    numKeys  : uInt32;
    tag      : uInt32;
    size     : uInt32;
    leaf     : longbool;
    reserved : uInt32;
    left     : uPtr;
    right    : uPtr;
    parent   : uPtr;
    data     : array[0..4040-1] of byte;
  end;

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

  (*
  TbNode = packed record
    magic1 : uInt32;
    used   : uInt32;
    parent : uPtr;
    tag    : int64;
    keys   : array[0..B_MAX_KEYS-1, 0..B_MAX_NAME_LENGTH-1] of char;
    present: array[0..B_MAX_KEYS] of boolean;
    head   : array[0..B_MAX_KEYS] of uPtr;
    childCount : array[0..B_MAX_KEYS] of uInt32;
    magic2 : uInt32;
    leaf   : boolean;
    reserved : array[0..18] of byte;
  end; {bNode}
  *)
type
  PbTreeHeader = ^TbTreeHeader;
  TbTreeHeader = packed record
    treeDepth       : uInt32;
    treeWidth       : uInt32;
    treeLeafCount   : uInt32;
    firstNodeOffset : int64;
    firstDeleted    : int64;
    paddington      : array[0..4067] of byte;
  end; {bTreeHeader}

type
  PbTree = ^bTree;
  bTree = object
   private
    root      : PbNode;
    curTag    : uInt32;
{    fs        : PUbixFS;
    header    : PbTreeHeader;
    fd        : PfileDescriptor;
    tag       : uInt32;}
    function    allocEmptyNode:PbNode;
{    function    getFirstNode(node:PbNode):PubixfsInode;
    function    inodeSearch(inode:PubixfsInode; const key:PChar):PubixfsInode;
    procedure   insertNode(node:PbNode; const key:PChar; headPtr:PbNode);
    procedure   splitNode(oldNode:PbNode);
    function    treeSearch(bnode:PbNode; const key:PChar):PubixfsInode;}
   public
    constructor init;
    function    Insert(key:PChar; var value):boolean;
{    function    Find(key:PChar):PubixfsInode;}
    procedure   Info(node:PbNode);
{    function    Insert(inode:PubixfsInode):boolean;
    procedure   Print(node:PbNode);
    procedure   PrintWholeTree;}
    destructor  done;
  end; {bTree}

implementation

uses strings, dos, crt;

constructor bTree.init;
begin
  root   := NIL;
  curTag := 0;
end; {bTree.init}

function bTree.allocEmptyNode;
var newNode:PbNode;
begin
  new(newNode);
  fillchar(newNode^, sizeof(newNode^), 0);
  inc(curTag);
  with newNode^ do
    begin
      magic1         := B_NODE_MAGIC;
      numKeys        := 1;
      tag            := curTag;
      leaf           := FALSE;  { assume? }
      left.vPtr      := NIL;
      right.vPtr     := NIL;
      parent.vPtr    := NIL;

      size           := sizeof(magic1) + sizeof(numKeys) + sizeof(tag) +
                        sizeof(leaf) + sizeof(left) + sizeof(right) +
                        sizeof(reserved) + sizeof(parent) + sizeof(size) +
                        sizeof(uPtr);
{      writeln('allocated size is: ', size);}
    end; {with}
  result := newNode;
end; {bTree.allocEmptyNode}

procedure bTree.Info;
var
  nodeData:PbNodeData;
  i:integer;

begin
  if ((node = NIL) or (root = NIL)) then exit;

  writeln(hex(longint(node)), ' | ', hex(longint(node^.parent.bPtr)));

  nodeData := @node^.data;
  for i := 0 to node^.numKeys do
    begin
      write('[');
      if (nodeData^.link.iPtr <> NIL) then
        write(nodeData^.link.iPtr^.name)
      else
        write('/');
      write(']');
      if (i <> node^.numKeys) then
        write(' {', nodeData^.key, '} ');
      inc(uInt8ArrayPtr(nodeData), ((sizeof(TbNodeData) + length(nodeData^.key) + 3) shr 2) shl 2);
    end; {for i}
  writeln;
end; {bTree.Info}

function bTree.Insert;
var
  dataNode, nextDataNode:PbNodeData;
  nodeSize, tmpSize:uInt32;
  keyLength:integer;
  bnode:PbNode;
  curNode : integer;
  p, src, dst:pointer;
  f:file;
begin
  result := FALSE;

  keyLength := strlen(key);
  if (keyLength = 0) or (keyLength > 255) then exit;

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

  if (root = NIL) then
    begin
  writeln('-- 1 --');
      root := allocEmptyNode;
      if (root = NIL) then halt;

      dataNode := @root^.data;

      fillchar(dataNode^, nodeSize, 0);

      dataNode^.link.iPtr := NIL;
      dataNode^.key[0] := char(keyLength);
      move(key^, dataNode^.key[1], keyLength);

      with root^ do
        begin
          inc(uInt8ArrayPtr(dataNode), nodeSize); {cast to byte size records}
          dataNode^.link.iPtr := uPtr(value).iPtr;
          leaf := TRUE;
          inc(size, nodeSize);
        end;
info(root);
      result := TRUE;
      exit
    end;

  bnode := root;

  while not bnode^.leaf do
    begin
    end;

  dataNode := @bnode^.data;

  with bNode^ do
    if (numKeys = 1) and (dataNode^.link.iPtr = NIL) then
      begin
        if (strcomp(key, @dataNode^.key[1]) < 0) then
          dataNode^.link.iPtr := uPtr(value).iPtr
        else
          begin
            writeln('-- 2 --');
            nodeSize := ((sizeof(TbNodeData) + length(dataNode^.key) + 7) shr 3) shl 3;
            // decrease the size by the size of the first node
            dec(size, nodeSize);

            nextDataNode := dataNode;
            inc(uInt8ArrayPtr(nextDataNode), nodeSize); {cast to byte size records}
            dataNode^.link.iPtr := nextDataNode^.link.iPtr;

            // make a string

            dataNode^.key[0] := char(keyLength);
            move(key^, dataNode^.key[1], keyLength);

            nodeSize := ((sizeof(TbNodeData) + length(dataNode^.key) + 7) shr 3) shl 3;
            inc(uInt8ArrayPtr(dataNode), nodeSize); {cast to byte size records}
            dataNode^.link.iPtr := uPtr(value).iPtr;
            // readjust the size based on the size of the new data
            inc(size, nodeSize);
          end;
info(bnode);
        result := TRUE;
        exit
    end; {if}


  curNode := 0;
  repeat
    if (strcomp(key, @dataNode^.key[1]) < 0) then break;
    inc(uInt8ArrayPtr(dataNode), ((sizeof(TbNodeData) + length(dataNode^.key) + 7) shr 3) shl 3); {cast to byte size records}
    inc(curNode);
  until curNode = bnode^.numKeys;

  if (curNode = bnode^.numKeys) then
    begin
      writeln('--- 3 ---');
      nextDataNode := dataNode;
      inc(uInt8ArrayPtr(nextDataNode), nodeSize); {cast to byte size records}

      dataNode^.key[0] := char(keyLength);
      move(key^, dataNode^.key[1], keyLength);
      nextDataNode^.link.iPtr := uPtr(value).iPtr;
      inc(bNode^.size, nodeSize);
    end
  else
    begin
      if (curNode = 0) and
          ((dataNode^.link.iPtr <> NIL) and (strcomp(key, dataNode^.link.iPtr^.name) < 0)) then
        begin
          writeln('---- 4a ----');

          // we need to move the data over by the size of the name in the first link
          // note that we might need to split the node first
           
          keyLength := strlen(dataNode^.link.iPtr^.name);
          nodeSize := ((sizeof(TbNodeData) + keyLength + 7) shr 3) shl 3;
          nextDataNode := dataNode;
          inc(uInt8ArrayPtr(nextDataNode), nodeSize); {cast to byte size records}
          move(dataNode^, nextDataNode^, bnode^.size - (dword(dataNode) - dword(bNode)));

          dataNode^.key[0] := char(keyLength);
          move(dataNode^.link.iPtr^.name, dataNode^.key[1], keyLength);
          dataNode^.link.iPtr := uPtr(value).iPtr;
          nodeSize := ((sizeof(TbNodeData) + keyLength + 7) shr 3) shl 3;
          inc(bNode^.size, nodeSize);
        end
      else
        begin
          writeln('---- 4b ----');
          nextDataNode := dataNode;
          inc(uInt8ArrayPtr(nextDataNode), nodeSize); {cast to byte size records}
          move(dataNode^, nextDataNode^, bnode^.size - (dword(dataNode) - dword(bNode)));
          dataNode^.key[0] := char(keyLength);
          move(key^, dataNode^.key[1], keyLength);
          nextDataNode^.link.iPtr := uPtr(value).iPtr;
          inc(bNode^.size, nodeSize);
        end;
    end;

  inc(bnode^.numKeys);
info(bnode);

  result := TRUE;
end; {bTree.Insert}

destructor bTree.done;
var f:file;
begin
  if (root <> NIL) then
    begin
      assign(f, 'tree.dat');
      rewrite(f, sizeof(TbNode));
      blockwrite(f, root^, 1);
      close(f);
    end;
  if (root<>NIL) then dispose(root);
end; {bTree.done}


{$ifdef foo}
constructor bTree.init;
begin
  root := NIL;
  tag := 0;
  new(header);
  if (header = NIL) then halt;
  fillchar(header^, sizeof(TbTreeHeader), 0);
  with header^ do
    begin
      treeDepth := 1;
      treeWidth := 0;
      treeLeafCount := 0;
      firstDeleted := -1;
      firstNodeOffset := sizeof(TbTreeHeader);
    end; {with}
  if (inode = NIL) then exit;
  root := allocEmptyNode;
  if (root = NIL) then exit;

  with root^ do
    begin
      used := 1;
      parent.bPtr := NIL;
      leaf := true;
      childCount[1] := 1;
      move(inode^.name, keys[0], B_MAX_NAME_LENGTH);
      head[1].iPtr := inode;
      present[1] := TRUE;
    end; {with}

  with inode^ do
    begin
      next.iPtr := NIL;
      prev.iPtr := NIL;
    end;

end; {bTree.init}

function bTree.allocEmptyNode;
var newNode:PbNode;
begin
  new(newNode);
  if (newNode = NIL) then halt;
  fillchar(newNode^, sizeof(TbNode), 0);
  inc(tag);
  with newNode^ do
    begin
      magic1 := B_NODE_MAGIC_1;
      magic2 := B_NODE_MAGIC_2;
      parent.bPtr := NIL;
      tag := self.tag;
    end; {with}
  result := newNode;
end; {bTree.allocEmptyNode}

function bTree.getFirstNode;
var
  tmpNode:PbNode;
  i:integer;
begin
  tmpnode := node;
  result := NIL;
  if (tmpNode = NIL) then exit;

  while not(tmpNode^.leaf) do
    begin
      for i := 0 to tmpNode^.used-1 do
        begin
          if (tmpnode^.head[i].bPtr <> NIL) then
            begin
              tmpNode := tmpNode^.head[i].bPtr;
              break;
            end; {if}
        end; {for i}
    end; {while}

  for i := 0 to tmpNode^.used do
    begin
      if (tmpNode^.head[i].iPtr <> NIL) then
        begin
          result := tmpNode^.head[i].iPtr;
          exit;
        end; {if}
    end; {for i}
end; {bTree.getFirstNode}

function bTree.inodeSearch;
var i:integer;
begin
  result := NIL;
  if ((inode = NIL) or (key = NIL)) then exit;

  i := strcomp(inode^.name, key);
  if (i = 0) then
    begin
      result := inode;
      exit
    end;

  if (i < 0) then
    begin
      inode := inode^.next.iPtr;
      if (inode <> NIL) then
        repeat
          i := strcomp(inode^.name, key);
          inode := inode^.next.iPtr;
        until ((inode = NIL) or (i >= 0));
    end
  else
    begin
      inode := inode^.prev.iPtr;
      if (inode <> NIL) then
        repeat
          i := strcomp(inode^.name, key);
          inode := inode^.prev.iPtr;
        until ((inode = NIL) or (i <= 0));
    end;
  if (i = 0) then result := inode;
end; {bTree.inodeSearch}

procedure bTree.insertNode;
var
  curSlot : integer;
  shift   : integer;
begin
  if ((node = NIL) or (key = NIL)) then exit;

  with node^ do
    if (strcomp(key, keys[used-1]) >= 0) then
      begin
        curSlot := used;
        fillchar(keys[curSlot], B_MAX_NAME_LENGTH, 0);
        move(key, keys[curSlot], B_MAX_NAME_LENGTH);
        head[curSlot+1].bPtr := headPtr;
        present[curSlot+1] := TRUE;
        childCount[used] := 0;
      end
    else
      begin
        for curSlot := 0 to used-1 do
          if (strcomp(key, keys[curSlot]) < 0) then break;

        (*
         * note that there is one more item for everything but keys
         * So, make the shift count +1 and just subtract it from the key shift
         * later
         *)

        shift := used - curSlot + 1;

        move(head[curSlot], head[curSlot+1], sizeof(head[0]) * shift);

        move(present[curSlot], present[curSlot+1], sizeof(present[0]) * shift);

        move(childCount[curSlot], childCount[curSlot+1], sizeof(childCount[0]) * shift);

        move(keys[curSlot], keys[curSlot+1], sizeof(keys[0]) * (shift-1));

        fillchar(keys[curSlot], B_MAX_NAME_LENGTH, 0);
        move(key, keys[curSlot], B_MAX_NAME_LENGTH);
        head[curSlot+1].bPtr := headPtr;
        present[curSlot+1] := TRUE;
      end;

  inc(node^.used);
  if (node^.used = B_MAX_KEYS) then splitNode(node);
end; {bTree.insertNode}

procedure bTree.splitNode;
var
  tmpInode : PubixfsInode;
  newNode  : PbNode;
  shift    : integer;
  splitLoc : integer;
  i        : integer;
begin
  tmpInode := NIL;
  if (oldNode = NIL) then halt;
  if (oldNode^.used <> B_MAX_KEYS) then exit;

  newNode := allocEmptyNode;
  if (newNode = NIL) then halt;

  shift := B_MAX_KEYS shr 1;
  splitLoc := B_MAX_KEYS - shift;
  inc(shift);

  newNode^.used := B_MAX_KEYS shr 1;
  oldNode^.used := B_MAX_KEYS shr 1;

  newNode^.parent.bPtr := oldNode^.parent.bPtr;
  newNode^.leaf := oldNode^.leaf;

  move(oldNode^.keys[splitLoc],
       newNode^.keys[0],
       sizeof(newNode^.keys[0]) * (shift-1));

  fillchar(oldNode^.keys[splitLoc], sizeof(newNode^.keys[0]) * (shift-1), 0);

  move(oldNode^.present[splitLoc],
       newNode^.present[0],
       sizeof(newNode^.present[0]) * shift);

  fillchar(oldNode^.present[splitLoc], sizeof(oldNode^.present[0]) * shift, 0);

  move(oldNode^.head[splitLoc], newNode^.head[0], sizeof(newNode^.head[0]) * shift);

  fillchar(oldNode^.head[splitLoc], sizeof(newNode^.head[0]) * shift, 0);

  move(oldNode^.childCount[splitLoc],
       newNode^.childCount[0],
       sizeof(newNode^.childCount[0]) * shift);

  fillchar(oldNode^.childCount[splitLoc], sizeof(oldNode^.childCount[0]) * shift, 0);

  if (not newNode^.leaf) then
    for i := 0 to newNode^.used do
      newNode^.head[i].bPtr^.parent.bPtr := newNode;

  tmpInode := GetFirstNode(newNode);
  if (tmpInode = NIL) then halt;

  if (oldNode = root) then
    begin
      inc(header^.treeDepth);
      root := allocEmptyNode;
      oldNode^.parent.bPtr := root;
      newNode^.parent.bPtr := root;

      with root^ do
        begin
          move(tmpInode^.name, keys[0], B_MAX_NAME_LENGTH);

          head[0].bPtr := oldNode;
          head[1].bPtr := newNode;
          used := 1;
          leaf := FALSE;
          present[0] := TRUE;
          present[1] := TRUE;
          childCount[0] := 0;
          childCount[1] := 0;
        end
    end
  else
    begin
      insertNode(newNode^.parent.bPtr, tmpInode^.name, newNode);
    end; {else}
end; {bTree.splitNode}

function bTree.treeSearch;
var i:integer;
begin
{  writeln('treeSearch(', hex(longint(bnode)), ', "', key, '");');}
  result := NIL;
  if ((bnode = NIL) or (key = NIL) or (bnode^.used = 0)) then exit;

  if (bnode^.leaf) then
    begin
      result := inodeSearch(GetFirstNode(bnode), key);
      exit;
    end;

  if (strcomp(key, bnode^.keys[0]) < 0) then
    begin
      result := treeSearch(bnode^.head[0].bPtr, key);
      exit;
    end;

  if (strcomp(key, bnode^.keys[bnode^.used-1]) >= 0) then
    begin
      result := treeSearch(bnode^.head[bnode^.used].bPtr, key);
      exit;
    end;

  for i := 1 to bnode^.used-1 do
    begin
      if (strcomp(key, bnode^.keys[i]) < 0) then
        begin
          result := treeSearch(bnode^.head[i].bPtr, key);
          exit;
        end; {if}
    end; {for i}
end;

function bTree.Find;
begin
  result := treeSearch(root, key);
end; {bTree.Find}

procedure bTree.Info;
var i:integer;
begin
  if ((node = NIL) or (root = NIL)) then exit;

  writeln(hex(longint(node)), ' | ', hex(longint(node^.parent.bPtr)));

  for i := 0 to node^.used do
    begin
      if (node^.head[i].iPtr <> NIL) then
        write('[', node^.head[i].iPtr^.name, ']');
      if (i <> node^.used) then
        write(' {', node^.keys[i], '} ');
    end; {for i}
  writeln;
end; {bTree.Info}

function bTree.Insert;
var
  bNode:PbNode;
  tmpInode, iptr:PubixfsInode;
  curSlot, i, shift:integer;
begin
  result := FALSE;
  bNode := root;
  tmpInode := NIL;
  curSlot := 0;

  if (inode = NIL) then exit;

  if (root = NIL) then
    begin
      if (header = NIL) then
        begin
          new(header);
          if (header = NIL) then halt;
          fillchar(header^, sizeof(TbTreeHeader), 0);
          with header^ do
            begin
              treeDepth := 1;
              treeWidth := 0;
              treeLeafCount := 0;
              firstDeleted := -1;
              firstNodeOffset := sizeof(TbTreeHeader);
            end; {with}
        end; {if header = NIL}

      root := allocEmptyNode;
      if (root = NIL) then halt;

      with root^ do
        begin
          used := 1;
          parent.bPtr := NIL;
          leaf := true;
          childCount[1] := 1;
          move(inode^.name, keys[0], B_MAX_NAME_LENGTH);
          head[1].iPtr := inode;
          present[1] := TRUE;
        end; {with}

      with inode^ do
        begin
          next.iPtr := NIL;
          prev.iPtr := NIL;
        end; {with}
      result := TRUE;
      exit;
    end; {if root = NIL}

  tmpInode := Find(inode^.name);
  if (tmpInode <> NIL) then exit;
  inc(header^.treeLeafCount);
  if (bnode^.used = 0) then halt;
writeln('---Inserting ', inode^.name, ' @ ', hex(longint(inode)));

  while ((bNode <> NIL) and not bnode^.leaf) do
    begin
      if (strcomp(inode^.name, bnode^.keys[0]) < 0) then
        bnode := bnode^.head[0].bPtr
      else
        begin
          if (strcomp(inode^.name, bnode^.keys[bnode^.used-1]) >= 0) then
            bnode := bnode^.head[bnode^.used].bPtr
          else
            for i := 1 to bnode^.used-1 do
              if (strcomp(inode^.name, bnode^.keys[i]) < 0) then
                begin
                  bnode := bnode^.head[i].bPtr;
                  break
                end {if}
        end {else}
    end; {while}

  if not(bnode^.leaf) then writeln('WARNING: NOT IN LEAF NODE');

  curSlot := 0;

  if (strcomp(inode^.name, bnode^.keys[0]) < 0) then
    tmpInode := bnode^.head[0].iPtr
  else
    begin
      curSlot := bnode^.used;
      if (strcomp(inode^.name, bnode^.keys[curSlot-1]) >= 0) then
        tmpInode := bnode^.head[curSlot].iPtr
      else
        begin
          for curSlot := 1 to bnode^.used-1 do
            if (strcomp(inode^.name, bnode^.keys[curSlot]) < 0) then
              begin
                tmpInode := bnode^.head[curSlot].iPtr;
                break;
              end; {if}
          tmpInode := bnode^.head[curSlot].iPtr;
        end; {else}
    end; {else}

  if (tmpInode = NIL) then
    begin
      (*
       * This is the first node in this leaf
       *)
      bnode^.head[curSlot].iPtr := inode;
      bnode^.present[curSlot] := TRUE;

      if (curSlot = 0) then
        begin

          if (bnode^.head[1].iPtr <> NIL) then
            begin
              iptr := bnode^.head[1].iPtr;
              inode^.prev.iPtr := iptr^.prev.iPtr;
              inode^.next.iPtr := iptr;
              iptr^.prev.iPtr := inode;

              if (inode^.prev.iPtr <> NIL) then
                inode^.prev.iPtr^.next.iPtr := inode;
            end
          else
            begin
              inode^.next.iPtr := NIL;
              inode^.prev.iPtr := NIL;
            end;
        end
      else
        inc(bnode^.used);

      result := TRUE;
      exit;
    end;

  writeln('---- before split ----');
  Info(bnode);

  with bnode^ do
    if (curSlot <> used) then
      begin
        shift := used - curSlot +1;

        move(head[curSlot], head[curSlot+1], sizeof(head[0]) * shift);

        move(present[curSlot], present[curSlot+1], sizeof(present[0]) * shift);

        move(childCount[curSlot], childCount[curSlot+1], sizeof(childCount[0]) * shift);

        move(keys[curSlot], keys[curSlot+1], sizeof(keys[0]) * (shift-1));
        fillchar(keys[curSlot], B_MAX_NAME_LENGTH, 0);
      end
    else
      begin
        head[curSlot+1] := head[curSlot];
        childCount[curSlot+1] := childCount[curSlot];
        present[curSlot+1] := present[curSlot];
      end;

  with bnode^ do
    if (curSlot = 0) then
      begin
        move(head[1].iPtr^.name, keys[curSlot], B_MAX_NAME_LENGTH);
        head[curSlot].iPtr := inode;
      end
    else
      begin
        move(inode^.name, keys[curSlot], B_MAX_NAME_LENGTH);
        head[curSlot+1].iPtr := inode;
      end;
  bnode^.present[curSlot] := TRUE;
  inc(bnode^.used);
  if (bnode^.used = B_MAX_KEYS) then splitNode(bnode);
  writeln('--- after split ---');
  Info(bnode);
  result := TRUE;
end; {bTree.Insert}

procedure bTree.Print;
var i:integer;
begin
  if (node = NIL) then exit;
  Info(node);
  if (not node^.leaf) then
    for i := 0 to node^.used do
      Print(node^.head[i].bPtr);
end; {bTree.Print}

procedure bTree.PrintWholeTree;
begin
  Print(root);
end; {bTree.PrintWholeTree}

destructor bTree.done;
begin
end;
{$endif}
end.