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.