Browse Source

XPath: reducing memory load:
* Store predicates of TStep and TXPathFilterNode in dynarrays instead of TList, this way we allocate
only as much memory as needed, and allocate anything only when predicates are actually present
(that's minority of all cases).
* Eliminated intermediate TList in filtering step results.
* Remaining TList's replaced by TFPList's.
* Fixed ordering of nodes on preceding-sibling axis.

git-svn-id: trunk@13230 -

sergei 16 years ago
parent
commit
e66e35ac2c
1 changed files with 76 additions and 64 deletions
  1. 76 64
      packages/fcl-xml/src/xpath.pp

+ 76 - 64
packages/fcl-xml/src/xpath.pp

@@ -107,6 +107,7 @@ type
       AEnvironment: TXPathEnvironment): TXPathVariable; virtual; abstract;
       AEnvironment: TXPathEnvironment): TXPathVariable; virtual; abstract;
   end;
   end;
 
 
+  TXPathNodeArray = array of TXPathExprNode;
 
 
   TXPathConstantNode = class(TXPathExprNode)
   TXPathConstantNode = class(TXPathExprNode)
   private
   private
@@ -132,7 +133,7 @@ type
   TXPathFunctionNode = class(TXPathExprNode)
   TXPathFunctionNode = class(TXPathExprNode)
   private
   private
     FName: DOMString;
     FName: DOMString;
-    FArgs: TList;
+    FArgs: TFPList;
   public
   public
     constructor Create(const AName: DOMString);
     constructor Create(const AName: DOMString);
     destructor Destroy; override;
     destructor Destroy; override;
@@ -220,7 +221,7 @@ type
   TXPathFilterNode = class(TXPathExprNode)
   TXPathFilterNode = class(TXPathExprNode)
   private
   private
     FExpr: TXPathExprNode;
     FExpr: TXPathExprNode;
-    FPredicates: TList;
+    FPredicates: TXPathNodeArray;
   public
   public
     constructor Create(AExpr: TXPathExprNode);
     constructor Create(AExpr: TXPathExprNode);
     destructor Destroy; override;
     destructor Destroy; override;
@@ -245,7 +246,7 @@ type
     Axis: TAxis;
     Axis: TAxis;
     NodeTestType: TNodeTestType;
     NodeTestType: TNodeTestType;
     NodeTestString: DOMString;
     NodeTestString: DOMString;
-    Predicates: TList;
+    Predicates: TXPathNodeArray;
     constructor Create(aAxis: TAxis; aTest: TNodeTestType);
     constructor Create(aAxis: TAxis; aTest: TNodeTestType);
     destructor Destroy; override;
     destructor Destroy; override;
   end;
   end;
@@ -263,7 +264,7 @@ type
   end;
   end;
 
 
 
 
-  TNodeSet = TList;
+  TNodeSet = TFPList;
 
 
 { Exceptions }
 { Exceptions }
 
 
@@ -351,6 +352,7 @@ type
     FTokenStart: DOMPChar;
     FTokenStart: DOMPChar;
     FTokenLength: Integer;
     FTokenLength: Integer;
     procedure Error(const Msg: String);
     procedure Error(const Msg: String);
+    procedure ParsePredicates(var Dest: TXPathNodeArray);
     procedure ParseStep(Dest: TStep);          // [4]
     procedure ParseStep(Dest: TStep);          // [4]
     function ParsePrimaryExpr: TXPathExprNode; // [15]
     function ParsePrimaryExpr: TXPathExprNode; // [15]
     function ParseUnionExpr: TXPathExprNode;   // [18]
     function ParseUnionExpr: TXPathExprNode;   // [18]
@@ -389,15 +391,15 @@ type
   the variables and functions, which are part of the context in the official
   the variables and functions, which are part of the context in the official
   standard). }
   standard). }
 
 
-  TXPathVarList = TList;
+  TXPathVarList = TFPList;
 
 
   TXPathFunction = function(Context: TXPathContext; Args: TXPathVarList):
   TXPathFunction = function(Context: TXPathContext; Args: TXPathVarList):
     TXPathVariable of object;
     TXPathVariable of object;
 
 
   TXPathEnvironment = class
   TXPathEnvironment = class
   private
   private
-    FFunctions: TList;
-    FVariables: TList;
+    FFunctions: TFPList;
+    FVariables: TFPList;
     function GetFunctionCount: Integer;
     function GetFunctionCount: Integer;
     function GetVariableCount: Integer;
     function GetVariableCount: Integer;
     function GetFunction(Index: Integer): TXPathFunction;
     function GetFunction(Index: Integer): TXPathFunction;
@@ -583,6 +585,15 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure AddNodes(var Dst: TXPathNodeArray; const Src: array of TXPathExprNode);
+var
+  L: Integer;
+begin
+  L := Length(Dst);
+  SetLength(Dst, L + High(Src)+1);
+  Move(Src[0], Dst[L], (High(Src)+1)*sizeof(TObject));
+end;
+
 { XPath parse tree classes }
 { XPath parse tree classes }
 
 
 function TXPathExprNode.EvalPredicate(AContext: TXPathContext;
 function TXPathExprNode.EvalPredicate(AContext: TXPathContext;
@@ -640,7 +651,7 @@ constructor TXPathFunctionNode.Create(const AName: DOMString);
 begin
 begin
   inherited Create;
   inherited Create;
   FName := AName;
   FName := AName;
-  FArgs := TList.Create;
+  FArgs := TFPList.Create;
 end;
 end;
 
 
 destructor TXPathFunctionNode.Destroy;
 destructor TXPathFunctionNode.Destroy;
@@ -984,16 +995,14 @@ constructor TXPathFilterNode.Create(AExpr: TXPathExprNode);
 begin
 begin
   inherited Create;
   inherited Create;
   FExpr := AExpr;
   FExpr := AExpr;
-  FPredicates := TList.Create;
 end;
 end;
 
 
 destructor TXPathFilterNode.Destroy;
 destructor TXPathFilterNode.Destroy;
 var
 var
   i: Integer;
   i: Integer;
 begin
 begin
-  for i := 0 to FPredicates.Count - 1 do
-    TXPathExprNode(FPredicates[i]).Free;
-  FPredicates.Free;
+  for i := 0 to High(FPredicates) do
+    FPredicates[i].Free;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
@@ -1020,9 +1029,9 @@ begin
         NewContext.ContextNode := CurContextNode;
         NewContext.ContextNode := CurContextNode;
         Inc(NewContext.ContextPosition);
         Inc(NewContext.ContextPosition);
         DoAdd := True;
         DoAdd := True;
-        for j := 0 to FPredicates.Count - 1 do
+        for j := 0 to High(FPredicates) do
         begin
         begin
-          DoAdd := TXPathExprNode(FPredicates[j]).EvalPredicate(NewContext,
+          DoAdd := FPredicates[j].EvalPredicate(NewContext,
             AEnvironment);
             AEnvironment);
           if not DoAdd then
           if not DoAdd then
             Break;
             Break;
@@ -1047,16 +1056,14 @@ begin
   inherited Create;
   inherited Create;
   Axis := aAxis;
   Axis := aAxis;
   NodeTestType := aTest;
   NodeTestType := aTest;
-  Predicates := TList.Create;
 end;
 end;
 
 
 destructor TStep.Destroy;
 destructor TStep.Destroy;
 var
 var
   i: Integer;
   i: Integer;
 begin
 begin
-  for i := 0 to Predicates.Count - 1 do
-    TXPathExprNode(Predicates[i]).Free;
-  Predicates.Free;
+  for i := 0 to High(Predicates) do
+    Predicates[i].Free;
   inherited destroy;
   inherited destroy;
 end;
 end;
 
 
@@ -1074,7 +1081,7 @@ var
 
 
   procedure EvaluateStep(AStep: TStep; AContext: TXPathContext);
   procedure EvaluateStep(AStep: TStep; AContext: TXPathContext);
   var
   var
-    StepNodes: TList;
+    StepNodes: TFPList;
 
 
     procedure DoNodeTest(Node: TDOMNode);
     procedure DoNodeTest(Node: TDOMNode);
     begin
     begin
@@ -1120,12 +1127,11 @@ var
     i, j: Integer;
     i, j: Integer;
 
 
     NewContext: TXPathContext;
     NewContext: TXPathContext;
-    NewStepNodes: TNodeSet;
     Predicate: TXPathExprNode;
     Predicate: TXPathExprNode;
-    TempList: TList;
+    TempList: TFPList;
 
 
   begin
   begin
-    StepNodes := TList.Create;
+    StepNodes := TFPList.Create;
     // !!!: Protect this with an try/finally block
     // !!!: Protect this with an try/finally block
     case AStep.Axis of
     case AStep.Axis of
       axisAncestor:
       axisAncestor:
@@ -1197,7 +1203,7 @@ var
           DoNodeTest(AContext.ContextNode);
           DoNodeTest(AContext.ContextNode);
       axisPreceding:
       axisPreceding:
         begin
         begin
-          TempList := TList.Create;
+          TempList := TFPList.Create;
           try
           try
             Node := AContext.ContextNode;
             Node := AContext.ContextNode;
             // build list of ancestors
             // build list of ancestors
@@ -1224,11 +1230,14 @@ var
         end;
         end;
       axisPrecedingSibling:
       axisPrecedingSibling:
         begin
         begin
-          Node := AContext.ContextNode.PreviousSibling;
-          while Assigned(Node) do
+          if Assigned(AContext.ContextNode.ParentNode) then
           begin
           begin
-            DoNodeTest(Node);
-            Node := Node.PreviousSibling;
+            Node := AContext.ContextNode.ParentNode.FirstChild;
+            while Assigned(Node) and (Node <> AContext.ContextNode) do
+            begin
+              DoNodeTest(Node);
+              Node := Node.NextSibling;
+            end;
           end;
           end;
         end;
         end;
       axisSelf:
       axisSelf:
@@ -1236,19 +1245,17 @@ var
     end;
     end;
 
 
     { Filter the nodes of this step using the predicates: The current
     { Filter the nodes of this step using the predicates: The current
-      node set (StepNodes) is filtered, all passed nodes will be added
-      to NewStepNodes. After one filter has been applied, NewStepNodes
-      gets copied to StepNodes, and the next filter will be processed.
+      node set (StepNodes) is filtered, nodes not passing the filter
+      are replaced by nil. After one filter has been applied, StepNodes
+      is packed, and the next filter will be processed.
       The final result will then be passed to the next step, or added
       The final result will then be passed to the next step, or added
       to the result of the LocationPath if this is the last step. }
       to the result of the LocationPath if this is the last step. }
 
 
-    for i := 0 to AStep.Predicates.Count - 1 do
+    for i := 0 to High(AStep.Predicates) do
     begin
     begin
       NewContext := TXPathContext.Create(nil, 0, StepNodes.Count);
       NewContext := TXPathContext.Create(nil, 0, StepNodes.Count);
-      NewStepNodes := nil;
       try
       try
-        NewStepNodes := TNodeSet.Create;
-        Predicate := TXPathExprNode(AStep.Predicates[i]);
+        Predicate := AStep.Predicates[i];
         for j := 0 to StepNodes.Count - 1 do
         for j := 0 to StepNodes.Count - 1 do
         begin
         begin
           // ContextPosition must honor the axis direction
           // ContextPosition must honor the axis direction
@@ -1260,13 +1267,12 @@ var
 
 
           Node := TDOMNode(StepNodes[j]);
           Node := TDOMNode(StepNodes[j]);
           NewContext.ContextNode := Node;
           NewContext.ContextNode := Node;
-          if Predicate.EvalPredicate(NewContext, AEnvironment) then
-            NewStepNodes.Add(Node);
+          if not Predicate.EvalPredicate(NewContext, AEnvironment) then
+            StepNodes[j] := nil;
         end;
         end;
+        StepNodes.Pack;
       finally
       finally
         NewContext.Free;
         NewContext.Free;
-        StepNodes.Free;
-        StepNodes := NewStepNodes;
       end;
       end;
     end;
     end;
 
 
@@ -1706,6 +1712,32 @@ begin
   raise Exception.Create(Msg) at get_caller_addr(get_frame);
   raise Exception.Create(Msg) at get_caller_addr(get_frame);
 end;
 end;
 
 
+procedure TXPathScanner.ParsePredicates(var Dest: TXPathNodeArray);
+var
+  Buffer: array[0..15] of TXPathExprNode;
+  I: Integer;
+begin
+  I := 0;
+  // accumulate nodes in local buffer, then add all at once
+  // this reduces amount of ReallocMem's
+  while CurToken = tkLeftSquareBracket do
+  begin
+    NextToken;
+    Buffer[I] := ParseOrExpr;
+    Inc(I);
+    if I > High(Buffer) then
+    begin
+      AddNodes(Dest, Buffer);
+      I := 0;
+    end;
+    if CurToken <> tkRightSquareBracket then
+      Error(SParserExpectedRightSquareBracket);
+    NextToken;
+  end;
+  if I > 0 then
+    AddNodes(Dest, Slice(Buffer, I));
+end;
+
 procedure TXPathScanner.ParseStep(Dest: TStep);  // [4]
 procedure TXPathScanner.ParseStep(Dest: TStep);  // [4]
 
 
   procedure NeedBrackets;
   procedure NeedBrackets;
@@ -1837,16 +1869,7 @@ begin
     end
     end
     else
     else
       Exit;
       Exit;
-
-    // Parse predicates
-    while CurToken = tkLeftSquareBracket do
-    begin
-      NextToken;
-      Dest.Predicates.Add(ParseOrExpr);
-      if CurToken <> tkRightSquareBracket then
-        Error(SParserExpectedRightSquareBracket);
-      NextToken;
-    end;
+    ParsePredicates(Dest.Predicates);
   end;
   end;
 end;
 end;
 
 
@@ -1986,24 +2009,13 @@ begin
 end;
 end;
 
 
 function TXPathScanner.ParseFilterExpr: TXPathExprNode;  // [20]
 function TXPathScanner.ParseFilterExpr: TXPathExprNode;  // [20]
-var
-  IsFirst: Boolean;
 begin
 begin
   Result := ParsePrimaryExpr;
   Result := ParsePrimaryExpr;
   // Parse predicates
   // Parse predicates
-  IsFirst := True;
-  while CurToken = tkLeftSquareBracket do
+  if CurToken = tkLeftSquareBracket then
   begin
   begin
-    NextToken;
-    if IsFirst then
-    begin
-      Result := TXPathFilterNode.Create(Result);
-      IsFirst := False;
-    end;
-    TXPathFilterNode(Result).FPredicates.Add(ParseOrExpr);
-    if CurToken <> tkRightSquareBracket then
-      Error(SParserExpectedRightSquareBracket);
-    NextToken;
+    Result := TXPathFilterNode.Create(Result);
+    ParsePredicates(TXPathFilterNode(Result).FPredicates);
   end;
   end;
 end;
 end;
 
 
@@ -2151,8 +2163,8 @@ type
 constructor TXPathEnvironment.Create;
 constructor TXPathEnvironment.Create;
 begin
 begin
   inherited Create;
   inherited Create;
-  FFunctions := TList.Create;
-  FVariables := TList.Create;
+  FFunctions := TFPList.Create;
+  FVariables := TFPList.Create;
 
 
   // Add the functions of the XPath Core Function Library
   // Add the functions of the XPath Core Function Library