Browse Source

Improvements on mem usage of TAbstractMemBTree

Will save info in 2 zones allowing to use less mem space on leaf nodes
Pascal Coin 4 years ago
parent
commit
0465f628e8

+ 13 - 1
src/libraries/abstractmem/UAbstractBTree.pas

@@ -141,6 +141,8 @@ type
     procedure CheckConsistency; virtual;
     property Height : Integer read GetHeight;
     property CircularProtection : Boolean read FCircularProtection write FCircularProtection;
+    procedure Lock;
+    procedure Unlock;
   End;
 
   TMemoryBTree<TData> = Class( TAbstractBTree<Integer,TData> )
@@ -404,7 +406,7 @@ begin
   FAllowDuplicates := AAllowDuplicates;
   FOrder := AOrder;
   if FOrder<3 then FOrder := 3 // Minimum order for a BTree is 3. Order = Max childs
-  else if FOrder>32 then FOrder := 32; // Maximum order will be established to 32
+  else if FOrder>255 then FOrder := 255; // Maximum order will be established to 255
   FCount := -1;                 // -1 Means there is no control
   {$IFDEF ABSTRACTMEM_CIRCULAR_SEARCH_PROTECTION}
   FCircularProtection := True;
@@ -1136,6 +1138,11 @@ begin
   end;
 end;
 
+procedure TAbstractBTree<TIdentify, TData>.Lock;
+begin
+  FAbstractBTreeLock.Acquire;
+end;
+
 function TAbstractBTree<TIdentify, TData>.MaxChildrenPerNode: Integer;
 begin
   Result := FOrder;
@@ -1288,6 +1295,11 @@ begin
   Result := '['+Result+']';
 end;
 
+procedure TAbstractBTree<TIdentify, TData>.Unlock;
+begin
+  FAbstractBTreeLock.Release;
+end;
+
 { TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode }
 
 function TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode.Count: Integer;

+ 117 - 90
src/libraries/abstractmem/UAbstractMemBTree.pas

@@ -57,8 +57,9 @@ type
     FInitialZone : TAMZone;
     FrootPosition : TAbstractMemPosition;
     procedure SaveHeader;
-    function GetNodeSize : Integer;
     Procedure CheckInitialized;
+    procedure LoadNodeHeader(const APosition : TAbstractMemPosition; var ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; var AChildsCount : Integer; var AChildsPosition : TAbstractMemPosition);
+    procedure SaveNodeHeader(const ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; const AChildsPosition : TAbstractMemPosition);
   protected
     FAbstractMem : TAbstractMem;
     function GetRoot: TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; override;
@@ -120,14 +121,14 @@ begin
   if (FInitialZone.position=0) then raise EAbstractMemBTree.Create(Format('%s initial position not initialized',[ClassName]));
 end;
 
-constructor TAbstractMemBTree.Create(AAbstractMem : TAbstractMem; const AInitialZone: TAMZone; AAllowDuplicates: Boolean;  AOrder: Integer);
+constructor TAbstractMemBTree.Create(AAbstractMem : TAbstractMem; const AInitialZone: TAMZone; AAllowDuplicates: Boolean; AOrder: Integer);
 var LBuff : TBytes;
  i : Integer;
  LOrder : Integer;
-
 begin
   FAbstractMem := AAbstractMem;
   FrootPosition := 0;
+
   inherited Create(TComparison_Integer,TComparison_Integer,AAllowDuplicates,AOrder);
   FCount := 0;
   //
@@ -138,6 +139,8 @@ begin
       Exit;
     end;
     raise EAbstractMemBTree.Create('Cannot capture zone info for initialize');
+  end else begin
+    if FInitialZone.position=0 then Exit;
   end;
   if (FInitialZone.size<MinAbstractMemInitialPositionSize) then begin
     raise EAbstractMemBTree.Create(Format('Invalid size %d for initialize',[FInitialZone.size]));
@@ -186,76 +189,47 @@ begin
 end;
 
 procedure TAbstractMemBTree.DisposeNode(var ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode);
+var LOld : TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+  LChildsCount : Integer;
+  LChildsPosition : TAbstractMemPosition;
 begin
+  LoadNodeHeader(ANode.identify,LOld,LChildsCount,LChildsPosition);
   FAbstractMem.Dispose( ANode.identify );
   ClearNode(ANode);
+  Assert(((LChildsCount=0) and (LChildsPosition=0))
+     or ((LChildsCount<>0) and (LChildsPosition<>0)),Format('Invalid Childs count %d and position %d',[LChildsCount,LChildsPosition]));
+  if LChildsCount>0 then begin
+    FAbstractMem.Dispose( LChildsPosition );
+  end;
 end;
 
 function TAbstractMemBTree.GetNode(AIdentify: TAbstractMemPosition): TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
 var LBuff : TBytes;
-  LStream : TStream;
-  LByte : Byte;
-  i, LItemsCount, LChildsCount : Integer;
+  i, LChildsCount : Integer;
+  LChildsPosition : TAbstractMemPosition;
 begin
-  // For each node:
-  // Size = (4+2+2)+(4*MaxItemsPerNode)+(4*MaxChildrenPerNode) = GetNodeSize
-  // 4 Bytes [0..3] : Parent
-  // 1 Byte  [4] : Used items (0..32)
-  // 1 Byte  [5] : Used childs (0 (leaf) or Used Items+1)
-  // 2 Bytes [6..7] : 0 (unusued)
-  // For each item:
-  //   4 Bytes : data (AbstractMemPosition or Data using 4 bytes)
-  // For each children:
-  //   4 Bytes : Children AbstractMem position
-  ClearNode(Result);
-  Result.identify := AIdentify;
-  SetLength(LBuff, GetNodeSize );
-  FAbstractMem.Read(AIdentify,LBuff[0],Length(LBuff));
-  LStream := TMemoryStream.Create;
-  try
-    LStream.Write(LBuff[0],Length(LBuff));
-    LStream.Position := 0;
-    //
-    LStream.Read(Result.parent,4); // Read parent position
-    LStream.Read(LByte,1);
-    LItemsCount := LByte;
-    LStream.Read(LByte,1);
-    LChildsCount := LByte;
-    LStream.Read(LByte,1);
-    Assert(LByte=0);
-    LStream.Read(LByte,1);
-    Assert(LByte=0);
-    if ((LItemsCount=0) and (Result.parent=0) and (LChildsCount=0)) then begin
-      // root without data
-    end else begin
-      if (Result.parent=0) then begin
-        if ((LItemsCount<1) or (LItemsCount>MaxItemsPerNode)) then
-          raise EAbstractMemBTree.Create(Format('Root Node items %d not in range [%d..%d]',[LItemsCount,MinItemsPerNode,MaxItemsPerNode]));
-      end else begin
-        if ((LItemsCount<MinItemsPerNode) or (LItemsCount>MaxItemsPerNode)) then
-          raise EAbstractMemBTree.Create(Format('Node items %d not in range [%d..%d]',[LItemsCount,MinItemsPerNode,MaxItemsPerNode]));
-      end;
-      if ((LChildsCount<>0) and (LChildsCount<>(LItemsCount+1))) then
-        raise EAbstractMemBTree.Create(Format('Node childrens %d not %d+1 in range [%d..%d]',[LChildsCount,LItemsCount,MinChildrenPerNode,MaxChildrenPerNode]));
-    end;
-    // Read items
-    SetLength(Result.data,LItemsCount);
+  LoadNodeHeader(AIdentify,Result,LChildsCount,LChildsPosition);
+  if LChildsCount>0 then begin
     SetLength(Result.childs,LChildsCount);
-    for i := 0 to LItemsCount-1 do begin
-      LStream.Read(Result.data[i],4);
-    end;
-    // Read childrens
+    SetLength(LBuff,(LChildsCount*4));
+    FAbstractMem.Read(LChildsPosition,LBuff[0],Length(LBuff));
     for i := 0 to LChildsCount-1 do begin
-      LStream.Read(Result.childs[i],4);
+      Move(LBuff[i*4],Result.childs[i],4);
     end;
-  finally
-    LStream.Free;
   end;
-end;
-
-function TAbstractMemBTree.GetNodeSize: Integer;
-begin
-  Result := 8 + (4 * MaxItemsPerNode) + (4 * MaxChildrenPerNode);
+  if ((Result.Count=0) and (Result.parent=0) and (LChildsCount=0)) then begin
+    // root without data
+  end else begin
+    if (Result.parent=0) then begin
+      if ((Result.Count<1) or (Result.Count>MaxItemsPerNode)) then
+        raise EAbstractMemBTree.Create(Format('Root Node items %d not in range [%d..%d]',[Result.Count,MinItemsPerNode,MaxItemsPerNode]));
+    end else begin
+      if ((Result.Count<MinItemsPerNode) or (Result.Count>MaxItemsPerNode)) then
+        raise EAbstractMemBTree.Create(Format('Node items %d not in range [%d..%d]',[Result.Count,MinItemsPerNode,MaxItemsPerNode]));
+    end;
+    if ((LChildsCount<>0) and (LChildsCount<>(Result.Count+1))) then
+      raise EAbstractMemBTree.Create(Format('Node childrens %d not %d+1 in range [%d..%d]',[LChildsCount,Result.Count,MinChildrenPerNode,MaxChildrenPerNode]));
+  end;
 end;
 
 function TAbstractMemBTree.GetRoot: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
@@ -270,6 +244,43 @@ begin
   Result := AIdentify=0;
 end;
 
+procedure TAbstractMemBTree.LoadNodeHeader(
+  const APosition : TAbstractMemPosition; var ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; var AChildsCount : Integer; var AChildsPosition : TAbstractMemPosition);
+var LBuff : TBytes;
+  i, LItemsCount : Integer;
+begin
+  // Node is stored in zone 2 positions:
+  //
+  // Zone 1: Header
+  //   Size = (4+2+2+4) + (4*MaxItemsPerNode)
+  // 4 Bytes [0..3] : Parent
+  // 1 Byte  [4]    : Used items (0..255)
+  // 1 Byte  [5]    : Used childs (0 (leaf) or Used Items+1)
+  // 2 Bytes [6..7] : 0 (unusued)
+  // 4 Bytes [8..11]: Zone 2 position ( If is a leaf must be 0 )
+  // For each item:
+  //   4 Bytes : data (AbstractMemPosition or Data using 4 bytes)
+  //
+  // Zone 2: OPTIONAL Only if NOT a leaf
+  // For each children:
+  //   4 Bytes : Children AbstractMem position
+  //
+  SetLength(LBuff, 8 + (4 * MaxItemsPerNode) + 4 );
+  FAbstractMem.Read(APosition,LBuff[0],Length(LBuff));
+  ClearNode(ANode);
+  LItemsCount := 0;
+  AChildsCount := 0;
+  ANode.identify := APosition;
+  Move(LBuff[0],ANode.parent,4);
+  Move(LBuff[4],LItemsCount,1);
+  Move(LBuff[5],AChildsCount,1);
+  Move(LBuff[8],AChildsPosition,4);
+  SetLength(ANode.data,LItemsCount);
+  for i := 0 to LItemsCount-1 do begin
+    Move(LBuff[12 + (i*4)], ANode.data[i], 4);
+  end;
+end;
+
 class function TAbstractMemBTree.MinAbstractMemInitialPositionSize: Integer;
 begin
   Result := CT_MIN_INITIAL_POSITION_SIZE;
@@ -279,7 +290,8 @@ function TAbstractMemBTree.NewNode: TAbstractBTree<TAbstractMemPosition, TAbstra
 begin
   CheckInitialized;
   ClearNode(Result);
-  Result.identify := FAbstractMem.New(GetNodeSize).position;
+  Result.identify := FAbstractMem.New( 8 + (4 * MaxItemsPerNode) + 4 ).position;
+  SaveNodeHeader(Result,0);
 end;
 
 procedure TAbstractMemBTree.SaveHeader;
@@ -301,41 +313,56 @@ end;
 
 procedure TAbstractMemBTree.SaveNode(var ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode);
 var LBuff : TBytes;
-  LStream : TStream;
-  LByte : Byte;
-  i, LItemsCount, LChildsCount : Integer;
+  i, LChildsCount : Integer;
+  LOld : TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+  LChildsPosition : TAbstractMemPosition;
+  LZone : TAMZone;
 begin
   CheckInitialized;
   if ((ANode.Count)>MaxItemsPerNode) or (Length(ANode.childs)>MaxChildrenPerNode) then begin
-    // Protection agains saving temporal Node info with extra datas or childs
+    // Protection against saving temporal Node info with extra datas or childs
     Exit;
   end;
+  LoadNodeHeader(ANode.identify,LOld,LChildsCount,LChildsPosition);
+  //
+  if (LChildsCount>0) And (ANode.IsLeaf) then begin
+    // Node wasn't a leaf previously
+    Assert(LChildsPosition<>0,'Old childs position<>0');
+    FAbstractMem.Dispose(LChildsPosition);
+  end else if (LChildsCount=0) And (Not ANode.IsLeaf) then begin
+    // Node was a leaf previously, now not
+    LZone := FAbstractMem.New( MaxChildrenPerNode * 4 );
+    LChildsPosition := LZone.position;
+  end;
+  LChildsCount := Length(ANode.childs);
+  //
+  SaveNodeHeader(ANode,LChildsPosition);
+  //
+  SetLength(LBuff, MaxChildrenPerNode * 4 );
+  FillChar(LBuff[0],Length(LBuff),0);
+  for i := 0 to LChildsCount-1 do begin
+    Move(ANode.childs[i],LBuff[i*4],4);
+  end;
+  FAbstractMem.Write(LChildsPosition,LBuff[0],LChildsCount*4);
+end;
 
-  // See GetNode info
-  LStream := TMemoryStream.Create;
-  try
-    LStream.Write(ANode.parent,4);
-    LItemsCount := Length(ANode.data);
-    LStream.Write(LItemsCount,1);
-    LChildsCount := Length(ANode.childs);
-    LStream.Write(LChildsCount,1);
-    LByte := 0;
-    LStream.Write(LByte,1);
-    LStream.Write(LByte,1);
-    for i := 0 to LItemsCount-1 do begin
-      LStream.Write(ANode.data[i],4)
-    end;
-    // Read childrens
-    for i := 0 to LChildsCount-1 do begin
-      LStream.Write(ANode.childs[i],4);
-    end;
-    SetLength(LBuff,LStream.Size);
-    LStream.Position := 0;
-    LStream.Read(LBuff[0],LStream.Size);
-    FAbstractMem.Write(ANode.identify,LBuff[0],Length(LBuff));
-  finally
-    LStream.Free;
+procedure TAbstractMemBTree.SaveNodeHeader(
+  const ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode; const AChildsPosition : TAbstractMemPosition);
+var LBuff : TBytes;
+  i, LItemsCount, LChildsCount : Integer;
+begin
+  SetLength(LBuff, 8 + (4 * MaxItemsPerNode) + 4 );
+  FillChar(LBuff[0],Length(LBuff),0);
+  Move(ANode.parent,LBuff[0],4);
+  LItemsCount := ANode.Count;
+  Move(LItemsCount,LBuff[4],1);
+  LChildsCount := Length(ANode.childs);
+  Move(LChildsCount,LBuff[5],1);
+  Move(AChildsPosition,LBuff[8],4);
+  for i := 0 to LItemsCount-1 do begin
+    Move(ANode.data[i], LBuff[12 + (i*4)], 4);
   end;
+  FAbstractMem.Write(ANode.identify,LBuff[0],Length(LBuff));
 end;
 
 procedure TAbstractMemBTree.SetCount(const ANewCount: Integer);