20.1.5.2 TMemoryStream對象的實現原理
TMemoryStream從TCustomMemoryStream對象直接繼承,因此可以享用TCustomMemoryStream的屬性和方法。前面講過,TCustomMemoryStream是用於內存中數據操作的抽象對象,它為MemoryStream對象的實現提供了框架,框架中的內容還要由具體MemoryStream對象去填充。TMemoryStream對象就是按動態內存管理的需要填充框架中的具體內容。下面介紹TMemoryStream對象的實現。
1. TMemoryStream屬性的實現
TMemoryStream在其protected部分增加了一個Capacity屬性,該屬性決定了MemoryStream所占動態內存的大小。TMemoryStream首先在private部分聲明了FCapacity變量作為存儲Capacity屬性值的數據域,然後在protected部分聲明了該屬性。在屬性聲明的讀控制部分簡單讀取FCapacity的值,在寫控制處調用了方法SetCapacity。該方法除了給FCapacity賦值外還執行了修改Capacity屬性所必需操作如狀態改變等。
下面是屬性的實現:
TMemoryStream = class(TCustomMemoryStream)
private
FCapacity: Longint;
procedure SetCapacity(NewCapacity: Longint);
protected
…
property Capacity: Longint read FCapacity write SetCapacity;
public
…
end;
寫控制方法SetCapacity的實現是這樣的:
procedure TMemoryStream.SetCapacity(NewCapacity: Longint);
begin
SetPointer(Realloc(NewCapacity), FSize);
FCapacity := NewCapacity;
end;
在SetCapacity 方法先是調用Realloc重新分配內存,然後用NewCapacity的值給FCapacity賦值。Realloc方法進行某些對象狀態的改變。
2. TMemoryStream對象方法的實現
⑴ Realloc方法
Realloc方法是TMemoryStream動態內存分配的核心,它的SetSize、SetCapacity等方法最終都是調用Realloc進行內存的分配和初始化工作的。它的實現如下:
const
MemoryDelta = $2000;
function TMemoryStream.Realloc(var NewCapacity: Longint): Pointer;
begin
if NewCapacity > 0 then
NewCapacity := (NewCapacity + (MemoryDelta - 1)) and not (MemoryDelta - 1);
Result := Memory;
if NewCapacity <> FCapacity then
begin
if NewCapacity = 0 then
begin
GlobalFreePtr(Memory);
Result := nil;
end else
begin
if Capacity = 0 then
Result := GlobalAllocPtr(HeapAllocFlags, NewCapacity)
else
Result := GlobalReallocPtr(Memory, NewCapacity, HeapAllocFlags);
if Result = nil then raise EStreamError.CreateRes(SMemoryStreamError);
end;
end;
end;
Realloc方法是以8K為單位分配動態內存的,方法中的第一句if語句就是執行該操作。如果傳入的NewCapacity參數值為0,則釋放流中的內存。Realloc方法用GLobal FreePtr函數釋放內存,用GlobalAllocPtr分配內存,用GlobalReallocPtr進行內存的重分配。如果原來的Capacity屬性值為0,則調用Globa|AllocPtr否則調用GlobalReallocPtr。最後如果Result為nil則觸發內存流錯的異常事件,否則返回指向分配的內存的指針。
⑵ Write方法
Write方法從內存流內部緩沖池的當前位置開始寫入二進制數據。其實現如下:
function TMemoryStream.Write(const Buffer; Count: Longint): Longint;
var
Pos: Longint;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
Pos := FPosition + Count;
if Pos > 0 then
begin
if Pos > FSize then
begin
if Pos > FCapacity then
SetCapacity(Pos);
FSize := Pos;
end;
System.Move(Buffer, Pointer(Longint(FMemory) + FPosition)^, Count);
FPosition := Pos;
Result := Count;
Exit;
end;
end;
Result := 0;
end;
Buffer中存儲要寫入流的二進制數據,如果要寫入的數據的字節超出了流的內存池的大小,則調用SetCapacity方法再分配內存,然後用內存復制函數將Buffer中的數據復制到FMemory中。接著移動位置指針,並返回寫入數據的字節數。分析這段程序可以知道,FCapacity的值和FSize的值是不同的。
⑶ Clear方法
Clear方法消除內存流中的數據,將Memory屬性置為nil,並將FSize和FPosition 的值設為0。其實現如下:
procedure TMemoryStream.Clear;
begin
SetCapacity(0);
FSize := 0;
FPosition := 0;
end;
⑷ LoadFromStream和LoadFromFile方法
LoadFromStream方法首先根據傳入的Stream的Size屬性值重新分配動態內存,然後調用Stream的ReadBuffer方法往FMemory中復制數據,結果Stream的全部內容在內存中有了一份完整拷貝。其實現如下:
procedure TMemoryStream.LoadFromStream(Stream: TStream);
var
Count: Longint;
begin
Stream.Position := 0;
Count := Stream.Size;
SetSize(Count);
if Count <> 0 then Stream.ReadBuffer(FMemory^, Count);
end;
LoadFromFile與LoadFromStream是一對方法。LoadFromFile首先創建了一個TFileStream對象,然後調用LoadFromStream方法,將FileStream文件流中的數據寫入MemoryStream中。