Browse Source

dom.pp: Added node memory management code (pure addition, no functionality changes this time).

git-svn-id: trunk@13184 -
sergei 16 years ago
parent
commit
d3dd0d6aa0
1 changed files with 150 additions and 0 deletions
  1. 150 0
      packages/fcl-xml/src/dom.pp

+ 150 - 0
packages/fcl-xml/src/dom.pp

@@ -97,7 +97,10 @@ type
   TDOMDocumentType = class;
   TDOMDocumentType = class;
   TDOMEntityReference = class;
   TDOMEntityReference = class;
   TDOMProcessingInstruction = class;
   TDOMProcessingInstruction = class;
+
   TDOMAttrDef = class;
   TDOMAttrDef = class;
+  PNodePool = ^TNodePool;
+  TNodePool = class;
 
 
 
 
 // -------------------------------------------------------
 // -------------------------------------------------------
@@ -188,6 +191,7 @@ type
 
 
   TDOMNode = class
   TDOMNode = class
   protected
   protected
+    FPool: TObject;
     FFlags: TNodeFlags;
     FFlags: TNodeFlags;
     FParentNode: TDOMNode;
     FParentNode: TDOMNode;
     FPreviousSibling, FNextSibling: TDOMNode;
     FPreviousSibling, FNextSibling: TDOMNode;
@@ -213,6 +217,7 @@ type
   public
   public
     constructor Create(AOwner: TDOMDocument);
     constructor Create(AOwner: TDOMDocument);
     destructor Destroy; override;
     destructor Destroy; override;
+    procedure FreeInstance; override;
 
 
     function GetChildNodes: TDOMNodeList;
     function GetChildNodes: TDOMNodeList;
 
 
@@ -252,6 +257,7 @@ type
     function CompareName(const name: DOMString): Integer; virtual;
     function CompareName(const name: DOMString): Integer; virtual;
   end;
   end;
 
 
+  TDOMNodeClass = class of TDOMNode;
 
 
   { The following class is an implementation specific extension, it is just an
   { The following class is an implementation specific extension, it is just an
     extended implementation of TDOMNode, the generic DOM::Node interface
     extended implementation of TDOMNode, the generic DOM::Node interface
@@ -414,6 +420,8 @@ type
     FNames: THashTable;
     FNames: THashTable;
     FEmptyNode: TDOMElement;
     FEmptyNode: TDOMElement;
     FNodeLists: THashTable;
     FNodeLists: THashTable;
+    FMaxPoolSize: Integer;
+    FPools: PNodePool;
     function GetDocumentElement: TDOMElement;
     function GetDocumentElement: TDOMElement;
     function GetDocType: TDOMDocumentType;
     function GetDocType: TDOMDocumentType;
     function GetNodeType: Integer; override;
     function GetNodeType: Integer; override;
@@ -425,6 +433,7 @@ type
     function GetChildNodeList(aNode: TDOMNode): TDOMNodeList;
     function GetChildNodeList(aNode: TDOMNode): TDOMNodeList;
     function GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString; UseNS: Boolean): TDOMNodeList;
     function GetElementList(aNode: TDOMNode; const nsURI, aLocalName: DOMString; UseNS: Boolean): TDOMNodeList;
     procedure NodeListDestroyed(aList: TDOMNodeList);
     procedure NodeListDestroyed(aList: TDOMNodeList);
+    function Alloc(AClass: TDOMNodeClass): TDOMNode;
   public
   public
     function IndexOfNS(const nsURI: DOMString; AddIfAbsent: Boolean = False): Integer;
     function IndexOfNS(const nsURI: DOMString; AddIfAbsent: Boolean = False): Integer;
     property DocType: TDOMDocumentType read GetDocType;
     property DocType: TDOMDocumentType read GetDocType;
@@ -735,6 +744,31 @@ type
     property Tag: Cardinal read FTag write FTag;
     property Tag: Cardinal read FTag write FTag;
   end;
   end;
 
 
+// TNodePool - custom memory management for TDOMNode's
+// One pool manages objects of the same InstanceSize (may be of various classes)
+
+  PExtent = ^TExtent;
+  TExtent = record
+    Next: PExtent;
+    // following: array of TDOMNode instances
+  end;
+
+  TNodePool = class(TObject)
+  private
+    FCurrExtent: PExtent;
+    FCurrExtentSize: Integer;
+    FElementSize: Integer;
+    FCurrBlock: TDOMNode;
+    FFirstFree: TDOMNode;
+    procedure AddExtent(AElemCount: Integer);
+  public
+    constructor Create(AElementSize: Integer; AElementCount: Integer = 32);
+    destructor Destroy; override;
+    function AllocNode(AClass: TDOMNodeClass): TDOMNode;
+    procedure FreeNode(ANode: TDOMNode);
+  end;
+
+
 // URIs of predefined namespaces
 // URIs of predefined namespaces
 const
 const
   stduri_xml: DOMString = 'http://www.w3.org/XML/1998/namespace';
   stduri_xml: DOMString = 'http://www.w3.org/XML/1998/namespace';
@@ -830,6 +864,17 @@ begin
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
+procedure TDOMNode.FreeInstance;
+begin
+  if Assigned(FPool) then
+  begin
+    CleanupInstance;
+    TNodePool(FPool).FreeNode(Self);
+  end
+  else
+    inherited FreeInstance;
+end;
+
 function TDOMNode.GetNodeValue: DOMString;
 function TDOMNode.GetNodeValue: DOMString;
 begin
 begin
   Result := '';
   Result := '';
@@ -1777,6 +1822,8 @@ constructor TDOMDocument.Create;
 begin
 begin
   inherited Create(nil);
   inherited Create(nil);
   FOwnerDocument := Self;
   FOwnerDocument := Self;
+  FMaxPoolSize := TDOMAttr.InstanceSize + sizeof(Pointer);
+  FPools := AllocMem(FMaxPoolSize);
   FNames := THashTable.Create(256, True);
   FNames := THashTable.Create(256, True);
   SetLength(FNamespaces, 3);
   SetLength(FNamespaces, 3);
   // Namespace #0 should always be an empty string
   // Namespace #0 should always be an empty string
@@ -1787,16 +1834,42 @@ begin
 end;
 end;
 
 
 destructor TDOMDocument.Destroy;
 destructor TDOMDocument.Destroy;
+var
+  i: Integer;
 begin
 begin
   Include(FFlags, nfDestroying);
   Include(FFlags, nfDestroying);
   FreeAndNil(FIDList);   // set to nil before starting destroying children
   FreeAndNil(FIDList);   // set to nil before starting destroying children
   FNodeLists.Free;
   FNodeLists.Free;
   FEmptyNode.Free;
   FEmptyNode.Free;
   inherited Destroy;
   inherited Destroy;
+  for i := 0 to (FMaxPoolSize div sizeof(TNodePool))-1 do
+    FPools[i].Free;
+  FreeMem(FPools);
   FNames.Free;           // free the nametable after inherited has destroyed the children
   FNames.Free;           // free the nametable after inherited has destroyed the children
                          // (because children reference the nametable)
                          // (because children reference the nametable)
 end;
 end;
 
 
+function TDOMDocument.Alloc(AClass: TDOMNodeClass): TDOMNode;
+var
+  pp: TNodePool;
+  size: Integer;
+begin
+  size := (AClass.InstanceSize + sizeof(Pointer)-1) and not (sizeof(Pointer)-1);
+  if size > FMaxPoolSize then
+  begin
+    Result := TDOMNode(AClass.NewInstance);
+    Exit;
+  end;
+
+  pp := FPools[size div sizeof(TNodePool)];
+  if pp = nil then
+  begin
+    pp := TNodePool.Create(size);
+    FPools[size div sizeof(TNodePool)] := pp;
+  end;
+  Result := pp.AllocNode(AClass);
+end;
+
 function TDOMDocument.AddID(Attr: TDOMAttr): Boolean;
 function TDOMDocument.AddID(Attr: TDOMAttr): Boolean;
 var
 var
   ID: DOMString;
   ID: DOMString;
@@ -2711,4 +2784,81 @@ begin
   Result := False;
   Result := False;
 end;
 end;
 
 
+{ TNodePool }
+
+constructor TNodePool.Create(AElementSize: Integer; AElementCount: Integer);
+begin
+  FElementSize := AElementSize;
+  AddExtent(AElementCount);
+end;
+
+destructor TNodePool.Destroy;
+var
+  ext, next: PExtent;
+  ptr, ptr_end: Pointer;
+  sz: Integer;
+begin
+  ext := FCurrExtent;
+  ptr := Pointer(FCurrBlock) + FElementSize;
+  sz := FCurrExtentSize;
+  while Assigned(ext) do
+  begin
+    // call destructors for everyone still there
+    ptr_end := Pointer(ext) + sizeof(TExtent) + (sz - 1) * FElementSize;
+    while ptr <= ptr_end do
+    begin
+      if TDOMNode(ptr).FPool = Self then
+        TObject(ptr).Destroy;
+      Inc(ptr, FElementSize);
+    end;
+    // dispose the extent and pass to the next one
+    next := ext^.Next;
+    FreeMem(ext);
+    ext := next;
+    sz := sz div 2;
+    ptr := Pointer(ext) + sizeof(TExtent);
+  end;
+  inherited Destroy;
+end;
+
+procedure TNodePool.AddExtent(AElemCount: Integer);
+var
+  ext: PExtent;
+begin
+  Assert((FCurrExtent = nil) or
+    (Pointer(FCurrBlock) = Pointer(FCurrExtent) + sizeof(TExtent)));
+  Assert(AElemCount > 0);
+
+  GetMem(ext, sizeof(TExtent) + AElemCount * FElementSize);
+  ext^.Next := FCurrExtent;
+  // point to the beginning of the last block of extent
+  FCurrBlock := TDOMNode(Pointer(ext) + sizeof(TExtent) + (AElemCount - 1) * FElementSize);
+  FCurrExtent := ext;
+  FCurrExtentSize := AElemCount;
+end;
+
+function TNodePool.AllocNode(AClass: TDOMNodeClass): TDOMNode;
+begin
+  if Assigned(FFirstFree) then
+  begin
+    Result := FFirstFree;       // remove from free list
+    FFirstFree := TDOMNode(Result.FPool);
+  end
+  else
+  begin
+    if Pointer(FCurrBlock) = Pointer(FCurrExtent) + sizeof(TExtent) then
+      AddExtent(FCurrExtentSize * 2);
+    Result := FCurrBlock;
+    Dec(PChar(FCurrBlock), FElementSize);
+  end;
+  AClass.InitInstance(Result);
+  Result.FPool := Self;        // mark as used
+end;
+
+procedure TNodePool.FreeNode(ANode: TDOMNode);
+begin
+  ANode.FPool := FFirstFree;
+  FFirstFree := ANode;
+end;
+
 end.
 end.