From 972b5181eb6501c54ce8194c71ce67469a0a5fe1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Van=20Canneyt?= <michael@freepascal.org>
Date: Wed, 8 Nov 2023 11:27:21 +0100
Subject: [PATCH] * Delta stream support

---
 rtl/objpas/classes/classes.inc  |  2 ++
 rtl/objpas/classes/classesh.inc | 10 +++++++++-
 rtl/objpas/classes/compon.inc   | 30 ++++++++++++++++++++++++++++++
 rtl/objpas/classes/reader.inc   | 29 +++++++++++++++++++++++++++++
 4 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/rtl/objpas/classes/classes.inc b/rtl/objpas/classes/classes.inc
index 98ed43a3aa..5cb5506c75 100644
--- a/rtl/objpas/classes/classes.inc
+++ b/rtl/objpas/classes/classes.inc
@@ -1462,6 +1462,8 @@ function DefaultInitHandler(Instance: TComponent; RootAncestor: TClass): Boolean
     try
 {$endif}
       result:=doinit(Instance.ClassType);
+      if Result then 
+        Instance.ReadDeltaState;
 {$ifdef FPC_HAS_FEATURE_THREADING}
     finally
       GlobalNameSpace.EndWrite;
diff --git a/rtl/objpas/classes/classesh.inc b/rtl/objpas/classes/classesh.inc
index 2d7dae7e6d..9be347524d 100644
--- a/rtl/objpas/classes/classesh.inc
+++ b/rtl/objpas/classes/classesh.inc
@@ -1808,6 +1808,8 @@ type
   TReadWriteStringPropertyEvent = procedure(Sender:TObject;
     const Instance: TPersistent; PropInfo: PPropInfo;
     var Content:string) of object;
+  TGetStreamProc = procedure (const S: TStream) of object;
+  TGetDeltaStreamsEvent = procedure (Sender: TObject; Proc: TGetStreamProc; var Handled: Boolean) of object;
 
 
   { TReader }
@@ -1888,6 +1890,7 @@ type
     procedure ReadListBegin;
     procedure ReadListEnd;
     function ReadRootComponent(ARoot: TComponent): TComponent;
+    function ReadComponentDeltaRes(Instance: TComponent; const DeltaCandidates: array of Ansistring; const Proc: TGetStreamProc): TComponent;
     function ReadVariant: Variant;
     procedure ReadSignature;
     function ReadString: RawBytestring;
@@ -2381,6 +2384,7 @@ type
   end;
 
   TBasicAction = class;
+  
 
   { TComponent }
 
@@ -2395,6 +2399,7 @@ type
     FVCLComObject: Pointer;
     FComponentState: TComponentState;
     FDObservers : TObservers;
+    FOnGetDeltaStreams: TGetDeltaStreamsEvent;
     function GetComObject: IUnknown;
     function GetComponent(AIndex: Integer): TComponent;
     function GetComponentCount: Integer;
@@ -2411,6 +2416,9 @@ type
     procedure WriteTop(Writer: TWriter);
   protected
     FComponentStyle: TComponentStyle;
+    procedure GetDeltaStreams(aProc: TGetStreamProc); virtual;
+    procedure ReadDeltaStream(const S: TStream);
+    procedure ReadDeltaState; virtual;
     procedure ChangeName(const NewName: TComponentName);
     procedure DefineProperties(Filer: TFiler); override;
     procedure GetChildren(Proc: TGetChildProc; Root: TComponent); dynamic;
@@ -2488,6 +2496,7 @@ type
     property Owner: TComponent read FOwner;
     property VCLComObject: Pointer read FVCLComObject write FVCLComObject;
     Property Observers : TObservers Read GetObservers;
+    property OnGetDeltaStreams: TGetDeltaStreamsEvent read FOnGetDeltaStreams write FOnGetDeltaStreams;    
   published
     property Name: TComponentName read FName write SetName stored False;
     property Tag: PtrInt read FTag write FTag default 0;
@@ -2811,7 +2820,6 @@ type
   TIntToIdent = function(Int: Longint; var Ident: string): Boolean;
   TFindGlobalComponent = function(const Name: string): TComponent;
   TInitComponentHandler = function(Instance: TComponent; RootAncestor : TClass): boolean;
-  TGetStreamProc = procedure (const S: TStream) of object;
 
 var
   MainThreadID: TThreadID;
diff --git a/rtl/objpas/classes/compon.inc b/rtl/objpas/classes/compon.inc
index f91d08d877..ce7bca6e2d 100644
--- a/rtl/objpas/classes/compon.inc
+++ b/rtl/objpas/classes/compon.inc
@@ -730,3 +730,33 @@ begin
   else
     Result := E_NOTIMPL;
 end;
+
+{ Delta stream support }
+
+
+procedure TComponent.GetDeltaStreams(aProc: TGetStreamProc); 
+
+begin
+  // To be implemented by descendants
+end;
+
+procedure TComponent.ReadDeltaStream(const S: TStream);
+begin
+  S.ReadComponent(Self);
+end;
+
+procedure TComponent.ReadDeltaState;
+
+var
+  Done : boolean;
+  
+begin
+  if (csDesigning in ComponentState) then
+    exit;
+  Done:=False;
+  if Assigned(FOnGetDeltaStreams) then
+    FOnGetDeltaStreams(Self,@ReadDeltaStream,Done);  
+  if not Done then
+    GetDeltaStreams(@ReadDeltaStream);
+end;
+
diff --git a/rtl/objpas/classes/reader.inc b/rtl/objpas/classes/reader.inc
index 9d4448ca47..016e3d02b2 100644
--- a/rtl/objpas/classes/reader.inc
+++ b/rtl/objpas/classes/reader.inc
@@ -1889,7 +1889,36 @@ begin
       raise EClassNotFound.CreateFmt(SNoFieldOfClassIn, [AClassName, Root.ClassName]);
 end;
 
+function TReader.ReadComponentDeltaRes(Instance: TComponent; const DeltaCandidates: array of Ansistring; const Proc: TGetStreamProc): TComponent;
 
+var
+  ResHandle: THandle;
+  RootName, Delta, DeltaName: AnsiString;
+  S: TStream;
+  
+begin
+  if (Instance=nil) or not Assigned(Proc) then
+    Raise EArgumentNilException.Create(SArgumentNil);
+  Result:=Instance;
+  RootName:=Instance.ClassName;
+  for Delta in DeltaCandidates do
+     begin
+     DeltaName:=RootName+'_'+Delta;
+     // No module support yet
+     ResHandle:=FindResource(Nilhandle,PAnsiChar(DeltaName), PAnsiChar(RT_RCDATA));
+     if ResHandle<>NilHandle then
+       Break;
+     end;
+  if ResHandle=NilHandle then 
+    exit;     
+  S:=TResourceStream.Create(NilHandle,DeltaName, PChar(RT_RCDATA));
+  try
+    Proc(S);
+  finally
+    S.Free;
+  end;
+end;
+    
 { TAbstractObjectReader }
 
 procedure TAbstractObjectReader.FlushBuffer;